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 "vdp-mpris-player"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <math.h>
9 : :
10 : : #include <gio/gio.h>
11 : : #include <valent.h>
12 : :
13 : : #include "vdp-mpris-player.h"
14 : : #include "valent-mpris-utils.h"
15 : :
16 : :
17 : : struct _VdpMprisPlayer
18 : : {
19 : : ValentMediaPlayer parent_instance;
20 : :
21 : : ValentDevice *device;
22 : :
23 : : ValentMediaActions flags;
24 : : char *name;
25 : : GVariant *metadata;
26 : : double position;
27 : : double position_time;
28 : : ValentMediaRepeat repeat;
29 : : unsigned int shuffle : 1;
30 : : ValentMediaState state;
31 : : double volume;
32 : : };
33 : :
34 [ + + + - ]: 21 : G_DEFINE_FINAL_TYPE (VdpMprisPlayer, vdp_mpris_player, VALENT_TYPE_MEDIA_PLAYER)
35 : :
36 : :
37 : : /*
38 : : * ValentMediaPlayer
39 : : */
40 : : static ValentMediaActions
41 : 7 : vdp_mpris_player_get_flags (ValentMediaPlayer *player)
42 : : {
43 : 7 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
44 : :
45 : 7 : return self->flags;
46 : : }
47 : :
48 : : static GVariant *
49 : 4 : vdp_mpris_player_get_metadata (ValentMediaPlayer *player)
50 : : {
51 : 4 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
52 : :
53 [ + + ]: 4 : if (self->metadata)
54 : 3 : return g_variant_ref (self->metadata);
55 : :
56 : : return NULL;
57 : : }
58 : :
59 : : static const char *
60 : 1 : vdp_mpris_player_get_name (ValentMediaPlayer *player)
61 : : {
62 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
63 : :
64 : 1 : return self->name;
65 : : }
66 : :
67 : : static double
68 : 3 : vdp_mpris_player_get_position (ValentMediaPlayer *player)
69 : : {
70 : 3 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
71 : :
72 [ - + ]: 3 : if (self->state == VALENT_MEDIA_STATE_PLAYING)
73 : 0 : return self->position + (valent_mpris_get_time () - self->position_time);
74 : :
75 : 3 : return self->position;
76 : : }
77 : :
78 : : static void
79 : 1 : vdp_mpris_player_set_position (ValentMediaPlayer *player,
80 : : double position)
81 : : {
82 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
83 : 2 : g_autoptr (JsonBuilder) builder = NULL;
84 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
85 : :
86 : : /* Convert seconds to milliseconds */
87 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
88 : 1 : json_builder_set_member_name (builder, "player");
89 : 1 : json_builder_add_string_value (builder, self->name);
90 : 1 : json_builder_set_member_name (builder, "SetPosition");
91 : 1 : json_builder_add_int_value (builder, (int64_t)(position * 1000L));
92 : 1 : packet = valent_packet_end (&builder);
93 : :
94 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
95 : 1 : }
96 : :
97 : : static ValentMediaRepeat
98 : 1 : vdp_mpris_player_get_repeat (ValentMediaPlayer *player)
99 : : {
100 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
101 : :
102 : 1 : return self->repeat;
103 : : }
104 : :
105 : : static void
106 : 1 : vdp_mpris_player_set_repeat (ValentMediaPlayer *player,
107 : : ValentMediaRepeat repeat)
108 : : {
109 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
110 : 1 : const char *loop_status = NULL;
111 : 2 : g_autoptr (JsonBuilder) builder = NULL;
112 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
113 : :
114 : 1 : loop_status = valent_mpris_repeat_to_string (repeat);
115 : :
116 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
117 : 1 : json_builder_set_member_name (builder, "player");
118 : 1 : json_builder_add_string_value (builder, self->name);
119 : 1 : json_builder_set_member_name (builder, "setLoopStatus");
120 : 1 : json_builder_add_string_value (builder, loop_status);
121 : 1 : packet = valent_packet_end (&builder);
122 : :
123 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
124 : 1 : }
125 : :
126 : : static gboolean
127 : 1 : vdp_mpris_player_get_shuffle (ValentMediaPlayer *player)
128 : : {
129 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
130 : :
131 : 1 : return self->shuffle;
132 : : }
133 : :
134 : : static void
135 : 1 : vdp_mpris_player_set_shuffle (ValentMediaPlayer *player,
136 : : gboolean shuffle)
137 : : {
138 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
139 : 2 : g_autoptr (JsonBuilder) builder = NULL;
140 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
141 : :
142 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
143 : 1 : json_builder_set_member_name (builder, "player");
144 : 1 : json_builder_add_string_value (builder, self->name);
145 : 1 : json_builder_set_member_name (builder, "setShuffle");
146 : 1 : json_builder_add_boolean_value (builder, shuffle);
147 : 1 : packet = valent_packet_end (&builder);
148 : :
149 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
150 : 1 : }
151 : :
152 : : static ValentMediaState
153 : 3 : vdp_mpris_player_get_state (ValentMediaPlayer *player)
154 : : {
155 : 3 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
156 : :
157 : 3 : return self->state;
158 : : }
159 : :
160 : : static double
161 : 1 : vdp_mpris_player_get_volume (ValentMediaPlayer *player)
162 : : {
163 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
164 : :
165 : 1 : return self->volume;
166 : : }
167 : :
168 : : static void
169 : 1 : vdp_mpris_player_set_volume (ValentMediaPlayer *player,
170 : : double volume)
171 : : {
172 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
173 : 2 : g_autoptr (JsonBuilder) builder = NULL;
174 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
175 : :
176 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
177 : 1 : json_builder_set_member_name (builder, "player");
178 : 1 : json_builder_add_string_value (builder, self->name);
179 : 1 : json_builder_set_member_name (builder, "setVolume");
180 : 1 : json_builder_add_int_value (builder, (int64_t)floor (volume * 100));
181 : 1 : packet = valent_packet_end (&builder);
182 : :
183 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
184 : 1 : }
185 : :
186 : : static void
187 : 1 : vdp_mpris_player_next (ValentMediaPlayer *player)
188 : : {
189 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
190 : 2 : g_autoptr (JsonBuilder) builder = NULL;
191 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
192 : :
193 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
194 : 1 : json_builder_set_member_name (builder, "player");
195 : 1 : json_builder_add_string_value (builder, self->name);
196 : 1 : json_builder_set_member_name (builder, "action");
197 : 1 : json_builder_add_string_value (builder, "Next");
198 : 1 : packet = valent_packet_end (&builder);
199 : :
200 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
201 : 1 : }
202 : :
203 : : static void
204 : 1 : vdp_mpris_player_pause (ValentMediaPlayer *player)
205 : : {
206 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
207 : 2 : g_autoptr (JsonBuilder) builder = NULL;
208 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
209 : :
210 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
211 : 1 : json_builder_set_member_name (builder, "player");
212 : 1 : json_builder_add_string_value (builder, self->name);
213 : 1 : json_builder_set_member_name (builder, "action");
214 : 1 : json_builder_add_string_value (builder, "Pause");
215 : 1 : packet = valent_packet_end (&builder);
216 : :
217 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
218 : 1 : }
219 : :
220 : : static void
221 : 1 : vdp_mpris_player_play (ValentMediaPlayer *player)
222 : : {
223 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
224 : 2 : g_autoptr (JsonBuilder) builder = NULL;
225 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
226 : :
227 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
228 : 1 : json_builder_set_member_name (builder, "player");
229 : 1 : json_builder_add_string_value (builder, self->name);
230 : 1 : json_builder_set_member_name (builder, "action");
231 : 1 : json_builder_add_string_value (builder, "Play");
232 : 1 : packet = valent_packet_end (&builder);
233 : :
234 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
235 : 1 : }
236 : :
237 : : #if 0
238 : : static void
239 : : vdp_mpris_player_play_pause (ValentMediaPlayer *player)
240 : : {
241 : : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
242 : : g_autoptr (JsonBuilder) builder = NULL;
243 : : g_autoptr (JsonNode) packet = NULL;
244 : :
245 : : valent_packet_init (&builder, "kdeconnect.mpris.request");
246 : : json_builder_set_member_name (builder, "player");
247 : : json_builder_add_string_value (builder, self->name);
248 : : json_builder_set_member_name (builder, "action");
249 : : json_builder_add_string_value (builder, "PlayPause");
250 : : packet = valent_packet_end (&builder);
251 : :
252 : : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
253 : : }
254 : : #endif
255 : :
256 : : static void
257 : 1 : vdp_mpris_player_previous (ValentMediaPlayer *player)
258 : : {
259 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
260 : 2 : g_autoptr (JsonBuilder) builder = NULL;
261 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
262 : :
263 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
264 : 1 : json_builder_set_member_name (builder, "player");
265 : 1 : json_builder_add_string_value (builder, self->name);
266 : 1 : json_builder_set_member_name (builder, "action");
267 : 1 : json_builder_add_string_value (builder, "Previous");
268 : 1 : packet = valent_packet_end (&builder);
269 : :
270 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
271 : 1 : }
272 : :
273 : : static void
274 : 1 : vdp_mpris_player_seek (ValentMediaPlayer *player,
275 : : double offset)
276 : : {
277 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
278 : 2 : g_autoptr (JsonBuilder) builder = NULL;
279 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
280 : :
281 : : /* Convert seconds to microseconds */
282 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
283 : 1 : json_builder_set_member_name (builder, "player");
284 : 1 : json_builder_add_string_value (builder, self->name);
285 : 1 : json_builder_set_member_name (builder, "Seek");
286 : 1 : json_builder_add_int_value (builder, (int64_t)(offset * G_TIME_SPAN_SECOND));
287 : 1 : packet = valent_packet_end (&builder);
288 : :
289 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
290 : 1 : }
291 : :
292 : : static void
293 : 1 : vdp_mpris_player_stop (ValentMediaPlayer *player)
294 : : {
295 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (player);
296 : 2 : g_autoptr (JsonBuilder) builder = NULL;
297 [ - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
298 : :
299 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
300 : 1 : json_builder_set_member_name (builder, "player");
301 : 1 : json_builder_add_string_value (builder, self->name);
302 : 1 : json_builder_set_member_name (builder, "action");
303 : 1 : json_builder_add_string_value (builder, "Stop");
304 : 1 : packet = valent_packet_end (&builder);
305 : :
306 [ + - ]: 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
307 : 1 : }
308 : :
309 : : static void
310 : 1 : vdp_mpris_player_request_album_art (VdpMprisPlayer *self,
311 : : const char *url,
312 : : GVariantDict *metadata)
313 : : {
314 : 1 : g_autoptr (JsonBuilder) builder = NULL;
315 [ - - - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
316 : 1 : ValentContext *context = NULL;
317 [ - - + - ]: 1 : g_autoptr (GFile) file = NULL;
318 [ - - + - ]: 1 : g_autofree char *filename = NULL;
319 : :
320 [ + - ]: 1 : g_assert (VDP_IS_MPRIS_PLAYER (self));
321 [ + - - + ]: 1 : g_assert (url != NULL && *url != '\0');
322 [ - + ]: 1 : g_assert (metadata != NULL);
323 : :
324 : 1 : context = valent_device_get_context (self->device);
325 : 1 : filename = g_compute_checksum_for_string (G_CHECKSUM_MD5, url, -1);
326 : 1 : file = valent_context_get_cache_file (context, filename);
327 : :
328 : : /* If the album art has been cached, update the metadata dictionary */
329 [ - + ]: 1 : if (g_file_query_exists (file, NULL))
330 : : {
331 : 0 : g_autofree char *art_url = NULL;
332 : :
333 : 0 : art_url = g_file_get_uri (file);
334 : 0 : g_variant_dict_insert (metadata, "mpris:artUrl", "s", art_url);
335 : :
336 : 0 : return;
337 : : }
338 : :
339 : : /* Request the album art payload */
340 : 1 : valent_packet_init (&builder, "kdeconnect.mpris.request");
341 : 1 : json_builder_set_member_name (builder, "player");
342 : 1 : json_builder_add_string_value (builder, self->name);
343 : 1 : json_builder_set_member_name (builder, "albumArtUrl");
344 : 1 : json_builder_add_string_value (builder, url);
345 : 1 : packet = valent_packet_end (&builder);
346 : :
347 : 1 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
348 : : }
349 : :
350 : : /*
351 : : * Private
352 : : */
353 : : static void
354 : 2 : vdp_mpris_player_update_flags (VdpMprisPlayer *player,
355 : : ValentMediaActions flags)
356 : : {
357 [ + - ]: 2 : g_assert (VDP_IS_MPRIS_PLAYER (player));
358 : :
359 [ + + ]: 2 : if ((player->flags ^ flags) == 0)
360 : : return;
361 : :
362 : 1 : player->flags = flags;
363 : 1 : g_object_notify (G_OBJECT (player), "flags");
364 : : }
365 : :
366 : : static void
367 : 3 : vdp_mpris_player_update_metadata (VdpMprisPlayer *player,
368 : : GVariant *value)
369 : : {
370 : 6 : g_autoptr (GVariant) metadata = NULL;
371 : :
372 [ + - ]: 3 : g_assert (VDP_IS_MPRIS_PLAYER (player));
373 [ - + ]: 3 : g_assert (value != NULL);
374 : :
375 [ + - ]: 3 : if (player->metadata == value)
376 : : return;
377 : :
378 : 3 : metadata = g_steal_pointer (&player->metadata);
379 : 3 : player->metadata = g_variant_ref_sink (value);
380 [ + + ]: 3 : g_object_notify (G_OBJECT (player), "metadata");
381 : : }
382 : :
383 : : static void
384 : 2 : vdp_mpris_player_update_position (VdpMprisPlayer *player,
385 : : int64_t position)
386 : : {
387 [ + - ]: 2 : g_assert (VDP_IS_MPRIS_PLAYER (player));
388 : :
389 : : /* Convert milliseconds to seconds */
390 : 2 : player->position = position / 1000L;
391 : 2 : player->position_time = valent_mpris_get_time ();
392 : 2 : g_object_notify (G_OBJECT (player), "position");
393 : 2 : }
394 : :
395 : : static void
396 : 2 : vdp_mpris_player_update_repeat (VdpMprisPlayer *player,
397 : : const char *loop_status)
398 : : {
399 : 2 : ValentMediaRepeat repeat = VALENT_MEDIA_REPEAT_NONE;
400 : :
401 [ + - ]: 2 : g_assert (VDP_IS_MPRIS_PLAYER (player));
402 [ - + ]: 2 : g_assert (loop_status != NULL);
403 : :
404 : 2 : repeat = valent_mpris_repeat_from_string (loop_status);
405 : :
406 [ - + ]: 2 : if (player->repeat == repeat)
407 : : return;
408 : :
409 : 0 : player->repeat = repeat;
410 : 0 : g_object_notify (G_OBJECT (player), "repeat");
411 : : }
412 : :
413 : : static void
414 : 2 : vdp_mpris_player_update_shuffle (VdpMprisPlayer *player,
415 : : gboolean shuffle)
416 : : {
417 [ + - ]: 2 : g_assert (VDP_IS_MPRIS_PLAYER (player));
418 : :
419 [ - + ]: 2 : if (player->shuffle == shuffle)
420 : : return;
421 : :
422 : 0 : player->shuffle = shuffle;
423 : 0 : g_object_notify (G_OBJECT (player), "shuffle");
424 : : }
425 : :
426 : : static void
427 : 2 : vdp_mpris_player_update_state (VdpMprisPlayer *player,
428 : : const char *playback_status)
429 : : {
430 : 2 : ValentMediaState state = VALENT_MEDIA_STATE_STOPPED;
431 : :
432 [ + - ]: 2 : g_assert (VDP_IS_MPRIS_PLAYER (player));
433 [ - + ]: 2 : g_assert (playback_status != NULL);
434 : :
435 : 2 : state = valent_mpris_state_from_string (playback_status);
436 : :
437 [ + - ]: 2 : if (player->state == state)
438 : : return;
439 : :
440 : 2 : player->state = state;
441 : :
442 [ - + ]: 2 : if (player->state == VALENT_MEDIA_STATE_STOPPED)
443 : : {
444 : 0 : player->position = 0.0;
445 : 0 : player->position_time = 0;
446 : 0 : g_object_notify (G_OBJECT (player), "position");
447 : : }
448 : :
449 : 2 : g_object_notify (G_OBJECT (player), "state");
450 : : }
451 : :
452 : : static void
453 : 2 : vdp_mpris_player_update_volume (VdpMprisPlayer *player,
454 : : int64_t volume)
455 : : {
456 [ + - ]: 2 : g_assert (VDP_IS_MPRIS_PLAYER (player));
457 : :
458 [ - + - + ]: 2 : if (G_APPROX_VALUE (player->volume, volume / 100.0, 0.01))
459 : : return;
460 : :
461 [ # # ]: 0 : player->volume = CLAMP ((volume / 100.0), 0.0, 1.0);
462 : 0 : g_object_notify (G_OBJECT (player), "volume");
463 : : }
464 : :
465 : : static void
466 : 0 : on_device_state_changed (ValentDevice *device,
467 : : GParamSpec *pspec,
468 : : VdpMprisPlayer *self)
469 : : {
470 : : #if 0
471 : : ValentDeviceState state = VALENT_DEVICE_STATE_NONE;
472 : : gboolean available;
473 : :
474 : : state = valent_device_get_state (device);
475 : : available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
476 : : (state & VALENT_DEVICE_STATE_PAIRED) != 0;
477 : : #endif
478 : 0 : }
479 : :
480 : : /*
481 : : * GObject
482 : : */
483 : : static void
484 : 1 : vdp_mpris_player_constructed (GObject *object)
485 : : {
486 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (object);
487 : :
488 : 1 : G_OBJECT_CLASS (vdp_mpris_player_parent_class)->constructed (object);
489 : :
490 : 1 : self->device = valent_resource_get_source (VALENT_RESOURCE (self));
491 : 1 : g_signal_connect_object (self->device,
492 : : "notify::state",
493 : : G_CALLBACK (on_device_state_changed),
494 : : self,
495 : : G_CONNECT_DEFAULT);
496 : 1 : }
497 : :
498 : : static void
499 : 1 : vdp_mpris_player_finalize (GObject *object)
500 : : {
501 : 1 : VdpMprisPlayer *self = VDP_MPRIS_PLAYER (object);
502 : :
503 [ + - ]: 1 : g_clear_pointer (&self->name, g_free);
504 [ + - ]: 1 : g_clear_pointer (&self->metadata, g_variant_unref);
505 : :
506 : 1 : G_OBJECT_CLASS (vdp_mpris_player_parent_class)->finalize (object);
507 : 1 : }
508 : :
509 : : static void
510 : 1 : vdp_mpris_player_class_init (VdpMprisPlayerClass *klass)
511 : : {
512 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
513 : 1 : ValentMediaPlayerClass *player_class = VALENT_MEDIA_PLAYER_CLASS (klass);
514 : :
515 : 1 : object_class->constructed = vdp_mpris_player_constructed;
516 : 1 : object_class->finalize = vdp_mpris_player_finalize;
517 : :
518 : 1 : player_class->get_flags = vdp_mpris_player_get_flags;
519 : 1 : player_class->get_metadata = vdp_mpris_player_get_metadata;
520 : 1 : player_class->get_name = vdp_mpris_player_get_name;
521 : 1 : player_class->get_position = vdp_mpris_player_get_position;
522 : 1 : player_class->set_position = vdp_mpris_player_set_position;
523 : 1 : player_class->get_repeat = vdp_mpris_player_get_repeat;
524 : 1 : player_class->set_repeat = vdp_mpris_player_set_repeat;
525 : 1 : player_class->get_shuffle = vdp_mpris_player_get_shuffle;
526 : 1 : player_class->set_shuffle = vdp_mpris_player_set_shuffle;
527 : 1 : player_class->get_state = vdp_mpris_player_get_state;
528 : 1 : player_class->get_volume = vdp_mpris_player_get_volume;
529 : 1 : player_class->set_volume = vdp_mpris_player_set_volume;
530 : :
531 : 1 : player_class->next = vdp_mpris_player_next;
532 : 1 : player_class->pause = vdp_mpris_player_pause;
533 : 1 : player_class->play = vdp_mpris_player_play;
534 : 1 : player_class->previous = vdp_mpris_player_previous;
535 : 1 : player_class->seek = vdp_mpris_player_seek;
536 : 1 : player_class->stop = vdp_mpris_player_stop;
537 : 1 : }
538 : :
539 : : static void
540 : 1 : vdp_mpris_player_init (VdpMprisPlayer *self)
541 : : {
542 : 1 : self->name = g_strdup ("Media Player");
543 : 1 : self->volume = 1.0;
544 : 1 : self->state = VALENT_MEDIA_STATE_STOPPED;
545 : 1 : }
546 : :
547 : : /**
548 : : * vdp_mpris_player_new:
549 : : * @device: a `ValentDevice`
550 : : *
551 : : * Get the `VdpMprisPlayer` instance.
552 : : *
553 : : * Returns: (transfer full) (nullable): a `VdpMprisPlayer`
554 : : */
555 : : VdpMprisPlayer *
556 : 1 : vdp_mpris_player_new (ValentDevice *device)
557 : : {
558 : 2 : g_autoptr (ValentContext) context = NULL;
559 [ + - ]: 1 : g_autofree char *iri = NULL;
560 : :
561 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
562 : :
563 : 1 : context = valent_context_new (valent_device_get_context (device),
564 : : "plugin",
565 : : "systemvolume");
566 : 1 : iri = tracker_sparql_escape_uri_printf ("urn:valent:mixer:%s",
567 : : valent_device_get_id (device));
568 : 1 : return g_object_new (VALENT_TYPE_MPRIS_PLAYER,
569 : : "iri", iri,
570 : : "source", device,
571 : : "title", valent_device_get_name (device),
572 : : NULL);
573 : : }
574 : :
575 : : /**
576 : : * valent_media_player_update_packet:
577 : : * @player: a `VdpMprisPlayer`
578 : : * @packet: a KDE Connect packet
579 : : *
580 : : * A convenience method for updating the internal state of the player from a
581 : : * `kdeconnect.mpris` packet.
582 : : */
583 : : void
584 : 2 : vdp_mpris_player_handle_packet (VdpMprisPlayer *player,
585 : : JsonNode *packet)
586 : : {
587 : 2 : const char *url;
588 : 2 : ValentMediaActions flags = VALENT_MEDIA_ACTION_NONE;
589 : 2 : GVariantDict metadata;
590 : 2 : const char *artist, *title, *album;
591 : 2 : int64_t length, position;
592 : 2 : const char *loop_status = NULL;
593 : 2 : gboolean shuffle = FALSE;
594 : 2 : gboolean is_playing;
595 : 2 : int64_t volume;
596 : :
597 : : /* Flags (available actions) */
598 [ + + ]: 2 : if (valent_packet_check_field (packet, "canGoNext"))
599 : 1 : flags |= VALENT_MEDIA_ACTION_NEXT;
600 : :
601 [ - + ]: 2 : if (valent_packet_check_field (packet, "canGoPrevious"))
602 : 0 : flags |= VALENT_MEDIA_ACTION_PREVIOUS;
603 : :
604 [ + + ]: 2 : if (valent_packet_check_field (packet, "canPause"))
605 : 1 : flags |= VALENT_MEDIA_ACTION_PAUSE;
606 : :
607 [ + + ]: 2 : if (valent_packet_check_field (packet, "canPlay"))
608 : 1 : flags |= VALENT_MEDIA_ACTION_PLAY;
609 : :
610 [ + + ]: 2 : if (valent_packet_check_field (packet, "canSeek"))
611 : 1 : flags |= VALENT_MEDIA_ACTION_SEEK;
612 : :
613 : 2 : vdp_mpris_player_update_flags (player, flags);
614 : :
615 : : /* Metadata */
616 : 2 : g_variant_dict_init (&metadata, NULL);
617 : :
618 [ + + ]: 2 : if (valent_packet_get_string (packet, "artist", &artist))
619 : : {
620 : 3 : g_auto (GStrv) artists = NULL;
621 : 1 : GVariant *value;
622 : :
623 : 1 : artists = g_strsplit (artist, ",", -1);
624 : 1 : value = g_variant_new_strv ((const char * const *)artists, -1);
625 [ + - ]: 1 : g_variant_dict_insert_value (&metadata, "xesam:artist", value);
626 : : }
627 : :
628 [ + + ]: 2 : if (valent_packet_get_string (packet, "title", &title))
629 : 1 : g_variant_dict_insert (&metadata, "xesam:title", "s", title);
630 : :
631 [ + + ]: 2 : if (valent_packet_get_string (packet, "album", &album))
632 : 1 : g_variant_dict_insert (&metadata, "xesam:album", "s", album);
633 : :
634 : : /* Convert milliseconds to microseconds */
635 [ + + ]: 2 : if (valent_packet_get_int (packet, "length", &length))
636 : 1 : g_variant_dict_insert (&metadata, "mpris:length", "x", length * 1000L);
637 : :
638 [ + + ]: 2 : if (valent_packet_get_string (packet, "albumArtUrl", &url))
639 : 1 : vdp_mpris_player_request_album_art (player, url, &metadata);
640 : :
641 : 2 : vdp_mpris_player_update_metadata (player, g_variant_dict_end (&metadata));
642 : :
643 : : /* Playback Status */
644 [ + - ]: 2 : if (valent_packet_get_int (packet, "pos", &position))
645 : 2 : vdp_mpris_player_update_position (player, position);
646 : :
647 [ + - ]: 2 : if (valent_packet_get_string (packet, "loopStatus", &loop_status))
648 : 2 : vdp_mpris_player_update_repeat (player, loop_status);
649 : :
650 [ + - ]: 2 : if (valent_packet_get_boolean (packet, "isPlaying", &is_playing))
651 [ + + ]: 3 : vdp_mpris_player_update_state (player, is_playing ? "Playing" : "Paused");
652 : :
653 [ + - ]: 2 : if (valent_packet_get_boolean (packet, "shuffle", &shuffle))
654 : 2 : vdp_mpris_player_update_shuffle (player, shuffle);
655 : :
656 [ + - ]: 2 : if (valent_packet_get_int (packet, "volume", &volume))
657 : 2 : vdp_mpris_player_update_volume (player, volume);
658 : 2 : }
659 : :
660 : : /**
661 : : * vdp_mpris_player_update_art:
662 : : * @player: a `VdpMprisPlayer`
663 : : * @file: a `GFile`
664 : : *
665 : : * Update the `mpris:artUrl` metadata field from @file.
666 : : */
667 : : void
668 : 1 : vdp_mpris_player_update_art (VdpMprisPlayer *player,
669 : : GFile *file)
670 : : {
671 : 1 : GVariantDict dict;
672 : 1 : GVariant *metadata;
673 : 2 : g_autofree char *uri = NULL;
674 : :
675 [ + - ]: 1 : g_assert (VDP_IS_MPRIS_PLAYER (player));
676 [ + - + - : 1 : g_assert (G_IS_FILE (file));
+ - - + ]
677 : :
678 : 1 : uri = g_file_get_uri (file);
679 : :
680 : 1 : g_variant_dict_init (&dict, player->metadata);
681 : 1 : g_variant_dict_insert (&dict, "mpris:artUrl", "s", uri);
682 : 1 : metadata = g_variant_dict_end (&dict);
683 : :
684 : 1 : vdp_mpris_player_update_metadata (player, metadata);
685 : 1 : }
686 : :
687 : : /**
688 : : * valent_media_player_update_name:
689 : : * @player: a `VdpMprisPlayer`
690 : : * @name: a name
691 : : *
692 : : * Set the user-visible name of the player to @identity.
693 : : */
694 : : void
695 : 1 : vdp_mpris_player_update_name (VdpMprisPlayer *player,
696 : : const char *name)
697 : : {
698 [ + - ]: 1 : g_return_if_fail (VDP_IS_MPRIS_PLAYER (player));
699 [ - + ]: 1 : g_return_if_fail (name != NULL);
700 : :
701 [ + - ]: 1 : if (g_set_str (&player->name, name))
702 : 1 : g_object_notify (G_OBJECT (player), "name");
703 : : }
|