From 6ab412e20019070304d3fdafcb42ef8dd99133ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 17 Apr 2025 17:21:24 -0700 Subject: [PATCH 1/4] Send artwork to MPRIS --- src/AudioObject.vala | 21 ++++++++++++++++++++- src/DBus/MprisPlayer.vala | 4 ++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/AudioObject.vala b/src/AudioObject.vala index b80934a9d..1c85d6426 100644 --- a/src/AudioObject.vala +++ b/src/AudioObject.vala @@ -5,12 +5,31 @@ public class Music.AudioObject : Object { public string uri { get; construct; } - public Gdk.Texture texture { get; set; } + public Gdk.Texture? texture { get; set; default = null; } public string artist { get; set; } public string title { get; set; } public int64 duration { get; set; default = 0; } + public string art_url { get; private set; default = ""; } public AudioObject (string uri) { Object (uri: uri); } + + construct { + notify["texture"].connect (() => { + save_art_file.begin (); + }); + } + + private async void save_art_file () throws Error requires (texture != null) { + FileIOStream iostream; + var file = yield File.new_tmp_async ("io.elementary.music-XXXXXX.png", GLib.Priority.DEFAULT, null, out iostream); + + var bytes = texture.save_to_png_bytes (); + + var ostream = iostream.output_stream; + yield ostream.write_bytes_async (bytes); + + art_url = file.get_uri (); + } } diff --git a/src/DBus/MprisPlayer.vala b/src/DBus/MprisPlayer.vala index cb2c57988..bd980d650 100644 --- a/src/DBus/MprisPlayer.vala +++ b/src/DBus/MprisPlayer.vala @@ -34,6 +34,10 @@ public class Music.MprisPlayer : Object { _metadata.insert ("xesam:artist", array); } + if (audio.art_url != "") { + _metadata.insert ("mpris:artUrl", audio.art_url); + } + if (audio.title != null) { _metadata.insert ("xesam:title", audio.title); } From 7ed687e3ebe44a699633a81314bb3d3ad3b94364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 17 Apr 2025 19:15:48 -0700 Subject: [PATCH 2/4] Use cache dir to fix sandbox --- src/AudioObject.vala | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/AudioObject.vala b/src/AudioObject.vala index 1c85d6426..56ba4e431 100644 --- a/src/AudioObject.vala +++ b/src/AudioObject.vala @@ -17,18 +17,35 @@ public class Music.AudioObject : Object { construct { notify["texture"].connect (() => { - save_art_file.begin (); + save_art_file.begin ((obj, res) => { + try { + save_art_file.end (res); + } catch (Error e) { + critical (e.message); + } + }); }); } private async void save_art_file () throws Error requires (texture != null) { - FileIOStream iostream; - var file = yield File.new_tmp_async ("io.elementary.music-XXXXXX.png", GLib.Priority.DEFAULT, null, out iostream); + var path = Path.build_path ( + Path.DIR_SEPARATOR_S, + Environment.get_user_cache_dir (), + GLib.Application.get_default ().application_id, + "art" + ); - var bytes = texture.save_to_png_bytes (); + DirUtils.create_with_parents (path, 0755); - var ostream = iostream.output_stream; - yield ostream.write_bytes_async (bytes); + //FIXME: make a hash that re-uses art instead of one per file + var file = File.new_for_path (Path.build_path ( + Path.DIR_SEPARATOR_S, + path, + Checksum.compute_for_string (SHA256, uri) + )); + + var ostream = yield file.create_async (NONE); + yield ostream.write_bytes_async (texture.save_to_png_bytes ()); art_url = file.get_uri (); } From acf75a98b7ceb72c7e8db001d19de3db2720dc33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 17 Apr 2025 19:16:34 -0700 Subject: [PATCH 3/4] Better variable name --- src/AudioObject.vala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AudioObject.vala b/src/AudioObject.vala index 56ba4e431..47e2a5c82 100644 --- a/src/AudioObject.vala +++ b/src/AudioObject.vala @@ -28,19 +28,19 @@ public class Music.AudioObject : Object { } private async void save_art_file () throws Error requires (texture != null) { - var path = Path.build_path ( + var art_cache_dir = Path.build_path ( Path.DIR_SEPARATOR_S, Environment.get_user_cache_dir (), GLib.Application.get_default ().application_id, "art" ); - DirUtils.create_with_parents (path, 0755); + DirUtils.create_with_parents (art_cache_dir, 0755); //FIXME: make a hash that re-uses art instead of one per file var file = File.new_for_path (Path.build_path ( Path.DIR_SEPARATOR_S, - path, + art_cache_dir, Checksum.compute_for_string (SHA256, uri) )); From ca8b33552774d2af36323d1a6e404571e39ed60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Thu, 17 Apr 2025 19:47:07 -0700 Subject: [PATCH 4/4] Don't re-fetch art if we already cached it --- src/AudioObject.vala | 38 ++------------------------ src/PlaybackManager.vala | 59 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/AudioObject.vala b/src/AudioObject.vala index 47e2a5c82..fdd1a1b52 100644 --- a/src/AudioObject.vala +++ b/src/AudioObject.vala @@ -6,47 +6,13 @@ public class Music.AudioObject : Object { public string uri { get; construct; } public Gdk.Texture? texture { get; set; default = null; } + public string album { get; set; } public string artist { get; set; } public string title { get; set; } public int64 duration { get; set; default = 0; } - public string art_url { get; private set; default = ""; } + public string art_url { get; set; default = ""; } public AudioObject (string uri) { Object (uri: uri); } - - construct { - notify["texture"].connect (() => { - save_art_file.begin ((obj, res) => { - try { - save_art_file.end (res); - } catch (Error e) { - critical (e.message); - } - }); - }); - } - - private async void save_art_file () throws Error requires (texture != null) { - var art_cache_dir = Path.build_path ( - Path.DIR_SEPARATOR_S, - Environment.get_user_cache_dir (), - GLib.Application.get_default ().application_id, - "art" - ); - - DirUtils.create_with_parents (art_cache_dir, 0755); - - //FIXME: make a hash that re-uses art instead of one per file - var file = File.new_for_path (Path.build_path ( - Path.DIR_SEPARATOR_S, - art_cache_dir, - Checksum.compute_for_string (SHA256, uri) - )); - - var ostream = yield file.create_async (NONE); - yield ostream.write_bytes_async (texture.save_to_png_bytes ()); - - art_url = file.get_uri (); - } } diff --git a/src/PlaybackManager.vala b/src/PlaybackManager.vala index 4b33f6039..b39b0b7d4 100644 --- a/src/PlaybackManager.vala +++ b/src/PlaybackManager.vala @@ -199,6 +199,12 @@ public class Music.PlaybackManager : Object { unowned Gst.TagList? tag_list = info.get_tags (); + string _album; + tag_list.get_string (Gst.Tags.ALBUM, out _album); + if (_album != null) { + audio_object.album = _album; + } + string _title; tag_list.get_string (Gst.Tags.TITLE, out _title); if (_title != null) { @@ -213,12 +219,37 @@ public class Music.PlaybackManager : Object { audio_object.artist = _("Unknown"); } - var sample = get_cover_sample (tag_list); - if (sample != null) { - var buffer = sample.get_buffer (); + string art_hash = uri; + if (_artist != null && _album != null) { + art_hash = "%s:%s".printf (_artist, _album); + } + + var art_file = File.new_for_path (Path.build_path ( + Path.DIR_SEPARATOR_S, + get_art_cache_dir (), + Checksum.compute_for_string (SHA256, art_hash) + )); - if (buffer != null) { - audio_object.texture = Gdk.Texture.for_pixbuf (get_pixbuf_from_buffer (buffer)); + if (art_file.query_exists ()) { + audio_object.art_url = art_file.get_uri (); + audio_object.texture = Gdk.Texture.from_file (art_file); + } else { + var sample = get_cover_sample (tag_list); + if (sample != null) { + var buffer = sample.get_buffer (); + + if (buffer != null) { + var texture = Gdk.Texture.for_pixbuf (get_pixbuf_from_buffer (buffer)); + audio_object.texture = texture; + + save_art_file.begin (texture, art_file, (obj, res) => { + try { + audio_object.art_url = save_art_file.end (res); + } catch (Error e) { + critical (e.message); + } + }); + } } } } else { @@ -451,4 +482,22 @@ public class Music.PlaybackManager : Object { return pix; } + + private async string save_art_file (Gdk.Texture texture, File file) throws Error requires (texture != null) { + DirUtils.create_with_parents (get_art_cache_dir (), 0755); + + var ostream = yield file.create_async (NONE); + yield ostream.write_bytes_async (texture.save_to_png_bytes ()); + + return file.get_uri (); + } + + private string get_art_cache_dir () { + return Path.build_path ( + Path.DIR_SEPARATOR_S, + Environment.get_user_cache_dir (), + GLib.Application.get_default ().application_id, + "art" + ); + } }