Commit a764a648 authored by Marinus Schraal's avatar Marinus Schraal

Use CoreSong for AlbumCover and AlbumArtCache.

This is flawed in many ways and does not actually look up anything it
seems. However it will show cached art as usual and stops using old
grilo.py and query.py altogether.
parent 629aa23b
Pipeline #95076 passed with stages
in 12 minutes and 6 seconds
......@@ -35,22 +35,24 @@ from gi.repository import (Gdk, GdkPixbuf, Gio, GLib, GObject, Gtk, MediaArt,
Gst, GstTag, GstPbutils)
from gnomemusic import log
import gnomemusic.utils as utils
logger = logging.getLogger(__name__)
@log
def lookup_art_file_from_cache(media):
def lookup_art_file_from_cache(coresong):
"""Lookup MediaArt cache art of an album or song.
:param Grl.Media media: song or album
:param CoreSong coresong: song or album
:returns: a cache file
:rtype: Gio.File
"""
album = utils.get_album_title(media)
artist = utils.get_artist_name(media)
try:
album = coresong.props.album
except AttributeError:
album = coresong.props.title
artist = coresong.props.artist
success, thumb_file = MediaArt.get_file(artist, album, "album")
if (not success
......@@ -197,12 +199,16 @@ class Art(GObject.GObject):
return '<Art>'
@log
def __init__(self, size, media, scale=1):
def __init__(self, size, coresong, scale=1):
super().__init__()
self._size = size
self._media = media
self._media_url = self._media.get_url()
self._coresong = coresong
# FIXME: Albums do not have a URL.
try:
self._url = self._coresong.props.url
except AttributeError:
self._url = None
self._surface = None
self._scale = scale
......@@ -216,14 +222,14 @@ class Art(GObject.GObject):
cache = Cache()
cache.connect('miss', self._cache_miss)
cache.connect('hit', self._cache_hit)
cache.query(self._media)
cache.query(self._coresong)
@log
def _cache_miss(self, klass):
embedded_art = EmbeddedArt()
embedded_art.connect('found', self._embedded_art_found)
embedded_art.connect('unavailable', self._embedded_art_unavailable)
embedded_art.query(self._media)
embedded_art.query(self._coresong)
@log
def _cache_hit(self, klass, pixbuf):
......@@ -245,7 +251,7 @@ class Art(GObject.GObject):
# chance of getting artwork.
cache.connect('miss', self._embedded_art_unavailable)
cache.connect('hit', self._cache_hit)
cache.query(self._media)
cache.query(self._coresong)
@log
def _embedded_art_unavailable(self, klass):
......@@ -253,14 +259,14 @@ class Art(GObject.GObject):
remote_art.connect('retrieved', self._remote_art_retrieved)
remote_art.connect('unavailable', self._remote_art_unavailable)
remote_art.connect('no-remote-sources', self._remote_art_no_sources)
remote_art.query(self._media)
remote_art.query(self._coresong)
@log
def _remote_art_retrieved(self, klass):
cache = Cache()
cache.connect('miss', self._remote_art_unavailable)
cache.connect('hit', self._cache_hit)
cache.query(self._media)
cache.query(self._coresong)
@log
def _remote_art_unavailable(self, klass):
......@@ -280,8 +286,12 @@ class Art(GObject.GObject):
@log
def _add_to_blacklist(self):
album = utils.get_album_title(self._media)
artist = utils.get_artist_name(self._media)
# FIXME: coresong can be a CoreAlbum
try:
album = self._coresong.props.album
except AttributeError:
album = self._coresong.props.title
artist = self._coresong.props.artist
if artist not in self._blacklist:
self._blacklist[artist] = []
......@@ -291,8 +301,12 @@ class Art(GObject.GObject):
@log
def _in_blacklist(self):
album = utils.get_album_title(self._media)
artist = utils.get_artist_name(self._media)
# FIXME: coresong can be a CoreAlbum
try:
album = self._coresong.props.album
except AttributeError:
album = self._coresong.props.title
artist = self._coresong.props.artist
album_stripped = MediaArt.strip_invalid_entities(album)
if artist in self._blacklist:
......@@ -339,12 +353,12 @@ class Cache(GObject.GObject):
return
@log
def query(self, media):
def query(self, coresong):
"""Start the cache query
:param Grl.Media media: The media object to search art for
:param CoreSong coresong: The CoreSong object to search art for
"""
thumb_file = lookup_art_file_from_cache(media)
thumb_file = lookup_art_file_from_cache(coresong)
if thumb_file:
thumb_file.read_async(
GLib.PRIORITY_LOW, None, self._open_stream, None)
......@@ -414,22 +428,30 @@ class EmbeddedArt(GObject.GObject):
self._album = None
self._artist = None
self._media = None
self._coresong = None
self._path = None
@log
def query(self, media):
def query(self, coresong):
"""Start the local query
:param Grl.Media media: The media object to search art for
:param CoreSong coresong: The CoreSong object to search art for
"""
if media.get_url() is None:
try:
if coresong.props.url is None:
self.emit('unavailable')
return
except AttributeError:
self.emit('unavailable')
return
self._album = utils.get_album_title(media)
self._artist = utils.get_artist_name(media)
self._media = media
# FIXME: coresong can be a CoreAlbum
try:
self._album = coresong.props.album
except AttributeError:
self._album = coresong.props.title
self._artist = coresong.props.artist
self._coresong = coresong
try:
discoverer = GstPbutils.Discoverer.new(Gst.SECOND)
......@@ -450,7 +472,7 @@ class EmbeddedArt(GObject.GObject):
self._path = path
success = discoverer.discover_uri_async(self._media.get_url())
success = discoverer.discover_uri_async(self._coresong.props.url)
if not success:
logger.warning("Could not add url to discoverer.")
......@@ -509,7 +531,7 @@ class EmbeddedArt(GObject.GObject):
# Find local art in cover.jpeg files.
self._media_art.uri_async(
MediaArt.Type.ALBUM, MediaArt.ProcessFlags.NONE,
self._media.get_url(), self._artist, self._album,
self._coresong.props.url, self._artist, self._album,
GLib.PRIORITY_LOW, None, self._uri_async_cb, None)
@log
......@@ -553,30 +575,47 @@ class RemoteArt(GObject.GObject):
self._artist = None
self._album = None
self._coresong = None
self._grilo = None
@log
def query(self, media):
def query(self, coresong):
"""Start the remote query
:param Grl.Media media: The media object to search art for
:param CoreSong coresong: The CoreSong object to search art for
"""
self._album = utils.get_album_title(media)
self._artist = utils.get_artist_name(media)
self._media = media
# FIXME: coresong can be a CoreAlbum
try:
self._album = coresong.props.album
except AttributeError:
self._album = coresong.props.title
self._artist = coresong.props.artist
self._coresong = coresong
self.emit('no-remote-sources')
# if not grilo.props.cover_sources:
# self.emit('no-remote-sources')
# grilo.connect(
# 'notify::cover-sources', self._on_grilo_cover_sources_changed)
# else:
# FIXME: It seems this Grilo query does not always return,
# especially on queries with little info.
# grilo.get_album_art_for_item(media, self._remote_album_art)
# def _on_grilo_cover_sources_changed(self, klass, data):
# if grilo.props.cover_sources:
# grilo.get_album_art_for_item(self._media, self._remote_album_art)
# FIXME: This is a total hack. It gets CoreModel from the
# CoreAlbum or CoreSong about and then retrieves the CoreGrilo
# instance.
try:
self._grilo = self._coresong._coremodel._grilo
except AttributeError:
self._grilo = self._coresong._grilo
if not self._grilo.props.cover_sources:
self.emit('no-remote-sources')
self._grilo.connect(
'notify::cover-sources', self._on_grilo_cover_sources_changed)
else:
# FIXME: It seems this Grilo query does not always return,
# especially on queries with little info.
self._grilo.get_album_art_for_item(
self._coresong, self._remote_album_art)
def _on_grilo_cover_sources_changed(self, klass, data):
if self._grilo.props.cover_sources:
self._grilo.get_album_art_for_item(
self._coresong, self._remote_album_art)
@log
def _delete_callback(self, src, result, data):
......
......@@ -18,6 +18,10 @@ class CoreGrilo(GObject.GObject):
'grl-spotify-cover'
]
_theaudiodb_api_key = "195003"
cover_sources = GObject.Property(type=bool, default=False)
def __repr__(self):
return "<CoreGrilo>"
......@@ -27,17 +31,31 @@ class CoreGrilo(GObject.GObject):
self._coremodel = coremodel
self._coreselection = coreselection
self._search_wrappers = {}
self._thumbnail_sources = []
self._thumbnail_sources_timeout = None
self._wrappers = {}
Grl.init(None)
self._registry = Grl.Registry.get_default()
config = Grl.Config.new("grl-lua-factory", "grl-theaudiodb-cover")
config.set_api_key(self._theaudiodb_api_key)
self._registry.add_config(config)
self._registry.connect('source-added', self._on_source_added)
self._registry.connect('source-removed', self._on_source_removed)
self._registry.load_all_plugins(True)
def _on_source_added(self, registry, source):
def _trigger_art_update():
self._thumbnail_sources_timeout = None
if len(self._thumbnail_sources) > 0:
self.props.cover_sources = True
return GLib.SOURCE_REMOVE
if ("net:plaintext" in source.get_tags()
or source.props.source_id in self._blacklist):
try:
......@@ -47,6 +65,14 @@ class CoreGrilo(GObject.GObject):
source.props.source_id))
return
if Grl.METADATA_KEY_THUMBNAIL in source.supported_keys():
self._thumbnail_sources.append(source)
if not self._thumbnail_sources_timeout:
# Aggregate sources being added, for example when the
# network comes online.
self._thumbnail_sources_timeout = GLib.timeout_add_seconds(
5, _trigger_art_update)
new_wrapper = None
if (source.props.source_id == "grl-tracker-source"
......@@ -119,3 +145,9 @@ class CoreGrilo(GObject.GObject):
wrapper.search(text)
for wrapper in self._search_wrappers.values():
wrapper.search(text)
def get_album_art_for_item(self, coresong, callback):
# Tracker not (yet) loaded.
if "grl-tracker-source" not in self._wrappers:
self._wrappers["grl-tracker-source"].get_album_art_for_item(
coresong, callback)
......@@ -28,6 +28,11 @@ class GrlTrackerSource(GObject.GObject):
Grl.METADATA_KEY_URL
]
METADATA_THUMBNAIL_KEYS = [
Grl.METADATA_KEY_ID,
Grl.METADATA_KEY_THUMBNAIL,
]
def __repr__(self):
return "<GrlTrackerSource>"
......@@ -645,3 +650,77 @@ class GrlTrackerSource(GObject.GObject):
self._source.query(
query, self.METADATA_KEYS, options, artist_search_cb)
def get_album_art_for_item(self, coresong, callback):
item_id = coresong.props.media.get_id()
if coresong.props.media.is_audio():
query = self._get_album_for_song_id(item_id)
else:
query = self._get_album_for_album_id(item_id)
full_options = Grl.OperationOptions()
full_options.set_resolution_flags(
Grl.ResolutionFlags.FULL
| Grl.ResolutionFlags.IDLE_RELAY)
full_options.set_count(1)
self.search_source.query(
query, self.METADATA_THUMBNAIL_KEYS, full_options, callback)
@staticmethod
def _get_album_for_album_id(album_id):
# Even though we check for the album_artist, we fill
# the artist key, since Grilo coverart plugins use
# only that key for retrieval.
query = """
SELECT DISTINCT
rdf:type(?album)
tracker:id(?album) AS ?id
tracker:coalesce(nmm:artistName(?album_artist),
nmm:artistName(?song_artist)) AS ?artist
nie:title(?album) AS ?album
WHERE {
?album a nmm:MusicAlbum .
?song a nmm:MusicPiece ;
nmm:musicAlbum ?album ;
nmm:performer ?song_artist .
OPTIONAL { ?album nmm:albumArtist ?album_artist . }
FILTER (
tracker:id(?album) = %(album_id)s
)
}
""".replace("\n", " ").strip() % {
'album_id': album_id,
}
return query
@staticmethod
def _get_album_for_song_id(song_id):
# See get_album_for_album_id comment.
query = """
SELECT DISTINCT
rdf:type(?album)
tracker:id(?album) AS ?id
tracker:coalesce(nmm:artistName(?album_artist),
nmm:artistName(?song_artist)) AS ?artist
nie:title(?album) AS ?album
WHERE {
?song a nmm:MusicPiece ;
nmm:musicAlbum ?album ;
nmm:performer ?song_artist .
OPTIONAL { ?album nmm:albumArtist ?album_artist . }
FILTER (
tracker:id(?song) = %(song_id)s
)
FILTER (
NOT EXISTS { ?song a nmm:Video }
&& NOT EXISTS { ?song a nmm:Playlist }
)
}
""".replace("\n", " ").strip() % {
'song_id': song_id
}
return query
......@@ -374,7 +374,7 @@ class MPRIS(DBusInterface):
# player.
art_url = coresong.props.media.get_thumbnail()
if not art_url:
thumb_file = lookup_art_file_from_cache(coresong.props.media)
thumb_file = lookup_art_file_from_cache(coresong)
if thumb_file:
art_url = GLib.filename_to_uri(thumb_file.get_path())
coresong.props.media.set_thumbnail(art_url)
......
......@@ -101,10 +101,9 @@ class AlbumCover(Gtk.FlowBoxChild):
# quick first show with a placeholder cover and then a
# reasonably responsive view while loading the actual
# covers.
# FIXME: Pass CoreAlbum to CoverStack.
GLib.timeout_add(
50 * self._nr_albums, self._cover_stack.update,
self._corealbum.props.media, priority=GLib.PRIORITY_LOW)
50 * self._nr_albums, self._cover_stack.update, self._corealbum,
priority=GLib.PRIORITY_LOW)
@GObject.Property(type=CoreAlbum, flags=GObject.ParamFlags.READABLE)
def corealbum(self):
......
......@@ -63,7 +63,7 @@ class AlbumWidget(Gtk.EventBox):
if self._signal_id:
self._album_model.disconnect(self._signal_id)
self._cover_stack.update(corealbum.props.media)
self._cover_stack.update(corealbum)
self._duration = 0
......
......@@ -64,7 +64,7 @@ class ArtistAlbumWidget(Gtk.Box):
self._selection_mode_allowed = selection_mode_allowed
self._cover_stack.props.size = Art.Size.MEDIUM
self._cover_stack.update(corealbum.props.media)
self._cover_stack.update(corealbum)
allowed = self._selection_mode_allowed
self._disc_list_box.props.selection_mode_allowed = allowed
......
......@@ -96,11 +96,11 @@ class CoverStack(Gtk.Stack):
self._loading_cover.props.surface = icon
@log
def update(self, media):
"""Update the stack with the given media
def update(self, coresong):
"""Update the stack with the given CoreSong
Update the stack with the art retrieved from the given media.
:param Grl.Media media: The media object
Update the stack with the art retrieved from the given Coresong.
:param CoreSong coresong: The CoreSong object
"""
if self._handler_id and self._art:
# Remove a possible dangling 'finished' callback if update
......@@ -113,7 +113,7 @@ class CoverStack(Gtk.Stack):
self._active_child = self.props.visible_child_name
self._art = Art(self.props.size, media, self.props.scale_factor)
self._art = Art(self.props.size, coresong, self.props.scale_factor)
self._handler_id = self._art.connect('finished', self._art_retrieved)
self._art.lookup()
......
......@@ -185,7 +185,7 @@ class PlayerToolbar(Gtk.ActionBar):
self._tooltip.props.title = title
self._tooltip.props.subtitle = artist
self._cover_stack.update(coresong.props.media)
self._cover_stack.update(coresong)
@Gtk.Template.Callback()
@log
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment