Skip to content

Update Stream string representation and refactor to_url + JSON data #4521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
stream: refactor to_url and string representation
- Change format of Stream string representations and error messages
  of `to_url` and `to_manifest_url`
- Don't override `__repr__` in `Stream` subclasses
- Raise TypeError if url or manifest URL is None
- Fix CLI
  - Abort passthrough if stream can't be translated to URL
  - Remove unneeded streamlink_cli.utils.stream module
- Rewrite stream URL tests
- Move FilmOnHLS stream URL tests to plugin test module (and rewrite)
  • Loading branch information
bastimeyer committed May 10, 2022
commit 51bd39322a69b669987040a10e951a43805ec945
2 changes: 1 addition & 1 deletion src/streamlink/plugins/filmon.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def to_url(self):
url = self.url
expires = self.watch_timeout - time.time()
if expires < 0:
raise TypeError("Stream has expired and cannot be converted to a URL")
raise TypeError("Stream has expired and cannot be translated to a URL")
return url


Expand Down
4 changes: 2 additions & 2 deletions src/streamlink/plugins/twitcasting.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ def __init__(self, session, url):
super().__init__(session)
self.url = url

def __repr__(self):
return f"<TwitCastingStream({self.url!r})>"
def to_url(self):
return self.url

def open(self):
reader = TwitCastingReader(self)
Expand Down
13 changes: 7 additions & 6 deletions src/streamlink/stream/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ def __json__(self):

return json

def to_url(self):
if self.mpd.url is None:
return super().to_url()

# the MPD URL has already been prepared by the initial request in `parse_manifest`
return self.mpd.url

@classmethod
def parse_manifest(
cls,
Expand Down Expand Up @@ -305,9 +312,3 @@ def open(self):
return video
elif self.audio_representation:
return audio

def to_url(self):
return self.mpd.url

def to_manifest_url(self):
return self.mpd.url
6 changes: 6 additions & 0 deletions src/streamlink/stream/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ def __json__(self):

return json

def to_url(self):
if self.path is None:
return super().to_url()

return self.path

def open(self):
return self.fileobj or open(self.path)
8 changes: 4 additions & 4 deletions src/streamlink/stream/hls.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,9 @@ def __init__(
self.url_master = url_master

def to_manifest_url(self):
if self.url_master is None:
return super().to_manifest_url()

return self.url_master


Expand Down Expand Up @@ -527,9 +530,6 @@ def __init__(
self.start_offset = start_offset
self.duration = duration

def __repr__(self):
return f"<HLSStream({self.url!r}, {self.url_master!r})>"

def __json__(self):
json = super().__json__()

Expand All @@ -544,7 +544,7 @@ def __json__(self):

def to_manifest_url(self):
if self.url_master is None:
return None
return super().to_manifest_url()

args = self.args.copy()
args.update(url=self.url_master)
Expand Down
9 changes: 3 additions & 6 deletions src/streamlink/stream/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ def __init__(
self.args = dict(url=url, **args)
self.buffered = buffered

def __repr__(self):
return "<HTTPStream({0!r})>".format(self.url)

def __json__(self):
req = self.session.http.prepare_new_request(**self.args)

Expand All @@ -47,6 +44,9 @@ def __json__(self):
body=req.body,
)

def to_url(self):
return self.url

@property
def url(self) -> str:
"""
Expand All @@ -71,6 +71,3 @@ def open(self):
fd = StreamIOThreadWrapper(self.session, fd, timeout=timeout)

return fd

def to_url(self):
return self.url
39 changes: 23 additions & 16 deletions src/streamlink/stream/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class Stream:

__shortname__ = "stream"

@classmethod
def shortname(cls):
return cls.__shortname__

def __init__(self, session):
"""
:param streamlink.Streamlink session: Streamlink session instance
Expand All @@ -21,11 +25,29 @@ def __init__(self, session):
self.session = session

def __repr__(self):
return "<Stream()>"
params = [repr(self.shortname())]
for method in self.to_url, self.to_manifest_url:
try:
params.append(repr(method()))
except TypeError:
pass

return f"<{self.__class__.__name__} [{', '.join(params)}]>"

def __json__(self):
return dict(type=self.shortname())

@property
def json(self):
obj = self.__json__()
return json.dumps(obj)

def to_url(self):
raise TypeError(f"<{self.__class__.__name__} [{self.shortname()}]> cannot be translated to a URL")

def to_manifest_url(self):
raise TypeError(f"<{self.__class__.__name__} [{self.shortname()}]> cannot be translated to a manifest URL")

def open(self) -> "StreamIO":
"""
Attempts to open a connection to the stream.
Expand All @@ -36,21 +58,6 @@ def open(self) -> "StreamIO":

raise NotImplementedError

@property
def json(self):
obj = self.__json__()
return json.dumps(obj)

@classmethod
def shortname(cls):
return cls.__shortname__

def to_url(self):
raise TypeError("{0} cannot be converted to a URL".format(self.shortname()))

def to_manifest_url(self):
raise TypeError("{0} cannot be converted to a URL".format(self.shortname()))


class StreamIO(io.IOBase):
pass
Expand Down
11 changes: 8 additions & 3 deletions src/streamlink_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from streamlink_cli.console import ConsoleOutput, ConsoleUserInputRequester
from streamlink_cli.constants import CONFIG_FILES, DEFAULT_STREAM_METADATA, LOG_DIR, PLUGIN_DIRS, STREAM_SYNONYMS
from streamlink_cli.output import FileOutput, Output, PlayerOutput
from streamlink_cli.utils import Formatter, HTTPServer, datetime, ignored, progress, stream_to_url
from streamlink_cli.utils import Formatter, HTTPServer, datetime, ignored, progress

ACCEPTABLE_ERRNO = (errno.EPIPE, errno.EINVAL, errno.ECONNRESET)
try:
Expand Down Expand Up @@ -257,11 +257,16 @@ def output_stream_passthrough(stream, formatter: Formatter):
"""Prepares a filename to be passed to the player."""
global output

filename = f'"{stream_to_url(stream)}"'
try:
url = stream.to_url()
except TypeError:
console.exit("The stream specified cannot be translated to a URL")
return False

output = PlayerOutput(
args.player,
args=args.player_args,
filename=filename,
filename=f'"{url}"',
call=True,
quiet=not args.verbose_player,
title=formatter.title(args.title, defaults=DEFAULT_STREAM_METADATA) if args.title else args.url
Expand Down
3 changes: 1 addition & 2 deletions src/streamlink_cli/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
from streamlink_cli.utils.http_server import HTTPServer
from streamlink_cli.utils.player import find_default_player
from streamlink_cli.utils.progress import progress
from streamlink_cli.utils.stream import stream_to_url

__all__ = [
"Formatter", "HTTPServer", "JSONEncoder",
"datetime",
"find_default_player", "ignored", "progress", "stream_to_url"
"find_default_player", "ignored", "progress",
]


Expand Down
5 changes: 0 additions & 5 deletions src/streamlink_cli/utils/stream.py

This file was deleted.

30 changes: 29 additions & 1 deletion tests/plugins/test_filmon.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
from streamlink.plugins.filmon import Filmon
import datetime
from unittest.mock import PropertyMock, patch

import freezegun
import pytest

from streamlink import Streamlink
from streamlink.plugins.filmon import FilmOnHLS, Filmon
from tests.plugins import PluginCanHandleUrl


Expand Down Expand Up @@ -31,3 +38,24 @@ class TestPluginCanHandleUrlFilmon(PluginCanHandleUrl):
('http://www.filmon.tv/vod/view/10250-0-crime-boss?extra', (None, None, '10250-0-crime-boss')),
('http://www.filmon.tv/vod/view/10250-0-crime-boss&extra', (None, None, '10250-0-crime-boss')),
]


@pytest.fixture()
def filmonhls():
with freezegun.freeze_time(datetime.datetime(2000, 1, 1, 0, 0, 0, 0)), \
patch("streamlink.plugins.filmon.FilmOnHLS.url", new_callable=PropertyMock) as url:
url.return_value = "http://filmon.tv/test.m3u8"
session = Streamlink()
yield FilmOnHLS(session, channel="test")


def test_filmonhls_to_url(filmonhls):
filmonhls.watch_timeout = datetime.datetime(2000, 1, 1, 0, 0, 0, 0).timestamp()
assert filmonhls.to_url() == "http://filmon.tv/test.m3u8"


def test_filmonhls_to_url_expired(filmonhls):
filmonhls.watch_timeout = datetime.datetime(1999, 12, 31, 23, 59, 59, 9999).timestamp()
with pytest.raises(TypeError) as cm:
filmonhls.to_url()
assert str(cm.value) == "Stream has expired and cannot be translated to a URL"
4 changes: 2 additions & 2 deletions tests/stream/test_hls.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ def test_repr(self):
session = Streamlink()

stream = HLSStream(session, "https://foo.bar/playlist.m3u8")
self.assertEqual(repr(stream), "<HLSStream('https://foo.bar/playlist.m3u8', None)>")
self.assertEqual(repr(stream), "<HLSStream ['hls', 'https://foo.bar/playlist.m3u8']>")

stream = HLSStream(session, "https://foo.bar/playlist.m3u8", "https://foo.bar/master.m3u8")
self.assertEqual(repr(stream), "<HLSStream('https://foo.bar/playlist.m3u8', 'https://foo.bar/master.m3u8')>")
self.assertEqual(repr(stream), "<HLSStream ['hls', 'https://foo.bar/playlist.m3u8', 'https://foo.bar/master.m3u8']>")


class TestHLSVariantPlaylist(unittest.TestCase):
Expand Down
Loading