Last active
October 9, 2024 07:47
-
-
Save nktknshn/581ec96f032ff7569718d76eba262e52 to your computer and use it in GitHub Desktop.
quodlibet player alternaitve playlists picker with autcomplete
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Dict, Set | |
from gi.repository import Gtk, Gdk | |
from quodlibet.library.playlist import Playlist, PlaylistLibrary | |
from quodlibet.plugins.songsmenu import SongsMenuPlugin | |
from quodlibet import app,library | |
from quodlibet.qltk.window import Dialog | |
from quodlibet.qltk.msg import ErrorMessage, Icons | |
class AddToPlaylistDialog(Dialog): | |
def __init__(self, parent, playlists, songs): | |
super().__init__( | |
title="Add To Playlist", | |
) | |
self.playlists = playlists | |
self.songs = songs | |
self.picked: Set[str] = set() | |
self.liststore = Gtk.ListStore(str) | |
for match in self.playlists: | |
self.liststore.append([match]) | |
self.completion = Gtk.EntryCompletion() | |
self.completion.set_model(self.liststore) | |
self.completion.set_text_column(0) | |
self.completion.set_inline_selection(True) | |
self.completion.set_match_func(self.match_playlist_func) | |
self.songs_listbox = Gtk.ListBox() | |
self.songs_listbox.set_selection_mode(Gtk.SelectionMode.NONE) | |
picked_songs_listbox = Gtk.Box() | |
picked_songs_listbox.pack_start(self.songs_listbox, False, False, 6) | |
scrolled_picked_songs = Gtk.ScrolledWindow() | |
scrolled_picked_songs.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS) | |
scrolled_picked_songs.set_size_request(800, 500) | |
scrolled_picked_songs.add(picked_songs_listbox) | |
artists_set = set[str]() | |
artists_list = [] | |
for song in songs: | |
song_row = Gtk.ListBoxRow() | |
song_row_box = Gtk.Box( | |
orientation=Gtk.Orientation.HORIZONTAL, | |
spacing=6, | |
) | |
song_row.add(song_row_box) | |
label_text = f"{song['artist']} - {song['album']} - {song['title']}" | |
song_row_label = Gtk.Label(label=label_text) | |
song_row_label.set_halign(Gtk.Align.START) | |
if song['artist'].lower() not in artists_set: | |
artists_set.add(song['artist'].lower()) | |
artists_list.append(song['artist']) | |
song_row_box.pack_start(song_row_label, True, True, 0) | |
self.songs_listbox.add(song_row) | |
self.songs_listbox.show_all() | |
self.picked_listbox = Gtk.ListBox() | |
self.picked_listbox.set_selection_mode(Gtk.SelectionMode.NONE) | |
self.add_button('Cancel', Gtk.ResponseType.REJECT) | |
self.add_button('Apply', Gtk.ResponseType.APPLY) | |
self.set_default_response(Gtk.ResponseType.APPLY) | |
self.completion.connect("match-selected", self.on_match_selected) | |
self.completion.connect("no-matches", self.on_no_matches) | |
entry_box = Gtk.Box( | |
orientation=Gtk.Orientation.HORIZONTAL, | |
spacing=6, | |
) | |
self.entry = Gtk.Entry() | |
self.entry.set_completion(self.completion) | |
self.entry.connect("key-press-event", self.on_entry_key_pressed) | |
self.create_playlist_button = Gtk.Button(label="+") | |
self.create_playlist_button.connect("clicked", self.on_create_playlist_button_clicked) | |
self.create_playlist_button.set_sensitive(False) | |
entry_box.pack_start(self.entry, False, False, 0) | |
entry_box.pack_start(self.create_playlist_button, False, False, 0) | |
self.grid = Gtk.Grid() | |
# songs_listbox | entry | + | |
# --------------|------------- | |
# entry_artists | picked | |
lb = scrolled_picked_songs | |
self.grid.add(lb) | |
self.entry_artists = Gtk.Entry() | |
self.entry_artists.set_editable(False) | |
self.entry_artists.set_text(", ".join(artists_list)) | |
self.grid.attach(lb, 0, 0, 3, 5) | |
self.grid.attach_next_to(self.entry_artists, lb, Gtk.PositionType.BOTTOM, 1, 1) | |
# width, height | |
self.grid.attach_next_to(entry_box, lb, Gtk.PositionType.RIGHT, 1, 1) | |
self.grid.attach_next_to(self.picked_listbox, entry_box, Gtk.PositionType.BOTTOM, 1, 1) | |
# self.grid.attach(self.table, 1, 0, 2, 1) | |
self.vbox.add(self.grid) | |
self.show_all() | |
self.entry.grab_focus() | |
def on_entry_key_pressed(self, entry, event): | |
if event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_Return: | |
self.response(Gtk.ResponseType.APPLY) | |
return True | |
if event.keyval != Gdk.KEY_Return: | |
return False | |
return self.pick_playlist(self.entry.get_text()) | |
def on_no_matches(self, completion): | |
text = self.entry.get_text() | |
if len(text) == 0: | |
self.create_playlist_button.set_sensitive(False) | |
return | |
self.create_playlist_button.set_sensitive(True) | |
def on_match_selected(self, completion, model, it: Gtk.TreeIter): | |
picked_item = model.get_value(it, 0) | |
self.pick_playlist(picked_item) | |
def match_playlist_func(self, completion, key, iter): | |
item_text: str = self.liststore.get_value(iter, 0) | |
return key.lower() in item_text.lower() | |
def on_create_playlist_button_clicked(self, button): | |
text = self.entry.get_text() | |
if len(text) == 0: | |
return | |
app.library.playlists.create(text) | |
print(f"Created {text}") | |
self.playlists.append(text) | |
self.pick_playlist(text) | |
def create_playlist(self, pl_name: str): | |
pass | |
def on_remove_clicked(self, button, item_row, pl_name): | |
if pl_name not in self.picked: | |
return | |
self.picked_listbox.remove(item_row) | |
self.picked.remove(pl_name) | |
def pick_playlist(self, pl_name: str): | |
if pl_name not in self.playlists: | |
return | |
if pl_name in self.picked: | |
return | |
self.picked.add(pl_name) | |
item_row = Gtk.ListBoxRow() | |
item_box = Gtk.Box( | |
orientation=Gtk.Orientation.HORIZONTAL, | |
spacing=6, | |
) | |
item_row.add(item_box) | |
label = Gtk.Label(label=pl_name) | |
item_box.pack_start(label, True, True, 0) | |
remove_button = Gtk.Button(label="x") | |
remove_button.connect("clicked", self.on_remove_clicked, item_row, pl_name) | |
item_box.pack_start(remove_button, False, False, 0) | |
self.picked_listbox.add(item_row) | |
self.picked_listbox.show_all() | |
self.entry.set_text("") | |
self.entry.grab_focus() | |
class AddToPlaylist(SongsMenuPlugin): | |
PLUGIN_ICON = Icons.APPLICATION_UTILITIES | |
PLUGIN_ID = "AddToPlaylist" | |
PLUGIN_NAME = "Add To Playlist" | |
PLUGIN_DESC = "Add To Playlist Plugin" | |
def __init__(self, songs=None, library=None): | |
super().__init__(songs, library) | |
submenu = Gtk.Menu() | |
item = Gtk.MenuItem(label="Add") | |
submenu.append(item) | |
if submenu.get_children(): | |
self.set_submenu(submenu) | |
@property | |
def playlists_dict(self) -> Dict[str, Playlist]: | |
return { | |
pl.name: pl for pl in app.library.playlists | |
} | |
@property | |
def playlists(self) -> list[str]: | |
return [pl.name for pl in app.library.playlists] | |
@property | |
def playlists_sorted(self) -> list[str]: | |
return sorted(self.playlists) | |
def add_songs_to_playlists(self, songs, playlists): | |
pl_dict = self.playlists_dict | |
for pl_name in playlists: | |
pl = pl_dict.get(pl_name) | |
if pl is None: | |
continue | |
for song in songs: | |
has, _ = pl.has_songs([song]) | |
if has: | |
continue | |
pl.extend([song]) | |
def plugin_songs(self, songs): | |
dlg = AddToPlaylistDialog( | |
parent=self, | |
playlists=self.playlists_sorted, | |
songs=songs, | |
) | |
if dlg.run() == Gtk.ResponseType.APPLY and len(dlg.picked) > 0: | |
self.add_songs_to_playlists(songs, dlg.picked) | |
dlg.destroy() | |
@classmethod | |
def PluginPreferences(cls, parent): | |
vbox = Gtk.VBox(spacing=6) | |
return vbox |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment