diff --git a/app/tools/yt.py b/app/tools/yt.py index 9067b176..025db73a 100644 --- a/app/tools/yt.py +++ b/app/tools/yt.py @@ -129,6 +129,19 @@ class YouTube: return None, rsn + def get_yt_playlist(self, list_id, url=None): + """ Returns tuple from the playlist header and list of tuples (title, video id). """ + if self._settings.enable_yt_dl and url: + try: + self._yt_dl.update_options({"noplaylist": False, "extract_flat": True}) + info = self._yt_dl.get_info(url, skip_errors=False) + return info.get("title", ""), [(e.get("title", ""), e.get("id", "")) for e in info.get("entries", [])] + finally: + # Restoring default options + self._yt_dl.update_options({"noplaylist": True, "extract_flat": False}) + + return PlayListParser.get_yt_playlist(list_id) + class PlayListParser(HTMLParser): """ Very simple parser to handle YouTube playlist pages. """ @@ -139,6 +152,7 @@ class PlayListParser(HTMLParser): self._header = "" self._playlist = [] self._is_script = False + self._scr_start = ('var ytInitialData = ', 'window["ytInitialData"] = ') def handle_starttag(self, tag, attrs): if tag == "script": @@ -147,8 +161,11 @@ class PlayListParser(HTMLParser): def handle_data(self, data): if self._is_script: data = data.lstrip() - if data.startswith('window["ytInitialData"] = '): - data = data.split(";")[0].lstrip('window["ytInitialData"] = ') + if data.startswith(self._scr_start): + data = data.split(";")[0] + for s in self._scr_start: + data = data.lstrip(s) + try: resp = json.loads(data) except JSONDecodeError as e: @@ -205,6 +222,7 @@ class YouTubeDL: _DownloadError = None _LATEST_RELEASE_URL = "https://api.github.com/repos/ytdl-org/youtube-dl/releases/latest" _OPTIONS = {"noplaylist": True, # Single video instead of a playlist [ignoring playlist in URL]. + "extract_flat": False, # Do not resolve URLs, return the immediate result. "quiet": True, # Do not print messages to stdout. "simulate": True, # Do not download the video files. "cookiefile": "cookies.txt"} # File name where cookies should be read from and dumped to. @@ -316,8 +334,17 @@ class YouTubeDL: self._callback("Update process. Please wait.", False) return {}, "" + info = self.get_info(url, skip_errors) + fmts = info.get("formats", None) + if fmts: + return {Quality.get(int(fm["format_id"])): fm.get("url", "") for fm in fmts if + fm.get("format_id", "") in self._supported}, info.get("title", "") + + return {}, info.get("title", "") + + def get_info(self, url, skip_errors=False): try: - info = self._dl.extract_info(url, download=False) + return self._dl.extract_info(url, download=False) except URLError as e: log(str(e)) raise YouTubeException(e) @@ -325,13 +352,13 @@ class YouTubeDL: log(str(e)) if not skip_errors: raise YouTubeException(e) - else: - fmts = info.get("formats", None) - if fmts: - return {Quality.get(int(fm["format_id"])): fm.get("url", "") for fm in fmts if - fm.get("format_id", "") in self._supported}, info.get("title", "") - return {}, info.get("title", "") + def update_options(self, options): + self._dl.params.update(options) + + @property + def options(self): + return self._dl.params def flat(key, d): diff --git a/app/ui/iptv.py b/app/ui/iptv.py index 55a81498..345b8375 100644 --- a/app/ui/iptv.py +++ b/app/ui/iptv.py @@ -11,7 +11,7 @@ from app.commons import run_idle, run_task from app.eparser.ecommons import BqServiceType, Service from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT, StreamType, ENIGMA2_FAV_ID_FORMAT, get_fav_id, MARKER_FORMAT from app.settings import SettingsType -from app.tools.yt import PlayListParser, YouTubeException, YouTube +from app.tools.yt import YouTubeException, YouTube from .dialogs import Action, show_dialog, DialogType, get_dialogs_string, get_message from .main_helper import get_base_model, get_iptv_url, on_popup_menu from .uicommons import (Gtk, Gdk, TEXT_DOMAIN, UI_RESOURCES_PATH, IPTV_ICON, Column, IS_GNOME_SESSION, KeyboardKey, @@ -666,7 +666,9 @@ class YtListImportDialog: def update_refs_list(self): if self._yt_list_id: try: - self._yt_list_title, links = PlayListParser.get_yt_playlist(self._yt_list_id) + if not self._yt: + self._yt = YouTube.get_instance(self._settings) + self._yt_list_title, links = self._yt.get_yt_playlist(self._yt_list_id, self._url_entry.get_text()) except Exception as e: self.show_info_message(str(e), Gtk.MessageType.ERROR) return