hythmbox-2.98/remote/dbus/rb-client.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2006 Jonathan Matthew
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2, or (at your option)
  8  *  any later version.
  9  *
 10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  *
 27  */
 28 
 29 #include <config.h>
 30 
 31 #include <locale.h>
 32 #include <stdlib.h>
 33 #include <glib.h>
 34 #include <glib/gi18n.h>
 35 #include <glib-object.h>
 36 #include <gio/gio.h>
 37 
 38 #include "rb-debug.h"
 39 
 40 static gboolean debug = FALSE;
 41 
 42 static gboolean no_start = FALSE;
 43 static gboolean quit = FALSE;
 44 static gboolean check_running = FALSE;
 45 
 46 static gboolean no_present = FALSE;
 47 
 48 static gboolean next = FALSE;
 49 static gboolean previous = FALSE;
 50 static gint32 seek = 0;
 51 
 52 static gboolean play = FALSE;
 53 static gboolean do_pause = FALSE;
 54 static gboolean play_pause = FALSE;
 55 static gboolean stop = FALSE;
 56 
 57 static gboolean enqueue = FALSE;
 58 
 59 static gboolean clear_queue = FALSE;
 60 
 61 static gchar *select_source = NULL;
 62 static gchar *activate_source = NULL;
 63 static gchar *play_source = NULL;
 64 static gchar *play_uri = NULL;
 65 static gboolean print_playing = FALSE;
 66 static gchar *print_playing_format = NULL;
 67 
 68 static gdouble set_volume = -1.0;
 69 static gboolean volume_up = FALSE;
 70 static gboolean volume_down = FALSE;
 71 static gboolean print_volume = FALSE;
 72 /*static gboolean mute = FALSE;
 73 static gboolean unmute = FALSE; */
 74 static gdouble set_rating = -1.0;
 75 
 76 static gchar **other_stuff = NULL;
 77 
 78 static GOptionEntry args[] = {
 79 	{ "debug", 0, 0, G_OPTION_ARG_NONE, &debug, NULL, NULL },
 80 
 81 	{ "no-start", 0, 0, G_OPTION_ARG_NONE, &no_start, N_("Don't start a new instance of Rhythmbox"), NULL },
 82 	{ "quit", 0, 0, G_OPTION_ARG_NONE, &quit, N_("Quit Rhythmbox"), NULL },
 83 	{ "check-running", 0, 0, G_OPTION_ARG_NONE, &check_running, N_("Check if Rhythmbox is already running"), NULL },
 84 
 85 	{ "no-present", 0, 0, G_OPTION_ARG_NONE, &no_present, N_("Don't present an existing Rhythmbox window"), NULL },
 86 
 87 	{ "next", 0, 0, G_OPTION_ARG_NONE, &next, N_("Jump to next song"), NULL },
 88 	{ "previous", 0, 0, G_OPTION_ARG_NONE, &previous, N_("Jump to previous song"), NULL },
 89 	{ "seek", 0, 0, G_OPTION_ARG_INT64, &seek, N_("Seek in current track"), NULL },
 90 
 91 	{ "play", 0, 0, G_OPTION_ARG_NONE, &play, N_("Resume playback if currently paused"), NULL },
 92 	{ "pause", 0, 0, G_OPTION_ARG_NONE, &do_pause, N_("Pause playback if currently playing"), NULL },
 93 	{ "play-pause", 0, 0, G_OPTION_ARG_NONE, &play_pause, N_("Toggle play/pause mode"), NULL },
 94 /*	{ "stop", 0, 0, G_OPTION_ARG_NONE, &stop, N_("Stop playback"), NULL }, */
 95 
 96 	{ "play-uri", 0, 0, G_OPTION_ARG_FILENAME, &play_uri, N_("Play a specified URI, importing it if necessary"), N_("URI to play")},
 97 	{ "enqueue", 0, 0, G_OPTION_ARG_NONE, &enqueue, N_("Add specified tracks to the play queue"), NULL },
 98 	{ "clear-queue", 0, 0, G_OPTION_ARG_NONE, &clear_queue, N_("Empty the play queue before adding new tracks"), NULL },
 99 
100 	{ "print-playing", 0, 0, G_OPTION_ARG_NONE, &print_playing, N_("Print the title and artist of the playing song"), NULL },
101 	{ "print-playing-format", 0, 0, G_OPTION_ARG_STRING, &print_playing_format, N_("Print formatted details of the song"), NULL },
102 	{ "select-source", 0, 0, G_OPTION_ARG_STRING, &select_source, N_("Select the source matching the specified URI"), N_("Source to select")},
103 	{ "activate-source", 0, 0, G_OPTION_ARG_STRING, &activate_source, N_("Activate the source matching the specified URI"), N_("Source to activate")},
104 	{ "play-source", 0, 0, G_OPTION_ARG_STRING, &play_source, N_("Play from the source matching the specified URI"), N_("Source to play from")},
105 
106 	{ "set-volume", 0, 0, G_OPTION_ARG_DOUBLE, &set_volume, N_("Set the playback volume"), NULL },
107 	{ "volume-up", 0, 0, G_OPTION_ARG_NONE, &volume_up, N_("Increase the playback volume"), NULL },
108 	{ "volume-down", 0, 0, G_OPTION_ARG_NONE, &volume_down, N_("Decrease the playback volume"), NULL },
109 	{ "print-volume", 0, 0, G_OPTION_ARG_NONE, &print_volume, N_("Print the current playback volume"), NULL },
110 /*	{ "mute", 0, 0, G_OPTION_ARG_NONE, &mute, N_("Mute playback"), NULL },
111 	{ "unmute", 0, 0, G_OPTION_ARG_NONE, &unmute, N_("Unmute playback"), NULL }, */
112 	{ "set-rating", 0, 0, G_OPTION_ARG_DOUBLE, &set_rating, N_("Set the rating of the current song"), NULL },
113 
114 	{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &other_stuff, NULL, NULL },
115 
116 	{ NULL }
117 };
118 
119 static gboolean
120 annoy (GError **error)
121 {
122 	if (*error) {
123 		g_warning ("%s", (*error)->message);
124 		g_clear_error (error);
125 		return TRUE;
126 	}
127 
128 	return FALSE;
129 }
130 
131 
132 static char *
133 rb_make_duration_string (gint64 duration)
134 {
135 	char *str;
136 	int hours, minutes, seconds;
137 
138 	duration /= G_USEC_PER_SEC;
139 	hours = duration / (60 * 60);
140 	minutes = (duration - (hours * 60 * 60)) / 60;
141 	seconds = duration % 60;
142 
143 	if (hours == 0 && minutes == 0 && seconds == 0)
144 		str = g_strdup (_("Unknown"));
145 	else if (hours == 0)
146 		str = g_strdup_printf (_("%d:%02d"), minutes, seconds);
147 	else
148 		str = g_strdup_printf (_("%d:%02d:%02d"), hours, minutes, seconds);
149 
150 	return str;
151 }
152 
153 /*
154  * Parse a filename pattern and replace markers with values from a TrackDetails
155  * structure.
156  *
157  * Valid markers so far are:
158  * %at -- album title
159  * %aa -- album artist
160  * %aA -- album artist (lowercase)
161  * %as -- album artist sortname
162  * %aS -- album artist sortname (lowercase)
163  * %ay -- album year
164  * %ag -- album genre
165  * %aG -- album genre (lowercase)
166  * %an -- album disc number
167  * %aN -- album disc number, zero padded
168  * %tn -- track number (i.e 8)
169  * %tN -- track number, zero padded (i.e 08)
170  * %tt -- track title
171  * %ta -- track artist
172  * %tA -- track artist (lowercase)
173  * %ts -- track artist sortname
174  * %tS -- track artist sortname (lowercase)
175  * %td -- track duration
176  * %te -- track elapsed time
177  * %tb -- track bitrate
178  * %st -- stream title
179  */
180 static char *
181 parse_pattern (const char *pattern, GHashTable *properties, gint64 elapsed)
182 {
183 	/* p is the pattern iterator, i is a general purpose iterator */
184 	const char *p;
185 	char *temp;
186 	GString *s;
187 
188 	if (pattern == NULL || pattern[0] == 0)
189 		return g_strdup (" ");
190 
191 	s = g_string_new (NULL);
192 
193 	p = pattern;
194 	while (*p) {
195 		char *string = NULL;
196 		const char **strv = NULL;
197 		GVariant *value = NULL;
198 
199 		/* If not a % marker, copy and continue */
200 		if (*p != '%') {
201 			g_string_append_c (s, *p++);
202 			/* Explicit increment as we continue past the increment */
203 			continue;
204 		}
205 
206 		/* Is a % marker, go to next and see what to do */
207 		switch (*++p) {
208 		case '%':
209 			/*
210 			 * Literal %
211 			 */
212 			g_string_append_c (s, '%');
213 			break;
214 		case 'a':
215 			/*
216 			 * Album tag
217 			 */
218 			switch (*++p) {
219 			case 't':
220 				value = g_hash_table_lookup (properties, "xesam:album");
221 				if (value)
222 					string = g_variant_dup_string (value, NULL);
223 				break;
224 			case 'T':
225 				value = g_hash_table_lookup (properties, "xesam:album");
226 				if (value)
227 					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
228 				break;
229 			case 'a':
230 				value = g_hash_table_lookup (properties, "xesam:artist");
231 				if (value) {
232 					strv = g_variant_get_strv (value, NULL);
233 					string = g_strdup (strv[0]);
234 				}
235 				break;
236 			case 'A':
237 				value = g_hash_table_lookup (properties, "xesam:artist");
238 				if (value) {
239 					strv = g_variant_get_strv (value, NULL);
240 					string = g_utf8_strdown (strv[0], -1);
241 				}
242 				break;
243 			case 's':
244 				value = g_hash_table_lookup (properties, "rhythmbox:albumArtistSortname");
245 				if (value)
246 					string = g_variant_dup_string (value, NULL);
247 				break;
248 			case 'S':
249 				value = g_hash_table_lookup (properties, "rhythmbox:albumArtistSortname");
250 				if (value)
251 					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
252 				break;
253 			case 'y':
254 				/* Release year */
255 				value = g_hash_table_lookup (properties, "xesam:contentCreated");
256 				if (value) {
257 					const char *iso8601;
258 					GTimeVal tv;
259 
260 					iso8601 = g_variant_get_string (value, NULL);
261 					if (g_time_val_from_iso8601 (iso8601, &tv)) {
262 						GDate d;
263 						g_date_set_time_val (&d, &tv);
264 
265 						string = g_strdup_printf ("%u", g_date_get_year (&d));
266 					}
267 				}
268 				break;
269 				/* Disc number */
270 			case 'n':
271 				value = g_hash_table_lookup (properties, "xesam:discNumber");
272 				if (value)
273 					string = g_strdup_printf ("%u", g_variant_get_int32 (value));
274 				break;
275 			case 'N':
276 				value = g_hash_table_lookup (properties, "xesam:discNumber");
277 				if (value)
278 					string = g_strdup_printf ("%02u", g_variant_get_int32 (value));
279 				break;
280 				/* genre */
281 			case 'g':
282 				value = g_hash_table_lookup (properties, "xesam:genre");
283 				if (value) {
284 					strv = g_variant_get_strv (value, NULL);
285 					string = g_strdup (strv[0]);
286 				}
287 				break;
288 			case 'G':
289 				value = g_hash_table_lookup (properties, "xesam:genre");
290 				if (value) {
291 					strv = g_variant_get_strv (value, NULL);
292 					string = g_utf8_strdown (strv[0], -1);
293 				}
294 				break;
295 			default:
296 				string = g_strdup_printf ("%%a%c", *p);
297 			}
298 
299 			break;
300 
301 		case 't':
302 			/*
303 			 * Track tag
304 			 */
305 			switch (*++p) {
306 			case 't':
307 				value = g_hash_table_lookup (properties, "rhythmbox:streamTitle");
308 				if (value == NULL)
309 					value = g_hash_table_lookup (properties, "xesam:title");
310 				if (value)
311 					string = g_variant_dup_string (value, NULL);
312 				break;
313 			case 'T':
314 				value = g_hash_table_lookup (properties, "rhythmbox:streamTitle");
315 				if (value == NULL)
316 					value = g_hash_table_lookup (properties, "xesam:title");
317 				if (value)
318 					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
319 				break;
320 			case 'a':
321 				value = g_hash_table_lookup (properties, "xesam:artist");
322 				if (value) {
323 					strv = g_variant_get_strv (value, NULL);
324 					string = g_strdup (strv[0]);
325 				}
326 				break;
327 			case 'A':
328 				value = g_hash_table_lookup (properties, "xesam:artist");
329 				if (value) {
330 					strv = g_variant_get_strv (value, NULL);
331 					string = g_utf8_strdown (strv[0], -1);
332 				}
333 				break;
334 			case 's':
335 				value = g_hash_table_lookup (properties, "rhythmbox:artistSortname");
336 				if (value)
337 					string = g_variant_dup_string (value, NULL);
338 				break;
339 			case 'S':
340 				value = g_hash_table_lookup (properties, "rhythmbox:artistSortname");
341 				if (value)
342 					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
343 				break;
344 			case 'n':
345 				/* Track number */
346 				value = g_hash_table_lookup (properties, "xesam:trackNumber");
347 				if (value)
348 					string = g_strdup_printf ("%u", g_variant_get_int32 (value));
349 				break;
350 			case 'N':
351 				/* Track number, zero-padded */
352 				value = g_hash_table_lookup (properties, "xesam:trackNumber");
353 				if (value)
354 					string = g_strdup_printf ("%02u", g_variant_get_int32 (value));
355 				break;
356 			case 'd':
357 				/* Track duration */
358 				value = g_hash_table_lookup (properties, "mpris:length");
359 				if (value)
360 					string = rb_make_duration_string (g_variant_get_int64 (value));
361 				break;
362 			case 'e':
363 				/* Track elapsed time */
364 				string = rb_make_duration_string (elapsed);
365 				break;
366 			case 'b':
367 				/* Track bitrate */
368 				value = g_hash_table_lookup (properties, "xesam:audioBitrate");
369 				if (value)
370 					string = g_strdup_printf ("%u", g_variant_get_int32 (value) / 1024);
371 				break;
372 
373 			default:
374 				string = g_strdup_printf ("%%t%c", *p);
375  			}
376 
377 			break;
378 
379 		case 's':
380 			/*
381 			 * Stream tag
382 			 */
383 			switch (*++p) {
384 			case 't':
385 				value = g_hash_table_lookup (properties, "rhythmbox:streamTitle");
386 				if (value) {
387 					value = g_hash_table_lookup (properties, "xesam:title");
388 					if (value) {
389 						string = g_variant_dup_string (value, NULL);
390 					}
391 				}
392 				break;
393 			default:
394 				string = g_strdup_printf ("%%s%c", *p);
395  			}
396 			break;
397 
398 		default:
399 			string = g_strdup_printf ("%%%c", *p);
400 		}
401 
402 		if (string)
403 			g_string_append (s, string);
404 		g_free (string);
405 
406 		++p;
407 	}
408 
409 	temp = s->str;
410 	g_string_free (s, FALSE);
411 	return temp;
412 }
413 
414 
415 static GHashTable *
416 get_playing_song_info (GDBusProxy *mpris)
417 {
418 	GHashTable *properties;
419 	GVariant *prop;
420 	GVariant *metadata;
421 	GVariantIter iter;
422 	GVariant *value;
423 	char *key;
424 	GError *error = NULL;
425 
426 	prop = g_dbus_proxy_call_sync (mpris,
427 				       "org.freedesktop.DBus.Properties.Get",
428 				       g_variant_new ("(ss)", "org.mpris.MediaPlayer2.Player", "Metadata"),
429 				       G_DBUS_CALL_FLAGS_NONE,
430 				       -1,
431 				       NULL,
432 				       &error);
433 	if (annoy (&error)) {
434 		return NULL;
435 	}
436 
437 	g_variant_get (prop, "(v)", &metadata);
438 
439 	properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
440 	g_variant_iter_init (&iter, metadata);
441 	while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
442 		g_hash_table_insert (properties, g_strdup (key), g_variant_ref (value));
443 	}
444 
445 	g_variant_unref (prop);
446 	return properties;
447 }
448 
449 static void
450 print_playing_song (GDBusProxy *mpris, const char *format)
451 {
452 	GHashTable *properties;
453 	GVariant *v;
454 	gint64 elapsed = 0;
455 	char *string;
456 
457 	properties = get_playing_song_info (mpris);
458 	if (properties == NULL) {
459 		g_print ("%s\n", _("Not playing"));
460 		return;
461 	}
462 
463 	v = g_dbus_proxy_get_cached_property (mpris, "Position");
464 	if (v != NULL) {
465 		elapsed = g_variant_get_int64 (v);
466 		g_variant_unref (v);
467 	}
468 
469 	string = parse_pattern (format, properties, elapsed);
470 	g_print ("%s\n", string);
471 	g_hash_table_destroy (properties);
472 	g_free (string);
473 }
474 
475 static void
476 print_playing_song_default (GDBusProxy *mpris)
477 {
478 	GHashTable *properties;
479 	char *string;
480 
481 	properties = get_playing_song_info (mpris);
482 	if (properties == NULL) {
483 		g_print ("%s\n", _("Not playing"));
484 		return;
485 	}
486 
487 	if (g_hash_table_lookup (properties, "rhythmbox:streamTitle") != NULL) {
488 		string = parse_pattern ("%st (%tt)", properties, 0);
489 	} else {
490 		string = parse_pattern ("%ta - %tt", properties, 0);
491 	}
492 
493 	g_print ("%s\n", string);
494 	g_hash_table_destroy (properties);
495 	g_free (string);
496 }
497 
498 static void
499 rate_song (GDBusProxy *mpris, gdouble song_rating)
500 {
501 	GHashTable *properties;
502 	GVariantBuilder props;
503 	GVariant *v;
504 	GError *error = NULL;
505 
506 	properties = get_playing_song_info (mpris);
507 	if (properties == NULL) {
508 		rb_debug ("can't set rating when not playing");
509 		return;
510 	}
511 
512 	v = g_hash_table_lookup (properties, "xesam:url");
513 	if (v == NULL) {
514 		rb_debug ("can't set rating, no url");
515 		return;
516 	}
517 
518 	g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}"));
519 	g_variant_builder_add (&props, "{sv}", "rating", g_variant_new_double (song_rating));
520 
521 	g_dbus_connection_call_sync (g_dbus_proxy_get_connection (mpris),
522 				     "org.gnome.Rhythmbox3",
523 				     "/org/gnome/Rhythmbox3/RhythmDB",
524 				     "org.gnome.Rhythmbox3.RhythmDB",
525 				     "SetEntryProperties",
526 				     g_variant_new ("(sa{sv})", g_variant_get_string (v, NULL), &props),
527 				     NULL,
528 				     G_DBUS_CALL_FLAGS_NONE,
529 				     -1,
530 				     NULL,
531 				     &error);
532 	if (error != NULL) {
533 		g_warning ("Error setting rating on %s: %s",
534 			   g_variant_get_string (v, NULL),
535 			   error->message);
536 		g_clear_error (&error);
537 	}
538 	g_hash_table_destroy (properties);
539 }
540 
541 static void
542 state_changed_cb (GActionGroup *action, const char *action_name, GVariant *state, GMainLoop *loop)
543 {
544 	if (g_strcmp0 (action_name, "LoadURI") == 0) {
545 		gboolean loaded, scanned;
546 
547 		g_variant_get (state, "(bb)", &loaded, &scanned);
548 		if (loaded && scanned) {
549 			/* give it a tiny bit longer to populate sources etc. */
550 			g_timeout_add (1500, (GSourceFunc) g_main_loop_quit, loop);
551 		}
552 	}
553 }
554 
555 static void
556 state_changed_signal_cb (GDBusProxy *proxy, const char *sender_name, const char *signal_name, GVariant *parameters, GMainLoop *loop)
557 {
558 	const char *action;
559 	GVariant *state;
560 	if (g_strcmp0 (signal_name, "StateChanged") != 0) {
561 		return;
562 	}
563 
564 	g_variant_get (parameters, "(sv)", &action, &state);
565 	if (g_strcmp0 (action, "LoadURI") == 0) {
566 		GApplication *app;
567 		app = g_object_get_data (G_OBJECT (proxy), "actual-app");
568 		state_changed_cb (G_ACTION_GROUP (app), action, state, loop);
569 	}
570 	g_variant_unref (state);
571 }
572 
573 static gboolean
574 proxy_has_name_owner (GDBusProxy *proxy)
575 {
576 	gboolean has_owner;
577 	char *owner = g_dbus_proxy_get_name_owner (proxy);
578 
579 	has_owner = (owner != NULL);
580 	g_free (owner);
581 	return has_owner;
582 }
583 
584 
585 int
586 main (int argc, char **argv)
587 {
588 	GOptionContext *context;
589 	GError *error = NULL;
590 	GDBusConnection *bus;
591 	GDBusProxy *mpris;
592 	GDBusProxy *queue;
593 	GApplication *app;
594 	gboolean loaded;
595 	gboolean scanned;
596 	GVariant *state;
597 
598 #ifdef ENABLE_NLS
599 	/* initialize i18n */
600 	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
601 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
602 	textdomain (GETTEXT_PACKAGE);
603 #endif
604 	/* setup */
605 	setlocale (LC_ALL, "");
606 	g_type_init ();
607 	g_set_prgname ("rhythmbox-client");
608 
609 	/* parse arguments */
610 	context = g_option_context_new (NULL);
611 	g_option_context_add_main_entries (context, args, NULL);
612 	g_option_context_parse (context, &argc, &argv, &error);
613 	if (annoy (&error))
614 		exit (1);
615 
616 	rb_debug_init (debug);
617 
618 	bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
619 	/* check if it's running before registering the application */
620 	if (no_start || check_running || quit) {
621 		GDBusProxy *app_proxy;
622 		app_proxy = g_dbus_proxy_new_sync (bus, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
623 						   "org.gnome.Rhythmbox3",
624 						   "/org/gnome/Rhythmbox3",
625 						   "org.gtk.Actions",
626 						   NULL,
627 						   &error);
628 		if (app_proxy == NULL || proxy_has_name_owner (app_proxy) == FALSE) {
629 			rb_debug ("not running");
630 			if (check_running) {
631 				exit (2);
632 			}
633 			exit (0);
634 		} else if (check_running) {
635 			rb_debug ("running instance found");
636 			exit (0);
637 		}
638 		g_object_unref (app_proxy);
639 	}
640 
641 	app = g_application_new ("org.gnome.Rhythmbox3", G_APPLICATION_IS_LAUNCHER);
642 	if (g_application_register (app, NULL, &error) == FALSE) {
643 		if (check_running) {
644 			rb_debug ("no running instance found");
645 			exit (2);
646 		} else if (quit) {
647 			rb_debug ("no existing instance to quit");
648 			exit (0);
649 		}
650 
651 		rb_debug ("uh.. what?");
652 		exit (0);
653 	}
654 
655 	/* wait until it's ready to accept control */
656 	state = g_action_group_get_action_state (G_ACTION_GROUP (app), "LoadURI");
657 	if (state == NULL) {
658 		rb_debug ("couldn't get app startup state");
659 		exit (0);
660 	}
661 
662 	g_variant_get (state, "(bb)", &loaded, &scanned);
663 	if ((loaded && scanned) == FALSE) {
664 		GMainLoop *loop;
665 		GDBusProxy *app_proxy;
666 
667 		rb_debug ("waiting for app startup");
668 		loop = g_main_loop_new (NULL, FALSE);
669 		g_signal_connect (app, "action-state-changed", G_CALLBACK (state_changed_cb), loop);
670 
671 		/* dbus implementation of GApplication doesn't do action state updates yet */
672 		app_proxy = g_dbus_proxy_new_sync (bus, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
673 						   "org.gnome.Rhythmbox3",
674 						   "/org/gnome/Rhythmbox3",
675 						   "org.gtk.Actions",
676 						   NULL,
677 						   &error);
678 		if (app_proxy == NULL || proxy_has_name_owner (app_proxy) == FALSE) {
679 			g_warning ("unable to wait for app startup: %s", error->message);
680 			g_clear_error (&error);
681 		} else {
682 			g_object_set_data (G_OBJECT (app_proxy), "actual-app", app);
683 			g_signal_connect (app_proxy, "g-signal", G_CALLBACK (state_changed_signal_cb), loop);
684 			g_main_loop_run (loop);
685 			rb_debug ("app is now started enough");
686 		}
687 	}
688 
689 	/* create proxies */
690 	mpris = g_dbus_proxy_new_sync (bus,
691 				       G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
692 				       NULL,
693 				       "org.mpris.MediaPlayer2.rhythmbox",
694 				       "/org/mpris/MediaPlayer2",
695 				       "org.mpris.MediaPlayer2.Player",
696 				       NULL,
697 				       &error);
698 	if (mpris == NULL || proxy_has_name_owner (mpris) == FALSE) {
699 		g_warning ("MPRIS D-Bus interface not available, some things won't work");
700 		if (next || previous || (seek != 0) || play || do_pause || play_pause || stop || volume_up || volume_down || (set_volume > -0.01)) {
701 			exit (1);
702 		}
703 	}
704 
705 	queue = g_dbus_proxy_new_sync (bus,
706 				       G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
707 				       NULL,
708 				       "org.gnome.Rhythmbox3",
709 				       "/org/gnome/Rhythmbox3/PlayQueue",
710 				       "org.gnome.Rhythmbox3.PlayQueue",
711 				       NULL,
712 				       &error);
713 	if (queue == NULL || proxy_has_name_owner (queue) == FALSE) {
714 		g_warning ("Play queue interface not available, some things won't work");
715 		if (enqueue || clear_queue) {
716 			exit (1);
717 		}
718 	}
719 
720 	/* activate or quit */
721 	if (quit) {
722 		rb_debug ("quitting existing instance");
723 		g_action_group_activate_action (G_ACTION_GROUP (app), "Quit", NULL);
724 		exit (0);
725 	}
726 
727 	/* don't present if we're doing something else */
728 	if (next || previous || (seek != 0) ||
729 	    clear_queue ||
730 	    play_uri || other_stuff ||
731 	    play || do_pause || play_pause || stop ||
732 	    print_playing || print_playing_format ||
733 	    (set_volume > -0.01) || volume_up || volume_down || print_volume /*|| mute || unmute*/ || (set_rating > -0.01))
734 		no_present = TRUE;
735 
736 	/* present */
737 	if (!no_present) {
738 		g_application_activate (app);
739 	}
740 
741 	/* set song rating */
742 	if (set_rating >= 0.0 && set_rating <= 5.0) {
743 		rb_debug ("rate song");
744 
745 		rate_song (mpris, set_rating);
746 	}
747 
748 	/* skip to next or previous track */
749 	if (next) {
750 		rb_debug ("next track");
751 		g_dbus_proxy_call_sync (mpris, "Next", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
752 		annoy (&error);
753 	} else if (previous) {
754 		rb_debug ("previous track");
755 		g_dbus_proxy_call_sync (mpris, "Previous", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
756 		annoy (&error);
757 	}
758 
759 	/* seek in track */
760 	if (seek != 0) {
761 		GHashTable *properties;
762 		rb_debug ("seek");
763 
764 		properties = get_playing_song_info (mpris);
765 		if (properties != NULL) {
766 			GVariant *v = g_hash_table_lookup (properties, "mpris:trackid");
767 			if (v != NULL) {
768 				g_dbus_proxy_call_sync (mpris,
769 							"SetPosition",
770 							g_variant_new ("(ox)", g_variant_get_string (v, NULL), seek),
771 							G_DBUS_CALL_FLAGS_NONE,
772 							-1,
773 							NULL,
774 							&error);
775 				annoy (&error);
776 			}
777 		}
778 	}
779 
780 	/* add/enqueue */
781 	if (clear_queue) {
782 		g_dbus_proxy_call_sync (queue, "ClearQueue", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
783 		annoy (&error);
784 	}
785 	if (other_stuff) {
786 		int i;
787 		for (i = 0; other_stuff[i] != NULL; i++) {
788 			GFile *file;
789 			char *fileuri;
790 
791 			file = g_file_new_for_commandline_arg (other_stuff[i]);
792 			fileuri = g_file_get_uri (file);
793 			if (fileuri == NULL) {
794 				g_warning ("couldn't convert \"%s\" to a URI", other_stuff[i]);
795 				continue;
796 			}
797 
798 			if (enqueue) {
799 				rb_debug ("enqueueing %s", fileuri);
800 				g_dbus_proxy_call_sync (queue, "AddToQueue", g_variant_new ("(s)", fileuri), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
801 				annoy (&error);
802 			} else {
803 				rb_debug ("importing %s", fileuri);
804 				g_action_group_activate_action (G_ACTION_GROUP (app), "LoadURI", g_variant_new ("(sb)", fileuri, FALSE));
805 			}
806 			g_free (fileuri);
807 			g_object_unref (file);
808 		}
809 	}
810 
811 	/* select/activate/play source */
812 	if (select_source) {
813 		rb_debug ("selecting source %s", select_source);
814 		g_action_group_activate_action (G_ACTION_GROUP (app), "ActivateSource", g_variant_new ("(su)", select_source, 0));
815 	} else if (activate_source) {
816 		rb_debug ("activating source %s", activate_source);
817 		g_action_group_activate_action (G_ACTION_GROUP (app), "ActivateSource", g_variant_new ("(su)", activate_source, 1));
818 	} else if (play_source) {
819 		rb_debug ("playing source %s", play_source);
820 		g_action_group_activate_action (G_ACTION_GROUP (app), "ActivateSource", g_variant_new ("(su)", play_source, 2));
821 	}
822 
823 	/* play uri */
824 	if (play_uri) {
825 		GFile *file;
826 		char *fileuri;
827 
828 		file = g_file_new_for_commandline_arg (play_uri);
829 		fileuri = g_file_get_uri (file);
830 		if (fileuri == NULL) {
831 			g_warning ("couldn't convert \"%s\" to a URI", play_uri);
832 		} else {
833 			rb_debug ("loading and playing %s", fileuri);
834 			g_action_group_activate_action (G_ACTION_GROUP (app), "LoadURI", g_variant_new ("(sb)", fileuri, TRUE));
835 			annoy (&error);
836 		}
837 		g_free (fileuri);
838 		g_object_unref (file);
839 	}
840 
841 	/* play/pause/stop */
842 	if (mpris) {
843 		GVariant *v;
844 		gboolean is_playing = FALSE;
845 
846 		v = g_dbus_proxy_get_cached_property (mpris, "PlaybackStatus");
847 		if (v != NULL) {
848 			is_playing = (g_strcmp0 (g_variant_get_string (v, NULL), "Playing") == 0);
849 			g_variant_unref (v);
850 		}
851 
852 		if (play || do_pause || play_pause) {
853 			if (is_playing != play || play_pause) {
854 				rb_debug ("calling PlayPause to change playback state");
855 				g_dbus_proxy_call_sync (mpris, "PlayPause", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
856 				annoy (&error);
857 			} else {
858 				rb_debug ("no need to change playback state");
859 			}
860 		} else if (stop) {
861 			g_warning ("not implemented yet");
862 		}
863 	}
864 
865 	/* get/set volume, mute/unmute */
866 	if (set_volume > -0.01) {
867 		g_dbus_proxy_call_sync (mpris,
868 					"org.freedesktop.DBus.Properties.Set",
869 					g_variant_new ("(ssv)", "org.mpris.MediaPlayer2.Player", "Volume", g_variant_new_double (set_volume)),
870 					G_DBUS_CALL_FLAGS_NONE,
871 					-1,
872 					NULL,
873 					&error);
874 		annoy (&error);
875 	} else if (volume_up || volume_down) {
876 		GVariant *v;
877 
878 		v = g_dbus_proxy_get_cached_property (mpris, "Volume");
879 		if (v != NULL) {
880 
881 			set_volume = g_variant_get_double (v) + (volume_up ? 0.1 : -0.1);
882 			g_dbus_proxy_call_sync (mpris,
883 						"org.freedesktop.DBus.Properties.Set",
884 						g_variant_new ("(ssv)", "org.mpris.MediaPlayer2.Player", "Volume", g_variant_new_double (set_volume)),
885 						G_DBUS_CALL_FLAGS_NONE,
886 						-1,
887 						NULL,
888 						&error);
889 			annoy (&error);
890 
891 			g_variant_unref (v);
892 		}
893 	}
894 	/* no mute for now? */
895 	/*
896 	} else if (unmute || mute) {
897 		org_gnome_Rhythmbox_Player_set_mute (player_proxy, unmute ? FALSE : TRUE, &error);
898 		annoy (&error);
899 	}
900 	*/
901 
902 	if (print_volume) {
903 		gdouble volume = 1.0;
904 		GVariant *v = g_dbus_proxy_get_cached_property (mpris, "Volume");
905 		if (v != NULL) {
906 			volume = g_variant_get_double (v);
907 			g_variant_unref (v);
908 		}
909 		g_print (_("Playback volume is %f.\n"), volume);
910 	}
911 
912 	/* print playing song */
913 	if (print_playing_format) {
914 		print_playing_song (mpris, print_playing_format);
915 	} else if (print_playing) {
916 		print_playing_song_default (mpris);
917 	}
918 
919 	if (mpris) {
920 		g_object_unref (mpris);
921 	}
922 
923 	g_dbus_connection_flush_sync (bus, NULL, NULL);
924 	g_option_context_free (context);
925 
926 	return 0;
927 }