[gnome-music/wip/mschraal/grltracker-rework-disc-splitting] Rework Tracker album query
- From: Marinus Schraal <mschraal src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-music/wip/mschraal/grltracker-rework-disc-splitting] Rework Tracker album query
- Date: Tue, 10 May 2022 11:03:21 +0000 (UTC)
commit 6d49590b936976c5484ba2a88939356b1dd0e29f
Author: Marinus Schraal <mschraal gnome org>
Date: Tue May 3 21:12:23 2022 +0200
Rework Tracker album query
Do one large query per album instead of several.
gnomemusic/corealbum.py | 61 ++++++++++++------
gnomemusic/coredisc.py | 33 ++++------
gnomemusic/coregrilo.py | 10 +++
gnomemusic/grilowrappers/grltrackerwrapper.py | 93 ++++++++++++++++++++++++++-
4 files changed, 155 insertions(+), 42 deletions(-)
---
diff --git a/gnomemusic/corealbum.py b/gnomemusic/corealbum.py
index af3a1198c..c62aec7ff 100644
--- a/gnomemusic/corealbum.py
+++ b/gnomemusic/corealbum.py
@@ -31,6 +31,7 @@ from gi.repository import Gio, Grl, Gtk, GObject
import gnomemusic.utils as utils
from gnomemusic.albumart import AlbumArt
+from gnomemusic.coredisc import CoreDisc
class CoreAlbum(GObject.GObject):
@@ -57,8 +58,11 @@ class CoreAlbum(GObject.GObject):
self._application = application
self._coregrilo = application.props.coregrilo
+
self._model = None
self._selected = False
+ self._songs_model = Gtk.FilterListModel.new(
+ application.props.coremodel.props.songs, Gtk.AnyFilter())
self._thumbnail = None
self.update(media)
@@ -75,6 +79,23 @@ class CoreAlbum(GObject.GObject):
self.props.url = media.get_url()
self.props.year = utils.get_media_year(media)
+ def _update_disc_model(self) -> None:
+ discs = set()
+ for coresong in self._songs_model:
+ discs.add(coresong.props.media.get_album_disc_number())
+
+ model_discs = []
+ for coredisc in self._model:
+ model_discs.append(coredisc.props.disc_nr)
+
+ missing = discs.difference(model_discs)
+
+ for disc_nr in missing:
+ self._model.props.model.append(
+ CoreDisc(
+ self._application, self.props.media, disc_nr,
+ self._songs_model))
+
def _get_album_model(self):
disc_model = Gio.ListStore()
disc_model_sort = Gtk.SortListModel.new(disc_model)
@@ -101,30 +122,28 @@ class CoreAlbum(GObject.GObject):
flags=GObject.ParamFlags.READABLE)
def model(self):
if self._model is None:
- self._model = self._get_album_model()
- self._model.connect("items-changed", self._on_list_items_changed)
- self._model.items_changed(0, 0, self._model.get_n_items())
+ self._model = Gtk.SortListModel.new(Gio.ListStore())
- return self._model
-
- def _on_list_items_changed(self, model, position, removed, added):
- with self.freeze_notify():
- for coredisc in model:
- coredisc.props.selected = self.props.selected
-
- if added > 0:
- for i in range(added):
- coredisc = model[position + i]
- coredisc.connect(
- "notify::duration", self._on_duration_changed)
+ self._songs_model.connect("items-changed", self._on_songs_list_changed)
+ self._coregrilo.get_album_songs(self.props.media, self._songs_model)
- def _on_duration_changed(self, coredisc, duration):
- duration = 0
-
- for coredisc in self.props.model:
- duration += coredisc.props.duration
+ return self._model
- self.props.duration = duration
+ def _on_songs_list_changed(
+ self, model: Gtk.FilterListModel, position: int, removed: int,
+ added: int) -> None:
+ with model.freeze_notify():
+ #for song in model:
+ # coredisc.props.selected = self.props.selected
+
+ if (added > 0
+ or removed > 0):
+ self._update_disc_model()
+ duration = 0
+ for coresong in model:
+ duration += coresong.props.duration
+
+ self.props.duration = duration
@GObject.Property(type=bool, default=False)
def selected(self):
diff --git a/gnomemusic/coredisc.py b/gnomemusic/coredisc.py
index ece33ba2c..64b827cbd 100644
--- a/gnomemusic/coredisc.py
+++ b/gnomemusic/coredisc.py
@@ -33,7 +33,7 @@ class CoreDisc(GObject.GObject):
duration = GObject.Property(type=int, default=None)
media = GObject.Property(type=Grl.Media, default=None)
- def __init__(self, application, media, nr):
+ def __init__(self, application, media, nr, album_model=None):
"""Initialize a CoreDisc object
:param Application application: The application object
@@ -42,6 +42,7 @@ class CoreDisc(GObject.GObject):
"""
super().__init__()
+ self._album_model = album_model
self._coregrilo = application.props.coregrilo
self._coremodel = application.props.coremodel
self._filter_model = None
@@ -66,32 +67,24 @@ class CoreDisc(GObject.GObject):
else:
return Gtk.Ordering.EQUAL
+ def _disc_nr_filter(coresong):
+ cs_dn = coresong.props.media.get_album_disc_number()
+ return cs_dn == self.props.disc_nr
+
if self._model is None:
self._filter_model = Gtk.FilterListModel.new(
- self._coremodel.props.songs)
- self._filter_model.set_filter(Gtk.AnyFilter())
+ self._album_model)
+ filter = Gtk.CustomFilter()
+ filter.set_filter_func(_disc_nr_filter)
+ self._filter_model.set_filter(filter)
self._model = Gtk.SortListModel.new(self._filter_model)
- disc_sorter = Gtk.CustomSorter()
- disc_sorter.set_sort_func(_disc_sort)
- self._model.set_sorter(disc_sorter)
-
- self._model.connect("items-changed", self._on_disc_changed)
-
- self._coregrilo.get_album_disc(
- self.props.media, self.props.disc_nr, self._filter_model)
+ song_sorter = Gtk.CustomSorter()
+ song_sorter.set_sort_func(_disc_sort)
+ self._model.set_sorter(song_sorter)
return self._model
- def _on_disc_changed(self, model, position, removed, added):
- with self.freeze_notify():
- duration = 0
- for coresong in model:
- coresong.props.selected = self._selected
- duration += coresong.props.duration
-
- self.props.duration = duration
-
@GObject.Property(
type=bool, default=False, flags=GObject.ParamFlags.READWRITE)
def selected(self):
diff --git a/gnomemusic/coregrilo.py b/gnomemusic/coregrilo.py
index e5c17acab..ccb4db3a3 100644
--- a/gnomemusic/coregrilo.py
+++ b/gnomemusic/coregrilo.py
@@ -204,6 +204,16 @@ class CoreGrilo(GObject.GObject):
source = media.get_source()
self._wrappers[source].get_album_disc(media, discnr, model)
+ def get_album_songs(
+ self, media: Grl.Media, model: Gtk.FilterListModel) -> None:
+ """Get all songs from an album
+
+ :param Grl.Media media: An album
+ :param Gtk.FilterListModel model: The model to fill
+ """
+ source = media.get_source()
+ self._wrappers[source].get_album_songs(media, model)
+
def writeback(self, media, key):
"""Store the values associated with the key.
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index d7c300744..a7e5c03b7 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -51,7 +51,7 @@ class GrlTrackerWrapper(GObject.GObject):
"""Wrapper for the Grilo Tracker source.
"""
- _SPLICE_SIZE: int = 100
+ _SPLICE_SIZE: int = 1000
_METADATA_ALBUM_CHANGED_KEYS: List[int] = [
Grl.METADATA_KEY_ARTIST,
@@ -889,6 +889,97 @@ class GrlTrackerWrapper(GObject.GObject):
self.props.source.query(
query, metadata_keys, self._fast_options, _callback)
+ def get_album_songs(
+ self, media: Grl.Media, model: Gtk.FilterListModel) -> None:
+ """Get all songs of an album
+
+ :param Grl.Media media: The media with the album id
+ :param Gtk.FilterListModel model: The model to fill
+ """
+ album_id = media.get_id()
+
+ query = """
+ SELECT
+ ?type ?id ?url ?title
+ ?artist ?album
+ ?duration ?trackNumber ?albumDiscNumber
+ ?publicationDate
+ nie:usageCounter(?id) AS ?playCount
+ ?tag AS ?favorite
+ WHERE {
+ SERVICE <dbus:%(miner_fs_busname)s> {
+ GRAPH tracker:Audio {
+ SELECT DISTINCT
+ %(media_type)s AS ?type
+ ?song AS ?id
+ nie:isStoredAs(?song) AS ?url
+ nie:title(?song) AS ?title
+ nmm:artistName(nmm:artist(?song)) AS ?artist
+ nie:title(nmm:musicAlbum(?song)) AS ?album
+ nfo:duration(?song) AS ?duration
+ nmm:trackNumber(?song) AS ?trackNumber
+ nmm:setNumber(nmm:musicAlbumDisc(?song))
+ AS ?albumDiscNumber
+ YEAR(?date) AS ?publicationDate
+ WHERE {
+ ?song a nmm:MusicPiece ;
+ nmm:musicAlbum ?album .
+ OPTIONAL { ?song nie:contentCreated ?date . }
+ FILTER (
+ ?album = <%(album_id)s>
+ )
+ %(location_filter)s
+ }
+ ORDER BY ?albumDiscNumber ?trackNumber
+ }
+ }
+ OPTIONAL {
+ ?id nao:hasTag ?tag .
+ FILTER (?tag = nao:predefined-tag-favorite)
+ }
+ }
+ """.replace('\n', ' ').strip() % {
+ "media_type": int(Grl.MediaType.AUDIO),
+ 'album_id': album_id,
+ 'location_filter': self._tracker_wrapper.location_filter(),
+ 'miner_fs_busname': self._tracker_wrapper.props.miner_fs_busname
+ }
+
+ metadata_keys: List[int] = [
+ Grl.METADATA_KEY_ALBUM,
+ Grl.METADATA_KEY_ALBUM_DISC_NUMBER,
+ Grl.METADATA_KEY_ARTIST,
+ Grl.METADATA_KEY_DURATION,
+ Grl.METADATA_KEY_FAVOURITE,
+ Grl.METADATA_KEY_ID,
+ Grl.METADATA_KEY_PLAY_COUNT,
+ Grl.METADATA_KEY_TITLE,
+ Grl.METADATA_KEY_URL
+ ]
+
+ album_song_ids: List[int] = []
+
+ def _filter_func(coresong: CoreSong) -> bool:
+ return coresong.props.grlid in album_song_ids
+
+ def _callback(
+ source: Grl.Source, op_id: int, media: Grl.Media,
+ remaining: int, error: GLib.Error) -> None:
+ if error:
+ self._log.warning(f"Error: {error.domain}, {error.message}")
+ return
+
+ if media is None:
+ custom_filter = Gtk.CustomFilter()
+ custom_filter.set_filter_func(_filter_func)
+ model.set_filter(custom_filter)
+ return
+
+ album_song_ids.append(media.get_source() + media.get_id())
+
+ self.props.source.query(
+ query, metadata_keys, self._fast_options, _callback)
+
def search(self, text: str) -> None:
# FIXME: Searches are limited to not bog down the UI with
# widget creation ({List,Flow}Box limitations). The limit is
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]