3 # Allow direct execution
8 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
11 from inspect
import getsource
13 from devscripts
.utils
import get_filename_args
, read_file
, write_file
16 STATIC_CLASS_PROPERTIES
= [
17 'IE_NAME', '_ENABLED', '_VALID_URL', # Used for URL matching
18 '_WORKING', 'IE_DESC', '_NETRC_MACHINE', 'SEARCH_KEY', # Used for --extractor-descriptions
19 'age_limit', # Used for --age-limit (evaluated)
20 '_RETURN_TYPE', # Accessed in CLI only with instance (evaluated)
23 'ie_key', 'suitable', '_match_valid_url', # Used for URL matching
24 'working', 'get_temp_id', '_match_id', # Accessed just before instance creation
25 'description', # Used for --extractor-descriptions
26 'is_suitable', # Used for --age-limit
27 'supports_login', 'is_single_video', # Accessed in CLI only with instance
30 class {name}({bases}):
33 MODULE_TEMPLATE
= read_file('devscripts/lazy_load_template.py')
37 lazy_extractors_filename
= get_filename_args(default_outfile
='yt_dlp/extractor/lazy_extractors.py')
38 if os
.path
.exists(lazy_extractors_filename
):
39 os
.remove(lazy_extractors_filename
)
41 _ALL_CLASSES
= get_all_ies() # Must be before import
44 from yt_dlp
.extractor
.common
import InfoExtractor
, SearchInfoExtractor
47 _ALL_CLASSES
= [cls
for cls
in _ALL_CLASSES
if not cls
.__module
__.startswith(f
'{yt_dlp.plugins.PACKAGE_NAME}.')]
49 DummyInfoExtractor
= type('InfoExtractor', (InfoExtractor
,), {'IE_NAME': NO_ATTR
})
50 module_src
= '\n'.join((
53 *extra_ie_code(DummyInfoExtractor
),
54 '\nclass LazyLoadSearchExtractor(LazyLoadExtractor):\n pass\n',
55 *build_ies(_ALL_CLASSES
, (InfoExtractor
, SearchInfoExtractor
), DummyInfoExtractor
),
58 write_file(lazy_extractors_filename
, f
'{module_src}\n')
62 PLUGINS_DIRNAME
= 'ytdlp_plugins'
63 BLOCKED_DIRNAME
= f
'{PLUGINS_DIRNAME}_blocked'
64 if os
.path
.exists(PLUGINS_DIRNAME
):
65 # os.rename cannot be used, e.g. in Docker. See https://github.com/yt-dlp/yt-dlp/pull/4958
66 shutil
.move(PLUGINS_DIRNAME
, BLOCKED_DIRNAME
)
68 from yt_dlp
.extractor
.extractors
import _ALL_CLASSES
70 if os
.path
.exists(BLOCKED_DIRNAME
):
71 shutil
.move(BLOCKED_DIRNAME
, PLUGINS_DIRNAME
)
75 def extra_ie_code(ie
, base
=None):
76 for var
in STATIC_CLASS_PROPERTIES
:
77 val
= getattr(ie
, var
)
78 if val
!= (getattr(base
, var
) if base
else NO_ATTR
):
79 yield f
' {var} = {val!r}'
82 for name
in CLASS_METHODS
:
84 if not base
or f
.__func
__ != getattr(base
, name
).__func
__:
88 def build_ies(ies
, bases
, attr_base
):
90 for ie
in sort_ies(ies
, bases
):
91 yield build_lazy_ie(ie
, ie
.__name
__, attr_base
)
93 names
.append(ie
.__name
__)
95 yield f
'\n_ALL_CLASSES = [{", ".join(names)}]'
98 def sort_ies(ies
, ignored_bases
):
99 """find the correct sorting and add the required base classes so that subclasses can be correctly created"""
100 classes
, returned_classes
= ies
[:-1], set()
101 assert ies
[-1].__name
__ == 'GenericIE', 'Last IE must be GenericIE'
104 bases
= set(c
.__bases
__) - {object, *ignored_bases
}
106 for b
in sorted(bases
, key
=lambda x
: x
.__name
__):
107 if b
not in classes
and b
not in returned_classes
:
108 assert b
.__name
__ != 'GenericIE', 'Cannot inherit from GenericIE'
113 if bases
<= returned_classes
:
115 returned_classes
.add(c
)
121 def build_lazy_ie(ie
, name
, attr_base
):
123 'InfoExtractor': 'LazyLoadExtractor',
124 'SearchInfoExtractor': 'LazyLoadSearchExtractor',
125 }.get(base
.__name
__, base
.__name
__) for base
in ie
.__bases
__)
127 s
= IE_TEMPLATE
.format(name
=name
, module
=ie
.__module
__, bases
=bases
)
128 return s
+ '\n'.join(extra_ie_code(ie
, attr_base
))
131 if __name__
== '__main__':