From 939f277f861877bd246583e24baae734679a0515 Mon Sep 17 00:00:00 2001 From: dalgr Date: Sun, 7 Jan 2018 20:52:19 +0100 Subject: [PATCH] Add support for live svtplay using hls --- lib/svtplay_dl/fetcher/dash.py | 3 +++ lib/svtplay_dl/fetcher/hls.py | 42 +++++++++++++++++++++++-------- lib/svtplay_dl/service/svtplay.py | 23 +++++++++++------ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/lib/svtplay_dl/fetcher/dash.py b/lib/svtplay_dl/fetcher/dash.py index e4f7006..d6096b3 100644 --- a/lib/svtplay_dl/fetcher/dash.py +++ b/lib/svtplay_dl/fetcher/dash.py @@ -35,6 +35,9 @@ def templateelemt(element, filename, idnumber): else: start = 0 timeline = element.find("{urn:mpeg:dash:schema:mpd:2011}SegmentTimeline") + if timeline is None: + return + rvalue = timeline.findall(".//{urn:mpeg:dash:schema:mpd:2011}S[@r]") selements = timeline.findall(".//{urn:mpeg:dash:schema:mpd:2011}S") selements.pop() diff --git a/lib/svtplay_dl/fetcher/hls.py b/lib/svtplay_dl/fetcher/hls.py index 77e2c91..cabea05 100644 --- a/lib/svtplay_dl/fetcher/hls.py +++ b/lib/svtplay_dl/fetcher/hls.py @@ -5,6 +5,8 @@ import sys import os import re import copy +import time +import datetime from svtplay_dl.output import progressbar, progress_stream, ETA, output from svtplay_dl.log import log @@ -83,10 +85,11 @@ class HLS(VideoRetriever): return "hls" def download(self): - if self.options.live and not self.options.force: - raise LiveHLSException(self.url) if self.audio: + if self.options.live: + raise LiveHLSException(self.url) + cookies = self.kwargs["cookies"] audio_data_m3u = self.http.request("get", self.audio, cookies=cookies).text audio_m3u8 = M3U8(audio_data_m3u) @@ -98,12 +101,12 @@ class HLS(VideoRetriever): total_size = m3u8.media_segment[-1]["EXT-X-BYTERANGE"]["n"] + m3u8.media_segment[-1]["EXT-X-BYTERANGE"]["o"] self._download_url(m3u8.media_segment[0]["URI"], total_size=total_size) else: - self._download(self.url) + self._download() - def _download(self, url): + def _download(self): cookies = self.kwargs["cookies"] - data_m3u = self.http.request("get", url, cookies=cookies).text - m3u8 = M3U8(data_m3u) + start_time = time.time() + m3u8 = M3U8(self.http.request("get", self.url, cookies=cookies).text) key = None if m3u8.encrypted: @@ -118,13 +121,20 @@ class HLS(VideoRetriever): return decryptor = None - eta = ETA(len(m3u8.media_segment)) + size_media = len(m3u8.media_segment) + eta = ETA(size_media) + duration = 0 for index, i in enumerate(m3u8.media_segment): - item = _get_full_url(i["URI"], url) + if "duration" in i["EXTINF"]: + duration += i["EXTINF"]["duration"] + item = _get_full_url(i["URI"], self.url) if not self.options.silent: - eta.increment() - progressbar(len(m3u8.media_segment), index+1, ''.join(['ETA: ', str(eta)])) + if self.options.live: + progressbar(size_media, index + 1, ''.join(['DU: ', str(datetime.timedelta(seconds=int(duration)))])) + else: + eta.increment() + progressbar(size_media, index + 1, ''.join(['ETA: ', str(eta)])) data = self.http.request("get", item, cookies=cookies) if data.status_code == 404: @@ -149,6 +159,18 @@ class HLS(VideoRetriever): file_d.write(data) + if (size_media == (index + 1)) and self.options.live: + while (start_time + i["EXTINF"]["duration"] * 2) >= time.time(): + time.sleep(1.0) + + start_time = time.time() + new_m3u8 = M3U8(self.http.request("get", self.url, cookies=cookies).text) + for n_m3u in new_m3u8.media_segment: + if n_m3u not in m3u8.media_segment: + m3u8.media_segment.append(n_m3u) + + size_media = len(m3u8.media_segment) + file_d.close() if not self.options.silent: progress_stream.write('\n') diff --git a/lib/svtplay_dl/service/svtplay.py b/lib/svtplay_dl/service/svtplay.py index 3d23211..0cc9bd4 100644 --- a/lib/svtplay_dl/service/svtplay.py +++ b/lib/svtplay_dl/service/svtplay.py @@ -18,6 +18,7 @@ from svtplay_dl.fetcher.dash import dashparse from svtplay_dl.subtitle import subtitle from svtplay_dl.error import ServiceError +URL_VIDEO_API = "http://api.svt.se/videoplayer-api/video/" class Svtplay(Service, OpenGraphThumbMixin): supported_domains = ['svtplay.se', 'svt.se', 'beta.svtplay.se', 'svtflow.se'] @@ -25,7 +26,7 @@ class Svtplay(Service, OpenGraphThumbMixin): def get(self): parse = urlparse(self.url) if parse.netloc == "www.svtplay.se" or parse.netloc == "svtplay.se": - if parse.path[:6] != "/video" and parse.path[:6] != "/klipp": + if parse.path[:6] != "/video" and parse.path[:6] != "/klipp" and parse.path[:8] != "/kanaler": yield ServiceError("This mode is not supported anymore. Need the url with the video.") return @@ -34,6 +35,19 @@ class Svtplay(Service, OpenGraphThumbMixin): if "accessService" in query: self.access = query["accessService"] + if parse.path[:8] == "/kanaler": + res = self.http.get(URL_VIDEO_API + "ch-{0}".format(parse.path[9:])) + try: + janson = res.json() + except json.decoder.JSONDecodeError: + yield ServiceError("Can't decode api request: {0}".format(res.request.url)) + return + videos = self._get_video(janson) + self.options.live = True + for i in videos: + yield i + return + match = re.search("__svtplay'] = ({.*});", self.get_urldata()) if not match: yield ServiceError("Can't find video info.") @@ -55,9 +69,6 @@ class Svtplay(Service, OpenGraphThumbMixin): return janson = json.loads(match.group(1))["videoPage"] - if "live" in janson["video"]: - self.options.live = janson["video"]["live"] - if self.options.output_auto: self.options.service = "svtplay" self.options.output = self.outputfilename(janson["video"], self.options.output) @@ -70,7 +81,7 @@ class Svtplay(Service, OpenGraphThumbMixin): vid = janson["video"]["programVersionId"] else: vid = janson["video"]["id"] - res = self.http.get("http://api.svt.se/videoplayer-api/video/{0}".format(vid)) + res = self.http.get(URL_VIDEO_API + vid) try: janson = res.json() except json.decoder.JSONDecodeError: @@ -81,8 +92,6 @@ class Svtplay(Service, OpenGraphThumbMixin): yield i def _get_video(self, janson): - if "live" in janson: - self.options.live = janson["live"] if "subtitleReferences" in janson: for i in janson["subtitleReferences"]: if i["format"] == "websrt" and "url" in i: