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