ignore multiple *sort tags

This commit is contained in:
Martin Wagner 2022-02-13 22:15:54 +01:00
parent 15987d5ed6
commit d80604b250

View File

@ -26,6 +26,7 @@ import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import threading import threading
import functools import functools
import itertools
import datetime import datetime
import collections import collections
import os import os
@ -782,9 +783,8 @@ class Client(MPDClient):
self.searchadd("any", "") self.searchadd("any", "")
self._to_playlist(append, mode) self._to_playlist(append, mode)
def album_to_playlist(self, albumartist, albumartistsort, album, albumsort, date, mode="default"): def album_to_playlist(self, albumartist, album, date, mode="default"):
tag_filter=("albumartist", albumartist, "albumartistsort", albumartistsort, "album", album, "albumsort", albumsort, "date", date) self.filter_to_playlist(("albumartist", albumartist, "album", album, "date", date), mode)
self.filter_to_playlist(tag_filter, mode)
def artist_to_playlist(self, artist, genre, mode="default"): def artist_to_playlist(self, artist, genre, mode="default"):
def append(): def append():
@ -793,16 +793,13 @@ class Client(MPDClient):
else: else:
genre_filter=("genre", genre) genre_filter=("genre", genre)
if artist is None: if artist is None:
artists=self.get_artists(genre) artists=self.comp_list("albumartist", *genre_filter)
else: else:
artists=[artist] artists=[artist]
for albumartist, albumartistsort in artists: for albumartist in artists:
albums=self.list( albums=self.list("album", "albumartist", albumartist, *genre_filter, "group", "date")
"album", "albumartist", albumartist, "albumartistsort", albumartistsort,
*genre_filter, "group", "date", "group", "albumsort")
for album in albums: for album in albums:
self.findadd("albumartist", albumartist, "albumartistsort", albumartistsort, self.findadd("albumartist", albumartist, "album", album["album"], "date", album["date"])
"album", album["album"], "albumsort", album["albumsort"], "date", album["date"])
self._to_playlist(append, mode) self._to_playlist(append, mode)
def comp_list(self, *args): # simulates listing behavior of python-mpd2 1.0 def comp_list(self, *args): # simulates listing behavior of python-mpd2 1.0
@ -815,13 +812,6 @@ class Client(MPDClient):
else: else:
return([]) return([])
def get_artists(self, genre):
if genre is None:
artists=self.list("albumartist", "group", "albumartistsort")
else:
artists=self.list("albumartist", "genre", genre, "group", "albumartistsort")
return [(artist["albumartist"], artist["albumartistsort"]) for artist in artists]
def get_cover_path(self, song): def get_cover_path(self, song):
path=None path=None
song_file=song["file"] song_file=song["file"]
@ -1521,14 +1511,14 @@ class AlbumPopover(Gtk.Popover):
self.add(vbox) self.add(vbox)
vbox.show_all() vbox.show_all()
def open(self, albumartist, albumartistsort, album, albumsort, date, widget, x, y): def open(self, albumartist, album, date, widget, x, y):
self._rect.x=x self._rect.x=x
self._rect.y=y self._rect.y=y
self.set_pointing_to(self._rect) self.set_pointing_to(self._rect)
self.set_relative_to(widget) self.set_relative_to(widget)
self._scroll.set_max_content_height(4*widget.get_allocated_height()//7) self._scroll.set_max_content_height(4*widget.get_allocated_height()//7)
self._songs_list.clear() self._songs_list.clear()
tag_filter=("albumartist", albumartist, "albumartistsort", albumartistsort, "album", album, "albumsort", albumsort, "date", date) tag_filter=("albumartist", albumartist, "album", album, "date", date)
count=self._client.count(*tag_filter) count=self._client.count(*tag_filter)
duration=str(Duration(float(count["playtime"]))) duration=str(Duration(float(count["playtime"])))
length=int(count["songs"]) length=int(count["songs"])
@ -1763,9 +1753,9 @@ class SelectionList(TreeView):
self._selected_path=None self._selected_path=None
# store # store
# (item, weight, initial-letter, weight-initials, sort-string) # item, weight, initial-letter, weight-initials
self._store=Gtk.ListStore(str, Pango.Weight, str, Pango.Weight, str) self._store=Gtk.ListStore(str, Pango.Weight, str, Pango.Weight)
self._store.append([self.select_all_string, Pango.Weight.NORMAL, "", Pango.Weight.NORMAL, ""]) self._store.append([self.select_all_string, Pango.Weight.NORMAL, "", Pango.Weight.NORMAL])
self.set_model(self._store) self.set_model(self._store)
self._selection=self.get_selection() self._selection=self.get_selection()
@ -1786,7 +1776,7 @@ class SelectionList(TreeView):
def clear(self): def clear(self):
self._store.clear() self._store.clear()
self._store.append([self.select_all_string, Pango.Weight.NORMAL, "", Pango.Weight.NORMAL, ""]) self._store.append([self.select_all_string, Pango.Weight.NORMAL, "", Pango.Weight.NORMAL])
self._selected_path=None self._selected_path=None
self.emit("clear") self.emit("clear")
@ -1800,14 +1790,14 @@ class SelectionList(TreeView):
if item[0] is None: if item[0] is None:
char=item[1] char=item[1]
else: else:
self._store.insert_with_valuesv(-1, range(5), [item[0], Pango.Weight.NORMAL, char, Pango.Weight.BOLD, item[1]]) self._store.insert_with_valuesv(-1, range(4), [item[0], Pango.Weight.NORMAL, char, Pango.Weight.BOLD])
char="" char=""
def get_item_at_path(self, path): def get_item_at_path(self, path):
if path == Gtk.TreePath(0): if path == Gtk.TreePath(0):
return None return None
else: else:
return self._store[path][0,4] return self._store[path][0]
def length(self): def length(self):
return len(self._store)-1 return len(self._store)-1
@ -1820,7 +1810,7 @@ class SelectionList(TreeView):
row_num=len(self._store) row_num=len(self._store)
for i in range(0, row_num): for i in range(0, row_num):
path=Gtk.TreePath(i) path=Gtk.TreePath(i)
if self._store[path][0] == item[0] and self._store[path][4] == item[1]: if self._store[path][0] == item:
self.select_path(path) self.select_path(path)
break break
@ -1896,16 +1886,23 @@ class ArtistList(SelectionList):
def _refresh(self, *args): def _refresh(self, *args):
genre=self.genre_list.get_item_selected() genre=self.genre_list.get_item_selected()
if genre is not None: if genre is None:
genre=genre[0] artists=self._client.list("albumartistsort", "group", "albumartist")
artists=self._client.get_artists(genre) else:
self.set_items(artists) artists=self._client.list("albumartistsort", "genre", genre, "group", "albumartist")
filtered_artists=[]
for name, artist in itertools.groupby(((artist["albumartist"], artist["albumartistsort"]) for artist in artists), key=lambda x: x[0]):
filtered_artists.append(next(artist))
# ignore multiple albumartistsort values
if next(artist, None) is not None:
filtered_artists[-1]=(name, name)
self.set_items(filtered_artists)
if genre is not None: if genre is not None:
self.select_all() self.select_all()
else: else:
song=self._client.currentsong() song=self._client.currentsong()
if song: if song:
artist=(song["albumartist"][0],song["albumartistsort"][0]) artist=song["albumartist"][0]
self.select(artist) self.select(artist)
else: else:
if self.length() > 0: if self.length() > 0:
@ -1930,8 +1927,6 @@ class ArtistList(SelectionList):
def get_artist_at_path(self, path): def get_artist_at_path(self, path):
genre=self.genre_list.get_item_selected() genre=self.genre_list.get_item_selected()
artist=self.get_item_at_path(path) artist=self.get_item_at_path(path)
if genre is not None:
genre=genre[0]
return (artist, genre) return (artist, genre)
def get_artist_selected(self): def get_artist_selected(self):
@ -1970,14 +1965,16 @@ class AlbumLoadingThread(threading.Thread):
self._genre=genre self._genre=genre
def _get_albums(self): def _get_albums(self):
for albumartist, albumartistsort in self._artists: for albumartist in self._artists:
albums=main_thread_function(self._client.list)( albums=main_thread_function(self._client.list)(
"album", "albumartist", albumartist, "albumartistsort", albumartistsort, "albumsort", "albumartist", albumartist, *self._genre_filter, "group", "date", "group", "album")
*self._genre_filter, "group", "date", "group", "albumsort") for _, album in itertools.groupby(albums, key=lambda x: (x["album"], x["date"])):
for album in albums: tmp=next(album)
album["albumartist"]=albumartist # ignore multiple albumsort values
album["albumartistsort"]=albumartistsort if next(album, None) is None:
yield album yield (albumartist, tmp["album"], tmp["date"], tmp["albumsort"])
else:
yield (albumartist, tmp["album"], tmp["date"], tmp["album"])
def set_callback(self, callback): def set_callback(self, callback):
self._callback=callback self._callback=callback
@ -2002,7 +1999,7 @@ class AlbumLoadingThread(threading.Thread):
else: else:
self._genre_filter=("genre", self._genre) self._genre_filter=("genre", self._genre)
if self._artist is None: if self._artist is None:
self._artists=self._client.get_artists(self._genre) self._artists=self._client.comp_list("albumartist")
else: else:
self._artists=[self._artist] self._artists=[self._artist]
super().start() super().start()
@ -2011,16 +2008,15 @@ class AlbumLoadingThread(threading.Thread):
# temporarily display all albums with fallback cover # temporarily display all albums with fallback cover
fallback_cover=GdkPixbuf.Pixbuf.new_from_file_at_size(FALLBACK_COVER, self._cover_size, self._cover_size) fallback_cover=GdkPixbuf.Pixbuf.new_from_file_at_size(FALLBACK_COVER, self._cover_size, self._cover_size)
add=main_thread_function(self._store.append) add=main_thread_function(self._store.append)
for i, album in enumerate(self._get_albums()): for i, (albumartist, album, date, albumsort) in enumerate(self._get_albums()):
# album label # album label
if album["date"]: if date:
display_label=f"<b>{GLib.markup_escape_text(album['album'])}</b> ({GLib.markup_escape_text(album['date'])})" display_label=f"<b>{GLib.markup_escape_text(album)}</b> ({GLib.markup_escape_text(date)})"
else: else:
display_label=f"<b>{GLib.markup_escape_text(album['album'])}</b>" display_label=f"<b>{GLib.markup_escape_text(album)}</b>"
display_label_artist=f"{display_label}\n{GLib.markup_escape_text(album['albumartist'])}" display_label_artist=f"{display_label}\n{GLib.markup_escape_text(albumartist)}"
# add album # add album
add([fallback_cover,display_label,display_label_artist, add([fallback_cover, display_label, display_label_artist, albumartist, album, date, albumsort])
album["albumartist"],album["albumartistsort"],album["album"],album["albumsort"],album["date"]])
if i%10 == 0: if i%10 == 0:
if self._stop_flag: if self._stop_flag:
self._exit() self._exit()
@ -2028,7 +2024,7 @@ class AlbumLoadingThread(threading.Thread):
GLib.idle_add(self._progress_bar.pulse) GLib.idle_add(self._progress_bar.pulse)
# sort model # sort model
if main_thread_function(self._settings.get_boolean)("sort-albums-by-year"): if main_thread_function(self._settings.get_boolean)("sort-albums-by-year"):
main_thread_function(self._store.set_sort_column_id)(7, Gtk.SortType.ASCENDING) main_thread_function(self._store.set_sort_column_id)(5, Gtk.SortType.ASCENDING)
else: else:
main_thread_function(self._store.set_sort_column_id)(6, Gtk.SortType.ASCENDING) main_thread_function(self._store.set_sort_column_id)(6, Gtk.SortType.ASCENDING)
GLib.idle_add(self._iconview.set_model, self._store) GLib.idle_add(self._iconview.set_model, self._store)
@ -2040,9 +2036,7 @@ class AlbumLoadingThread(threading.Thread):
return None return None
else: else:
self._client.restrict_tagtypes("albumartist", "album") self._client.restrict_tagtypes("albumartist", "album")
song=self._client.find("albumartist", row[3], "albumartistsort", song=self._client.find("albumartist", row[3], "album", row[4], "date", row[5], "window", "0:1")[0]
row[4], "album", row[5], "albumsort", row[6],
"date", row[7], "window", "0:1")[0]
self._client.tagtypes("all") self._client.tagtypes("all")
return self._client.get_cover(song) return self._client.get_cover(song)
covers=[] covers=[]
@ -2086,8 +2080,8 @@ class AlbumList(Gtk.IconView):
self._client=client self._client=client
self._artist_list=artist_list self._artist_list=artist_list
# cover, display_label, display_label_artist, albumartist, albumartistsort, album, albumsort, date # cover, display_label, display_label_artist, albumartist, album, date, albumsort
self._store=Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, str, str, str, str, str) self._store=Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, str, str, str, str)
self._store.set_default_sort_func(lambda *args: 0) self._store.set_default_sort_func(lambda *args: 0)
self.set_model(self._store) self.set_model(self._store)
@ -2134,7 +2128,7 @@ class AlbumList(Gtk.IconView):
row_num=len(self._store) row_num=len(self._store)
for i in range(0, row_num): for i in range(0, row_num):
path=Gtk.TreePath(i) path=Gtk.TreePath(i)
if self._store[path][5] == album: if self._store[path][4] == album:
self.set_cursor(path, None, False) self.set_cursor(path, None, False)
self.select_path(path) self.select_path(path)
self.scroll_to_path(path, True, 0, 0) self.scroll_to_path(path, True, 0, 0)
@ -2147,7 +2141,7 @@ class AlbumList(Gtk.IconView):
def _sort_settings(self, *args): def _sort_settings(self, *args):
if not self._cover_thread.is_alive(): if not self._cover_thread.is_alive():
if self._settings.get_boolean("sort-albums-by-year"): if self._settings.get_boolean("sort-albums-by-year"):
self._store.set_sort_column_id(7, Gtk.SortType.ASCENDING) self._store.set_sort_column_id(5, Gtk.SortType.ASCENDING)
else: else:
self._store.set_sort_column_id(6, Gtk.SortType.ASCENDING) self._store.set_sort_column_id(6, Gtk.SortType.ASCENDING)
@ -2165,7 +2159,7 @@ class AlbumList(Gtk.IconView):
callback() callback()
def _path_to_playlist(self, path, mode="default"): def _path_to_playlist(self, path, mode="default"):
tags=self._store[path][3:8] tags=self._store[path][3:6]
self._client.album_to_playlist(*tags, mode) self._client.album_to_playlist(*tags, mode)
def _on_button_press_event(self, widget, event): def _on_button_press_event(self, widget, event):
@ -2180,7 +2174,7 @@ class AlbumList(Gtk.IconView):
v=self.get_vadjustment().get_value() v=self.get_vadjustment().get_value()
h=self.get_hadjustment().get_value() h=self.get_hadjustment().get_value()
if path is not None: if path is not None:
tags=self._store[path][3:8] tags=self._store[path][3:6]
# when using "button-press-event" in iconview popovers only show up in combination with idle_add (bug in GTK?) # when using "button-press-event" in iconview popovers only show up in combination with idle_add (bug in GTK?)
GLib.idle_add(self._album_popover.open, *tags, widget, event.x-h, event.y-v) GLib.idle_add(self._album_popover.open, *tags, widget, event.x-h, event.y-v)
@ -2201,7 +2195,7 @@ class AlbumList(Gtk.IconView):
rect=self.get_allocation() rect=self.get_allocation()
x=max(min(rect.x+cell.width//2, rect.x+rect.width), rect.x) x=max(min(rect.x+cell.width//2, rect.x+rect.width), rect.x)
y=max(min(cell.y+cell.height//2, rect.y+rect.height), rect.y) y=max(min(cell.y+cell.height//2, rect.y+rect.height), rect.y)
tags=self._store[path][3:8] tags=self._store[path][3:6]
self._album_popover.open(*tags, self, x, y) self._album_popover.open(*tags, self, x, y)
def add_to_playlist(self, mode): def add_to_playlist(self, mode):
@ -2254,7 +2248,7 @@ class Browser(Gtk.Paned):
if artist is None and not force: # all artists selected if artist is None and not force: # all artists selected
self._artist_list.highlight_selected() self._artist_list.highlight_selected()
else: # one artist selected else: # one artist selected
self._artist_list.select((song["albumartist"][0],song["albumartistsort"][0])) self._artist_list.select(song["albumartist"][0])
self._album_list.scroll_to_current_album() self._album_list.scroll_to_current_album()
else: else:
self._genre_list.deactivate() self._genre_list.deactivate()
@ -2641,8 +2635,7 @@ class CoverEventBox(Gtk.EventBox):
if self._client.connected(): if self._client.connected():
song=self._client.currentsong() song=self._client.currentsong()
if song: if song:
tags=(song["albumartist"][0], song["albumartistsort"][0], tags=(song["albumartist"][0], song["album"][0], song["date"][0])
song["album"][0], song["albumsort"][0], song["date"][0])
if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS: if event.button == 1 and event.type == Gdk.EventType.BUTTON_PRESS:
self._client.album_to_playlist(*tags) self._client.album_to_playlist(*tags)
elif event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS: elif event.button == 1 and event.type == Gdk.EventType._2BUTTON_PRESS: