2 * Copyright (C) 2006 Jonathan Matthew <jonathan@kaolin.hn.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Rhythmbox out-of-process metadata reader.
29 #include <glib/gi18n.h>
30 #include <libgnomeui/gnome-authentication-manager.h>
31 #include <libgnomevfs/gnome-vfs.h>
34 #include <dbus/dbus.h>
35 #include <dbus/dbus-glib.h>
36 #include <dbus/dbus-glib-lowlevel.h>
38 #include "rb-metadata.h"
39 #include "rb-metadata-dbus.h"
43 /* number of seconds to hang around doing nothing */
44 #define ATTENTION_SPAN 30
48 DBusConnection
*connection
;
55 static DBusHandlerResult
56 _send_error (DBusConnection
*connection
,
58 gboolean include_flag
,
62 DBusMessage
*reply
= dbus_message_new_method_return (request
);
66 rb_debug ("attempting to return error with no message");
68 rb_debug ("attempting to return error: %s", message
);
73 if (!dbus_message_append_args (reply
, DBUS_TYPE_BOOLEAN
, &ok
, DBUS_TYPE_INVALID
)) {
74 rb_debug ("couldn't append error flag");
75 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
79 if (!dbus_message_append_args (reply
,
80 DBUS_TYPE_UINT32
, &error_type
,
81 DBUS_TYPE_STRING
, &message
,
83 rb_debug ("couldn't append error data");
84 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
87 dbus_connection_send (connection
, reply
, NULL
);
88 dbus_message_unref (reply
);
89 return DBUS_HANDLER_RESULT_HANDLED
;
92 static DBusHandlerResult
93 rb_metadata_dbus_load (DBusConnection
*connection
,
100 GError
*error
= NULL
;
102 const char *mimetype
= NULL
;
104 if (!dbus_message_iter_init (message
, &iter
)) {
105 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
108 if (!rb_metadata_dbus_get_string (&iter
, &uri
)) {
109 /* make translatable? */
110 return _send_error (connection
, message
, TRUE
,
111 RB_METADATA_ERROR_INTERNAL
,
112 "Unable to read URI from request");
115 rb_debug ("loading metadata from %s", uri
);
117 rb_metadata_load (svc
->metadata
, uri
, &error
);
121 rb_debug ("metadata error: %s", error
->message
);
123 r
= _send_error (connection
, message
, TRUE
, error
->code
, error
->message
);
124 g_clear_error (&error
);
127 rb_debug ("metadata load finished; mimetype = %s", rb_metadata_get_mime (svc
->metadata
));
129 /* construct reply */
130 reply
= dbus_message_new_method_return (message
);
132 rb_debug ("out of memory creating return message");
133 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
136 mimetype
= rb_metadata_get_mime (svc
->metadata
);
137 dbus_message_iter_init_append (reply
, &iter
);
139 if (!dbus_message_iter_append_basic (&iter
, DBUS_TYPE_BOOLEAN
, &ok
) ||
140 !dbus_message_iter_append_basic (&iter
, DBUS_TYPE_STRING
, &mimetype
)) {
141 rb_debug ("out of memory adding data to return message");
142 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
145 if (!rb_metadata_dbus_add_to_message (svc
->metadata
, &iter
)) {
146 /* make translatable? */
147 return _send_error (connection
, message
, TRUE
,
148 RB_METADATA_ERROR_INTERNAL
,
149 "Unable to add metadata to return message");
152 if (!dbus_connection_send (connection
, reply
, NULL
)) {
153 rb_debug ("failed to send return message");
154 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
157 dbus_message_unref (reply
);
159 return DBUS_HANDLER_RESULT_HANDLED
;
162 static DBusHandlerResult
163 rb_metadata_dbus_can_save (DBusConnection
*connection
,
164 DBusMessage
*message
,
168 DBusMessageIter iter
;
172 if (!dbus_message_iter_init (message
, &iter
)) {
173 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
176 if (!rb_metadata_dbus_get_string (&iter
, &mimetype
)) {
177 /* make translatable? */
178 return _send_error (connection
, message
, TRUE
,
179 RB_METADATA_ERROR_INTERNAL
,
180 "Unable to read MIME type from request");
183 can_save
= rb_metadata_can_save (svc
->metadata
, mimetype
);
186 /* construct reply */
187 reply
= dbus_message_new_method_return (message
);
189 rb_debug ("out of memory creating return message");
190 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
193 dbus_message_iter_init_append (reply
, &iter
);
194 if (!dbus_message_iter_append_basic (&iter
, DBUS_TYPE_BOOLEAN
, &can_save
)) {
195 rb_debug ("out of memory adding data to return message");
196 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
199 if (!dbus_connection_send (connection
, reply
, NULL
)) {
200 rb_debug ("failed to send return message");
201 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
204 dbus_message_unref (reply
);
206 return DBUS_HANDLER_RESULT_HANDLED
;
210 _set_metadata (gpointer key
, GValue
*data
, RBMetaData
*metadata
)
212 RBMetaDataField field
= GPOINTER_TO_INT (key
);
213 rb_metadata_set (metadata
, field
, data
);
217 static DBusHandlerResult
218 rb_metadata_dbus_save (DBusConnection
*connection
,
219 DBusMessage
*message
,
222 DBusMessageIter iter
;
225 GError
*error
= NULL
;
227 /* get metadata from message */
228 if (!dbus_message_iter_init (message
, &iter
)) {
229 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
231 data
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, (GDestroyNotify
)rb_value_free
);
232 if (!rb_metadata_dbus_read_from_message (svc
->metadata
,
235 /* make translatable? */
236 return _send_error (connection
, message
, FALSE
,
237 RB_METADATA_ERROR_INTERNAL
,
238 "Unable to read metadata from message");
241 /* pass to real metadata instance, and save it */
242 g_hash_table_foreach_remove (data
, (GHRFunc
) _set_metadata
, svc
->metadata
);
243 g_hash_table_destroy (data
);
245 rb_metadata_save (svc
->metadata
, &error
);
249 rb_debug ("metadata error: %s", error
->message
);
251 r
= _send_error (connection
, message
, FALSE
, error
->code
, error
->message
);
252 g_clear_error (&error
);
256 reply
= dbus_message_new_method_return (message
);
258 rb_debug ("out of memory creating return message");
259 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
262 if (!dbus_connection_send (connection
, reply
, NULL
)) {
263 rb_debug ("failed to send return message");
264 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
267 dbus_message_unref (reply
);
269 return DBUS_HANDLER_RESULT_HANDLED
;
272 static DBusHandlerResult
273 rb_metadata_dbus_ping (DBusConnection
*connection
,
274 DBusMessage
*message
,
281 DBusMessage
*reply
= dbus_message_new_method_return (message
);
283 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
285 if (!dbus_message_append_args (reply
,
286 DBUS_TYPE_BOOLEAN
, &ok
,
287 DBUS_TYPE_INVALID
)) {
288 return DBUS_HANDLER_RESULT_NEED_MEMORY
;
291 dbus_connection_send (connection
, reply
, NULL
);
292 dbus_message_unref (reply
);
293 return DBUS_HANDLER_RESULT_HANDLED
;
297 electromagnetic_shotgun (gpointer data
)
299 ServiceData
*c
= (ServiceData
*)data
;
300 GTime now
= time(NULL
);
301 int idle
= now
- c
->last_active
;
303 /* quit if we haven't done anything for a while */
304 if (idle
> ATTENTION_SPAN
) {
305 rb_debug ("shutting down (%ds idle)", idle
);
306 g_main_loop_quit (c
->loop
);
313 _unregister_handler (DBusConnection
*connection
, void *data
)
318 static DBusHandlerResult
319 _handle_message (DBusConnection
*connection
, DBusMessage
*message
, void *data
)
321 ServiceData
*svc
= (ServiceData
*)data
;
322 rb_debug ("handling metadata service message");
324 svc
->last_active
= time (NULL
);
325 if (dbus_message_is_method_call (message
, RB_METADATA_DBUS_INTERFACE
, "load")) {
326 return rb_metadata_dbus_load (connection
, message
, svc
);
327 } else if (dbus_message_is_method_call (message
, RB_METADATA_DBUS_INTERFACE
, "canSave")) {
328 return rb_metadata_dbus_can_save (connection
, message
, svc
);
329 } else if (dbus_message_is_method_call (message
, RB_METADATA_DBUS_INTERFACE
, "save")) {
330 return rb_metadata_dbus_save (connection
, message
, svc
);
331 } else if (dbus_message_is_method_call (message
, RB_METADATA_DBUS_INTERFACE
, "ping")) {
332 return rb_metadata_dbus_ping (connection
, message
, svc
);
334 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
339 _new_connection (DBusServer
*server
,
340 DBusConnection
*connection
,
343 ServiceData
*svc
= (ServiceData
*)data
;
344 DBusObjectPathVTable vt
= {
347 NULL
, NULL
, NULL
, NULL
350 rb_debug ("new connection to metadata service");
352 /* don't allow more than one connection at a time */
353 if (svc
->connection
) {
354 rb_debug ("metadata service already has a client. go away.");
358 dbus_connection_register_object_path (connection
,
359 RB_METADATA_DBUS_OBJECT_PATH
,
362 dbus_connection_ref (connection
);
363 dbus_connection_setup_with_g_main (connection
,
364 g_main_loop_get_context (svc
->loop
));
366 dbus_connection_set_exit_on_disconnect (connection
, TRUE
);
370 test_can_save (const char *mimetype
)
375 md
= rb_metadata_new ();
376 can_save
= rb_metadata_can_save (md
, mimetype
);
377 g_print ("%s save %s\n", can_save
? "Can" : "Can't", mimetype
);
378 g_object_unref (G_OBJECT (md
));
383 test_load (const char *uri
)
386 GError
*error
= NULL
;
389 md
= rb_metadata_new ();
390 rb_metadata_load (md
, uri
, &error
);
392 if (error
->code
== RB_METADATA_ERROR_NOT_AUDIO_IGNORE
) {
393 g_print ("%s is not an audio stream (ignored)\n", uri
);
394 } else if (error
->code
== RB_METADATA_ERROR_NOT_AUDIO
) {
395 g_print ("%s is not an audio stream\n", uri
);
397 g_print ("Error loading metadata from %s: %s\n", uri
, error
->message
);
399 g_clear_error (&error
);
403 g_print ("mimetype: %s\n", rb_metadata_get_mime (md
));
404 for (i
=0; i
<RB_METADATA_FIELD_LAST
; i
++) {
407 if (rb_metadata_get (md
, i
, &v
)) {
408 g_value_init (&sv
, G_TYPE_STRING
);
409 g_value_transform (&v
, &sv
);
411 g_print ("%s: %s\n", rb_metadata_get_field_name (i
), g_value_get_string (&sv
));
418 g_object_unref (G_OBJECT (md
));
423 main (int argc
, char **argv
)
425 ServiceData svc
= {0,};
426 DBusError dbus_error
= {0,};
427 gboolean debug
= FALSE
;
428 const char *address
= NULL
;
431 /* initialize i18n */
432 bindtextdomain (GETTEXT_PACKAGE
, GNOMELOCALEDIR
);
433 bind_textdomain_codeset (GETTEXT_PACKAGE
, "UTF-8");
434 textdomain (GETTEXT_PACKAGE
);
438 gnome_authentication_manager_init ();
439 #ifdef HAVE_GSTREAMER
440 gst_init (NULL
, NULL
);
441 g_set_prgname ("rhythmbox-metadata");
444 if (argv
[1] != NULL
&& strcmp(argv
[1], "--debug") == 0) {
448 rb_debug_init (debug
);
450 /* bug report modes */
451 if (argv
[1] != NULL
&& strcmp(argv
[1], "--load") == 0) {
452 return test_load (argv
[2]);
454 if (argv
[1] != NULL
&& strcmp(argv
[1], "--can-save") == 0) {
455 return test_can_save (argv
[2]);
458 if (argv
[1] != NULL
&& strcmp (argv
[1], "--external") == 0) {
462 if (argv
[1] == NULL
) {
463 address
= "unix:tmpdir=/tmp";
468 rb_debug ("initializing metadata service; pid = %d; address = %s", getpid (), address
);
469 svc
.metadata
= rb_metadata_new ();
471 /* set up D-BUS server */
472 svc
.server
= dbus_server_listen (address
, &dbus_error
);
474 rb_debug ("D-BUS server init failed: %s", dbus_error
.message
);
478 dbus_server_set_new_connection_function (svc
.server
,
483 /* write the server address back to the parent process */
486 addr
= dbus_server_get_address (svc
.server
);
487 rb_debug ("D-BUS server listening on address %s", addr
);
488 printf ("%s\n", addr
);
493 /* run main loop until we get bored */
494 svc
.loop
= g_main_loop_new (NULL
, TRUE
);
495 dbus_server_setup_with_g_main (svc
.server
,
496 g_main_loop_get_context (svc
.loop
));
499 g_timeout_add (ATTENTION_SPAN
* 500, (GSourceFunc
) electromagnetic_shotgun
, &svc
);
501 g_main_loop_run (svc
.loop
);
503 if (svc
.connection
) {
505 dbus_connection_disconnect (svc
.connection
);
507 dbus_connection_close (svc
.connection
);
509 dbus_connection_unref (svc
.connection
);
512 dbus_server_disconnect (svc
.server
);
513 dbus_server_unref (svc
.server
);
514 #ifdef HAVE_GSTREAMER_0_10