]>
Commit | Line | Data |
---|---|---|
cc52de43 | 1 | #!/usr/bin/env python3 |
54007a45 | 2 | |
733d8e8f | 3 | import os |
ff88a05c | 4 | import platform |
733d8e8f | 5 | import sys |
0e5927ee | 6 | |
c1714454 | 7 | from PyInstaller.__main__ import run as run_pyinstaller |
733d8e8f | 8 | |
52009769 | 9 | OS_NAME, MACHINE = sys.platform, platform.machine() |
10 | if MACHINE in ('x86_64', 'amd64'): | |
11 | MACHINE = '' | |
12 | elif 'i' in MACHINE and '86' in MACHINE: | |
13 | MACHINE = 'x86' | |
e38df8f9 | 14 | |
0e5927ee | 15 | |
733d8e8f | 16 | def main(): |
17 | opts = parse_options() | |
c1714454 | 18 | version = read_version('yt_dlp/version.py') |
19 | ||
20 | onedir = '--onedir' in opts or '-D' in opts | |
21 | if not onedir and '-F' not in opts and '--onefile' not in opts: | |
22 | opts.append('--onefile') | |
5d535b4a | 23 | |
b5899f4f | 24 | name, final_file = exe(onedir) |
52009769 | 25 | print(f'Building yt-dlp v{version} for {OS_NAME} {platform.machine()} with options {opts}') |
49a57e70 | 26 | print('Remember to update the version using "devscripts/update-version.py"') |
733d8e8f | 27 | if not os.path.isfile('yt_dlp/extractor/lazy_extractors.py'): |
28 | print('WARNING: Building without lazy_extractors. Run ' | |
49a57e70 | 29 | '"devscripts/make_lazy_extractors.py" to build lazy extractors', file=sys.stderr) |
733d8e8f | 30 | print(f'Destination: {final_file}\n') |
e38df8f9 | 31 | |
733d8e8f | 32 | opts = [ |
e1e1ea54 | 33 | f'--name={name}', |
733d8e8f | 34 | '--icon=devscripts/logo.ico', |
35 | '--upx-exclude=vcruntime140.dll', | |
36 | '--noconfirm', | |
c1714454 | 37 | # NB: Modules that are only imported dynamically must be added here. |
38 | # --collect-submodules may not work correctly if user has a yt-dlp installed via PIP | |
39 | '--hidden-import=yt_dlp.compat._legacy', | |
e75bb0d6 | 40 | *dependency_options(), |
733d8e8f | 41 | *opts, |
42 | 'yt_dlp/__main__.py', | |
43 | ] | |
733d8e8f | 44 | |
c1714454 | 45 | print(f'Running PyInstaller with {opts}') |
46 | run_pyinstaller(opts) | |
733d8e8f | 47 | set_version_info(final_file, version) |
48 | ||
49 | ||
50 | def parse_options(): | |
962ffcf8 | 51 | # Compatibility with older arguments |
733d8e8f | 52 | opts = sys.argv[1:] |
53 | if opts[0:1] in (['32'], ['64']): | |
52009769 | 54 | ARCH = platform.architecture()[0][:2] |
733d8e8f | 55 | if ARCH != opts[0]: |
56 | raise Exception(f'{opts[0]}bit executable cannot be built on a {ARCH}bit system') | |
57 | opts = opts[1:] | |
c1714454 | 58 | return opts |
e38df8f9 | 59 | |
733d8e8f | 60 | |
c1714454 | 61 | # Get the version from yt_dlp/version.py without importing the package |
62 | def read_version(fname): | |
63 | with open(fname, encoding='utf-8') as f: | |
64 | exec(compile(f.read(), fname, 'exec')) | |
65 | return locals()['__version__'] | |
733d8e8f | 66 | |
67 | ||
b5899f4f | 68 | def exe(onedir): |
69 | """@returns (name, path)""" | |
70 | name = '_'.join(filter(None, ( | |
71 | 'yt-dlp', | |
e4afcfde | 72 | {'win32': '', 'darwin': 'macos'}.get(OS_NAME, OS_NAME), |
52009769 | 73 | MACHINE |
b5899f4f | 74 | ))) |
75 | return name, ''.join(filter(None, ( | |
76 | 'dist/', | |
77 | onedir and f'{name}/', | |
78 | name, | |
79 | OS_NAME == 'win32' and '.exe' | |
80 | ))) | |
81 | ||
82 | ||
733d8e8f | 83 | def version_to_list(version): |
84 | version_list = version.split('.') | |
85 | return list(map(int, version_list)) + [0] * (4 - len(version_list)) | |
86 | ||
87 | ||
e75bb0d6 | 88 | def dependency_options(): |
c1714454 | 89 | # Due to the current implementation, these are auto-detected, but explicitly add them just in case |
90 | dependencies = [pycryptodome_module(), 'mutagen', 'brotli', 'certifi', 'websockets'] | |
91 | excluded_modules = ['test', 'ytdlp_plugins', 'youtube_dl', 'youtube_dlc'] | |
733d8e8f | 92 | |
e75bb0d6 | 93 | yield from (f'--hidden-import={module}' for module in dependencies) |
c1714454 | 94 | yield '--collect-submodules=websockets' |
733d8e8f | 95 | yield from (f'--exclude-module={module}' for module in excluded_modules) |
e38df8f9 | 96 | |
49e7e9c3 | 97 | |
98 | def pycryptodome_module(): | |
99 | try: | |
100 | import Cryptodome # noqa: F401 | |
101 | except ImportError: | |
102 | try: | |
103 | import Crypto # noqa: F401 | |
104 | print('WARNING: Using Crypto since Cryptodome is not available. ' | |
105 | 'Install with: pip install pycryptodomex', file=sys.stderr) | |
106 | return 'Crypto' | |
107 | except ImportError: | |
108 | pass | |
109 | return 'Cryptodome' | |
110 | ||
111 | ||
733d8e8f | 112 | def set_version_info(exe, version): |
1890fc63 | 113 | if OS_NAME == 'win32': |
733d8e8f | 114 | windows_set_version(exe, version) |
115 | ||
116 | ||
117 | def windows_set_version(exe, version): | |
b5899f4f | 118 | from PyInstaller.utils.win32.versioninfo import ( |
119 | FixedFileInfo, | |
120 | SetVersion, | |
121 | StringFileInfo, | |
122 | StringStruct, | |
123 | StringTable, | |
124 | VarFileInfo, | |
125 | VarStruct, | |
126 | VSVersionInfo, | |
127 | ) | |
128 | ||
733d8e8f | 129 | version_list = version_to_list(version) |
52009769 | 130 | suffix = MACHINE and f'_{MACHINE}' |
733d8e8f | 131 | SetVersion(exe, VSVersionInfo( |
132 | ffi=FixedFileInfo( | |
133 | filevers=version_list, | |
134 | prodvers=version_list, | |
135 | mask=0x3F, | |
136 | flags=0x0, | |
137 | OS=0x4, | |
138 | fileType=0x1, | |
139 | subtype=0x0, | |
140 | date=(0, 0), | |
141 | ), | |
142 | kids=[ | |
143 | StringFileInfo([StringTable('040904B0', [ | |
52009769 | 144 | StringStruct('Comments', 'yt-dlp%s Command Line Interface' % suffix), |
733d8e8f | 145 | StringStruct('CompanyName', 'https://github.com/yt-dlp'), |
52009769 | 146 | StringStruct('FileDescription', 'yt-dlp%s' % (MACHINE and f' ({MACHINE})')), |
733d8e8f | 147 | StringStruct('FileVersion', version), |
148 | StringStruct('InternalName', f'yt-dlp{suffix}'), | |
149 | StringStruct('LegalCopyright', 'pukkandan.ytdlp@gmail.com | UNLICENSE'), | |
150 | StringStruct('OriginalFilename', f'yt-dlp{suffix}.exe'), | |
151 | StringStruct('ProductName', f'yt-dlp{suffix}'), | |
152 | StringStruct( | |
153 | 'ProductVersion', f'{version}{suffix} on Python {platform.python_version()}'), | |
154 | ])]), VarFileInfo([VarStruct('Translation', [0, 1200])]) | |
155 | ] | |
156 | )) | |
e36d50c5 | 157 | |
0e5927ee | 158 | |
733d8e8f | 159 | if __name__ == '__main__': |
160 | main() |