Skip to content

Commit

Permalink
plugins.twitch: add --twitch-supported-codecs
Browse files Browse the repository at this point in the history
Add the temporary `--twitch-supported-codecs` plugin argument
for being able to set the client's video codec preference.

Set the default value to "h264" to ensure that no compatibility
issues arise on clients without AV1/HEVC decode capabilities.
  • Loading branch information
bastimeyer committed Jan 17, 2024
1 parent ae0576b commit 535ce45
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 3 deletions.
31 changes: 28 additions & 3 deletions src/streamlink/plugins/twitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from datetime import datetime, timedelta
from json import dumps as json_dumps
from random import random
from typing import ClassVar, Mapping, Optional, Tuple, Type
from typing import ClassVar, List, Mapping, Optional, Tuple, Type
from urllib.parse import urlparse

from requests.exceptions import HTTPError
Expand Down Expand Up @@ -203,19 +203,24 @@ def __init__(self, *args, disable_ads: bool = False, low_latency: bool = False,


class UsherService:
def __init__(self, session):
def __init__(self, session: Streamlink, supported_codecs: Optional[List[str]] = None):
self.session = session
self.supported_codecs = supported_codecs

def _create_url(self, endpoint, **extra_params):
url = f"https://usher.ttvnw.net{endpoint}"
params = {
"platform": "web",
"player": "twitchweb",
"p": int(random() * 999999),
"type": "any",
"allow_source": "true",
"allow_audio_only": "true",
"allow_spectre": "false",
"playlist_include_framerate": "true",
}
if self.supported_codecs:
params["supported_codecs"] = ",".join(self.supported_codecs)
params.update(extra_params)

req = self.session.http.prepare_new_request(url=url, params=params)
Expand Down Expand Up @@ -651,6 +656,23 @@ def decode_client_integrity_token(data: str, schema: Optional[validate.Schema] =
Regular streams can cause buffering issues with this option enabled due to the reduced --hls-live-edge value.
""",
)
@pluginargument(
"supported-codecs",
type="comma_list_filter",
type_kwargs={
"acceptable": ["h264", "h265", "av1"],
"unique": True,
},
default=["h264"],
help="""
A comma-separated list of codec names which signals Twitch the client's stream codec preference.
Which streams and which codecs are available depends on the specific channel and broadcast.
Default is "h264".
Supported codecs are "h264", "h265", "av1". Set to "h264,h265,av1" to enable all codecs.
""",
)
@pluginargument(
"api-header",
metavar="KEY=VALUE",
Expand Down Expand Up @@ -718,7 +740,10 @@ def __init__(self, *args, **kwargs):
api_header=self.get_option("api-header"),
access_token_param=self.get_option("access-token-param"),
)
self.usher = UsherService(session=self.session)
self.usher = UsherService(
session=self.session,
supported_codecs=self.get_option("supported-codecs"),
)

def method_factory(parent_method):
def inner():
Expand Down
10 changes: 10 additions & 0 deletions tests/plugins/test_twitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,16 @@ def test_service(self, caplog: pytest.LogCaptureFixture, endpoint: str, expected

assert [(r.name, r.levelname, r.message) for r in caplog.get_records(when="setup")] == logs

@pytest.mark.parametrize(("endpoint", "expected"), [
pytest.param({"options": {}}, None, id="unset"),
pytest.param({"options": {"supported_codecs": ["h264"]}}, "h264", id="h264"),
pytest.param({"options": {"supported_codecs": ["av1", "h264"]}}, "av1,h264", id="av1,h264"),
pytest.param({"options": {"supported_codecs": ["av1", "h264", "h265"]}}, "av1,h264,h265", id="av1,h264,h265"),
], indirect=["endpoint"])
def test_supported_codecs(self, endpoint: str, expected: str):
qs = dict(parse_qsl(urlparse(endpoint).query))
assert qs.get("supported_codecs") == expected


class TestTwitchAPIAccessToken:
@pytest.fixture(autouse=True)
Expand Down

0 comments on commit 535ce45

Please sign in to comment.