diff --git a/app/tools/yt.py b/app/tools/yt.py index 1a494c40..e80e6f6d 100644 --- a/app/tools/yt.py +++ b/app/tools/yt.py @@ -1,7 +1,9 @@ """ Module for working with YouTube service """ +import json import re import urllib from html.parser import HTMLParser +from json import JSONDecodeError from urllib.request import Request from app.commons import log @@ -41,8 +43,31 @@ class YouTube: """ req = Request("https://youtube.com/get_video_info?video_id={}".format(video_id), headers=_HEADERS) with urllib.request.urlopen(req, timeout=2) as resp: - data = resp.read().decode("utf-8").split("&") + data = urllib.request.unquote(resp.read().decode("utf-8")).split("&") out = {k: v for k, sep, v in (str(d).partition("=") for d in map(urllib.request.unquote, data))} + player_resp = out.get("player_response", None) + + if player_resp: + try: + resp = json.loads(player_resp) + except JSONDecodeError as e: + log("{}: Parsing player response error: {}".format(__class__.__name__, e)) + else: + det = resp.get("videoDetails", None) + title = det.get("title", None) if det else None + streaming_data = resp.get("streamingData", None) + fmts = streaming_data.get("formats", None) if streaming_data else None + + if fmts: + url = None + for f in fmts: + # TODO implement the choice of quality. + url = f.get("url", None) + break + + if url and title: + return url, title.replace("+", " ") + stream_map = out.get("url_encoded_fmt_stream_map", None) if stream_map: s_map = {k: v for k, sep, v in (str(d).partition("=") for d in stream_map.split("&"))} diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index 14b523be..0e79a03b 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -1426,7 +1426,7 @@ class Application(Gtk.Application): YtListImportDialog(self._main_window, Profile(self._profile), get_max_marker_num(self._services), - self.append_imported_data).show() + self.append_imported_services).show() def on_import_m3u(self, item): """ Imports iptv from m3u files. """ @@ -1441,9 +1441,9 @@ class Application(Gtk.Application): channels = parse_m3u(response, Profile(self._profile)) if channels and self._bq_selected: - self.append_imported_data(channels) + self.append_imported_services(channels) - def append_imported_data(self, services): + def append_imported_services(self, services): bq_services = self._bouquets.get(self._bq_selected) self._fav_model.clear() for srv in services: