added quality selection for yt playlist

This commit is contained in:
DYefremov
2019-08-17 22:53:05 +03:00
parent fb6951896f
commit d976e02cf6
3 changed files with 80 additions and 22 deletions

View File

@@ -13,6 +13,8 @@ _YT_LIST_PATTERN = re.compile(r"https://www.youtube.com/.+?(?:list=)([\w-]{23,})
_YT_VIDEO_PATTERN = re.compile(r"https://r\d+---sn-[\w]{10}-[\w]{3,5}.googlevideo.com/videoplayback?.*")
_HEADERS = {"User-Agent": "Mozilla/5.0"}
Quality = {137: "1080p", 136: "720p", 135: "480p", 134: "360p", 133: "240p", 160: "144p", 0: "0p"}
class YouTube:
""" Helper class for working with YouTube service. """
@@ -39,7 +41,7 @@ class YouTube:
def get_yt_link(video_id):
""" Getting link to YouTube video by id.
returns tuple from the video link and title
returns tuple from the video links dict and title
"""
req = Request("https://youtube.com/get_video_info?video_id={}".format(video_id), headers=_HEADERS)
with urllib.request.urlopen(req, timeout=2) as resp:
@@ -56,29 +58,28 @@ class YouTube:
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
fmts = streaming_data.get("adaptiveFormats", 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
urls = {Quality[i["itag"]]: i["url"] for i in
filter(lambda i: i.get("itag", -1) in Quality, fmts)}
if url and title:
return url, title.replace("+", " ")
if urls and title:
return urls, 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("&"))}
url, title = s_map.get("url", None), out.get("title", None)
return urllib.request.unquote(url) if url else "", title.replace("+", " ") if title else ""
url, title = urllib.request.unquote(url) if url else "", title.replace("+", " ") if title else ""
if url and title:
return {Quality[0]: url}, title.replace("+", " ")
rsn = out.get("reason", None)
rsn = rsn.replace("+", " ") if rsn else ""
log("{}: Getting link to video with id {} filed! Cause: {}".format(__class__.__name__, video_id, rsn))
return "", rsn
return None, rsn
class PlayListParser(HTMLParser):

View File

@@ -1228,6 +1228,35 @@ Author: Dmitriy Yefremov
<column type="gchararray"/>
</columns>
</object>
<object class="GtkListStore" id="yt_quality_liststore">
<columns>
<!-- column-name quality -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Auto</col>
</row>
<row>
<col id="0">1080p</col>
</row>
<row>
<col id="0">720p</col>
</row>
<row>
<col id="0">480p</col>
</row>
<row>
<col id="0">360p</col>
</row>
<row>
<col id="0">240p</col>
</row>
<row>
<col id="0">144p</col>
</row>
</data>
</object>
<object class="GtkWindow" id="yt_import_dialog_window">
<property name="width_request">480</property>
<property name="can_focus">False</property>
@@ -1270,8 +1299,7 @@ Author: Dmitriy Yefremov
</child>
<child>
<object class="GtkButton" id="yt_import_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="visible">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Import</property>
@@ -1290,6 +1318,25 @@ Author: Dmitriy Yefremov
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="yt_quality_combobox">
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Desired video quality</property>
<property name="model">yt_quality_liststore</property>
<property name="active">0</property>
<property name="id_column">0</property>
<child>
<object class="GtkCellRendererText" id="yt_quality_renderer"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<child>

View File

@@ -14,7 +14,7 @@ from app.commons import run_idle, run_task, log
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.properties import Profile
from app.tools.yt import YouTube, PlayListParser
from app.tools.yt import YouTube, PlayListParser, Quality
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
@@ -235,7 +235,8 @@ class IptvDialog:
self._name_entry.set_text(title)
if link:
entry.set_text(link)
# TODO implement the choice of quality.
entry.set_text(link[sorted(link, key=lambda x: int(x.rstrip("p")), reverse=True)[0]])
else:
msg = get_message("Get link error:") + " No link received for id: {}".format(video_id)
self.show_info_message(msg, Gtk.MessageType.ERROR)
@@ -560,8 +561,8 @@ class YtListImportDialog:
builder = Gtk.Builder()
builder.set_translation_domain(TEXT_DOMAIN)
builder.add_objects_from_string(get_dialogs_string(_UI_PATH).format(use_header=IS_GNOME_SESSION),
("yt_import_dialog_window", "yt_liststore", "yt_popup_menu",
"remove_selection_image"))
("yt_import_dialog_window", "yt_liststore", "yt_quality_liststore",
"yt_popup_menu", "remove_selection_image"))
builder.connect_signals(handlers)
self._dialog = builder.get_object("yt_import_dialog_window")
@@ -576,6 +577,11 @@ class YtListImportDialog:
self._url_entry = builder.get_object("yt_url_entry")
self._receive_button = builder.get_object("yt_receive_button")
self._import_button = builder.get_object("yt_import_button")
self._quality_box = builder.get_object("yt_quality_combobox")
self._quality_model = builder.get_object("yt_quality_liststore")
self._import_button.bind_property("visible", self._quality_box, "visible")
self._import_button.bind_property("sensitive", self._quality_box, "sensitive")
self._receive_button.bind_property("sensitive", self._import_button, "sensitive")
# style
self._style_provider = Gtk.CssProvider()
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
@@ -650,7 +656,9 @@ class YtListImportDialog:
for l in links:
yield self._model.append((l[0], l[1], True, None))
self._yt_count_label.set_text(str(len(self._model)))
size = len(self._model)
self._yt_count_label.set_text(str(size))
self._import_button.set_visible(size)
yield True
@run_idle
@@ -666,9 +674,12 @@ class YtListImportDialog:
mk = Service(None, None, None, title, *aggr[0:3], BqServiceType.MARKER.name, *aggr, data_id, fav_id, None)
srvs.append(mk)
for l in links:
fav_id = get_fav_id(*l, self._profile)
srv = Service(None, None, IPTV_ICON, l[1], *aggr[0:3], BqServiceType.IPTV.name, *aggr, None, fav_id, None)
act = self._quality_model.get_value(self._quality_box.get_active_iter(), 0)
for link in links:
lnk, title = link
ln = lnk.get(act) if act in lnk else lnk[sorted(lnk, key=lambda x: int(x.rstrip("p")), reverse=True)[0]]
fav_id = get_fav_id(ln, title, self._profile)
srv = Service(None, None, IPTV_ICON, title, *aggr[0:3], BqServiceType.IPTV.name, *aggr, None, fav_id, None)
srvs.append(srv)
self.appender(srvs)
@@ -676,7 +687,6 @@ class YtListImportDialog:
def update_active_elements(self, sensitive):
self._url_entry.set_sensitive(sensitive)
self._receive_button.set_sensitive(sensitive)
self._import_button.set_sensitive(sensitive)
def show_invisible_elements(self):
self._list_view_scrolled_window.set_visible(True)