]> jfr.im git - yt-dlp.git/blob - pyinst.py
[docs] Improve embedding docs and other minor fixes
[yt-dlp.git] / pyinst.py
1 #!/usr/bin/env python3
2 import os
3 import platform
4 import sys
5
6 from PyInstaller.utils.hooks import collect_submodules
7
8 OS_NAME = platform.system()
9 if OS_NAME == 'Windows':
10 from PyInstaller.utils.win32.versioninfo import (
11 FixedFileInfo,
12 SetVersion,
13 StringFileInfo,
14 StringStruct,
15 StringTable,
16 VarFileInfo,
17 VarStruct,
18 VSVersionInfo,
19 )
20 elif OS_NAME == 'Darwin':
21 pass
22 else:
23 raise Exception('{OS_NAME} is not supported')
24
25 ARCH = platform.architecture()[0][:2]
26
27
28 def main():
29 opts = parse_options()
30 version = read_version()
31
32 suffix = '_macos' if OS_NAME == 'Darwin' else '_x86' if ARCH == '32' else ''
33 final_file = 'dist/%syt-dlp%s%s' % (
34 'yt-dlp/' if '--onedir' in opts else '', suffix, '.exe' if OS_NAME == 'Windows' else '')
35
36 print(f'Building yt-dlp v{version} {ARCH}bit for {OS_NAME} with options {opts}')
37 print('Remember to update the version using "devscripts/update-version.py"')
38 if not os.path.isfile('yt_dlp/extractor/lazy_extractors.py'):
39 print('WARNING: Building without lazy_extractors. Run '
40 '"devscripts/make_lazy_extractors.py" to build lazy extractors', file=sys.stderr)
41 print(f'Destination: {final_file}\n')
42
43 opts = [
44 f'--name=yt-dlp{suffix}',
45 '--icon=devscripts/logo.ico',
46 '--upx-exclude=vcruntime140.dll',
47 '--noconfirm',
48 *dependency_options(),
49 *opts,
50 'yt_dlp/__main__.py',
51 ]
52 print(f'Running PyInstaller with {opts}')
53
54 import PyInstaller.__main__
55
56 PyInstaller.__main__.run(opts)
57
58 set_version_info(final_file, version)
59
60
61 def parse_options():
62 # Compatability with older arguments
63 opts = sys.argv[1:]
64 if opts[0:1] in (['32'], ['64']):
65 if ARCH != opts[0]:
66 raise Exception(f'{opts[0]}bit executable cannot be built on a {ARCH}bit system')
67 opts = opts[1:]
68 return opts or ['--onefile']
69
70
71 def read_version():
72 exec(compile(open('yt_dlp/version.py').read(), 'yt_dlp/version.py', 'exec'))
73 return locals()['__version__']
74
75
76 def version_to_list(version):
77 version_list = version.split('.')
78 return list(map(int, version_list)) + [0] * (4 - len(version_list))
79
80
81 def dependency_options():
82 dependencies = [pycryptodome_module(), 'mutagen', 'brotli', 'certifi'] + collect_submodules('websockets')
83 excluded_modules = ['test', 'ytdlp_plugins', 'youtube-dl', 'youtube-dlc']
84
85 yield from (f'--hidden-import={module}' for module in dependencies)
86 yield from (f'--exclude-module={module}' for module in excluded_modules)
87
88
89 def pycryptodome_module():
90 try:
91 import Cryptodome # noqa: F401
92 except ImportError:
93 try:
94 import Crypto # noqa: F401
95 print('WARNING: Using Crypto since Cryptodome is not available. '
96 'Install with: pip install pycryptodomex', file=sys.stderr)
97 return 'Crypto'
98 except ImportError:
99 pass
100 return 'Cryptodome'
101
102
103 def set_version_info(exe, version):
104 if OS_NAME == 'Windows':
105 windows_set_version(exe, version)
106
107
108 def windows_set_version(exe, version):
109 version_list = version_to_list(version)
110 suffix = '_x86' if ARCH == '32' else ''
111 SetVersion(exe, VSVersionInfo(
112 ffi=FixedFileInfo(
113 filevers=version_list,
114 prodvers=version_list,
115 mask=0x3F,
116 flags=0x0,
117 OS=0x4,
118 fileType=0x1,
119 subtype=0x0,
120 date=(0, 0),
121 ),
122 kids=[
123 StringFileInfo([StringTable('040904B0', [
124 StringStruct('Comments', 'yt-dlp%s Command Line Interface.' % suffix),
125 StringStruct('CompanyName', 'https://github.com/yt-dlp'),
126 StringStruct('FileDescription', 'yt-dlp%s' % (' (32 Bit)' if ARCH == '32' else '')),
127 StringStruct('FileVersion', version),
128 StringStruct('InternalName', f'yt-dlp{suffix}'),
129 StringStruct('LegalCopyright', 'pukkandan.ytdlp@gmail.com | UNLICENSE'),
130 StringStruct('OriginalFilename', f'yt-dlp{suffix}.exe'),
131 StringStruct('ProductName', f'yt-dlp{suffix}'),
132 StringStruct(
133 'ProductVersion', f'{version}{suffix} on Python {platform.python_version()}'),
134 ])]), VarFileInfo([VarStruct('Translation', [0, 1200])])
135 ]
136 ))
137
138
139 if __name__ == '__main__':
140 main()