mirror of https://github.com/streamlink/streamlink
cli.output: split up module into subpackage
- Move abstract `Output` base class into `streamlink_cli.output.abc` and properly declare abstract methods - Move `FileOutput` into `streamlink_cli.output.file` - Move `PlayerOutput` into `streamlink_cli.output.player` - Update patched module paths in tests
This commit is contained in:
parent
e4691b8891
commit
61977d352a
|
@ -0,0 +1,3 @@
|
|||
from streamlink_cli.output.abc import Output
|
||||
from streamlink_cli.output.file import FileOutput
|
||||
from streamlink_cli.output.player import PlayerOutput
|
|
@ -0,0 +1,34 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class Output(metaclass=ABCMeta):
|
||||
def __init__(self):
|
||||
self.opened = False
|
||||
|
||||
def open(self):
|
||||
self._open()
|
||||
self.opened = True
|
||||
|
||||
def close(self):
|
||||
if self.opened:
|
||||
self._close()
|
||||
|
||||
self.opened = False
|
||||
|
||||
def write(self, data):
|
||||
if not self.opened:
|
||||
raise OSError("Output is not opened")
|
||||
|
||||
return self._write(data)
|
||||
|
||||
@abstractmethod
|
||||
def _open(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def _close(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def _write(self, data):
|
||||
raise NotImplementedError
|
|
@ -0,0 +1,46 @@
|
|||
from pathlib import Path
|
||||
from typing import BinaryIO, Optional
|
||||
|
||||
from streamlink.compat import is_win32
|
||||
from streamlink_cli.compat import stdout
|
||||
from streamlink_cli.output.abc import Output
|
||||
|
||||
|
||||
if is_win32:
|
||||
import msvcrt
|
||||
from os import O_BINARY
|
||||
|
||||
|
||||
class FileOutput(Output):
|
||||
def __init__(
|
||||
self,
|
||||
filename: Optional[Path] = None,
|
||||
fd: Optional[BinaryIO] = None,
|
||||
record: Optional["FileOutput"] = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.filename = filename
|
||||
self.fd = fd
|
||||
self.record = record
|
||||
|
||||
def _open(self):
|
||||
if self.filename:
|
||||
self.filename.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.fd = open(self.filename, "wb")
|
||||
|
||||
if self.record:
|
||||
self.record.open()
|
||||
|
||||
if is_win32:
|
||||
msvcrt.setmode(self.fd.fileno(), O_BINARY)
|
||||
|
||||
def _close(self):
|
||||
if self.fd is not stdout:
|
||||
self.fd.close()
|
||||
if self.record:
|
||||
self.record.close()
|
||||
|
||||
def _write(self, data):
|
||||
self.fd.write(data)
|
||||
if self.record:
|
||||
self.record.write(data)
|
|
@ -5,87 +5,17 @@ import shlex
|
|||
import subprocess
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
from typing import BinaryIO, Optional
|
||||
|
||||
from streamlink.compat import is_win32
|
||||
from streamlink_cli.compat import stdout
|
||||
from streamlink_cli.constants import PLAYER_ARGS_INPUT_DEFAULT, PLAYER_ARGS_INPUT_FALLBACK, SUPPORTED_PLAYERS
|
||||
from streamlink_cli.output.abc import Output
|
||||
from streamlink_cli.utils import Formatter
|
||||
|
||||
|
||||
if is_win32:
|
||||
import msvcrt
|
||||
|
||||
log = logging.getLogger("streamlink.cli.output")
|
||||
|
||||
|
||||
class Output:
|
||||
def __init__(self):
|
||||
self.opened = False
|
||||
|
||||
def open(self):
|
||||
self._open()
|
||||
self.opened = True
|
||||
|
||||
def close(self):
|
||||
if self.opened:
|
||||
self._close()
|
||||
|
||||
self.opened = False
|
||||
|
||||
def write(self, data):
|
||||
if not self.opened:
|
||||
raise OSError("Output is not opened")
|
||||
|
||||
return self._write(data)
|
||||
|
||||
def _open(self):
|
||||
pass
|
||||
|
||||
def _close(self):
|
||||
pass
|
||||
|
||||
def _write(self, data):
|
||||
pass
|
||||
|
||||
|
||||
class FileOutput(Output):
|
||||
def __init__(
|
||||
self,
|
||||
filename: Optional[Path] = None,
|
||||
fd: Optional[BinaryIO] = None,
|
||||
record: Optional["FileOutput"] = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.filename = filename
|
||||
self.fd = fd
|
||||
self.record = record
|
||||
|
||||
def _open(self):
|
||||
if self.filename:
|
||||
self.filename.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.fd = open(self.filename, "wb")
|
||||
|
||||
if self.record:
|
||||
self.record.open()
|
||||
|
||||
if is_win32:
|
||||
msvcrt.setmode(self.fd.fileno(), os.O_BINARY)
|
||||
|
||||
def _close(self):
|
||||
if self.fd is not stdout:
|
||||
self.fd.close()
|
||||
if self.record:
|
||||
self.record.close()
|
||||
|
||||
def _write(self, data):
|
||||
self.fd.write(data)
|
||||
if self.record:
|
||||
self.record.write(data)
|
||||
|
||||
|
||||
class PlayerOutput(Output):
|
||||
PLAYER_TERMINATE_TIMEOUT = 10.0
|
||||
|
||||
|
@ -281,6 +211,3 @@ class PlayerOutput(Output):
|
|||
self.http.write(data)
|
||||
else:
|
||||
self.player.stdin.write(data)
|
||||
|
||||
|
||||
__all__ = ["PlayerOutput", "FileOutput"]
|
|
@ -10,7 +10,7 @@ import pytest
|
|||
from streamlink_cli.output import FileOutput, PlayerOutput
|
||||
|
||||
|
||||
@patch("streamlink_cli.output.stdout")
|
||||
@patch("streamlink_cli.output.file.stdout")
|
||||
class TestFileOutput(unittest.TestCase):
|
||||
@staticmethod
|
||||
def subject(filename, fd):
|
||||
|
@ -75,7 +75,7 @@ class TestFileOutput(unittest.TestCase):
|
|||
self._test_open(mock_open, mock_stdout)
|
||||
|
||||
@pytest.mark.windows_only()
|
||||
@patch("streamlink_cli.output.msvcrt")
|
||||
@patch("streamlink_cli.output.file.msvcrt")
|
||||
@patch("builtins.open")
|
||||
def test_open_windows(self, mock_open: Mock, mock_msvcrt: Mock, mock_stdout: Mock):
|
||||
mock_path = self._test_open(mock_open, mock_stdout)
|
||||
|
@ -91,34 +91,34 @@ class TestPlayerOutput(unittest.TestCase):
|
|||
assert PlayerOutput.supported_player("mpv") == "mpv"
|
||||
assert PlayerOutput.supported_player("potplayermini.exe") == "potplayer"
|
||||
|
||||
@patch("streamlink_cli.output.os.path.basename", new=ntpath.basename)
|
||||
@patch("streamlink_cli.output.player.os.path.basename", new=ntpath.basename)
|
||||
def test_supported_player_win32(self):
|
||||
assert PlayerOutput.supported_player("C:\\MPV\\mpv.exe") == "mpv"
|
||||
assert PlayerOutput.supported_player("C:\\VLC\\vlc.exe") == "vlc"
|
||||
assert PlayerOutput.supported_player("C:\\PotPlayer\\PotPlayerMini64.exe") == "potplayer"
|
||||
|
||||
@patch("streamlink_cli.output.os.path.basename", new=posixpath.basename)
|
||||
@patch("streamlink_cli.output.player.os.path.basename", new=posixpath.basename)
|
||||
def test_supported_player_posix(self):
|
||||
assert PlayerOutput.supported_player("/usr/bin/mpv") == "mpv"
|
||||
assert PlayerOutput.supported_player("/usr/bin/vlc") == "vlc"
|
||||
|
||||
@patch("streamlink_cli.output.os.path.basename", new=ntpath.basename)
|
||||
@patch("streamlink_cli.output.player.os.path.basename", new=ntpath.basename)
|
||||
def test_supported_player_args_win32(self):
|
||||
assert PlayerOutput.supported_player("C:\\MPV\\mpv.exe --argh") == "mpv"
|
||||
assert PlayerOutput.supported_player("C:\\VLC\\vlc.exe --argh") == "vlc"
|
||||
assert PlayerOutput.supported_player("C:\\PotPlayer\\PotPlayerMini64.exe --argh") == "potplayer"
|
||||
|
||||
@patch("streamlink_cli.output.os.path.basename", new=posixpath.basename)
|
||||
@patch("streamlink_cli.output.player.os.path.basename", new=posixpath.basename)
|
||||
def test_supported_player_args_posix(self):
|
||||
assert PlayerOutput.supported_player("/usr/bin/mpv --argh") == "mpv"
|
||||
assert PlayerOutput.supported_player("/usr/bin/vlc --argh") == "vlc"
|
||||
|
||||
@patch("streamlink_cli.output.os.path.basename", new=posixpath.basename)
|
||||
@patch("streamlink_cli.output.player.os.path.basename", new=posixpath.basename)
|
||||
def test_supported_player_negative_posix(self):
|
||||
assert PlayerOutput.supported_player("/usr/bin/xmpvideo") is None
|
||||
assert PlayerOutput.supported_player("/usr/bin/echo") is None
|
||||
|
||||
@patch("streamlink_cli.output.os.path.basename", new=ntpath.basename)
|
||||
@patch("streamlink_cli.output.player.os.path.basename", new=ntpath.basename)
|
||||
def test_supported_player_negative_win32(self):
|
||||
assert PlayerOutput.supported_player("C:\\mpc\\mpc-hd.exe") is None
|
||||
assert PlayerOutput.supported_player("C:\\mplayer\\not-vlc.exe") is None
|
||||
|
|
|
@ -7,7 +7,7 @@ from streamlink_cli.output import PlayerOutput
|
|||
|
||||
@pytest.fixture()
|
||||
def playeroutput(request: pytest.FixtureRequest):
|
||||
with patch("streamlink_cli.output.sleep"):
|
||||
with patch("streamlink_cli.output.player.sleep"):
|
||||
playeroutput = PlayerOutput(**getattr(request, "param", {}))
|
||||
yield playeroutput
|
||||
playeroutput.close()
|
||||
|
@ -16,7 +16,7 @@ def playeroutput(request: pytest.FixtureRequest):
|
|||
@pytest.fixture()
|
||||
def mock_popen(playeroutput: PlayerOutput):
|
||||
mock_popen = Mock(return_value=Mock(poll=Mock(side_effect=Mock(return_value=None))))
|
||||
with patch("streamlink_cli.output.sleep"), \
|
||||
with patch("streamlink_cli.output.player.sleep"), \
|
||||
patch("subprocess.Popen", mock_popen):
|
||||
yield mock_popen
|
||||
|
||||
|
|
|
@ -34,9 +34,9 @@ class CommandLineTestCase(unittest.TestCase):
|
|||
patch("streamlink_cli.main.setup_plugins"), \
|
||||
patch("streamlink_cli.main.setup_streamlink") as mock_setup_streamlink, \
|
||||
patch("streamlink_cli.main.streamlink", session), \
|
||||
patch("streamlink_cli.output.subprocess.Popen") as mock_popen, \
|
||||
patch("streamlink_cli.output.subprocess.call") as mock_call, \
|
||||
patch("streamlink_cli.output.sleep"):
|
||||
patch("streamlink_cli.output.player.subprocess.Popen") as mock_popen, \
|
||||
patch("streamlink_cli.output.player.subprocess.call") as mock_call, \
|
||||
patch("streamlink_cli.output.player.sleep"):
|
||||
mock_argv.__getitem__.side_effect = lambda x: args[x]
|
||||
mock_popen.return_value = Mock(poll=Mock(side_effect=poll_factory([None, 0])))
|
||||
try:
|
||||
|
|
|
@ -164,7 +164,7 @@ class TestPlayerOutput:
|
|||
@pytest.fixture()
|
||||
def output(self, player_process: Mock):
|
||||
with patch("subprocess.Popen") as mock_popen, \
|
||||
patch("streamlink_cli.output.sleep"):
|
||||
patch("streamlink_cli.output.player.sleep"):
|
||||
mock_popen.return_value = player_process
|
||||
output = FakePlayerOutput("mocked")
|
||||
output.open()
|
||||
|
|
Loading…
Reference in New Issue