mirror of
https://github.com/streamlink/streamlink
synced 2024-11-01 01:19:33 +01:00
Merge pull request #1804 from back-to/times
api.utils: hours_minutes_seconds update, twitch automate time offset
This commit is contained in:
commit
3e021c548c
@ -13,6 +13,7 @@ from streamlink.plugin.api.utils import parse_json, parse_query
|
||||
from streamlink.stream import (
|
||||
HTTPStream, HLSStream, FLVPlaylist, extract_flv_header_tags
|
||||
)
|
||||
from streamlink.utils.times import hours_minutes_seconds
|
||||
|
||||
try:
|
||||
from itertools import izip as zip
|
||||
@ -55,17 +56,6 @@ _url_re = re.compile(r"""
|
||||
(?P<clip_name>[\w]+)
|
||||
)?
|
||||
""", re.VERBOSE)
|
||||
_time_re = re.compile(r"""
|
||||
(?:
|
||||
(?P<hours>\d+)h
|
||||
)?
|
||||
(?:
|
||||
(?P<minutes>\d+)m
|
||||
)?
|
||||
(?:
|
||||
(?P<seconds>\d+)s
|
||||
)?
|
||||
""", re.VERBOSE)
|
||||
|
||||
_access_token_schema = validate.Schema(
|
||||
{
|
||||
@ -138,18 +128,6 @@ _quality_options_schema = validate.Schema(
|
||||
)
|
||||
|
||||
|
||||
def time_to_offset(t):
|
||||
match = _time_re.match(t)
|
||||
if match:
|
||||
offset = int(match.group("hours") or "0") * 60 * 60
|
||||
offset += int(match.group("minutes") or "0") * 60
|
||||
offset += int(match.group("seconds") or "0")
|
||||
else:
|
||||
offset = 0
|
||||
|
||||
return offset
|
||||
|
||||
|
||||
class UsherService(object):
|
||||
def _create_url(self, endpoint, **extra_params):
|
||||
url = "https://usher.ttvnw.net{0}".format(endpoint)
|
||||
@ -541,7 +519,12 @@ class Twitch(Plugin):
|
||||
# start offset if needed.
|
||||
time_offset = self.params.get("t")
|
||||
if time_offset:
|
||||
videos["start_offset"] += time_to_offset(self.params.get("t"))
|
||||
try:
|
||||
time_offset = hours_minutes_seconds(time_offset)
|
||||
except ValueError:
|
||||
time_offset = 0
|
||||
|
||||
videos["start_offset"] += time_offset
|
||||
|
||||
return self._create_playlist_streams(videos)
|
||||
|
||||
@ -599,10 +582,18 @@ class Twitch(Plugin):
|
||||
self.logger.debug("Unknown HLS stream type: {0}".format(stream_type))
|
||||
return {}
|
||||
|
||||
time_offset = self.params.get("t", 0)
|
||||
if time_offset:
|
||||
try:
|
||||
time_offset = hours_minutes_seconds(time_offset)
|
||||
except ValueError:
|
||||
time_offset = 0
|
||||
|
||||
try:
|
||||
# If the stream is a VOD that is still being recorded the stream should start at the
|
||||
# beginning of the recording
|
||||
streams = HLSStream.parse_variant_playlist(self.session, url,
|
||||
start_offset=time_offset,
|
||||
force_restart=not stream_type == "live")
|
||||
except IOError as err:
|
||||
err = str(err)
|
||||
|
52
src/streamlink/utils/times.py
Normal file
52
src/streamlink/utils/times.py
Normal file
@ -0,0 +1,52 @@
|
||||
import re
|
||||
|
||||
_hours_minutes_seconds_re = re.compile(r"""
|
||||
^-?(?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d+)$
|
||||
""", re.VERBOSE)
|
||||
|
||||
_hours_minutes_seconds_2_re = re.compile(r"""^-?
|
||||
(?:
|
||||
(?P<hours>\d+)h
|
||||
)?
|
||||
(?:
|
||||
(?P<minutes>\d+)m
|
||||
)?
|
||||
(?:
|
||||
(?P<seconds>\d+)s
|
||||
)?$
|
||||
""", re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
|
||||
def hours_minutes_seconds(value):
|
||||
"""converts a timestamp to seconds
|
||||
|
||||
- hours:minutes:seconds to seconds
|
||||
- 11h22m33s to seconds
|
||||
- 11h to seconds
|
||||
- 20h15m to seconds
|
||||
- seconds to seconds
|
||||
|
||||
:param value: hh:mm:ss ; 00h00m00s ; seconds
|
||||
:return: seconds
|
||||
"""
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
match = (_hours_minutes_seconds_re.match(value)
|
||||
or _hours_minutes_seconds_2_re.match(value))
|
||||
if not match:
|
||||
raise ValueError
|
||||
|
||||
s = 0
|
||||
s += int(match.group("hours") or "0") * 60 * 60
|
||||
s += int(match.group("minutes") or "0") * 60
|
||||
s += int(match.group("seconds") or "0")
|
||||
|
||||
return s
|
||||
|
||||
|
||||
__all__ = [
|
||||
"hours_minutes_seconds",
|
||||
]
|
@ -4,6 +4,7 @@ from string import printable
|
||||
from textwrap import dedent
|
||||
|
||||
from streamlink import logger
|
||||
from streamlink.utils.times import hours_minutes_seconds
|
||||
from .constants import (
|
||||
LIVESTREAMER_VERSION, STREAM_PASSTHROUGH, DEFAULT_PLAYER_ARGUMENTS
|
||||
)
|
||||
@ -24,7 +25,6 @@ _option_re = re.compile(r"""
|
||||
\s*
|
||||
(?P<value>.*) # The value, anything goes.
|
||||
""", re.VERBOSE)
|
||||
_hours_minutes_seconds_re = re.compile(r"-?(?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d+)")
|
||||
|
||||
|
||||
class ArgumentParser(argparse.ArgumentParser):
|
||||
@ -140,23 +140,6 @@ def boolean(value):
|
||||
return value.lower() in truths
|
||||
|
||||
|
||||
def hours_minutes_seconds(value):
|
||||
"""
|
||||
converts hours:minutes:seconds to seconds
|
||||
:param value: hh:mm:ss
|
||||
:return: seconds
|
||||
"""
|
||||
match = _hours_minutes_seconds_re.match(value)
|
||||
if not match:
|
||||
raise ValueError
|
||||
s = 0
|
||||
s += int(match.group("hours")) * 60 * 60
|
||||
s += int(match.group("minutes")) * 60
|
||||
s += int(match.group("seconds"))
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def build_parser():
|
||||
parser = ArgumentParser(
|
||||
prog="streamlink",
|
||||
|
37
tests/test_utils_times.py
Normal file
37
tests/test_utils_times.py
Normal file
@ -0,0 +1,37 @@
|
||||
import unittest
|
||||
|
||||
from streamlink.utils.times import hours_minutes_seconds
|
||||
|
||||
|
||||
class TestUtilsTimes(unittest.TestCase):
|
||||
|
||||
def test_hours_minutes_seconds(self):
|
||||
self.assertEqual(hours_minutes_seconds("00:01:30"), 90)
|
||||
self.assertEqual(hours_minutes_seconds("01:20:15"), 4815)
|
||||
self.assertEqual(hours_minutes_seconds("26:00:00"), 93600)
|
||||
|
||||
self.assertEqual(hours_minutes_seconds("07"), 7)
|
||||
self.assertEqual(hours_minutes_seconds("444"), 444)
|
||||
self.assertEqual(hours_minutes_seconds("8888"), 8888)
|
||||
|
||||
self.assertEqual(hours_minutes_seconds("01h"), 3600)
|
||||
self.assertEqual(hours_minutes_seconds("01h22m33s"), 4953)
|
||||
self.assertEqual(hours_minutes_seconds("01H22M37S"), 4957)
|
||||
self.assertEqual(hours_minutes_seconds("01h30s"), 3630)
|
||||
self.assertEqual(hours_minutes_seconds("1m33s"), 93)
|
||||
self.assertEqual(hours_minutes_seconds("55s"), 55)
|
||||
|
||||
self.assertEqual(hours_minutes_seconds("-00:01:40"), 100)
|
||||
self.assertEqual(hours_minutes_seconds("-00h02m30s"), 150)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
hours_minutes_seconds("02:04")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
hours_minutes_seconds("FOO")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
hours_minutes_seconds("BAR")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
hours_minutes_seconds("11:ERR:00")
|
Loading…
Reference in New Issue
Block a user