mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2025-06-02 19:55:50 -05:00

https://github.com/yt-dlp/yt-dlp/tree/master/yt_dlp/extractor/youtube/pot/README.md Authored by: coletdjnz
156 lines
4.7 KiB
Python
156 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
import abc
|
|
import enum
|
|
import functools
|
|
|
|
from yt_dlp.extractor.common import InfoExtractor
|
|
from yt_dlp.utils import NO_DEFAULT, bug_reports_message, classproperty, traverse_obj
|
|
from yt_dlp.version import __version__
|
|
|
|
# xxx: these could be generalized outside YoutubeIE eventually
|
|
|
|
|
|
class IEContentProviderLogger(abc.ABC):
|
|
|
|
class LogLevel(enum.IntEnum):
|
|
TRACE = 0
|
|
DEBUG = 10
|
|
INFO = 20
|
|
WARNING = 30
|
|
ERROR = 40
|
|
|
|
@classmethod
|
|
def _missing_(cls, value):
|
|
if isinstance(value, str):
|
|
value = value.upper()
|
|
if value in dir(cls):
|
|
return cls[value]
|
|
|
|
return cls.INFO
|
|
|
|
log_level = LogLevel.INFO
|
|
|
|
@abc.abstractmethod
|
|
def trace(self, message: str):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def debug(self, message: str):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def info(self, message: str):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def warning(self, message: str, *, once=False):
|
|
pass
|
|
|
|
@abc.abstractmethod
|
|
def error(self, message: str):
|
|
pass
|
|
|
|
|
|
class IEContentProviderError(Exception):
|
|
def __init__(self, msg=None, expected=False):
|
|
super().__init__(msg)
|
|
self.expected = expected
|
|
|
|
|
|
class IEContentProvider(abc.ABC):
|
|
PROVIDER_VERSION: str = '0.0.0'
|
|
BUG_REPORT_LOCATION: str = '(developer has not provided a bug report location)'
|
|
|
|
def __init__(
|
|
self,
|
|
ie: InfoExtractor,
|
|
logger: IEContentProviderLogger,
|
|
settings: dict[str, list[str]], *_, **__,
|
|
):
|
|
self.ie = ie
|
|
self.settings = settings or {}
|
|
self.logger = logger
|
|
super().__init__()
|
|
|
|
@classmethod
|
|
def __init_subclass__(cls, *, suffix=None, **kwargs):
|
|
if suffix:
|
|
cls._PROVIDER_KEY_SUFFIX = suffix
|
|
return super().__init_subclass__(**kwargs)
|
|
|
|
@classproperty
|
|
def PROVIDER_NAME(cls) -> str:
|
|
return cls.__name__[:-len(cls._PROVIDER_KEY_SUFFIX)]
|
|
|
|
@classproperty
|
|
def BUG_REPORT_MESSAGE(cls):
|
|
return f'please report this issue to the provider developer at {cls.BUG_REPORT_LOCATION} .'
|
|
|
|
@classproperty
|
|
def PROVIDER_KEY(cls) -> str:
|
|
assert hasattr(cls, '_PROVIDER_KEY_SUFFIX'), 'Content Provider implementation must define a suffix for the provider key'
|
|
assert cls.__name__.endswith(cls._PROVIDER_KEY_SUFFIX), f'PoTokenProvider class names must end with "{cls._PROVIDER_KEY_SUFFIX}"'
|
|
return cls.__name__[:-len(cls._PROVIDER_KEY_SUFFIX)]
|
|
|
|
@abc.abstractmethod
|
|
def is_available(self) -> bool:
|
|
"""
|
|
Check if the provider is available (e.g. all required dependencies are available)
|
|
This is used to determine if the provider should be used and to provide debug information.
|
|
|
|
IMPORTANT: This method should not make any network requests or perform any expensive operations.
|
|
It is called multiple times.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def close(self): # noqa: B027
|
|
pass
|
|
|
|
def _configuration_arg(self, key, default=NO_DEFAULT, *, casesense=False):
|
|
"""
|
|
@returns A list of values for the setting given by "key"
|
|
or "default" if no such key is present
|
|
@param default The default value to return when the key is not present (default: [])
|
|
@param casesense When false, the values are converted to lower case
|
|
"""
|
|
val = traverse_obj(self.settings, key)
|
|
if val is None:
|
|
return [] if default is NO_DEFAULT else default
|
|
return list(val) if casesense else [x.lower() for x in val]
|
|
|
|
|
|
class BuiltinIEContentProvider(IEContentProvider, abc.ABC):
|
|
PROVIDER_VERSION = __version__
|
|
BUG_REPORT_MESSAGE = bug_reports_message(before='')
|
|
|
|
|
|
def register_provider_generic(
|
|
provider,
|
|
base_class,
|
|
registry,
|
|
):
|
|
"""Generic function to register a provider class"""
|
|
assert issubclass(provider, base_class), f'{provider} must be a subclass of {base_class.__name__}'
|
|
assert provider.PROVIDER_KEY not in registry, f'{base_class.__name__} {provider.PROVIDER_KEY} already registered'
|
|
registry[provider.PROVIDER_KEY] = provider
|
|
return provider
|
|
|
|
|
|
def register_preference_generic(
|
|
base_class,
|
|
registry,
|
|
*providers,
|
|
):
|
|
"""Generic function to register a preference for a provider"""
|
|
assert all(issubclass(provider, base_class) for provider in providers)
|
|
|
|
def outer(preference):
|
|
@functools.wraps(preference)
|
|
def inner(provider, *args, **kwargs):
|
|
if not providers or isinstance(provider, providers):
|
|
return preference(provider, *args, **kwargs)
|
|
return 0
|
|
registry.add(inner)
|
|
return preference
|
|
return outer
|