# ex:ts=4:sw=4:sts=4:et # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- import json import logging import re from datetime import datetime from datetime import timedelta from urllib.parse import urlparse from svtplay_dl.error import ServiceError from svtplay_dl.fetcher.dash import dashparse from svtplay_dl.fetcher.hls import hlsparse from svtplay_dl.service import OpenGraphThumbMixin from svtplay_dl.service import Service from svtplay_dl.utils.http import download_thumbnails class Tv4play(Service, OpenGraphThumbMixin): supported_domains = ["tv4play.se"] def get(self): parse = urlparse(self.url) if parse.path[:8] == "/kanaler": end_time_stamp = (datetime.utcnow() - timedelta(minutes=1, seconds=20)).replace(microsecond=0) start_time_stamp = end_time_stamp - timedelta(minutes=1) url = ( f"https://bbr-l2v.akamaized.net/live/{parse.path[9:]}/master.m3u8?in={start_time_stamp.isoformat()}&out={end_time_stamp.isoformat()}?" ) self.config.set("live", True) streams = hlsparse(self.config, self.http.request("get", url), url, output=self.output, hls_time_stamp=True) for n in list(streams.keys()): yield streams[n] return match = self._getjson() if not match: yield ServiceError("Can't find json data") return jansson = json.loads(match.group(1)) if "assetId" not in jansson["query"]: yield ServiceError("Cant find video id for the video") return vid = jansson["query"]["assetId"] janson2 = jansson["props"]["pageProps"]["initialApolloState"] item = janson2[f"VideoAsset:{vid}"] if item["is_drm_protected"]: yield ServiceError("We can't download DRM protected content from this site.") return if item["live"]: self.config.set("live", True) if item["season"] > 0: self.output["season"] = item["season"] if item["episode"] > 0: self.output["episode"] = item["episode"] self.output["title"] = item["program_nid"] self.output["episodename"] = item["title"] self.output["id"] = str(vid) self.output["episodethumbnailurl"] = item["image"] if vid is None: yield ServiceError("Cant find video id for the video") return url = f"https://playback-api.b17g.net/media/{vid}?service=tv4&device=browser&protocol=hls%2Cdash&drm=widevine&browser=GoogleChrome" res = self.http.request("get", url, cookies=self.cookies) if res.status_code > 200: yield ServiceError("Can't play this because the video is geoblocked or not available.") return jansson = res.json() if jansson["playbackItem"]["type"] == "hls": yield from hlsparse( self.config, self.http.request("get", jansson["playbackItem"]["manifestUrl"]), jansson["playbackItem"]["manifestUrl"], output=self.output, httpobject=self.http, ) yield from dashparse( self.config, self.http.request("get", jansson["playbackItem"]["manifestUrl"].replace(".m3u8", ".mpd")), jansson["playbackItem"]["manifestUrl"].replace(".m3u8", ".mpd"), output=self.output, httpobject=self.http, ) def _getjson(self): match = re.search(r"application\/json\">(.*\})<\/script>", self.get_urldata()) return match def find_all_episodes(self, config): episodes = [] items = [] show = None match = self._getjson() jansson = json.loads(match.group(1)) if "nid" not in jansson["query"]: logging.info("Can't find show name.") return episodes show = jansson["query"]["nid"] graph_list = self._graphql(show, "EPISODE") for i in graph_list: if i not in items: items.append(i) if config.get("include_clips"): items.extend(self._graphql(show, "CLIP")) items = sorted(items) for item in items: episodes.append(f"https://www.tv4play.se/program/{show}/{item}") if config.get("all_last") > 0: return episodes[-config.get("all_last") :] return episodes def _graphql(self, show, panel_type): items = [] gql = { "variables": { "programPanelsInput": {"offset": 0, "limit": 1000}, "videoAssetListInput": {"limit": 100, "offset": 0, "sortOrder": "ASCENDING"}, "programNid": show, }, "query": "query Seasons($programNid: String, $videoAssetListInput: VideoAssetListInput!, $programPanelsInput: ProgramPanelsInput!)" " {\n program(nid: $programNid) {\n __typename\n upcoming {\n __typename\n ...EpisodeVideoAssetField\n }\n" " panels2(input: $programPanelsInput) {\n __typename\n pageInfo {\n __typename\n totalCount\n" " hasNextPage\n nextPageOffset\n }\n items {\n __typename\n ...EpisodePanelField\n }\n" " }\n }\n}fragment EpisodeVideoAssetField on VideoAsset {\n __typename\n id\n title\n description\n expireDateTime\n" " humanDuration\n freemium\n broadcastDateTime\n live\n daysLeftInService\n duration\n season\n episode\n" " humanBroadcastDateTime\n humanBroadcastDateWithWeekday\n program {\n __typename\n nid\n name\n displayCategory\n" " images2 {\n __typename\n main16x9 {\n __typename\n url\n }\n }\n }\n progress {\n" " __typename\n position\n percentage\n }\n image2 {\n __typename\n url\n }\n}fragment" " EpisodePanelField on VideoPanel {\n __typename\n id\n name\n assetType\n totalNumberOfEpisodes\n" " videoList2(input: $videoAssetListInput) {\n __typename\n pageInfo {\n __typename\n totalCount\n" " hasNextPage\n nextPageOffset\n }\n initialSortOrder\n items {\n __typename\n id\n" " title\n ...EpisodeVideoAssetField\n }\n }\n}", } res = self.http.request("post", "https://graphql.tv4play.se/graphql", json=gql) janson = res.json() for mediatype in janson["data"]["program"]["panels2"]["items"]: offset = 0 if mediatype["assetType"] != panel_type: continue moreData = mediatype["videoList2"]["pageInfo"]["hasNextPage"] seasonid = mediatype["id"] for video in mediatype["videoList2"]["items"]: items.append(video["id"]) while moreData: offset += 100 gql2 = { "variables": {"id": seasonid, "videoAssetListInput": {"limit": 100, "sortOrder": "ASCENDING", "offset": offset}}, "query": "query MoreEpisodes($id: String!, $videoAssetListInput: VideoAssetListInput!) {\n" " videoPanel(id: $id) {\n __typename\n videoList2(input: $videoAssetListInput) {\n __typename\n" " pageInfo {\n __typename\n totalCount\n hasNextPage\n nextPageOffset\n" " }\n items {\n __typename\n id\n title\n ...EpisodeVideoAssetField\n" " }\n }\n }\n}fragment EpisodeVideoAssetField on VideoAsset {\n __typename\n id\n title\n" " description\n expireDateTime\n humanDuration\n freemium\n broadcastDateTime\n live\n daysLeftInService\n" " duration\n season\n episode\n humanBroadcastDateTime\n humanBroadcastDateWithWeekday\n program {\n" " __typename\n nid\n name\n displayCategory\n images2 {\n __typename\n main16x9 {\n" " __typename\n url\n }\n }\n }\n progress {\n __typename\n position\n percentage\n" " }\n image2 {\n __typename\n url\n }\n}", } res = self.http.request("post", "https://graphql.tv4play.se/graphql", json=gql2) moreData = res.json()["data"]["videoPanel"]["videoList2"]["pageInfo"]["hasNextPage"] for video in res.json()["data"]["videoPanel"]["videoList2"]["items"]: items.append(video["id"]) return items def get_thumbnail(self, options): download_thumbnails(self.output, options, [(False, self.output["episodethumbnailurl"])]) class Tv4(Service, OpenGraphThumbMixin): supported_domains = ["tv4.se"] def get(self): match = re.search(r"application\/json\"\>(\{.*\})\<\/script", self.get_urldata()) if not match: yield ServiceError("Can't find video data'") return janson = json.loads(match.group(1)) self.output["id"] = janson["query"]["id"] self.output["title"] = janson["query"]["slug"] if janson["query"]["type"] == "Article": vidasset = janson["props"]["pageProps"]["apolloState"][f"Article:{janson['query']['id']}"]["featured"]["__ref"] self.output["id"] = janson["props"]["pageProps"]["apolloState"][vidasset]["id"] url = f"https://playback-api.b17g.net/media/{self.output['id']}?service=tv4&device=browser&protocol=hls%2Cdash&drm=widevine" res = self.http.request("get", url, cookies=self.cookies) if res.status_code > 200: yield ServiceError("Can't play this because the video is geoblocked.") return if res.json()["playbackItem"]["type"] == "hls": yield from hlsparse( self.config, self.http.request("get", res.json()["playbackItem"]["manifestUrl"]), res.json()["playbackItem"]["manifestUrl"], output=self.output, )