From 7cdf6ca765ab32a84c319f4c40cf132521dd0d3b Mon Sep 17 00:00:00 2001 From: Jakob Meier Date: Fri, 8 Mar 2024 20:12:52 +0100 Subject: [PATCH] fetch video data on separate thread when creating/picking playlists --- melon/playlist/create.py | 38 ++++++++++++++++++--- melon/playlist/pick.py | 69 +++++++++++++++++++++++++-------------- melon/widgets/feeditem.py | 10 ++++-- melon/window.py | 13 +++----- 4 files changed, 91 insertions(+), 39 deletions(-) diff --git a/melon/playlist/create.py b/melon/playlist/create.py index bfbafe4..2785a7b 100644 --- a/melon/playlist/create.py +++ b/melon/playlist/create.py @@ -4,17 +4,16 @@ gi.require_version('Gtk', '4.0') gi.require_version('Adw', '1') from gi.repository import Gtk, Adw, Gio, GLib from gettext import gettext as _ +import threading from melon.widgets.iconbutton import IconButton from melon.widgets.simpledialog import SimpleDialog from melon.widgets.feeditem import AdaptiveFeedItem -from melon.models import new_local_playlist +from melon.models import new_local_playlist, Video from melon.servers.utils import get_app_settings class PlaylistCreatorDialog(SimpleDialog): - def __init__(self, video=None, *args, **kwargs): - super().__init__(*args, **kwargs) - self.set_title(_("New Playlist")) + def display(self, video=None): page = Adw.PreferencesPage() self.content = [] if not video is None: @@ -73,6 +72,37 @@ class PlaylistCreatorDialog(SimpleDialog): self.toolbar_view.add_bottom_bar(bottom_bar) self.set_widget(page) + def background(self, target=None): + video = None + if isinstance(target, Video): + # target is already a video object + video = target + elif not target is None: + # we have to fetch the video ourselves + server_id = target[0] + video_id = target[1] + servers = get_servers_list() + if server_id in servers: + instance = get_server_instance(servers[server_id]) + video = instance.get_video_info(video_id) + GLib.idle_add(self.display, video) + def __init__(self, video=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_title(_("New Playlist")) + + # show spinner + # will be cleared by display + spinner = Gtk.Spinner() + spinner.set_size_request(50, 50) + spinner.start() + cb = Gtk.CenterBox() + cb.set_center_widget(spinner) + self.set_widget(cb) + + # start background thread + self.thread = threading.Thread(target=self.background, args=[video]) + self.thread.daemon = True + self.thread.start() def create_playlist(self, x): new_local_playlist( diff --git a/melon/playlist/pick.py b/melon/playlist/pick.py index fedc916..0a54cde 100644 --- a/melon/playlist/pick.py +++ b/melon/playlist/pick.py @@ -5,12 +5,13 @@ gi.require_version('Adw', '1') from gi.repository import Gtk, Adw, Gio, GLib, Gdk from unidecode import unidecode from gettext import gettext as _ +import threading -from melon.widgets.feeditem import AdaptiveFeedItem +from melon.widgets.feeditem import AdaptiveFeedItem, AdaptivePlaylistFeedItem from melon.widgets.iconbutton import IconButton from melon.widgets.simpledialog import SimpleDialog from melon.models import get_playlists, PlaylistWrapperType, add_to_local_playlist -from melon.servers.utils import get_app_settings, pixbuf_from_url +from melon.servers.utils import get_app_settings, pixbuf_from_url, get_servers_list, get_server_instance from melon.playlist.create import PlaylistCreatorDialog class PlaylistPickerDialog(SimpleDialog): @@ -18,9 +19,8 @@ class PlaylistPickerDialog(SimpleDialog): diag = PlaylistCreatorDialog(video) self.hide() diag.show() - def __init__(self, video, *args, **kwargs): - super().__init__(*args, **kwargs) - self.set_title(_("Add to Playlist")) + + def display(self, video): page = Adw.PreferencesPage() # use preference group as preview for video element @@ -58,25 +58,9 @@ class PlaylistPickerDialog(SimpleDialog): for playlist in get_playlists(): if playlist.type == PlaylistWrapperType.EXTERNAL: continue - row = Adw.ActionRow() - row.set_title(unidecode(playlist.inner.title).replace("&","&")) - row.set_subtitle(unidecode(playlist.inner.description).replace("&","&")) - pixbuf = None - if app_conf["show_images_in_feed"]: - pixbuf = pixbuf_from_url(playlist.inner.thumbnail) - avatar = Adw.Avatar() - avatar.set_size(48) - if not pixbuf is None: - texture = Gdk.Texture.new_for_pixbuf(pixbuf) - avatar.set_custom_image(texture) - else: - avatar.set_show_initials(True) - avatar.set_text(playlist.inner.title) - row.add_prefix(avatar) - row.set_activatable(True) - row.connect( - "activated", - pass_me( + row = AdaptivePlaylistFeedItem( + playlist, + onClick=pass_me( lambda _, playlist, video: add_to_local_playlist(playlist.inner.id, video) or self.hide(), playlist, video) ) @@ -98,5 +82,42 @@ class PlaylistPickerDialog(SimpleDialog): self.toolbar_view.add_bottom_bar(bottom_bar) self.set_widget(page) + + def background(self, target=None): + video = None + + if not target is None: + server_id = target[0] + video_id = target[1] + servers = get_servers_list() + if server_id in servers: + instance = get_server_instance(servers[server_id]) + video = instance.get_video_info(video_id) + + if not video is None: + GLib.idle_add(self.display, video) + + def __init__(self, video, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_title(_("Add to Playlist")) + + # should never happen but still + if video is None: + self.hide() + + # show spinner + # will be cleared by display + spinner = Gtk.Spinner() + spinner.set_size_request(50, 50) + spinner.start() + cb = Gtk.CenterBox() + cb.set_center_widget(spinner) + self.set_widget(cb) + + # start background thread + self.thread = threading.Thread(target=self.background, args=[video]) + self.thread.daemon = True + self.thread.start() + def pass_me(func, *args): return lambda x: func(x, *args) diff --git a/melon/widgets/feeditem.py b/melon/widgets/feeditem.py index 0fba985..819f126 100644 --- a/melon/widgets/feeditem.py +++ b/melon/widgets/feeditem.py @@ -81,11 +81,17 @@ class AdaptiveFeedItem(Adw.ActionRow): class AdaptivePlaylistFeedItem(Adw.ActionRow): - def __init__(self, playlist:PlaylistWrapper, show_preview=True, *args, **kwargs): + def __init__(self, playlist:PlaylistWrapper, show_preview=True, onClick=None, *args, **kwargs): super().__init__(*args, **kwargs) self.set_title(unidecode(playlist.inner.title).replace("&","&")) self.set_subtitle(unidecode(playlist.inner.description).replace("&","&")) - if playlist.type == PlaylistWrapperType.EXTERNAL: + if not onClick is None: + # use custom click callback + self.connect( + "activated", + onClick + ) + elif playlist.type == PlaylistWrapperType.EXTERNAL: self.set_action_name("win.browse_playlist") self.set_action_target_value( GLib.Variant("as", [playlist.inner.server, playlist.inner.id])) diff --git a/melon/window.py b/melon/window.py index 6c29543..310e791 100644 --- a/melon/window.py +++ b/melon/window.py @@ -48,11 +48,9 @@ class MainWindow(Adw.ApplicationWindow): server_id = prefs[0] video_id = prefs[1] servers = get_servers_list() - if server_id in servers: - instance = get_server_instance(servers[server_id]) - video = instance.get_video_info(video_id) - diag = PlaylistPickerDialog(video) - diag.show() + video = (server_id, video_id) + diag = PlaylistPickerDialog(video) + diag.show() def open_playlist_creator(self, action, prefs): act = action.get_name() # video to be automatically added (if available) @@ -61,10 +59,7 @@ class MainWindow(Adw.ApplicationWindow): # open new playlist dialog & add video to said playlist server_id = prefs[0] video_id = prefs[1] - servers = get_servers_list() - if server_id in servers: - instance = get_server_instance(servers[server_id]) - video = instance.get_video_info(video_id) + video = (server_id, video_id) # create basic dialog diag = PlaylistCreatorDialog(video) diag.show() -- 2.38.5