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