Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package mpv-mpris for openSUSE:Factory 
checked in at 2026-09-21 12:01:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mpv-mpris (Old)
 and      /work/SRC/openSUSE:Factory/.mpv-mpris.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mpv-mpris"

Mon Sep 21 12:01:54 2026 rev:9 rq:1379065 version:1.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/mpv-mpris/mpv-mpris.changes      2026-01-08 
15:27:15.072614183 +0100
+++ /work/SRC/openSUSE:Factory/.mpv-mpris.new.383539/mpv-mpris.changes  
2026-09-21 12:02:57.903599835 +0200
@@ -1,0 +2,24 @@
+Sat Sep 19 20:22:58 UTC 2026 - Mia Herkt <[email protected]>
+
+- Update to 1.3
+  * Use mpv’s audio-client-name as the player’s display name.
+  * Always include a unique instance suffix in the D-Bus player
+    name, and handle custom names containing special characters or
+    long strings.
+  * Fix play/pause toggling and report CanPlay and CanPause
+    correctly when mpv is idle.
+  * Fix seeking in locales that use decimal commas and improve seek
+    position precision.
+  * Use MPRIS-compliant track IDs and fix creation-date metadata
+    formatting.
+  * Fix a crash when freeing cached artwork and report the correct
+    MIME type for embedded images.
+  * Skip embedded artwork extraction from pipes and devices to
+    avoid consuming playback input.
+  * Fix YouTube short-link matching for thumbnails.
+  * Improve resource cleanup, memory management, and handling of
+    session bus initialization failures.
+  * Update tests for instance-suffixed player names and remove the
+    executable-permission requirement for test plugins.
+
+-------------------------------------------------------------------

Old:
----
  mpv-mpris-1.2.tar.gz

New:
----
  mpv-mpris-1.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ mpv-mpris.spec ++++++
--- /var/tmp/diff_new_pack.cKlx3C/_old  2026-09-21 12:02:58.713633807 +0200
+++ /var/tmp/diff_new_pack.cKlx3C/_new  2026-09-21 12:02:58.716633933 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           mpv-mpris
-Version:        1.2
+Version:        1.3
 Release:        0
 Summary:        MPRIS plugin for mpv
 License:        MIT

++++++ mpv-mpris-1.2.tar.gz -> mpv-mpris-1.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpv-mpris-1.2/mpris.c new/mpv-mpris-1.3/mpris.c
--- old/mpv-mpris-1.2/mpris.c   2026-01-01 23:21:19.000000000 +0100
+++ new/mpv-mpris-1.3/mpris.c   2026-09-17 01:23:53.000000000 +0200
@@ -5,8 +5,6 @@
 #include <inttypes.h>
 #include <string.h>
 
-
-
 static const char *introspection_xml =
     "<node>\n"
     "  <interface name=\"org.mpris.MediaPlayer2\">\n"
@@ -74,7 +72,11 @@
     GMainLoop *loop;
     GMainContext *ctx;
     int wakeup_pipe[2];
-    gint bus_id;
+    guint bus_id;
+    guint pending_bus_owners;
+    GSource *mpv_pipe_source;
+    GSource *timeout_source;
+    GSource *bus_retry_source;
     GDBusConnection *connection;
     GDBusInterfaceInfo *root_interface_info;
     GDBusInterfaceInfo *player_interface_info;
@@ -92,6 +94,8 @@
     gboolean events_setup;
     int64_t playlist_count;
     int64_t playlist_pos;
+    gchar *cached_path;
+    gchar *cached_art_url;
 } UserData;
 
 static const char *STATUS_PLAYING = "Playing";
@@ -100,10 +104,13 @@
 static const char *LOOP_NONE = "None";
 static const char *LOOP_TRACK = "Track";
 static const char *LOOP_PLAYLIST = "Playlist";
+static const char *TRACK_PATH_PREFIX = "/mpv/mpris/Track/";
+static const char *NO_TRACK_ID = "/org/mpris/MediaPlayer2/TrackList/NoTrack";
 
-static void setup_mpv_event_sources(UserData *ud);
+static gboolean setup_mpv_event_sources(UserData *ud);
 static gboolean can_go_next(UserData *ud);
 static gboolean can_go_previous(UserData *ud);
+static gboolean can_play_pause(UserData *ud);
 
 static gchar *string_to_utf8(gchar *maybe_utf8)
 {
@@ -265,7 +272,7 @@
 }
 
 static const char *youtube_url_pattern =
-    
"^https?:\\/\\/(?:youtu.be\\/|(?:www\\.)?youtube\\.com\\/watch\\?v=)(?<id>[a-zA-Z0-9_-]*)\\??.*";
+    
"^https?:\\/\\/(?:youtu\\.be\\/|(?:www\\.)?youtube\\.com\\/watch\\?v=)(?<id>[a-zA-Z0-9_-]*)\\??.*";
 
 static GRegex *youtube_url_regex;
 
@@ -292,6 +299,7 @@
 
 static gchar* extract_embedded_art(AVFormatContext *context) {
     AVPacket *packet = NULL;
+    enum AVCodecID codec_id = AV_CODEC_ID_NONE;
     for (unsigned int i = 0; i < context->nb_streams; i++) {
         if (context->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
             AVPacket *p = &context->streams[i]->attached_pic;
@@ -299,6 +307,7 @@
             // Skip the thumbnail if the size is bigger than 25MiB to avoid 
crashes
             if (p->size <= 25*0x100000) {
                 packet = p;
+                codec_id = context->streams[i]->codecpar->codec_id;
                 break;
             }
         }
@@ -307,8 +316,27 @@
         return NULL;
     }
 
+    const char *mime;
+    switch (codec_id) {
+    case AV_CODEC_ID_PNG:
+        mime = "image/png";
+        break;
+    case AV_CODEC_ID_GIF:
+        mime = "image/gif";
+        break;
+    case AV_CODEC_ID_WEBP:
+        mime = "image/webp";
+        break;
+    case AV_CODEC_ID_BMP:
+        mime = "image/bmp";
+        break;
+    default:
+        mime = "image/jpeg";
+        break;
+    }
+
     gchar *data = g_base64_encode(packet->data, packet->size);
-    gchar *img = g_strconcat("data:image/jpeg;base64,", data, NULL);
+    gchar *img = g_strconcat("data:", mime, ";base64,", data, NULL);
 
     g_free(data);
     return img;
@@ -318,6 +346,12 @@
 {
     gchar *out = NULL;
     AVFormatContext *context = NULL;
+
+    // Do not let FFmpeg open pipes/devices/fd aliases: that can consume mpv's 
input.
+    if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
+        return NULL;
+    }
+
     if (!avformat_open_input(&context, path, NULL, NULL)) {
         out = extract_embedded_art(context);
         avformat_close_input(&context);
@@ -343,14 +377,9 @@
     return NULL;
 }
 
-// cached last file path, owned by mpv
-static char *cached_path = NULL;
-
-// cached last artwork url, owned by glib
-static gchar *cached_art_url = NULL;
-
-static void add_metadata_art(mpv_handle *mpv, GVariantDict *dict)
+static void add_metadata_art(UserData *ud, GVariantDict *dict)
 {
+    mpv_handle *mpv = ud->mpv;
     char *path = mpv_get_property_string(mpv, "path");
 
     if (!path) {
@@ -358,17 +387,17 @@
     }
 
     // mpv may call create_metadata multiple times, so cache to save CPU
-    if (!cached_path || strcmp(path, cached_path)) {
-        mpv_free(cached_path);
-        g_free(cached_art_url);
-        cached_path = path;
-        cached_art_url = get_art_url(mpv, path);
-    } else {
-        mpv_free(path);
+    if (!ud->cached_path || strcmp(path, ud->cached_path)) {
+        g_free(ud->cached_path);
+        g_free(ud->cached_art_url);
+        ud->cached_path = g_strdup(path);
+        ud->cached_art_url = get_art_url(mpv, path);
     }
 
-    if (cached_art_url) {
-        g_variant_dict_insert(dict, "mpris:artUrl", "s", cached_art_url);
+    mpv_free(path);
+
+    if (ud->cached_art_url) {
+        g_variant_dict_insert(dict, "mpris:artUrl", "s", ud->cached_art_url);
     }
 }
 
@@ -391,8 +420,8 @@
     }
 
     if (g_date_valid(date)) {
-        gchar iso8601[20];
-        g_date_strftime(iso8601, 20, "%Y-%m-%dT00:00:00Z", date);
+        gchar iso8601[21];
+        g_date_strftime(iso8601, 21, "%Y-%m-%dT00:00:00Z", date);
         g_variant_dict_insert(dict, "xesam:contentCreated", "s", iso8601);
     }
 
@@ -412,9 +441,9 @@
     // mpris:trackid
     // playlist_pos < 0 if there is no playlist or current track
     if (ud->playlist_pos < 0) {
-        temp_str = g_strdup("/noplaylist");
+        temp_str = g_strdup(NO_TRACK_ID);
     } else {
-        temp_str = g_strdup_printf("/%" PRId64, ud->playlist_pos);
+        temp_str = g_strdup_printf("%s%" PRId64, TRACK_PATH_PREFIX, 
ud->playlist_pos);
     }
     g_variant_dict_insert(&dict, "mpris:trackid", "o", temp_str);
     g_free(temp_str);
@@ -457,7 +486,7 @@
     add_metadata_item_int(ud->mpv, &dict, "metadata/by-key/Disc", 
"xesam:discNumber");
 
     add_metadata_uri(ud->mpv, &dict);
-    add_metadata_art(ud->mpv, &dict);
+    add_metadata_art(ud, &dict);
     add_metadata_content_created(ud->mpv, &dict);
 
     return g_variant_dict_end(&dict);
@@ -504,12 +533,12 @@
         ret = g_variant_new_boolean(TRUE);
 
     } else if (g_strcmp0(property_name, "Fullscreen") == 0) {
-        int fullscreen;
+        int fullscreen = 0;
         mpv_get_property(ud->mpv, "fullscreen", MPV_FORMAT_FLAG, &fullscreen);
         ret = g_variant_new_boolean(fullscreen);
 
     } else if (g_strcmp0(property_name, "CanSetFullscreen") == 0) {
-        int can_fullscreen;
+        int can_fullscreen = 0;
         mpv_get_property(ud->mpv, "vo-configured", MPV_FORMAT_FLAG, 
&can_fullscreen);
         ret = g_variant_new_boolean(can_fullscreen);
 
@@ -520,7 +549,9 @@
         ret = g_variant_new_boolean(FALSE);
 
     } else if (g_strcmp0(property_name, "Identity") == 0) {
-        ret = g_variant_new_string("mpv");
+        char *client_name = mpv_get_property_string(ud->mpv, 
"audio-client-name");
+        ret = g_variant_new_string(client_name);
+        mpv_free(client_name);
 
     } else if (g_strcmp0(property_name, "DesktopEntry") == 0) {
         ret = g_variant_new_string("mpv");
@@ -600,13 +631,8 @@
         g_dbus_method_invocation_return_value(invocation, NULL);
 
     } else if (g_strcmp0(method_name, "PlayPause") == 0) {
-        int paused;
-        if (ud->status == STATUS_PAUSED) {
-            paused = FALSE;
-        } else {
-            paused = TRUE;
-        }
-        mpv_set_property(ud->mpv, "pause", MPV_FORMAT_FLAG, &paused);
+        const char *cmd[] = {"cycle", "pause", NULL};
+        mpv_command_async(ud->mpv, 0, cmd);
         g_dbus_method_invocation_return_value(invocation, NULL);
 
     } else if (g_strcmp0(method_name, "Play") == 0) {
@@ -631,15 +657,14 @@
 
     } else if (g_strcmp0(method_name, "Seek") == 0) {
         int64_t offset_us; // in microseconds
-        char *offset_str;
+        char offset_str[G_ASCII_DTOSTR_BUF_SIZE];
         g_variant_get(parameters, "(x)", &offset_us);
         double offset_s = offset_us / 1000000.0;
-        offset_str = g_strdup_printf("%f", offset_s);
+        g_ascii_dtostr(offset_str, G_ASCII_DTOSTR_BUF_SIZE, offset_s);
 
         const char *cmd[] = {"seek", offset_str, NULL};
         mpv_command_async(ud->mpv, 0, cmd);
         g_dbus_method_invocation_return_value(invocation, NULL);
-        g_free(offset_str);
 
     } else if (g_strcmp0(method_name, "SetPosition") == 0) {
         char *object_path;
@@ -647,9 +672,11 @@
         int64_t new_position_us;
 
         g_variant_get(parameters, "(&ox)", &object_path, &new_position_us);
-        new_position_s = ((float)new_position_us) / 1000000.0; // us -> s
+        new_position_s = ((double)new_position_us) / 1000000.0; // us -> s
 
-        if (ud->playlist_pos == g_ascii_strtoll(object_path + 1, NULL, 10)) {
+        if (g_str_has_prefix(object_path, TRACK_PATH_PREFIX) &&
+            ud->playlist_pos == g_ascii_strtoll(object_path + 
strlen(TRACK_PATH_PREFIX),
+                                                NULL, 10)) {
             mpv_set_property(ud->mpv, "time-pos", MPV_FORMAT_DOUBLE, 
&new_position_s);
         }
 
@@ -686,31 +713,31 @@
         ret = g_variant_new_string(ud->loop_status);
 
     } else if (g_strcmp0(property_name, "Rate") == 0) {
-        double rate;
+        double rate = 1.0;
         mpv_get_property(ud->mpv, "speed", MPV_FORMAT_DOUBLE, &rate);
         ret = g_variant_new_double(rate);
 
     } else if (g_strcmp0(property_name, "Shuffle") == 0) {
-        int shuffle;
+        int shuffle = 0;
         mpv_get_property(ud->mpv, "shuffle", MPV_FORMAT_FLAG, &shuffle);
         ret = g_variant_new_boolean(shuffle);
 
     } else if (g_strcmp0(property_name, "Metadata") == 0) {
         if (!ud->metadata) {
-            ud->metadata = create_metadata(ud);
+            ud->metadata = g_variant_ref_sink(create_metadata(ud));
         }
         // Increase reference count to prevent it from being freed after 
returning
         g_variant_ref(ud->metadata);
         ret = ud->metadata;
 
     } else if (g_strcmp0(property_name, "Volume") == 0) {
-        double volume;
+        double volume = 0;
         mpv_get_property(ud->mpv, "volume", MPV_FORMAT_DOUBLE, &volume);
         volume /= 100;
         ret = g_variant_new_double(volume);
 
     } else if (g_strcmp0(property_name, "Position") == 0) {
-        double position_s;
+        double position_s = 0;
         int64_t position_us;
         mpv_get_property(ud->mpv, "time-pos", MPV_FORMAT_DOUBLE, &position_s);
         position_us = position_s * 1000000.0; // s -> us
@@ -729,10 +756,10 @@
         ret = g_variant_new_boolean(can_go_previous(ud));
 
     } else if (g_strcmp0(property_name, "CanPlay") == 0) {
-        ret = g_variant_new_boolean(TRUE);
+        ret = g_variant_new_boolean(can_play_pause(ud));
 
     } else if (g_strcmp0(property_name, "CanPause") == 0) {
-        ret = g_variant_new_boolean(TRUE);
+        ret = g_variant_new_boolean(can_play_pause(ud));
 
     } else if (g_strcmp0(property_name, "CanSeek") == 0) {
         ret = g_variant_new_boolean(TRUE);
@@ -852,7 +879,7 @@
 static void emit_seeked_signal(UserData *ud)
 {
     GVariant *params;
-    double position_s;
+    double position_s = 0;
     int64_t position_us;
     GError *error = NULL;
     mpv_get_property(ud->mpv, "time-pos", MPV_FORMAT_DOUBLE, &position_s);
@@ -893,6 +920,11 @@
     return ud->playlist_pos > 0;
 }
 
+static gboolean can_play_pause(UserData *ud)
+{
+    return !ud->idle;
+}
+
 static GVariant * set_playback_status(UserData *ud)
 {
     if (ud->idle) {
@@ -908,12 +940,17 @@
 static void set_stopped_status(UserData *ud)
 {
   const char *prop_name = "PlaybackStatus";
-  GVariant *prop_value = g_variant_new_string(STATUS_STOPPED);
+  GVariant *prop_value = 
g_variant_ref_sink(g_variant_new_string(STATUS_STOPPED));
 
+  ud->idle = TRUE;
   ud->status = STATUS_STOPPED;
 
   g_hash_table_insert(ud->changed_properties,
                       (gpointer)prop_name, prop_value);
+  g_hash_table_insert(ud->changed_properties, "CanPlay",
+                      
g_variant_ref_sink(g_variant_new_boolean(can_play_pause(ud))));
+  g_hash_table_insert(ud->changed_properties, "CanPause",
+                      
g_variant_ref_sink(g_variant_new_boolean(can_play_pause(ud))));
 
   emit_property_changes(ud);
 }
@@ -925,7 +962,7 @@
 {
     GError *error = NULL;
     UserData *ud = user_data;
-    ud->connection = connection;
+    g_set_object(&ud->connection, connection);
 
     if (ud->root_interface_id == 0) {
         ud->root_interface_id =
@@ -952,8 +989,10 @@
     }
 
     if (!ud->events_setup) {
-        setup_mpv_event_sources(ud);
-        ud->events_setup = TRUE;
+        ud->events_setup = setup_mpv_event_sources(ud);
+        if (!ud->events_setup) {
+            g_main_loop_quit(ud->loop);
+        }
     }
 }
 
@@ -970,38 +1009,94 @@
     return id;
 }
 
-static char *build_bus_name(const char *client_name, gboolean unique_suffix)
+static char *build_bus_name(const char *client_name)
 {
     GString *name = g_string_new("org.mpris.MediaPlayer2.mpv");
 
     // don't append client name if it is the default value 'mpv'
     if (g_strcmp0(client_name, "mpv") != 0) {
-        g_string_append_printf(name, ".%s", client_name);
-    }
+        g_string_append_c(name, '.');
+        if (!client_name || !*client_name) {
+            g_string_append_c(name, '_');
+        } else {
+            for (const unsigned char *p = (const unsigned char *)client_name; 
*p; p++) {
+                if (g_ascii_isalpha(*p) || *p == '-' ||
+                    (g_ascii_isdigit(*p) && p != (const unsigned char 
*)client_name)) {
+                    g_string_append_c(name, *p);
+                } else if (*p == ' ') {
+                    g_string_append_c(name, '_');
+                } else {
+                    g_string_append_printf(name, "_%02X", *p);
+                }
+            }
+        }
 
-    if (unique_suffix) {
-        char *id = generate_random_id();
-        g_string_append_printf(name, ".instance-%s", id);
-        g_free(id);
+        // Reserve 18 bytes for the instance suffix within D-Bus's 255-byte 
limit.
+        if (name->len > 237) {
+            gchar *hash = g_compute_checksum_for_string(G_CHECKSUM_SHA256, 
name->str, -1);
+            g_string_truncate(name, 172);
+            g_string_append_printf(name, "_%s", hash);
+            g_free(hash);
+        }
     }
 
+    char *id = generate_random_id();
+    g_string_append_printf(name, ".instance-%s", id);
+    g_free(id);
+
     return g_string_free(name, FALSE);
 }
 
+static void bus_owner_destroyed(gpointer data)
+{
+    UserData *ud = data;
+    ud->pending_bus_owners--;
+}
+
+static gboolean retry_bus_name(gpointer data)
+{
+    UserData *ud = data;
+    char *name = build_bus_name(ud->client_name);
+    guint previous_id = ud->bus_id;
+    ud->bus_id = 0;
+    g_bus_unown_name(previous_id);
+    ud->bus_id = g_bus_own_name(G_BUS_TYPE_SESSION,
+                                name,
+                                G_BUS_NAME_OWNER_FLAGS_NONE,
+                                NULL, NULL, NULL,
+                                ud, bus_owner_destroyed);
+    if (ud->bus_id != 0) {
+        ud->pending_bus_owners++;
+    }
+    g_free(name);
+    return G_SOURCE_REMOVE;
+}
+
 static void on_name_lost(GDBusConnection *connection,
                          G_GNUC_UNUSED const char *_name,
                          gpointer user_data)
 {
     UserData *ud = user_data;
 
+    // Stop the plugin if the initial connection to the session bus failed.
+    if (!connection && !ud->events_setup) {
+        g_main_loop_quit(ud->loop);
+        return;
+    }
+
     if (connection) {
-        char *name = build_bus_name(ud->client_name, TRUE);
+        char *name = build_bus_name(ud->client_name);
         ud->bus_id = g_bus_own_name(G_BUS_TYPE_SESSION,
                                     name,
                                     G_BUS_NAME_OWNER_FLAGS_NONE,
                                     NULL, NULL, NULL,
-                                    &ud, NULL);
+                                    ud, NULL);
         g_free(name);
+
+        // Let GLib finish processing the failed request before releasing it.
+        ud->bus_retry_source = g_idle_source_new();
+        g_source_set_callback(ud->bus_retry_source, retry_bus_name, ud, NULL);
+        g_source_attach(ud->bus_retry_source, ud->ctx);
     } else {
       ud->root_interface_id = 0;
       ud->player_interface_id = 0;
@@ -1013,6 +1108,7 @@
     const char *prop_name = NULL;
     GVariant *prop_value = NULL;
     gboolean update_can_go_next_prev = FALSE;
+    gboolean update_can_play_pause = FALSE;
 
     if (g_strcmp0(name, "pause") == 0) {
         ud->paused = *(int*)data;
@@ -1023,6 +1119,7 @@
         ud->idle = *(int*)data;
         prop_name = "PlaybackStatus";
         prop_value = set_playback_status(ud);
+        update_can_play_pause = TRUE;
 
     } else if (g_strcmp0(name, "media-title") == 0 ||
                g_strcmp0(name, "duration") == 0) {
@@ -1030,7 +1127,7 @@
         if (ud->metadata) {
             g_variant_unref(ud->metadata);
         }
-        ud->metadata = create_metadata(ud);
+        ud->metadata = g_variant_ref_sink(create_metadata(ud));
         prop_name = "Metadata";
         prop_value = ud->metadata;
 
@@ -1050,7 +1147,7 @@
         if (g_strcmp0(status, "no") != 0) {
             ud->loop_status = LOOP_TRACK;
         } else {
-            char *playlist_status;
+            char *playlist_status = NULL;
             mpv_get_property(ud->mpv, "loop-playlist", MPV_FORMAT_STRING, 
&playlist_status);
             if (g_strcmp0(playlist_status, "no") != 0) {
                 ud->loop_status = LOOP_PLAYLIST;
@@ -1068,7 +1165,7 @@
         if (g_strcmp0(status, "no") != 0) {
             ud->loop_status = LOOP_PLAYLIST;
         } else {
-            char *file_status;
+            char *file_status = NULL;
             mpv_get_property(ud->mpv, "loop-file", MPV_FORMAT_STRING, 
&file_status);
             if (g_strcmp0(file_status, "no") != 0) {
                 ud->loop_status = LOOP_TRACK;
@@ -1103,7 +1200,7 @@
 
     if (prop_name) {
         if (prop_value) {
-            g_variant_ref(prop_value);
+            g_variant_ref_sink(prop_value);
         }
         g_hash_table_insert(ud->changed_properties,
                             (gpointer)prop_name, prop_value);
@@ -1111,9 +1208,16 @@
 
     if (update_can_go_next_prev) {
         g_hash_table_insert(ud->changed_properties, "CanGoNext",
-                            g_variant_new_boolean(can_go_next(ud)));
+                            
g_variant_ref_sink(g_variant_new_boolean(can_go_next(ud))));
         g_hash_table_insert(ud->changed_properties, "CanGoPrevious",
-                            g_variant_new_boolean(can_go_previous(ud)));
+                            
g_variant_ref_sink(g_variant_new_boolean(can_go_previous(ud))));
+    }
+
+    if (update_can_play_pause) {
+        g_hash_table_insert(ud->changed_properties, "CanPlay",
+                            
g_variant_ref_sink(g_variant_new_boolean(can_play_pause(ud))));
+        g_hash_table_insert(ud->changed_properties, "CanPause",
+                            
g_variant_ref_sink(g_variant_new_boolean(can_play_pause(ud))));
     }
 }
 
@@ -1162,35 +1266,37 @@
     (void)!write(*((int*)fd), "0", 1);
 }
 
-static void setup_mpv_event_sources(UserData *ud)
+static gboolean setup_mpv_event_sources(UserData *ud)
 {
     GError *error = NULL;
-    GSource *mpv_pipe_source;
-    GSource *timeout_source;
 
-    g_unix_open_pipe(ud->wakeup_pipe, 0, &error);
-    if (error != NULL) {
-        g_printerr("%s", error->message);
+    if (!g_unix_open_pipe(ud->wakeup_pipe, FD_CLOEXEC, &error)) {
+        g_printerr("%s\n", error->message);
         g_clear_error(&error);
+        return FALSE;
+    }
+    if (!g_unix_set_fd_nonblocking(ud->wakeup_pipe[0], TRUE, &error) ||
+        !g_unix_set_fd_nonblocking(ud->wakeup_pipe[1], TRUE, &error)) {
+        g_printerr("%s\n", error->message);
+        g_clear_error(&error);
+        return FALSE;
     }
-    fcntl(ud->wakeup_pipe[0], F_SETFL, O_NONBLOCK);
     mpv_set_wakeup_callback(ud->mpv, wakeup_handler, &ud->wakeup_pipe[1]);
 
-    mpv_pipe_source = g_unix_fd_source_new(ud->wakeup_pipe[0], G_IO_IN);
-    g_source_set_callback(mpv_pipe_source,
+    ud->mpv_pipe_source = g_unix_fd_source_new(ud->wakeup_pipe[0], G_IO_IN);
+    g_source_set_callback(ud->mpv_pipe_source,
                           G_SOURCE_FUNC(event_handler),
                           ud,
                           NULL);
-    g_source_attach(mpv_pipe_source, ud->ctx);
-    g_source_unref(mpv_pipe_source);
+    g_source_attach(ud->mpv_pipe_source, ud->ctx);
 
-    timeout_source = g_timeout_source_new(100);
-    g_source_set_callback(timeout_source,
+    ud->timeout_source = g_timeout_source_new(100);
+    g_source_set_callback(ud->timeout_source,
                           G_SOURCE_FUNC(emit_property_changes),
                           ud,
                           NULL);
-    g_source_attach(timeout_source, ud->ctx);
-    g_source_unref(timeout_source);
+    g_source_attach(ud->timeout_source, ud->ctx);
+    return TRUE;
 }
 
 // Plugin entry point
@@ -1198,7 +1304,7 @@
 {
     GMainContext *ctx;
     GMainLoop *loop;
-    UserData ud = {0};
+    UserData ud = {.wakeup_pipe = {-1, -1}};
     GError *error = NULL;
     GDBusNodeInfo *introspection_data = NULL;
 
@@ -1221,25 +1327,33 @@
     ud.ctx = ctx;
     ud.status = STATUS_STOPPED;
     ud.loop_status = LOOP_NONE;
-    ud.changed_properties = g_hash_table_new(g_str_hash, g_str_equal);
+    ud.changed_properties = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                  NULL,
+                                                  
(GDestroyNotify)g_variant_unref);
     ud.seek_expected = FALSE;
     ud.idle = FALSE;
     ud.paused = FALSE;
     ud.shuffle = FALSE;
-    ud.client_name = mpv_get_property_string(mpv, "audio-client-name");
+    char *client_name = mpv_get_property_string(mpv, "audio-client-name");
+    ud.client_name = g_strdup(client_name);
+    mpv_free(client_name);
     mpv_get_property(mpv, "playlist-count", MPV_FORMAT_INT64, 
&ud.playlist_count);
     mpv_get_property(mpv, "playlist-pos", MPV_FORMAT_INT64, &ud.playlist_pos);
 
-    char *bus_name = build_bus_name(ud.client_name, FALSE);
+    char *bus_name = build_bus_name(ud.client_name);
     g_main_context_push_thread_default(ctx);
-    ud.bus_id = g_bus_own_name(G_BUS_TYPE_SESSION,
+    if (g_dbus_is_name(bus_name)) {
+        ud.bus_id = g_bus_own_name(G_BUS_TYPE_SESSION,
                                bus_name,
                                G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE,
                                on_bus_acquired,
                                NULL,
                                on_name_lost,
-                               &ud, NULL);
-    g_main_context_pop_thread_default(ctx);
+                               &ud, bus_owner_destroyed);
+        if (ud.bus_id != 0) {
+            ud.pending_bus_owners++;
+        }
+    }
     g_free(bus_name);
 
     // Receive event for property changes
@@ -1256,16 +1370,58 @@
     mpv_observe_property(mpv, 0, "playlist-count", MPV_FORMAT_INT64);
     mpv_observe_property(mpv, 0, "playlist-pos", MPV_FORMAT_INT64);
 
-    g_main_loop_run(loop);
+    if (ud.bus_id != 0) {
+        g_main_loop_run(loop);
+    } else {
+        g_printerr("Failed to register MPRIS bus name\n");
+    }
 
-    g_dbus_connection_unregister_object(ud.connection, ud.root_interface_id);
-    g_dbus_connection_unregister_object(ud.connection, ud.player_interface_id);
+    if (ud.bus_retry_source) {
+        g_source_destroy(ud.bus_retry_source);
+        g_source_unref(ud.bus_retry_source);
+    }
+    mpv_set_wakeup_callback(mpv, NULL, NULL);
+    if (ud.mpv_pipe_source) {
+        g_source_destroy(ud.mpv_pipe_source);
+        g_source_unref(ud.mpv_pipe_source);
+    }
+    if (ud.timeout_source) {
+        g_source_destroy(ud.timeout_source);
+        g_source_unref(ud.timeout_source);
+    }
+    for (int i = 0; i < 2; i++) {
+        if (ud.wakeup_pipe[i] >= 0) {
+            close(ud.wakeup_pipe[i]);
+        }
+    }
 
-    g_bus_unown_name(ud.bus_id);
+    if (ud.connection) {
+        g_dbus_connection_unregister_object(ud.connection, 
ud.root_interface_id);
+        g_dbus_connection_unregister_object(ud.connection, 
ud.player_interface_id);
+    }
+
+    if (ud.bus_id != 0) {
+        g_bus_unown_name(ud.bus_id);
+    }
+    // Unowning is asynchronous; keep callback data alive until all owners 
finish.
+    while (ud.pending_bus_owners != 0) {
+        g_main_context_iteration(ctx, TRUE);
+    }
+
+    if (ud.metadata) {
+        g_variant_unref(ud.metadata);
+    }
+    g_hash_table_unref(ud.changed_properties);
+
+    g_clear_object(&ud.connection);
+    g_main_context_pop_thread_default(ctx);
     g_main_loop_unref(loop);
     g_main_context_unref(ctx);
     g_dbus_node_info_unref(introspection_data);
-    mpv_free(ud.client_name);
+
+    g_free(ud.client_name);
+    g_free(ud.cached_path);
+    g_free(ud.cached_art_url);
 
     return 0;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpv-mpris-1.2/test/env new/mpv-mpris-1.3/test/env
--- old/mpv-mpris-1.2/test/env  2026-01-01 23:21:19.000000000 +0100
+++ new/mpv-mpris-1.3/test/env  2026-09-17 01:23:53.000000000 +0200
@@ -41,9 +41,8 @@
 
 if [ -n "$MPV_MPRIS_TEST_PLUGIN" ] ; then
        if [ ! -f "$MPV_MPRIS_TEST_PLUGIN" ] ||
-          [ ! -r "$MPV_MPRIS_TEST_PLUGIN" ] ||
-          [ ! -x "$MPV_MPRIS_TEST_PLUGIN" ] ; then
-               echo "$MPV_MPRIS_TEST_PLUGIN not an existing file with rx 
perms" >&2
+          [ ! -r "$MPV_MPRIS_TEST_PLUGIN" ] ; then
+               echo "$MPV_MPRIS_TEST_PLUGIN not an existing file with read 
perms" >&2
                exit 1
        fi
 fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpv-mpris-1.2/test/setup new/mpv-mpris-1.3/test/setup
--- old/mpv-mpris-1.2/test/setup        2026-01-01 23:21:19.000000000 +0100
+++ new/mpv-mpris-1.3/test/setup        2026-09-17 01:23:53.000000000 +0200
@@ -64,7 +64,7 @@
 
 playerctl_list_all_is_mpv () {
        ret=0 ; player="$(playerctl --list-all 2> /dev/null)" || ret=$?
-       if [ $ret -ne 0 ] || [ "$player" != mpv ] ; then
+       if [ $ret -ne 0 ] || [[ ! "$player" =~ ^mpv\.instance-[A-Za-z]{8}$ ]] ; 
then
                playerctl --list-all ; return $((ret?ret:1))
        fi
 }
@@ -77,7 +77,7 @@
 mpris_quit () {
        # playerctl does not yet support the MPRIS Quit API
        # https://github.com/altdesktop/playerctl/issues/171
-       dbus-send --print-reply --dest=org.mpris.MediaPlayer2.mpv 
/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Quit
+       dbus-send --print-reply --dest="org.mpris.MediaPlayer2.$player" 
/org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Quit
 }
 
 

Reply via email to