diff options
Diffstat (limited to 'gnomemusic/storeart.py')
-rw-r--r-- | gnomemusic/storeart.py | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/gnomemusic/storeart.py b/gnomemusic/storeart.py new file mode 100644 index 00000000..368d61b2 --- /dev/null +++ b/gnomemusic/storeart.py @@ -0,0 +1,162 @@ +# Copyright 2020 The GNOME Music developers +# +# GNOME Music is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# GNOME Music is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with GNOME Music; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# The GNOME Music authors hereby grant permission for non-GPL compatible +# GStreamer plugins to be used and distributed together with GStreamer +# and GNOME Music. This permission is above and beyond the permissions +# granted by the GPL license by which GNOME Music is covered. If you +# modify this code, you may extend this exception to your version of the +# code, but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. + +import gi +gi.require_versions({"MediaArt": "2.0", "Soup": "2.4"}) +from gi.repository import Gio, GLib, GObject, MediaArt, Soup + +from gnomemusic.musiclogger import MusicLogger +from gnomemusic.coreartist import CoreArtist +from gnomemusic.corealbum import CoreAlbum +from gnomemusic.coresong import CoreSong + + +class StoreArt(GObject.Object): + """Stores Art in the MediaArt cache. + """ + + def __init__(self, coreobject, uri): + """Initialize StoreArtistArt + + :param coreobject: The CoreArtist or CoreAlbum to store art for + :param string uri: The art uri + """ + self._coreobject = coreobject + + self._log = MusicLogger() + self._soup_session = Soup.Session.new() + + if (uri is None + or uri == ""): + self._coreobject.props.thumbnail = "generic" + return + + cache_dir = GLib.build_filenamev( + [GLib.get_user_cache_dir(), "media-art"]) + cache_dir_file = Gio.File.new_for_path(cache_dir) + cache_dir_file.query_info_async( + Gio.FILE_ATTRIBUTE_ACCESS_CAN_READ, Gio.FileQueryInfoFlags.NONE, + GLib.PRIORITY_LOW, None, self._cache_dir_info_read, uri) + + def _cache_dir_info_read(self, cache_dir_file, res, uri): + try: + cache_dir_file.query_info_finish(res) + except GLib.Error: + # directory does not exist yet + try: + cache_dir_file.make_directory(None) + except GLib.Error as error: + self._log.warning( + "Error: {}, {}".format(error.domain, error.message)) + self._coreobject.props.thumbnail = "generic" + return + + msg = Soup.Message.new("GET", uri) + self._soup_session.queue_message(msg, self._read_callback, None) + + def _read_callback(self, src, result, data): + if result.props.status_code != 200: + self._log.debug( + "Failed to get remote art: {}".format( + result.props.reason_phrase)) + return + + try: + [tmp_file, iostream] = Gio.File.new_tmp() + except GLib.Error as error: + self._log.warning( + "Error: {}, {}".format(error.domain, error.message)) + self._coreobject.props.thumbnail = "generic" + return + + istream = Gio.MemoryInputStream.new_from_bytes( + result.props.response_body_data) + ostream = iostream.get_output_stream() + # FIXME: Passing the iostream here, otherwise it gets + # closed. PyGI specific issue? + ostream.splice_async( + istream, Gio.OutputStreamSpliceFlags.CLOSE_SOURCE + | Gio.OutputStreamSpliceFlags.CLOSE_TARGET, GLib.PRIORITY_LOW, + None, self._splice_callback, [tmp_file, iostream]) + + def _delete_callback(self, src, result, data): + try: + src.delete_finish(result) + except GLib.Error as error: + self._log.warning( + "Error: {}, {}".format(error.domain, error.message)) + + def _splice_callback(self, src, result, data): + tmp_file, iostream = data + + iostream.close_async( + GLib.PRIORITY_LOW, None, self._close_iostream_callback, None) + + try: + src.splice_finish(result) + except GLib.Error as error: + self._log.warning( + "Error: {}, {}".format(error.domain, error.message)) + self._coreobject.props.thumbnail = "generic" + return + + if isinstance(self._coreobject, CoreArtist): + success, cache_file = MediaArt.get_file( + self._coreobject.props.artist, None, "artist") + elif isinstance(self._coreobject, CoreAlbum): + success, cache_file = MediaArt.get_file( + self._coreobject.props.artist, self._coreobject.props.title, + "album") + elif isinstance(self._coreobject, CoreSong): + success, cache_file = MediaArt.get_file( + self._coreobject.props.artist, self._coreobject.props.album, + "album") + else: + success = False + + if not success: + self._coreobject.props.thumbnail = "generic" + return + + try: + # FIXME: I/O blocking + MediaArt.file_to_jpeg(tmp_file.get_path(), cache_file.get_path()) + except GLib.Error as error: + self._log.warning( + "Error: {}, {}".format(error.domain, error.message)) + self._coreobject.props.thumbnail = "generic" + return + + self._coreobject.props.media.set_thumbnail(cache_file.get_uri()) + self._coreobject.props.thumbnail = cache_file.get_uri() + + tmp_file.delete_async( + GLib.PRIORITY_LOW, None, self._delete_callback, None) + + def _close_iostream_callback(self, src, result, data): + try: + src.close_finish(result) + except GLib.Error as error: + self._log.warning( + "Error: {}, {}".format(error.domain, error.message)) |