LCOV - code coverage report
Current view: top level - src/plugins/mpris - valent-mpris-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 93.8 % 449 421
Test Date: 2024-03-22 06:22:29 Functions: 100.0 % 30 30
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 56.6 % 288 163

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-3.0-or-later
       2                 :             : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
       3                 :             : 
       4                 :             : #define G_LOG_DOMAIN "valent-mpris-plugin"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <math.h>
       9                 :             : 
      10                 :             : #include <glib/gi18n.h>
      11                 :             : #include <gio/gio.h>
      12                 :             : #include <json-glib/json-glib.h>
      13                 :             : #include <valent.h>
      14                 :             : 
      15                 :             : #include "valent-mpris-device.h"
      16                 :             : #include "valent-mpris-plugin.h"
      17                 :             : #include "valent-mpris-utils.h"
      18                 :             : 
      19                 :             : 
      20                 :             : struct _ValentMprisPlugin
      21                 :             : {
      22                 :             :   ValentDevicePlugin  parent_instance;
      23                 :             : 
      24                 :             :   ValentMedia        *media;
      25                 :             :   unsigned int        media_watch : 1;
      26                 :             : 
      27                 :             :   GPtrArray          *players;
      28                 :             :   GHashTable         *transfers;
      29                 :             : 
      30                 :             :   GHashTable         *pending;
      31                 :             :   unsigned int        flush_id;
      32                 :             : };
      33                 :             : 
      34   [ +  +  +  - ]:          98 : G_DEFINE_FINAL_TYPE (ValentMprisPlugin, valent_mpris_plugin, VALENT_TYPE_DEVICE_PLUGIN)
      35                 :             : 
      36                 :             : static void valent_mpris_plugin_send_player_info    (ValentMprisPlugin *self,
      37                 :             :                                                      ValentMediaPlayer *player,
      38                 :             :                                                      gboolean           now_playing,
      39                 :             :                                                      gboolean           volume);
      40                 :             : static void valent_mpris_plugin_send_player_list    (ValentMprisPlugin *self);
      41                 :             : 
      42                 :             : 
      43                 :             : static gpointer
      44                 :          11 : _valent_media_lookup_player (ValentMedia *media,
      45                 :             :                              const char  *name)
      46                 :             : {
      47                 :          11 :   unsigned int n_players = 0;
      48                 :             : 
      49         [ +  - ]:          11 :   g_assert (VALENT_IS_MEDIA (media));
      50   [ +  -  -  + ]:          11 :   g_assert (name != NULL && *name != '\0');
      51                 :             : 
      52                 :          11 :   n_players = g_list_model_get_n_items (G_LIST_MODEL (media));
      53                 :             : 
      54         [ +  - ]:          11 :   for (unsigned int i = 0; i < n_players; i++)
      55                 :             :     {
      56                 :          11 :       g_autoptr (ValentMediaPlayer) player = g_list_model_get_item (G_LIST_MODEL (media), i);
      57                 :             : 
      58         [ +  - ]:          11 :       if (g_strcmp0 (valent_media_player_get_name (player), name) == 0)
      59         [ +  - ]:          11 :         return player;
      60                 :             :     }
      61                 :             : 
      62                 :             :   return NULL;
      63                 :             : }
      64                 :             : 
      65                 :             : 
      66                 :             : /*
      67                 :             :  * Local Players
      68                 :             :  */
      69                 :             : static void
      70                 :           1 : send_album_art_cb (ValentTransfer    *transfer,
      71                 :             :                    GAsyncResult      *result,
      72                 :             :                    ValentMprisPlugin *self)
      73                 :             : {
      74                 :           2 :   g_autoptr (GError) error = NULL;
      75         [ -  + ]:           1 :   g_autofree char *id = NULL;
      76                 :             : 
      77         [ +  - ]:           1 :   g_assert (VALENT_IS_TRANSFER (transfer));
      78                 :             : 
      79         [ -  + ]:           1 :   if (!valent_transfer_execute_finish (transfer, result, &error))
      80                 :           0 :     g_debug ("Failed to upload album art: %s", error->message);
      81                 :             : 
      82                 :           1 :   id = valent_transfer_dup_id (transfer);
      83                 :           1 :   g_hash_table_remove (self->transfers, id);
      84                 :           1 : }
      85                 :             : 
      86                 :             : static void
      87                 :           1 : valent_mpris_plugin_send_album_art (ValentMprisPlugin *self,
      88                 :             :                                     ValentMediaPlayer *player,
      89                 :             :                                     const char        *requested_uri)
      90                 :             : {
      91                 :           1 :   g_autoptr (GVariant) metadata = NULL;
      92                 :           1 :   const char *real_uri;
      93         [ -  - ]:           1 :   g_autoptr (GFile) real_file = NULL;
      94   [ +  -  -  - ]:           1 :   g_autoptr (GFile) requested_file = NULL;
      95   [ +  -  -  - ]:           1 :   g_autoptr (JsonBuilder) builder = NULL;
      96   [ -  +  -  - ]:           1 :   g_autoptr (JsonNode) packet = NULL;
      97   [ +  -  -  - ]:           1 :   g_autoptr (ValentTransfer) transfer = NULL;
      98                 :           1 :   ValentDevice *device;
      99                 :             : 
     100         [ +  - ]:           1 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     101                 :             : 
     102                 :             :   /* Ignore concurrent requests */
     103         [ +  - ]:           1 :   if (g_hash_table_contains (self->transfers, requested_uri))
     104                 :             :     return;
     105                 :             : 
     106                 :             :   /* Check player and URL are safe */
     107   [ +  -  -  + ]:           2 :   if ((metadata = valent_media_player_get_metadata (player)) == NULL ||
     108                 :           1 :       !g_variant_lookup (metadata, "mpris:artUrl", "&s", &real_uri))
     109                 :             :     {
     110                 :           0 :       g_warning ("Album art request \"%s\" for track without album art",
     111                 :             :                  requested_uri);
     112                 :           0 :       return;
     113                 :             :     }
     114                 :             : 
     115                 :             :   /* Compare normalized URLs */
     116                 :           1 :   requested_file = g_file_new_for_uri (requested_uri);
     117                 :           1 :   real_file = g_file_new_for_uri (real_uri);
     118                 :             : 
     119         [ -  + ]:           1 :   if (!g_file_equal (requested_file, real_file))
     120                 :             :     {
     121                 :           0 :       g_warning ("Album art request \"%s\" doesn't match current track \"%s\"",
     122                 :             :                  requested_uri, real_uri);
     123                 :           0 :       return;
     124                 :             :     }
     125                 :             : 
     126                 :             :   /* Build the payload packet */
     127                 :           1 :   valent_packet_init (&builder, "kdeconnect.mpris");
     128                 :           1 :   json_builder_set_member_name (builder, "player");
     129                 :           1 :   json_builder_add_string_value (builder, valent_media_player_get_name (player));
     130                 :           1 :   json_builder_set_member_name (builder, "albumArtUrl");
     131                 :           1 :   json_builder_add_string_value (builder, requested_uri);
     132                 :           1 :   json_builder_set_member_name (builder, "transferringAlbumArt");
     133                 :           1 :   json_builder_add_boolean_value (builder, TRUE);
     134                 :           1 :   packet = valent_packet_end (&builder);
     135                 :             : 
     136                 :             :   /* Start the transfer */
     137                 :           1 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     138                 :           1 :   transfer = valent_device_transfer_new (device, packet, real_file);
     139                 :             : 
     140         [ -  + ]:           1 :   g_hash_table_insert (self->transfers,
     141                 :           1 :                        g_strdup (requested_uri),
     142                 :             :                        g_object_ref (transfer));
     143                 :             : 
     144         [ +  - ]:           1 :   valent_transfer_execute (transfer,
     145                 :             :                            NULL,
     146                 :             :                            (GAsyncReadyCallback)send_album_art_cb,
     147                 :             :                            self);
     148                 :             : }
     149                 :             : 
     150                 :             : static gboolean
     151                 :           9 : valent_mpris_plugin_flush (gpointer data)
     152                 :             : {
     153                 :           9 :   ValentMprisPlugin *self = VALENT_MPRIS_PLUGIN (data);
     154                 :           9 :   GHashTableIter iter;
     155                 :           9 :   ValentMediaPlayer *player;
     156                 :             : 
     157                 :           9 :   g_hash_table_iter_init (&iter, self->pending);
     158         [ +  + ]:          18 :   while (g_hash_table_iter_next (&iter, (void **)&player, NULL))
     159                 :             :     {
     160                 :           9 :       valent_mpris_plugin_send_player_info (self, player, TRUE, TRUE);
     161                 :           9 :       g_hash_table_iter_remove (&iter);
     162                 :             :     }
     163                 :             : 
     164                 :           9 :   self->flush_id = 0;
     165                 :             : 
     166                 :           9 :   return G_SOURCE_REMOVE;
     167                 :             : }
     168                 :             : 
     169                 :             : static void
     170                 :           2 : on_player_seeked (ValentMediaPlayer *player,
     171                 :             :                   double             position,
     172                 :             :                   ValentMprisPlugin *self)
     173                 :             : {
     174                 :           2 :   const char *name;
     175                 :           4 :   g_autoptr (JsonBuilder) builder = NULL;
     176         [ -  + ]:           2 :   g_autoptr (JsonNode) packet = NULL;
     177                 :             : 
     178         [ +  - ]:           2 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     179                 :             : 
     180                 :           2 :   name = valent_media_player_get_name (player);
     181                 :             : 
     182                 :             :   /* Convert seconds to milliseconds */
     183                 :           2 :   valent_packet_init (&builder, "kdeconnect.mpris");
     184                 :           2 :   json_builder_set_member_name (builder, "player");
     185                 :           2 :   json_builder_add_string_value (builder, name);
     186                 :           2 :   json_builder_set_member_name (builder, "pos");
     187                 :           2 :   json_builder_add_int_value (builder, position * 1000L);
     188                 :           2 :   packet = valent_packet_end (&builder);
     189                 :             : 
     190         [ +  - ]:           2 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     191                 :           2 : }
     192                 :             : 
     193                 :             : static void
     194                 :          21 : on_player_changed (ValentMediaPlayer *player,
     195                 :             :                    GParamSpec        *pspec,
     196                 :             :                    ValentMprisPlugin *self)
     197                 :             : {
     198         [ +  - ]:          21 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     199                 :             : 
     200         [ +  + ]:          21 :   if (g_str_equal (pspec->name, "position"))
     201                 :             :     {
     202                 :           2 :       double position = 0.0;
     203                 :             : 
     204                 :           2 :       position = valent_media_player_get_position (player);
     205                 :           2 :       on_player_seeked (player, position, self);
     206                 :             :     }
     207                 :             :   else
     208                 :             :     {
     209                 :          19 :       g_hash_table_add (self->pending, player);
     210                 :             : 
     211         [ +  + ]:          19 :       if (self->flush_id == 0)
     212                 :           9 :         self->flush_id = g_idle_add (valent_mpris_plugin_flush, self);
     213                 :             :     }
     214                 :          21 : }
     215                 :             : 
     216                 :             : static void
     217                 :           3 : on_players_changed (ValentMedia       *media,
     218                 :             :                     unsigned int       position,
     219                 :             :                     unsigned int       removed,
     220                 :             :                     unsigned int       added,
     221                 :             :                     ValentMprisPlugin *self)
     222                 :             : {
     223                 :           3 :   gboolean changed = FALSE;
     224                 :             : 
     225         [ +  + ]:           3 :   if (removed > 0)
     226                 :             :     {
     227                 :           2 :       changed = TRUE;
     228                 :           2 :       g_hash_table_remove_all (self->pending);
     229                 :             :     }
     230                 :             : 
     231         [ +  + ]:           4 :   for (unsigned int i = 0; i < added; i++)
     232                 :             :     {
     233                 :           1 :       g_autoptr (ValentMediaPlayer) player = NULL;
     234                 :             : 
     235                 :           1 :       player = g_list_model_get_item (G_LIST_MODEL (media), position + i);
     236                 :             : 
     237                 :             :       /* Here, and below when building the player list, all `ValentMprisDevice`
     238                 :             :        * players are being skipped. An advanced option could control whether
     239                 :             :        * `!g_ptr_array_find (self->players, player, NULL)` passes, enabling a
     240                 :             :        * device to act as a hub for other devices. */
     241         [ +  - ]:           1 :       if (VALENT_IS_MPRIS_DEVICE (player))
     242         [ +  - ]:           1 :         continue;
     243                 :             : 
     244                 :           0 :       changed = TRUE;
     245                 :           0 :       g_signal_connect_object (player,
     246                 :             :                                "notify",
     247                 :             :                                G_CALLBACK (on_player_changed),
     248                 :             :                                self, 0);
     249                 :             : 
     250         [ #  # ]:           0 :       VALENT_NOTE ("tracking %s (%s)",
     251                 :             :                    G_OBJECT_TYPE_NAME (player),
     252                 :             :                    valent_media_player_get_name (player));
     253                 :             :     }
     254                 :             : 
     255         [ +  + ]:           3 :   if (changed)
     256                 :           2 :     valent_mpris_plugin_send_player_list (self);
     257                 :           3 : }
     258                 :             : 
     259                 :             : static void
     260                 :           5 : valent_mpris_plugin_handle_action (ValentMprisPlugin *self,
     261                 :             :                                    ValentMediaPlayer *player,
     262                 :             :                                    const char        *action)
     263                 :             : {
     264         [ +  - ]:           5 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     265         [ -  + ]:           5 :   g_assert (VALENT_IS_MEDIA_PLAYER (player));
     266   [ +  -  -  + ]:           5 :   g_assert (action && *action);
     267                 :             : 
     268         [ +  + ]:           5 :   if (g_str_equal (action, "Next"))
     269                 :           1 :     valent_media_player_next (player);
     270                 :             : 
     271         [ +  + ]:           4 :   else if (g_str_equal (action, "Pause"))
     272                 :           1 :     valent_media_player_pause (player);
     273                 :             : 
     274         [ +  + ]:           3 :   else if (g_str_equal (action, "Play"))
     275                 :           1 :     valent_media_player_play (player);
     276                 :             : 
     277         [ -  + ]:           2 :   else if (g_str_equal (action, "PlayPause"))
     278   [ #  #  #  #  :           0 :     valent_mpris_play_pause (player);
                   #  # ]
     279                 :             : 
     280         [ +  + ]:           2 :   else if (g_str_equal (action, "Previous"))
     281                 :           1 :     valent_media_player_previous (player);
     282                 :             : 
     283         [ +  - ]:           1 :   else if (g_str_equal (action, "Stop"))
     284                 :           1 :     valent_media_player_stop (player);
     285                 :             : 
     286                 :             :   else
     287                 :           0 :     g_warning ("%s(): Unknown action: %s", G_STRFUNC, action);
     288                 :           5 : }
     289                 :             : 
     290                 :             : static void
     291                 :          11 : valent_mpris_plugin_handle_mpris_request (ValentMprisPlugin *self,
     292                 :             :                                           JsonNode          *packet)
     293                 :             : {
     294                 :          11 :   ValentMediaPlayer *player = NULL;
     295                 :          11 :   const char *name;
     296                 :          11 :   const char *action;
     297                 :          11 :   const char *url;
     298                 :          11 :   int64_t offset_us;
     299                 :          11 :   int64_t position;
     300                 :          11 :   gboolean request_now_playing;
     301                 :          11 :   gboolean request_volume;
     302                 :          11 :   const char *loop_status;
     303                 :          11 :   ValentMediaRepeat repeat;
     304                 :          11 :   gboolean shuffle;
     305                 :          11 :   int64_t volume;
     306                 :             : 
     307         [ +  - ]:          11 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     308                 :             : 
     309                 :             :   /* Start by checking for a player */
     310         [ +  - ]:          11 :   if (valent_packet_get_string (packet, "player", &name))
     311                 :          11 :     player = _valent_media_lookup_player (self->media, name);
     312                 :             : 
     313   [ +  -  -  + ]:          11 :   if (player == NULL || valent_packet_check_field (packet, "requestPlayerList"))
     314                 :             :     {
     315                 :           0 :       valent_mpris_plugin_send_player_list (self);
     316                 :           0 :       return;
     317                 :             :     }
     318                 :             : 
     319                 :             :   /* A request for a player's status */
     320                 :          11 :   request_now_playing = valent_packet_check_field (packet, "requestNowPlaying");
     321                 :          11 :   request_volume = valent_packet_check_field (packet, "requestVolume");
     322                 :             : 
     323         [ +  + ]:          11 :   if (request_now_playing || request_volume)
     324                 :           1 :     valent_mpris_plugin_send_player_info (self,
     325                 :             :                                           player,
     326                 :             :                                           request_now_playing,
     327                 :             :                                           request_volume);
     328                 :             : 
     329                 :             :   /* A player command */
     330         [ +  + ]:          11 :   if (valent_packet_get_string (packet, "action", &action))
     331                 :           5 :     valent_mpris_plugin_handle_action (self, player, action);
     332                 :             : 
     333                 :             :   /* A request to change the relative position (microseconds to seconds) */
     334         [ +  + ]:          11 :   if (valent_packet_get_int (packet, "Seek", &offset_us))
     335                 :           1 :     valent_media_player_seek (player, offset_us / G_TIME_SPAN_SECOND);
     336                 :             : 
     337                 :             :   /* A request to change the absolute position (milliseconds to seconds) */
     338         [ -  + ]:          11 :   if (valent_packet_get_int (packet, "SetPosition", &position))
     339                 :           0 :     valent_media_player_set_position (player, position / 1000L);
     340                 :             : 
     341                 :             :   /* A request to change the loop status */
     342         [ +  + ]:          11 :   if (valent_packet_get_string (packet, "setLoopStatus", &loop_status))
     343                 :             :     {
     344                 :           1 :       repeat = valent_mpris_repeat_from_string (loop_status);
     345                 :           1 :       valent_media_player_set_repeat (player, repeat);
     346                 :             :     }
     347                 :             : 
     348                 :             :   /* A request to change the shuffle mode */
     349         [ +  + ]:          11 :   if (valent_packet_get_boolean (packet, "setShuffle", &shuffle))
     350                 :           1 :     valent_media_player_set_shuffle (player, shuffle);
     351                 :             : 
     352                 :             :   /* A request to change the player volume */
     353         [ +  + ]:          11 :   if (valent_packet_get_int (packet, "setVolume", &volume))
     354                 :           1 :     valent_media_player_set_volume (player, volume / 100.0);
     355                 :             : 
     356                 :             :   /* An album art request */
     357         [ +  + ]:          11 :   if (valent_packet_get_string (packet, "albumArtUrl", &url))
     358                 :           1 :     valent_mpris_plugin_send_album_art (self, player, url);
     359                 :             : }
     360                 :             : 
     361                 :             : static void
     362                 :          10 : valent_mpris_plugin_send_player_info (ValentMprisPlugin *self,
     363                 :             :                                       ValentMediaPlayer *player,
     364                 :             :                                       gboolean           request_now_playing,
     365                 :             :                                       gboolean           request_volume)
     366                 :             : {
     367                 :          10 :   const char *name;
     368                 :          20 :   g_autoptr (JsonBuilder) builder = NULL;
     369         [ -  + ]:          10 :   g_autoptr (JsonNode) response = NULL;
     370                 :             : 
     371         [ +  - ]:          10 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     372         [ -  + ]:          10 :   g_assert (VALENT_IS_MEDIA_PLAYER (player));
     373                 :             : 
     374                 :             :   /* Start the packet */
     375                 :          10 :   valent_packet_init (&builder, "kdeconnect.mpris");
     376                 :             : 
     377                 :          10 :   name = valent_media_player_get_name (player);
     378                 :          10 :   json_builder_set_member_name (builder, "player");
     379                 :          10 :   json_builder_add_string_value (builder, name);
     380                 :             : 
     381                 :             :   /* Player State & Metadata */
     382         [ +  - ]:          10 :   if (request_now_playing)
     383                 :             :     {
     384                 :          10 :       ValentMediaActions flags;
     385                 :          10 :       ValentMediaRepeat repeat;
     386                 :          10 :       gboolean is_playing;
     387                 :          10 :       double position;
     388                 :          10 :       gboolean shuffle;
     389                 :          10 :       const char *loop_status = "None";
     390                 :          10 :       g_autoptr (GVariant) metadata = NULL;
     391         [ +  - ]:          10 :       g_autofree char *artist = NULL;
     392                 :          10 :       const char *title = NULL;
     393                 :             : 
     394                 :             :       /* Player State */
     395                 :          10 :       flags = valent_media_player_get_flags (player);
     396                 :          10 :       json_builder_set_member_name (builder, "canPause");
     397                 :          10 :       json_builder_add_boolean_value (builder, (flags & VALENT_MEDIA_ACTION_PAUSE) != 0);
     398                 :          10 :       json_builder_set_member_name (builder, "canPlay");
     399                 :          10 :       json_builder_add_boolean_value (builder, (flags & VALENT_MEDIA_ACTION_PLAY) != 0);
     400                 :          10 :       json_builder_set_member_name (builder, "canGoNext");
     401                 :          10 :       json_builder_add_boolean_value (builder, (flags & VALENT_MEDIA_ACTION_NEXT) != 0);
     402                 :          10 :       json_builder_set_member_name (builder, "canGoPrevious");
     403                 :          10 :       json_builder_add_boolean_value (builder,(flags & VALENT_MEDIA_ACTION_PREVIOUS) != 0);
     404                 :          10 :       json_builder_set_member_name (builder, "canSeek");
     405                 :          10 :       json_builder_add_boolean_value (builder, (flags & VALENT_MEDIA_ACTION_SEEK) != 0);
     406                 :             : 
     407                 :          10 :       repeat = valent_media_player_get_repeat (player);
     408                 :          10 :       loop_status = valent_mpris_repeat_to_string (repeat);
     409                 :          10 :       json_builder_set_member_name (builder, "loopStatus");
     410                 :          10 :       json_builder_add_string_value (builder, loop_status);
     411                 :             : 
     412                 :          10 :       shuffle = valent_media_player_get_shuffle (player);
     413                 :          10 :       json_builder_set_member_name (builder, "shuffle");
     414                 :          10 :       json_builder_add_boolean_value (builder, shuffle);
     415                 :             : 
     416                 :          10 :       is_playing = valent_media_player_get_state (player) == VALENT_MEDIA_STATE_PLAYING;
     417                 :          10 :       json_builder_set_member_name (builder, "isPlaying");
     418                 :          10 :       json_builder_add_boolean_value (builder, is_playing);
     419                 :             : 
     420                 :             :       /* Convert seconds to milliseconds */
     421                 :          10 :       position = valent_media_player_get_position (player);
     422                 :          10 :       json_builder_set_member_name (builder, "pos");
     423                 :          10 :       json_builder_add_int_value (builder, position * 1000L);
     424                 :             : 
     425                 :             :       /* Track Metadata
     426                 :             :        *
     427                 :             :        * See: https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/
     428                 :             :        */
     429         [ +  - ]:          10 :       if ((metadata = valent_media_player_get_metadata (player)) != NULL)
     430                 :             :         {
     431                 :          10 :           g_autofree const char **artists = NULL;
     432                 :          10 :           int64_t length_us;
     433                 :          10 :           const char *art_url;
     434                 :          10 :           const char *album;
     435                 :             : 
     436         [ +  + ]:          10 :           if (g_variant_lookup (metadata, "xesam:artist", "^a&s", &artists) &&
     437   [ +  -  +  - ]:           4 :               artists[0] != NULL && *artists[0] != '\0')
     438                 :             :             {
     439                 :           4 :               artist = g_strjoinv (", ", (char **)artists);
     440                 :           4 :               json_builder_set_member_name (builder, "artist");
     441                 :           4 :               json_builder_add_string_value (builder, artist);
     442                 :             :             }
     443                 :             : 
     444         [ +  + ]:          10 :           if (g_variant_lookup (metadata, "xesam:title", "&s", &title) &&
     445         [ +  - ]:           4 :               *title != '\0')
     446                 :             :             {
     447                 :           4 :               json_builder_set_member_name (builder, "title");
     448                 :           4 :               json_builder_add_string_value (builder, title);
     449                 :             :             }
     450                 :             : 
     451         [ +  + ]:          10 :           if (g_variant_lookup (metadata, "xesam:album", "&s", &album) &&
     452         [ +  - ]:           4 :               *album != '\0')
     453                 :             :             {
     454                 :           4 :               json_builder_set_member_name (builder, "album");
     455                 :           4 :               json_builder_add_string_value (builder, album);
     456                 :             :             }
     457                 :             : 
     458                 :             :           /* Convert microseconds to milliseconds */
     459         [ +  + ]:          10 :           if (g_variant_lookup (metadata, "mpris:length", "x", &length_us))
     460                 :             :             {
     461                 :           4 :               json_builder_set_member_name (builder, "length");
     462                 :           4 :               json_builder_add_int_value (builder, length_us / 1000L);
     463                 :             :             }
     464                 :             : 
     465         [ +  + ]:          10 :           if (g_variant_lookup (metadata, "mpris:artUrl", "&s", &art_url))
     466                 :             :             {
     467                 :           1 :               json_builder_set_member_name (builder, "albumArtUrl");
     468                 :           1 :               json_builder_add_string_value (builder, art_url);
     469                 :             :             }
     470                 :             :         }
     471                 :             :     }
     472                 :             : 
     473                 :             :   /* Volume Level */
     474         [ +  - ]:          10 :   if (request_volume)
     475                 :             :     {
     476                 :          10 :       int64_t level;
     477                 :             : 
     478                 :          10 :       level = floor (valent_media_player_get_volume (player) * 100);
     479                 :          10 :       json_builder_set_member_name (builder, "volume");
     480                 :          10 :       json_builder_add_int_value (builder, level);
     481                 :             :     }
     482                 :             : 
     483                 :             :   /* Send Response */
     484                 :          10 :   response = valent_packet_end (&builder);
     485         [ +  - ]:          10 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), response);
     486                 :          10 : }
     487                 :             : 
     488                 :             : static void
     489                 :           5 : valent_mpris_plugin_send_player_list (ValentMprisPlugin *self)
     490                 :             : {
     491                 :          10 :   g_autoptr (JsonBuilder) builder = NULL;
     492         [ -  + ]:           5 :   g_autoptr (JsonNode) packet = NULL;
     493                 :           5 :   unsigned int n_players = 0;
     494                 :             : 
     495         [ +  - ]:           5 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     496                 :             : 
     497                 :           5 :   valent_packet_init (&builder, "kdeconnect.mpris");
     498                 :             : 
     499                 :             :   /* Player List */
     500                 :           5 :   json_builder_set_member_name (builder, "playerList");
     501                 :           5 :   json_builder_begin_array (builder);
     502                 :             : 
     503                 :           5 :   n_players = g_list_model_get_n_items (G_LIST_MODEL (self->media));
     504                 :             : 
     505         [ +  + ]:           6 :   for (unsigned int i = 0; i < n_players; i++)
     506                 :             :     {
     507                 :           1 :       g_autoptr (ValentMediaPlayer) player = NULL;
     508                 :           1 :       const char *name;
     509                 :             : 
     510                 :           1 :       player = g_list_model_get_item (G_LIST_MODEL (self->media), i);
     511                 :             : 
     512         [ -  + ]:           1 :       if (VALENT_IS_MPRIS_DEVICE (player))
     513         [ #  # ]:           0 :         continue;
     514                 :             : 
     515                 :           1 :       name = valent_media_player_get_name (player);
     516                 :             : 
     517         [ +  - ]:           1 :       if (name != NULL)
     518                 :           1 :         json_builder_add_string_value (builder, name);
     519                 :             :     }
     520                 :             : 
     521                 :           5 :   json_builder_end_array (builder);
     522                 :             : 
     523                 :             :   /* Indicate that the remote device may send us album art payloads */
     524                 :           5 :   json_builder_set_member_name (builder, "supportAlbumArtPayload");
     525                 :           5 :   json_builder_add_boolean_value (builder, TRUE);
     526                 :             : 
     527                 :           5 :   packet = valent_packet_end (&builder);
     528         [ +  - ]:           5 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     529                 :           5 : }
     530                 :             : 
     531                 :             : static void
     532                 :          14 : valent_mpris_plugin_watch_media (ValentMprisPlugin *self,
     533                 :             :                                  gboolean           state)
     534                 :             : {
     535         [ +  + ]:          14 :   if (self->media_watch == state)
     536                 :             :     return;
     537                 :             : 
     538                 :           6 :   self->media = valent_media_get_default ();
     539                 :             : 
     540         [ +  + ]:           6 :   if (state)
     541                 :             :     {
     542                 :           3 :       unsigned int n_players = 0;
     543                 :             : 
     544                 :           3 :       n_players = g_list_model_get_n_items (G_LIST_MODEL (self->media));
     545                 :             : 
     546         [ +  + ]:           4 :       for (unsigned int i = 0; i < n_players; i++)
     547                 :             :         {
     548                 :           1 :           g_autoptr (ValentMediaPlayer) player = NULL;
     549                 :             : 
     550                 :           1 :           player = g_list_model_get_item (G_LIST_MODEL (self->media), i);
     551         [ +  - ]:           1 :           g_signal_connect_object (player,
     552                 :             :                                    "notify",
     553                 :             :                                    G_CALLBACK (on_player_changed),
     554                 :             :                                    self, 0);
     555                 :             :         }
     556                 :             : 
     557                 :           3 :       g_signal_connect_object (self->media,
     558                 :             :                                "items-changed",
     559                 :             :                                G_CALLBACK (on_players_changed),
     560                 :             :                                self, 0);
     561                 :             : 
     562                 :           3 :       self->media_watch = TRUE;
     563                 :             :     }
     564                 :             :   else
     565                 :             :     {
     566                 :           3 :       unsigned int n_players = 0;
     567                 :             : 
     568                 :           3 :       n_players = g_list_model_get_n_items (G_LIST_MODEL (self->media));
     569                 :             : 
     570         [ -  + ]:           3 :       for (unsigned int i = 0; i < n_players; i++)
     571                 :             :         {
     572                 :           0 :           g_autoptr (ValentMediaPlayer) player = NULL;
     573                 :             : 
     574                 :           0 :           player = g_list_model_get_item (G_LIST_MODEL (self->media), i);
     575         [ #  # ]:           0 :           g_signal_handlers_disconnect_by_data (player, self);
     576                 :             :         }
     577                 :             : 
     578         [ -  + ]:           3 :       g_clear_handle_id (&self->flush_id, g_source_remove);
     579                 :           3 :       g_signal_handlers_disconnect_by_data (self->media, self);
     580                 :           3 :       self->media_watch = FALSE;
     581                 :             :     }
     582                 :             : }
     583                 :             : 
     584                 :             : /*
     585                 :             :  * Remote Players
     586                 :             :  */
     587                 :             : static void
     588                 :           1 : _valent_mpris_device_free (gpointer player)
     589                 :             : {
     590                 :           1 :   valent_media_unexport_player (valent_media_get_default (), player);
     591                 :           1 :   g_object_unref (player);
     592                 :           1 : }
     593                 :             : 
     594                 :             : static gboolean
     595                 :           4 : valent_mpris_plugin_find_player (ValentMprisPlugin  *self,
     596                 :             :                                  const char         *name,
     597                 :             :                                  ValentMediaPlayer **player)
     598                 :             : {
     599         [ +  - ]:           4 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     600   [ +  -  -  + ]:           4 :   g_assert (name != NULL && *name != '\0');
     601                 :             : 
     602         [ +  - ]:           4 :   for (unsigned int i = 0, len = self->players->len; i < len; i++)
     603                 :             :     {
     604                 :           4 :       *player = g_ptr_array_index (self->players, i);
     605                 :             : 
     606         [ -  + ]:           4 :       if (g_strcmp0 (valent_media_player_get_name (*player), name) == 0)
     607                 :             :         return TRUE;
     608                 :             : 
     609                 :           0 :       *player = NULL;
     610                 :             :     }
     611                 :             : 
     612                 :             :   return FALSE;
     613                 :             : }
     614                 :             : 
     615                 :             : static void
     616                 :           3 : valent_mpris_plugin_request_player_list (ValentMprisPlugin *self)
     617                 :             : {
     618                 :           6 :   g_autoptr (JsonBuilder) builder = NULL;
     619         [ -  + ]:           3 :   g_autoptr (JsonNode) packet = NULL;
     620                 :             : 
     621                 :           3 :   valent_packet_init (&builder, "kdeconnect.mpris.request");
     622                 :           3 :   json_builder_set_member_name (builder, "requestPlayerList");
     623                 :           3 :   json_builder_add_boolean_value (builder, TRUE);
     624                 :           3 :   packet = valent_packet_end (&builder);
     625                 :             : 
     626         [ +  - ]:           3 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     627                 :           3 : }
     628                 :             : 
     629                 :             : static void
     630                 :           1 : receive_art_cb (ValentTransfer    *transfer,
     631                 :             :                 GAsyncResult      *result,
     632                 :             :                 ValentMprisPlugin *self)
     633                 :             : {
     634                 :           1 :   g_autoptr (JsonNode) packet = NULL;
     635   [ -  -  +  - ]:           1 :   g_autoptr (GFile) file = NULL;
     636   [ -  -  +  - ]:           1 :   g_autoptr (GError) error = NULL;
     637                 :           1 :   ValentMediaPlayer *player = NULL;
     638                 :           1 :   const char *name;
     639                 :             : 
     640         [ -  + ]:           1 :   if (!valent_transfer_execute_finish (transfer, result, &error))
     641                 :             :     {
     642         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     643                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     644                 :             : 
     645         [ #  # ]:           0 :       return;
     646                 :             :     }
     647                 :             : 
     648                 :           1 :   g_object_get (transfer,
     649                 :             :                 "file",   &file,
     650                 :             :                 "packet", &packet,
     651                 :             :                 NULL);
     652                 :             : 
     653   [ +  -  +  - ]:           2 :   if (valent_packet_get_string (packet, "player", &name) &&
     654                 :           1 :       valent_mpris_plugin_find_player (self, name, &player))
     655                 :           1 :     valent_mpris_device_update_art (VALENT_MPRIS_DEVICE (player), file);
     656                 :             : }
     657                 :             : 
     658                 :             : static void
     659                 :           1 : valent_mpris_plugin_receive_album_art (ValentMprisPlugin *self,
     660                 :             :                                        JsonNode          *packet)
     661                 :             : {
     662                 :           1 :   ValentDevice *device;
     663                 :           1 :   ValentContext *context = NULL;
     664                 :           1 :   const char *url;
     665                 :           1 :   g_autofree char *filename = NULL;
     666                 :           1 :   g_autoptr (GFile) file = NULL;
     667         [ +  - ]:           1 :   g_autoptr (ValentTransfer) transfer = NULL;
     668                 :             : 
     669         [ -  + ]:           1 :   if (!valent_packet_get_string (packet, "albumArtUrl", &url))
     670                 :             :     {
     671                 :           0 :       g_debug ("%s(): expected \"albumArtUrl\" field holding a string",
     672                 :             :                G_STRFUNC);
     673                 :           0 :       return;
     674                 :             :     }
     675                 :             : 
     676                 :           1 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     677                 :           1 :   context = valent_device_get_context (device);
     678                 :           1 :   filename = g_compute_checksum_for_string (G_CHECKSUM_MD5, url, -1);
     679                 :           1 :   file = valent_context_get_cache_file (context, filename);
     680                 :             : 
     681                 :           1 :   transfer = valent_device_transfer_new (device, packet, file);
     682         [ +  - ]:           1 :   valent_transfer_execute (transfer,
     683                 :             :                            NULL,
     684                 :             :                            (GAsyncReadyCallback)receive_art_cb,
     685                 :             :                            self);
     686                 :             : }
     687                 :             : 
     688                 :             : static void
     689                 :           1 : valent_mpris_plugin_request_update (ValentMprisPlugin *self,
     690                 :             :                                     const char        *player)
     691                 :             : {
     692                 :           2 :   g_autoptr (JsonBuilder) builder = NULL;
     693         [ -  + ]:           1 :   g_autoptr (JsonNode) packet = NULL;
     694                 :             : 
     695                 :           1 :   valent_packet_init (&builder, "kdeconnect.mpris.request");
     696                 :           1 :   json_builder_set_member_name (builder, "player");
     697                 :           1 :   json_builder_add_string_value (builder, player);
     698                 :           1 :   json_builder_set_member_name (builder, "requestNowPlaying");
     699                 :           1 :   json_builder_add_boolean_value (builder, TRUE);
     700                 :           1 :   json_builder_set_member_name (builder, "requestVolume");
     701                 :           1 :   json_builder_add_boolean_value (builder, TRUE);
     702                 :           1 :   packet = valent_packet_end (&builder);
     703                 :             : 
     704         [ +  - ]:           1 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     705                 :           1 : }
     706                 :             : 
     707                 :             : static void
     708                 :           2 : valent_mpris_plugin_handle_player_list (ValentMprisPlugin *self,
     709                 :             :                                         JsonArray         *player_list)
     710                 :             : {
     711                 :           2 :   unsigned int n_remote, n_local, n_extant = 0;
     712                 :           4 :   g_autofree const char **remote_names = NULL;
     713                 :           2 :   g_autofree const char **local_names = NULL;
     714                 :           2 :   ValentDevice *device = NULL;
     715                 :             : 
     716         [ +  - ]:           2 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     717         [ -  + ]:           2 :   g_assert (player_list != NULL);
     718                 :             : 
     719                 :             : #ifndef __clang_analyzer__
     720                 :             :   /* Collect the remote player names */
     721                 :           2 :   n_remote = json_array_get_length (player_list);
     722         [ -  + ]:           2 :   remote_names = g_new0 (const char *, n_remote + 1);
     723                 :             : 
     724         [ +  + ]:           3 :   for (unsigned int i = 0; i < n_remote; i++)
     725                 :           1 :     remote_names[i] = json_array_get_string_element (player_list, i);
     726                 :             : 
     727                 :             :   /* Remove old players */
     728                 :           2 :   n_local = self->players->len;
     729         [ -  + ]:           2 :   local_names = g_new0 (const char *, n_local + 1);
     730                 :             : 
     731         [ +  + ]:           3 :   for (unsigned int i = n_local; i-- > 0;)
     732                 :             :     {
     733                 :           1 :       ValentMediaPlayer *export = g_ptr_array_index (self->players, i);
     734                 :           1 :       const char *name = valent_media_player_get_name (export);
     735                 :             : 
     736         [ -  + ]:           1 :       if (g_strv_contains (remote_names, name))
     737                 :             :         {
     738                 :           0 :           local_names[n_extant++] = name;
     739                 :           0 :           continue;
     740                 :             :         }
     741                 :             : 
     742                 :           1 :       g_ptr_array_remove_index (self->players, i);
     743                 :             :     }
     744                 :             : 
     745                 :             :   /* Add new players */
     746                 :           2 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     747                 :             : 
     748         [ +  + ]:           3 :   for (unsigned int i = 0; remote_names[i] != NULL; i++)
     749                 :             :     {
     750                 :           1 :       g_autoptr (ValentMprisDevice) player = NULL;
     751                 :           1 :       const char *name = remote_names[i];
     752                 :             : 
     753         [ -  + ]:           1 :       if (g_strv_contains (local_names, name))
     754                 :           0 :         continue;
     755                 :             : 
     756                 :           1 :       player = valent_mpris_device_new (device);
     757                 :           1 :       valent_mpris_device_update_name (player, name);
     758                 :             : 
     759                 :           1 :       g_ptr_array_add (self->players, g_object_ref (player));
     760                 :           1 :       valent_media_export_player (self->media, VALENT_MEDIA_PLAYER (player));
     761                 :             : 
     762         [ +  - ]:           1 :       valent_mpris_plugin_request_update (self, name);
     763                 :             :     }
     764                 :             : #endif /* __clang_analyzer__ */
     765                 :           2 : }
     766                 :             : 
     767                 :             : static void
     768                 :           3 : valent_mpris_plugin_handle_player_update (ValentMprisPlugin *self,
     769                 :             :                                           JsonNode          *packet)
     770                 :             : {
     771                 :           3 :   ValentMediaPlayer *player = NULL;
     772                 :           3 :   const char *name;
     773                 :             : 
     774                 :             :   /* Get the remote */
     775   [ +  -  -  + ]:           6 :   if (!valent_packet_get_string (packet, "player", &name) ||
     776                 :           3 :       !valent_mpris_plugin_find_player (self, name, &player))
     777                 :             :     {
     778                 :           0 :       valent_mpris_plugin_request_player_list (self);
     779                 :           1 :       return;
     780                 :             :     }
     781                 :             : 
     782         [ +  + ]:           3 :   if (valent_packet_check_field (packet, "transferringAlbumArt"))
     783                 :             :     {
     784                 :           1 :       valent_mpris_plugin_receive_album_art (self, packet);
     785                 :           1 :       return;
     786                 :             :     }
     787                 :             : 
     788                 :           2 :   valent_mpris_device_handle_packet (VALENT_MPRIS_DEVICE (player), packet);
     789                 :             : }
     790                 :             : 
     791                 :             : static void
     792                 :           5 : valent_mpris_plugin_handle_mpris (ValentMprisPlugin *self,
     793                 :             :                                   JsonNode          *packet)
     794                 :             : {
     795                 :           5 :   JsonArray *player_list;
     796                 :             : 
     797         [ +  - ]:           5 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     798         [ -  + ]:           5 :   g_assert (VALENT_IS_PACKET (packet));
     799                 :             : 
     800         [ +  + ]:           5 :   if (valent_packet_get_array (packet, "playerList", &player_list))
     801                 :           2 :     valent_mpris_plugin_handle_player_list (self, player_list);
     802                 :             : 
     803         [ +  - ]:           3 :   else if (valent_packet_get_string (packet, "player", NULL))
     804                 :           3 :     valent_mpris_plugin_handle_player_update (self, packet);
     805                 :           5 : }
     806                 :             : 
     807                 :             : 
     808                 :             : /*
     809                 :             :  * ValentDevicePlugin
     810                 :             :  */
     811                 :             : static void
     812                 :          10 : valent_mpris_plugin_update_state (ValentDevicePlugin *plugin,
     813                 :             :                                   ValentDeviceState   state)
     814                 :             : {
     815                 :          10 :   ValentMprisPlugin *self = VALENT_MPRIS_PLUGIN (plugin);
     816                 :          10 :   gboolean available;
     817                 :             : 
     818         [ +  - ]:          10 :   g_assert (VALENT_IS_MPRIS_PLUGIN (self));
     819                 :             : 
     820                 :          10 :   available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
     821                 :             :               (state & VALENT_DEVICE_STATE_PAIRED) != 0;
     822                 :             : 
     823         [ +  + ]:          10 :   if (available)
     824                 :             :     {
     825                 :           3 :       valent_mpris_plugin_watch_media (self, TRUE);
     826                 :           3 :       valent_mpris_plugin_request_player_list (self);
     827                 :           3 :       valent_mpris_plugin_send_player_list (self);
     828                 :             :     }
     829                 :             :   else
     830                 :             :     {
     831                 :           7 :       valent_mpris_plugin_watch_media (self, FALSE);
     832                 :           7 :       g_ptr_array_remove_range (self->players, 0, self->players->len);
     833                 :             :     }
     834                 :          10 : }
     835                 :             : 
     836                 :             : static void
     837                 :          16 : valent_mpris_plugin_handle_packet (ValentDevicePlugin *plugin,
     838                 :             :                                    const char         *type,
     839                 :             :                                    JsonNode           *packet)
     840                 :             : {
     841                 :          16 :   ValentMprisPlugin *self = VALENT_MPRIS_PLUGIN (plugin);
     842                 :             : 
     843         [ +  - ]:          16 :   g_assert (VALENT_IS_MPRIS_PLUGIN (plugin));
     844         [ -  + ]:          16 :   g_assert (type != NULL);
     845         [ -  + ]:          16 :   g_assert (VALENT_IS_PACKET (packet));
     846                 :             : 
     847         [ +  + ]:          16 :   if (g_str_equal (type, "kdeconnect.mpris"))
     848                 :           5 :     valent_mpris_plugin_handle_mpris (self, packet);
     849                 :             : 
     850         [ +  - ]:          11 :   else if (g_str_equal (type, "kdeconnect.mpris.request"))
     851                 :          11 :     valent_mpris_plugin_handle_mpris_request (self, packet);
     852                 :             : 
     853                 :             :   else
     854                 :           0 :     g_assert_not_reached ();
     855                 :          16 : }
     856                 :             : 
     857                 :             : /*
     858                 :             :  * ValentObject
     859                 :             :  */
     860                 :             : static void
     861                 :           4 : valent_mpris_plugin_destroy (ValentObject *object)
     862                 :             : {
     863                 :           4 :   ValentMprisPlugin *self = VALENT_MPRIS_PLUGIN (object);
     864                 :             : 
     865                 :           4 :   valent_mpris_plugin_watch_media (self, FALSE);
     866                 :           4 :   self->media = NULL;
     867                 :             : 
     868                 :           4 :   VALENT_OBJECT_CLASS (valent_mpris_plugin_parent_class)->destroy (object);
     869                 :           4 : }
     870                 :             : 
     871                 :             : /*
     872                 :             :  * GObject
     873                 :             :  */
     874                 :             : static void
     875                 :           3 : valent_mpris_plugin_constructed (GObject *object)
     876                 :             : {
     877                 :           3 :   ValentMprisPlugin *self = VALENT_MPRIS_PLUGIN (object);
     878                 :             : 
     879                 :           3 :   self->media = valent_media_get_default ();
     880                 :             : 
     881                 :           3 :   G_OBJECT_CLASS (valent_mpris_plugin_parent_class)->constructed (object);
     882                 :           3 : }
     883                 :             : 
     884                 :             : static void
     885                 :           2 : valent_mpris_plugin_finalize (GObject *object)
     886                 :             : {
     887                 :           2 :   ValentMprisPlugin *self = VALENT_MPRIS_PLUGIN (object);
     888                 :             : 
     889         [ +  - ]:           2 :   g_clear_pointer (&self->pending, g_hash_table_unref);
     890         [ +  - ]:           2 :   g_clear_pointer (&self->players, g_ptr_array_unref);
     891         [ +  - ]:           2 :   g_clear_pointer (&self->transfers, g_hash_table_unref);
     892                 :             : 
     893                 :           2 :   G_OBJECT_CLASS (valent_mpris_plugin_parent_class)->finalize (object);
     894                 :           2 : }
     895                 :             : 
     896                 :             : static void
     897                 :           2 : valent_mpris_plugin_class_init (ValentMprisPluginClass *klass)
     898                 :             : {
     899                 :           2 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     900                 :           2 :   ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
     901                 :           2 :   ValentDevicePluginClass *plugin_class = VALENT_DEVICE_PLUGIN_CLASS (klass);
     902                 :             : 
     903                 :           2 :   object_class->constructed = valent_mpris_plugin_constructed;
     904                 :           2 :   object_class->finalize = valent_mpris_plugin_finalize;
     905                 :             : 
     906                 :           2 :   vobject_class->destroy = valent_mpris_plugin_destroy;
     907                 :             : 
     908                 :           2 :   plugin_class->handle_packet = valent_mpris_plugin_handle_packet;
     909                 :           2 :   plugin_class->update_state = valent_mpris_plugin_update_state;
     910                 :             : }
     911                 :             : 
     912                 :             : static void
     913                 :           3 : valent_mpris_plugin_init (ValentMprisPlugin *self)
     914                 :             : {
     915                 :           3 :   self->players = g_ptr_array_new_with_free_func (_valent_mpris_device_free);
     916                 :           3 :   self->transfers = g_hash_table_new_full (g_str_hash,
     917                 :             :                                            g_str_equal,
     918                 :             :                                            g_free,
     919                 :             :                                            g_object_unref);
     920                 :           3 :   self->pending = g_hash_table_new (NULL, NULL);
     921                 :           3 : }
     922                 :             : 
        

Generated by: LCOV version 2.0-1