[gnome-music/wip/mschraal/remove-mediaart-blocking-calls: 2/2] storeart: Replace blocking MediaArt call




commit ea1959e009b2dc0797d3dd77b15413092bee76c2
Author: Marinus Schraal <mschraal gnome org>
Date:   Fri Aug 20 01:02:08 2021 +0200

    storeart: Replace blocking MediaArt call
    
    MediaArt.file_to_jpeg is a blocking call.
    
    Replace it with async Gio + GdkPixbuf based calls, also removing the
    need to use a temporary file.

 gnomemusic/storeart.py | 129 ++++++++++++++++++++++---------------------------
 1 file changed, 59 insertions(+), 70 deletions(-)
---
diff --git a/gnomemusic/storeart.py b/gnomemusic/storeart.py
index 89f13c0ec..75dd47eaf 100644
--- a/gnomemusic/storeart.py
+++ b/gnomemusic/storeart.py
@@ -24,7 +24,7 @@
 
 import gi
 gi.require_versions({"MediaArt": "2.0", "Soup": "2.4"})
-from gi.repository import Gio, GLib, GObject, MediaArt, Soup
+from gi.repository import Gio, GLib, GObject, MediaArt, Soup, GdkPixbuf
 
 from gnomemusic.musiclogger import MusicLogger
 from gnomemusic.coreartist import CoreArtist
@@ -50,6 +50,7 @@ class StoreArt(GObject.Object):
 
         self._coreobject = None
 
+        self._file = None
         self._log = MusicLogger()
         self._soup_session = Soup.Session.new()
 
@@ -62,6 +63,25 @@ class StoreArt(GObject.Object):
             self.emit("finished")
             return
 
+        if isinstance(self._coreobject, CoreArtist):
+            success, self._file = MediaArt.get_file(
+                self._coreobject.props.artist, None, "artist")
+        elif isinstance(self._coreobject, CoreAlbum):
+            success, self._file = MediaArt.get_file(
+                self._coreobject.props.artist, self._coreobject.props.title,
+                "album")
+        elif isinstance(self._coreobject, CoreSong):
+            success, self._file = MediaArt.get_file(
+                self._coreobject.props.artist, self._coreobject.props.album,
+                "album")
+        else:
+            success = False
+
+        if not success:
+            self._coreobject.props.thumbnail = "generic"
+            self.emit("finished")
+            return
+
         cache_dir = GLib.build_filenamev(
             [GLib.get_user_cache_dir(), "media-art"])
         cache_dir_file = Gio.File.new_for_path(cache_dir)
@@ -91,89 +111,58 @@ class StoreArt(GObject.Object):
             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"
             self.emit("finished")
             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)
+        GdkPixbuf.Pixbuf.new_from_stream_async(
+            istream, None, self._pixbuf_from_stream_finished)
 
+    def _pixbuf_from_stream_finished(
+            self, stream: Gio.MemoryInputStream,
+            result: Gio.AsyncResult) -> None:
         try:
-            src.splice_finish(result)
+            pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(result)
         except GLib.Error as error:
-            self._log.warning(
-                "Error: {}, {}".format(error.domain, error.message))
-            self._coreobject.props.thumbnail = "generic"
+            self._log.warning(f"Error: {error.domain}, {error.message}")
             self.emit("finished")
-            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"
-            self.emit("finished")
-            return
+            self._file.create_async(
+                Gio.FileCreateFlags.NONE, GLib.PRIORITY_LOW, None,
+                self._output_stream_created, pixbuf)
+        finally:
+            stream.close_async(GLib.PRIORITY_LOW, None, self._stream_closed)
+
+    def _output_stream_created(
+            self, stream: Gio.FileOutputStream, result: Gio.AsyncResult,
+            pixbuf: GdkPixbuf.Pixbuf) -> None:
+        try:
+            output_stream = stream.create_finish(result)
+        except GLib.Error as error:
+            # File already exists.
+            self._log.info(f"Error: {error.domain}, {error.message}")
+        else:
+            pixbuf.save_to_streamv_async(
+                output_stream, "jpeg", None, None, None,
+                self._output_stream_saved, output_stream)
 
+    def _output_stream_saved(
+            self, pixbuf: GdkPixbuf.Pixbuf, result: Gio.AsyncResult,
+            output_stream: Gio.FileOutputStream) -> None:
         try:
-            # FIXME: I/O blocking
-            MediaArt.file_to_jpeg(tmp_file.get_path(), cache_file.get_path())
+            pixbuf.save_to_stream_finish(result)
         except GLib.Error as error:
-            self._log.warning(
-                "Error: {}, {}".format(error.domain, error.message))
-            self._coreobject.props.thumbnail = "generic"
+            self._log.warning(f"Error: {error.domain}, {error.message}")
+        else:
             self.emit("finished")
-            return
-
-        self._coreobject.props.media.set_thumbnail(cache_file.get_uri())
-        self._coreobject.props.thumbnail = cache_file.get_uri()
-
-        self.emit("finished")
-
-        tmp_file.delete_async(
-            GLib.PRIORITY_LOW, None, self._delete_callback, None)
+        finally:
+            output_stream.close_async(
+                GLib.PRIORITY_LOW, None, self._stream_closed)
 
-    def _close_iostream_callback(self, src, result, data):
+    def _stream_closed(
+            self, stream: Gio.OutputStream, result: Gio.AsyncResult) -> None:
         try:
-            src.close_finish(result)
+            stream.close_finish(result)
         except GLib.Error as error:
-            self._log.warning(
-                "Error: {}, {}".format(error.domain, error.message))
+            self._log.warning(f"Error: {error.domain}, {error.message}")


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]