Skip to content

Commit

Permalink
version: 4.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexELEC committed Feb 12, 2024
1 parent 3d7e122 commit 5c83783
Show file tree
Hide file tree
Showing 27 changed files with 1,529 additions and 1,158 deletions.
Binary file modified core.so
Binary file not shown.
4 changes: 2 additions & 2 deletions libs/streamlink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
"""

__version__ = "6.5.0"
__version__ = "6.5.1"
__title__ = "streamlink"
__license__ = "Simplified BSD"
__author__ = "Streamlink"
__copyright__ = "Copyright 2023 Streamlink"
__copyright__ = "Copyright 2024 Streamlink"
__credits__ = ["https://github.com/streamlink/streamlink/blob/master/AUTHORS"]

from streamlink.api import streams
Expand Down
55 changes: 54 additions & 1 deletion libs/streamlink/compat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import importlib
import inspect
import os
import sys
import warnings
from typing import Any, Callable, Dict, Optional, Tuple

from streamlink.exceptions import StreamlinkDeprecationWarning


# compatibility import of charset_normalizer/chardet via requests<3.0
Expand All @@ -16,8 +22,55 @@
detect_encoding = charset_normalizer.detect


def deprecated(items: Dict[str, Tuple[Optional[str], Any, Any]]) -> None:
"""
Deprecate specific module attributes.
This removes the deprecated attributes from the module's global context,
adds/overrides the module's :func:`__getattr__` function, and emits a :class:`StreamlinkDeprecationWarning`
if one of the deprecated attributes is accessed.
:param items: A mapping of module attribute names to tuples which contain the following optional items:
1. an import path string (for looking up an external object while accessing the attribute)
2. a direct return object (if no import path was set)
3. a custom warning message
"""

mod_globals = inspect.stack()[1].frame.f_globals
orig_getattr: Optional[Callable[[str], Any]] = mod_globals.get("__getattr__", None)

def __getattr__(name: str) -> Any:
if name in items:
origin = f"{mod_globals['__spec__'].name}.{name}"
path, obj, msg = items[name]
warnings.warn(
msg or f"'{origin}' has been deprecated",
StreamlinkDeprecationWarning,
stacklevel=2,
)
if path:
*_path, name = path.split(".")
imported = importlib.import_module(".".join(_path))
obj = getattr(imported, name, None)

return obj

if orig_getattr is not None:
return orig_getattr(name)

raise AttributeError

mod_globals["__getattr__"] = __getattr__

# delete the deprecated module attributes and the imported `deprecated` function
for item in items.keys() | [deprecated.__name__]:
if item in mod_globals:
del mod_globals[item]


__all__ = [
"deprecated",
"detect_encoding",
"is_darwin",
"is_win32",
"detect_encoding",
]
154 changes: 130 additions & 24 deletions libs/streamlink/options.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
from typing import Any, Callable, ClassVar, Dict, Iterator, Mapping, Optional, Sequence, Union
from typing import (
Any,
Callable,
ClassVar,
Dict,
Iterable,
Iterator,
Literal,
Mapping,
Optional,
Sequence,
Tuple,
TypeVar,
Union,
)


class Options:
Expand Down Expand Up @@ -100,47 +114,85 @@ def __iter__(self):
return self.options.__iter__()


class Argument:
"""
Accepts most of the parameters accepted by :meth:`ArgumentParser.add_argument()`,
except that ``requires`` is a special case which is only enforced if the plugin is in use.
In addition, the ``name`` parameter is the name relative to the plugin name, but can be overridden by ``argument_name``.
_TChoices = TypeVar("_TChoices", bound=Iterable)

Should not be called directly, see the :func:`pluginargument <streamlink.plugin.pluginargument>` decorator.
"""

class Argument:
# noinspection PyShadowingBuiltins
def __init__(
self,
name: str,
# `ArgumentParser.add_argument()` keywords
action: Optional[str] = None,
nargs: Optional[Union[int, Literal["?", "*", "+"]]] = None,
const: Any = None,
default: Any = None,
type: Optional[Callable[[Any], Union[_TChoices, Any]]] = None, # noqa: A002
choices: Optional[_TChoices] = None,
required: bool = False,
help: Optional[str] = None, # noqa: A002
metavar: Optional[Union[str, Sequence[str]]] = None,
dest: Optional[str] = None,
# additional `Argument()` keywords
requires: Optional[Union[str, Sequence[str]]] = None,
prompt: Optional[str] = None,
sensitive: bool = False,
argument_name: Optional[str] = None,
dest: Optional[str] = None,
**options,
):
"""
Accepts most of the parameters accepted by :meth:`argparse.ArgumentParser.add_argument()`, except that
- ``name`` is the name relative to the plugin name (can be overridden by ``argument_name``)
and that only one argument name is supported
- ``action`` must be a string and can't be a custom :class:`Action <argparse.Action>`
- ``required`` is a special case which is only enforced if the plugin is in use
This class should not be instantiated directly.
See the :func:`pluginargument <streamlink.plugin.pluginargument>` decorator for adding custom plugin arguments.
:param name: Argument name, without leading ``--`` or plugin name prefixes, e.g. ``"username"``, ``"password"``, etc.
:param required: Whether the argument is required for the plugin
:param requires: List of arguments which this argument requires, eg ``["password"]``
:param prompt: If the argument is required and not set, this prompt message will be shown instead
:param sensitive: Whether the argument is sensitive (passwords, etc.) and should be masked
:param argument_name: Custom CLI argument name without plugin name prefix
:param dest: Custom plugin option name
:param options: Arguments passed to :meth:`ArgumentParser.add_argument()`, excluding ``requires`` and ``dest``
:param action: See :meth:`ArgumentParser.add_argument()`
:param nargs: See :meth:`ArgumentParser.add_argument()`
:param const: See :meth:`ArgumentParser.add_argument()`
:param default: See :meth:`ArgumentParser.add_argument()`
:param type: See :meth:`ArgumentParser.add_argument()`
:param choices: See :meth:`ArgumentParser.add_argument()`
:param required: See :meth:`ArgumentParser.add_argument()`
:param help: See :meth:`ArgumentParser.add_argument()`
:param metavar: See :meth:`ArgumentParser.add_argument()`
:param dest: See :meth:`ArgumentParser.add_argument()`
:param requires: List of other arguments which this argument requires, e.g. ``["password"]``
:param prompt: If the argument is required and not set, then this prompt message will be shown instead
:param sensitive: Whether the argument is sensitive and should be masked (passwords, etc.)
:param argument_name: Custom CLI argument name without the automatically added plugin name prefix
"""

self.required = required
self.name = name
self.options = options
self._argument_name = self._normalize_name(argument_name) if argument_name else None

self.action = action
self.nargs = nargs
self.const = const
self.type = type
self.choices: Optional[Tuple[Any, ...]] = tuple(choices) if choices else None
self.required = required
self.help = help
self.metavar: Optional[Union[str, Tuple[str, ...]]] = (
tuple(metavar)
if metavar is not None and not isinstance(metavar, str)
else metavar
)

self._default = default
self._dest = self._normalize_dest(dest) if dest else None
requires = requires or []
self.requires = list(requires) if isinstance(requires, (list, tuple)) else [requires]

self.requires: Tuple[str, ...] = (
tuple(requires)
if requires is not None and not isinstance(requires, str)
else ((requires,) if requires is not None else ())
)
self.prompt = prompt
self.sensitive = sensitive
self._default = options.get("default")
self._argument_name = self._normalize_name(argument_name) if argument_name else None

@staticmethod
def _normalize_name(name: str) -> str:
Expand All @@ -167,6 +219,50 @@ def dest(self):
def default(self): # read-only
return self._default

# `ArgumentParser.add_argument()` keywords, except `name_or_flags` and `required`
_ARGPARSE_ARGUMENT_KEYWORDS: ClassVar[Mapping[str, str]] = {
"action": "action",
"nargs": "nargs",
"const": "const",
"default": "default",
"type": "type",
"choices": "choices",
"help": "help",
"metavar": "metavar",
"dest": "_dest",
}

@property
def options(self) -> Mapping[str, Any]:
return {
name: getattr(self, attr)
for name, attr in self._ARGPARSE_ARGUMENT_KEYWORDS.items()
# don't pass keywords with ``None`` values to ``ArgumentParser.add_argument()``
if getattr(self, attr) is not None
}

def __hash__(self):
return hash((
self.name,
self.action,
self.nargs,
self.const,
self.type,
self.choices,
self.required,
self.help,
self.metavar,
self._default,
self._dest,
self.requires,
self.prompt,
self.sensitive,
self._argument_name,
))

def __eq__(self, other):
return isinstance(other, self.__class__) and hash(self) == hash(other)


class Arguments:
"""
Expand All @@ -183,6 +279,12 @@ def __iter__(self) -> Iterator[Argument]:
# iterate in reverse order due to add() being called by multiple pluginargument decorators in reverse order
return reversed(self.arguments.values())

def __hash__(self):
return hash(tuple(self.arguments.items()))

def __eq__(self, other):
return isinstance(other, self.__class__) and hash(self) == hash(other)

def add(self, argument: Argument) -> None:
self.arguments[argument.name] = argument

Expand Down Expand Up @@ -213,4 +315,8 @@ def requires(self, name: str) -> Iterator[Argument]:
yield r


__all__ = ["Options", "Arguments", "Argument"]
__all__ = [
"Argument",
"Arguments",
"Options",
]
6 changes: 4 additions & 2 deletions libs/streamlink/plugin/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from streamlink.plugin.api.http_session import HTTPSession
from streamlink.compat import deprecated


__all__ = ["HTTPSession"]
deprecated({
"HTTPSession": ("streamlink.session.http.HTTPSession", None, None),
})

0 comments on commit 5c83783

Please sign in to comment.