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

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rb-metadata-dbus-client.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found rb-metadata-dbus-client.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /*
  2  *  Copyright (C) 2006 Jonathan Matthew <jonathan@kaolin.hn.org>
  3  *
  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.
  8  *
  9  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 10  *  GStreamer plugins to be used and distributed together with GStreamer
 11  *  and Rhythmbox. This permission is above and beyond the permissions granted
 12  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 13  *  you may extend this exception to your version of the code, but you are not
 14  *  obligated to do so. If you do not wish to do so, delete this exception
 15  *  statement from your version.
 16  *
 17  *  This program is distributed in the hope that it will be useful,
 18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20  *  GNU General Public License for more details.
 21  *
 22  *  You should have received a copy of the GNU General Public License
 23  *  along with this program; if not, write to the Free Software
 24  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 25  *
 26  */
 27 
 28 /*
 29  * Client for out-of-process metadata reader communicating via D-BUS.
 30  *
 31  * How this works:
 32  * - spawn rb-metadata process, with pipes
 33  * - child process sets up its dbus server or whatever
 34  * - if successful, child writes dbus server address to stdout; otherwise, dies.
 35  * - parent opens dbus connection
 36  *
 37  * For each request, the parent checks if the dbus connection is still alive,
 38  * and pings the child to see if it's still responding.  If the child has
 39  * exited or is not responding, the parent starts a new metadata helper as
 40  * described above.
 41  *
 42  * The child process exits after a certain period of inactivity (30s
 43  * currently), so the ping message serves two purposes - it checks that the
 44  * child is still capable of handling messages, and it ensures the child
 45  * doesn't time out between when we check the child is still running and when
 46  * we actually send it the request.
 47  */
 48 
 49 /**
 50  * SECTION:rb-metadata
 51  * @short_description: metadata reader and writer interface
 52  *
 53  * Provides a simple synchronous interface for metadata extraction and updating.
 54  */
 55 
 56 #include <config.h>
 57 
 58 #include "rb-metadata.h"
 59 #include "rb-metadata-dbus.h"
 60 #include "rb-debug.h"
 61 #include "rb-util.h"
 62 
 63 #include <glib/gi18n.h>
 64 #include <gio/gio.h>
 65 
 66 #include <unistd.h>
 67 #include <sys/types.h>
 68 #include <sys/signal.h>
 69 #include <sys/wait.h>
 70 #include <string.h>
 71 #include <stdlib.h>
 72 
 73 static void rb_metadata_class_init (RBMetaDataClass *klass);
 74 static void rb_metadata_init (RBMetaData *md);
 75 static void rb_metadata_finalize (GObject *object);
 76 
 77 static gboolean tried_env_address = FALSE;
 78 static GDBusConnection *dbus_connection = NULL;
 79 static GPid metadata_child = 0;
 80 static int metadata_stdout = -1;
 81 static GMainContext *main_context = NULL;
 82 static GStaticMutex conn_mutex = G_STATIC_MUTEX_INIT;
 83 static char **saveable_types = NULL;
 84 
 85 struct RBMetaDataPrivate
 86 {
 87 	char       *media_type;
 88 	char      **missing_plugins;
 89 	char      **plugin_descriptions;
 90 	gboolean    has_audio;
 91 	gboolean    has_video;
 92 	gboolean    has_other_data;
 93 	GHashTable *metadata;
 94 };
 95 
 96 G_DEFINE_TYPE (RBMetaData, rb_metadata, G_TYPE_OBJECT)
 97 
 98 static void
 99 rb_metadata_class_init (RBMetaDataClass *klass)
100 {
101 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
102 
103 	object_class->finalize = rb_metadata_finalize;
104 
105 	g_type_class_add_private (object_class, sizeof (RBMetaDataPrivate));
106 
107 	main_context = g_main_context_new ();	/* maybe not needed? */
108 }
109 
110 static void
111 rb_metadata_init (RBMetaData *md)
112 {
113 	md->priv = G_TYPE_INSTANCE_GET_PRIVATE (md, RB_TYPE_METADATA, RBMetaDataPrivate);
114 }
115 
116 static void
117 rb_metadata_finalize (GObject *object)
118 {
119 	RBMetaData *md;
120 
121 	md = RB_METADATA (object);
122 
123 	g_free (md->priv->media_type);
124 	if (md->priv->metadata)
125 		g_hash_table_destroy (md->priv->metadata);
126 
127 	G_OBJECT_CLASS (rb_metadata_parent_class)->finalize (object);
128 }
129 
130 /**
131  * rb_metadata_new:
132  *
133  * Creates a new metadata backend instance.
134  *
135  * Return value: new #RBMetaData instance
136  */
137 RBMetaData *
138 rb_metadata_new (void)
139 {
140 	return RB_METADATA (g_object_new (RB_TYPE_METADATA, NULL));
141 }
142 
143 static void
144 kill_metadata_service (void)
145 {
146 	if (dbus_connection) {
147 		if (g_dbus_connection_is_closed (dbus_connection) == FALSE) {
148 			rb_debug ("closing dbus connection");
149 			g_dbus_connection_close_sync (dbus_connection, NULL, NULL);
150 		} else {
151 			rb_debug ("dbus connection already closed");
152 		}
153 		g_object_unref (dbus_connection);
154 		dbus_connection = NULL;
155 	}
156 
157 	if (metadata_child) {
158 		rb_debug ("killing child process");
159 		kill (metadata_child, SIGINT);
160 		g_spawn_close_pid (metadata_child);
161 		metadata_child = 0;
162 	}
163 
164 	if (metadata_stdout != -1) {
165 		rb_debug ("closing metadata child process stdout pipe");
166 		close (metadata_stdout);
167 		metadata_stdout = -1;
168 	}
169 }
170 
171 static gboolean
172 ping_metadata_service (GError **error)
173 {
174 	GDBusMessage *message;
175 	GDBusMessage *response;
176 
177 	if (g_dbus_connection_is_closed (dbus_connection))
178 		return FALSE;
179 
180 	message = g_dbus_message_new_method_call (RB_METADATA_DBUS_NAME,
181 						  RB_METADATA_DBUS_OBJECT_PATH,
182 						  RB_METADATA_DBUS_INTERFACE,
183 						  "ping");
184 	response = g_dbus_connection_send_message_with_reply_sync (dbus_connection,
185 								   message,
186 								   G_DBUS_MESSAGE_FLAGS_NONE,
187 								   RB_METADATA_DBUS_TIMEOUT,
188 								   NULL,
189 								   NULL,
190 								   error);
191 	g_object_unref (message);
192 
193 	if (*error != NULL) {
194 		/* ignore 'no reply', just means the service is dead */
195 		if ((*error)->domain == G_DBUS_ERROR && (*error)->code == G_DBUS_ERROR_NO_REPLY) {
196 			g_clear_error (error);
197 		}
198 		return FALSE;
199 	}
200 	g_object_unref (response);
201 	return TRUE;
202 }
203 
204 static gboolean
205 start_metadata_service (GError **error)
206 {
207 	GIOChannel *stdout_channel;
208 	GIOStatus status;
209 	gchar *dbus_address = NULL;
210 	char *saveable_type_list;
211 	GVariant *response_body;
212 
213 	if (dbus_connection) {
214 		if (ping_metadata_service (error))
215 			return TRUE;
216 
217 		/* Metadata service is broken.  Kill it, and if we haven't run
218 		 * into any errors yet, we can try to restart it.
219 		 */
220 		kill_metadata_service ();
221 
222 		if (*error)
223 			return FALSE;
224 	}
225 
226 	if (!tried_env_address) {
227 		const char *addr = g_getenv ("RB_DBUS_METADATA_ADDRESS");
228 		tried_env_address = TRUE;
229 		if (addr) {
230 			rb_debug ("trying metadata service address %s (from environment)", addr);
231 			dbus_address = g_strdup (addr);
232 			metadata_child = 0;
233 		}
234 	}
235 
236 	if (dbus_address == NULL) {
237 		GPtrArray *argv;
238 		gboolean res;
239 		char **debug_args;
240 		GError *local_error;
241 		int i;
242 
243 		argv = g_ptr_array_new ();
244 		/*
245 		 * Normally, we find the metadata helper in the libexec dir,
246 		 * but when --enable-uninstalled-build is specified, we look
247 		 * in the directory it's built in.
248 		 */
249 #ifdef USE_UNINSTALLED_DIRS
250 		g_ptr_array_add (argv, METADATA_UNINSTALLED_DIR "/rhythmbox-metadata");
251 #else
252 		g_ptr_array_add (argv, LIBEXEC_DIR G_DIR_SEPARATOR_S INSTALLED_METADATA_HELPER);
253 #endif
254 		debug_args = rb_debug_get_args ();
255 		i = 0;
256 		while (debug_args[i] != NULL) {
257 			g_ptr_array_add (argv, debug_args[i]);
258 			i++;
259 		}
260 
261 		g_ptr_array_add (argv, "unix:tmpdir=/tmp");
262 		g_ptr_array_add (argv, NULL);
263 
264 		local_error = NULL;
265 		res = g_spawn_async_with_pipes (NULL,
266 						(char **)argv->pdata,
267 						NULL,
268 						0,
269 						NULL, NULL,
270 						&metadata_child,
271 						NULL,
272 						&metadata_stdout,
273 						NULL,
274 						&local_error);
275 		g_ptr_array_free (argv, TRUE);
276 		g_strfreev (debug_args);
277 
278 		if (res == FALSE) {
279 			g_propagate_error (error, local_error);
280 			return FALSE;
281 		}
282 
283 		stdout_channel = g_io_channel_unix_new (metadata_stdout);
284 		status = g_io_channel_read_line (stdout_channel, &dbus_address, NULL, NULL, error);
285 		g_io_channel_unref (stdout_channel);
286 		if (status != G_IO_STATUS_NORMAL) {
287 			kill_metadata_service ();
288 			return FALSE;
289 		}
290 
291 		g_strchomp (dbus_address);
292 		rb_debug ("Got metadata helper D-BUS address %s", dbus_address);
293 	}
294 
295 	dbus_connection = g_dbus_connection_new_for_address_sync (dbus_address,
296 								  G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
297 								  NULL,
298 								  NULL,
299 								  error);
300 	g_free (dbus_address);
301 	if (*error != NULL) {
302 		kill_metadata_service ();
303 		return FALSE;
304 	}
305 
306 	g_dbus_connection_set_exit_on_close (dbus_connection, FALSE);
307 
308 	rb_debug ("Metadata process %d started", metadata_child);
309 
310 	/* now ask it what types it can re-tag */
311 	if (saveable_types != NULL) {
312 		g_strfreev (saveable_types);
313 	}
314 
315 	response_body = g_dbus_connection_call_sync (dbus_connection,
316 						     RB_METADATA_DBUS_NAME,
317 						     RB_METADATA_DBUS_OBJECT_PATH,
318 						     RB_METADATA_DBUS_INTERFACE,
319 						     "getSaveableTypes",
320 						     NULL,
321 						     NULL,
322 						     G_DBUS_CALL_FLAGS_NONE,
323 						     RB_METADATA_DBUS_TIMEOUT,
324 						     NULL,
325 						     error);
326 	if (response_body == NULL) {
327 		rb_debug ("saveable type query failed: %s", (*error)->message);
328 		return FALSE;
329 	}
330 
331 	g_variant_get (response_body, "(^as)", &saveable_types);
332 	if (saveable_types != NULL) {
333 		saveable_type_list = g_strjoinv (", ", saveable_types);
334 		rb_debug ("saveable types from metadata helper: %s", saveable_type_list);
335 		g_free (saveable_type_list);
336 	} else {
337 		rb_debug ("unable to save metadata for any file types");
338 	}
339 	g_variant_unref (response_body);
340 
341 	return TRUE;
342 }
343 
344 /**
345  * rb_metadata_reset:
346  * @md: a #RBMetaData
347  *
348  * Resets the state of the metadata interface.  Call this before
349  * setting tags to be written to a file.
350  */
351 void
352 rb_metadata_reset (RBMetaData *md)
353 {
354 	g_free (md->priv->media_type);
355 	md->priv->media_type = NULL;
356 
357 	if (md->priv->metadata)
358 		g_hash_table_destroy (md->priv->metadata);
359 	md->priv->metadata = g_hash_table_new_full (g_direct_hash,
360 						    g_direct_equal,
361 						    NULL,
362 						    (GDestroyNotify)rb_value_free);
363 }
364 
365 /**
366  * rb_metadata_load:
367  * @md: a #RBMetaData
368  * @uri: URI from which to load metadata
369  * @error: returns error information
370  *
371  * Reads metadata information from the specified URI.
372  * Once this has returned successfully (with *error == NULL),
373  * rb_metadata_get, rb_metadata_get_media_type, rb_metadata_has_missing_plugins,
374  * and rb_metadata_get_missing_plugins can usefully be called.
375  */
376 void
377 rb_metadata_load (RBMetaData *md,
378 		  const char *uri,
379 		  GError **error)
380 {
381 	GVariant *response;
382 	GError *fake_error = NULL;
383 
384 	if (error == NULL)
385 		error = &fake_error;
386 
387 	rb_metadata_reset (md);
388 	if (uri == NULL)
389 		return;
390 	g_static_mutex_lock (&conn_mutex);
391 
392 	start_metadata_service (error);
393 
394 	if (*error == NULL) {
395 		rb_debug ("sending metadata load request: %s", uri);
396 		response = g_dbus_connection_call_sync (dbus_connection,
397 							RB_METADATA_DBUS_NAME,
398 							RB_METADATA_DBUS_OBJECT_PATH,
399 							RB_METADATA_DBUS_INTERFACE,
400 							"load",
401 							g_variant_new ("(s)", uri),
402 							NULL, /* complicated return type */
403 							G_DBUS_CALL_FLAGS_NONE,
404 							RB_METADATA_DBUS_TIMEOUT,
405 							NULL,
406 							error);
407 	}
408 
409 	if (*error == NULL) {
410 		GVariantIter *metadata;
411 		gboolean ok = FALSE;
412 		int error_code;
413 		char *error_string = NULL;
414 
415 		g_variant_get (response,
416 			       "(^as^asbbbsbisa{iv})",
417 			       &md->priv->missing_plugins,
418 			       &md->priv->plugin_descriptions,
419 			       &md->priv->has_audio,
420 			       &md->priv->has_video,
421 			       &md->priv->has_other_data,
422 			       &md->priv->media_type,
423 			       &ok,
424 			       &error_code,
425 			       &error_string,
426 			       &metadata);
427 
428 		if (ok) {
429 			guint32 key;
430 			GVariant *value;
431 
432 			while (g_variant_iter_next (metadata, "{iv}", &key, &value)) {
433 				GValue *val = g_slice_new0 (GValue);
434 
435 				switch (rb_metadata_get_field_type (key)) {
436 				case G_TYPE_STRING:
437 					g_value_init (val, G_TYPE_STRING);
438 					g_value_set_string (val, g_variant_get_string (value, NULL));
439 					break;
440 				case G_TYPE_ULONG:
441 					g_value_init (val, G_TYPE_ULONG);
442 					g_value_set_ulong (val, g_variant_get_uint32 (value));
443 					break;
444 				case G_TYPE_DOUBLE:
445 					g_value_init (val, G_TYPE_DOUBLE);
446 					g_value_set_double (val, g_variant_get_double (value));
447 					break;
448 				default:
449 					g_assert_not_reached ();
450 					break;
451 				}
452 				g_hash_table_insert (md->priv->metadata, GINT_TO_POINTER (key), val);
453 				g_variant_unref (value);
454 			}
455 
456 		} else {
457 			g_set_error (error, RB_METADATA_ERROR,
458 				     error_code,
459 				     "%s", error_string);
460 		}
461 		g_variant_iter_free (metadata);
462 
463 		/* if we're missing some plugins, we'll need to make sure the
464 		 * metadata helper rereads the registry before the next load.
465 		 * the easiest way to do this is to kill it.
466 		 */
467 		if (*error == NULL && g_strv_length (md->priv->missing_plugins) > 0) {
468 			rb_debug ("missing plugins; killing metadata service to force registry reload");
469 			kill_metadata_service ();
470 		}
471 	}
472 	if (fake_error)
473 		g_error_free (fake_error);
474 
475 	g_static_mutex_unlock (&conn_mutex);
476 }
477 
478 /**
479  * rb_metadata_get_media_type:
480  * @md: a #RBMetaData
481  *
482  * Returns the type of the file from which metadata was read.
483  * This may look like a MIME type, but it isn't.
484  *
485  * Return value: media type string
486  */
487 const char *
488 rb_metadata_get_media_type (RBMetaData *md)
489 {
490 	return md->priv->media_type;
491 }
492 
493 /**
494  * rb_metadata_has_missing_plugins:
495  * @md: a #RBMetaData
496  *
497  * If the metadata reader could not decode the file it was asked to
498  * because one or more media framework plugins (specifically, for the
499  * existing implementations, GStreamer plugins) required are missing,
500  * this will return TRUE.
501  *
502  * Return value: TRUE if required plugins are missing
503  */
504 gboolean
505 rb_metadata_has_missing_plugins (RBMetaData *md)
506 {
507 	return (md->priv->missing_plugins != NULL &&
508 	        g_strv_length (md->priv->missing_plugins) > 0);
509 }
510 
511 /**
512  * rb_metadata_get_missing_plugins:
513  * @md: a #RBMetaData
514  * @missing_plugins: (out) (array zero-terminated=1): returns machine-readable
515  * missing plugin information
516  * @plugin_descriptions: (out) (array zero-terminated=1): returns human-readable
517  * missing plugin descriptions
518  *
519  * This function returns the information used to request automatic
520  * installation of media framework plugins required to decode the target URI.
521  * Use g_strfreev() to free the returned information arrays.
522  *
523  * Return value: TRUE if missing plugin information was returned
524  */
525 gboolean
526 rb_metadata_get_missing_plugins (RBMetaData *md,
527 				 char ***missing_plugins,
528 				 char ***plugin_descriptions)
529 {
530 	if (rb_metadata_has_missing_plugins (md) == FALSE) {
531 		return FALSE;
532 	}
533 
534 	*missing_plugins = g_strdupv (md->priv->missing_plugins);
535 	*plugin_descriptions = g_strdupv (md->priv->plugin_descriptions);
536 	return TRUE;
537 }
538 
539 
540 /**
541  * rb_metadata_get:
542  * @md: a #RBMetaData
543  * @field: the #RBMetaDataField to retrieve
544  * @val: (out caller-allocates) (transfer full): returns the field value
545  *
546  * Retrieves the value of a metadata field extracted from the target URI.
547  * If the target URI contained no value for the field, returns FALSE.
548  *
549  * Return value: TRUE if a value was returned
550  */
551 gboolean
552 rb_metadata_get (RBMetaData *md, RBMetaDataField field, GValue *ret)
553 {
554 	GValue *val;
555 	if (!md->priv->metadata)
556 		return FALSE;
557 
558 	if ((val = g_hash_table_lookup (md->priv->metadata,
559 					GINT_TO_POINTER (field)))) {
560 		g_value_init (ret, G_VALUE_TYPE (val));
561 		g_value_copy (val, ret);
562 		return TRUE;
563 	}
564 	return FALSE;
565 }
566 
567 /**
568  * rb_metadata_set:
569  * @md: a #RBMetaData
570  * @field: the #RBMetaDataField to set
571  * @val: the value to set
572  *
573  * Sets a metadata field value.  The value is only stored inside the
574  * #RBMetaData object until rb_metadata_save is called.
575  *
576  * Return value: TRUE if the field is valid
577  */
578 gboolean
579 rb_metadata_set (RBMetaData *md, RBMetaDataField field,
580 		 const GValue *val)
581 {
582 	GValue *newval;
583 	GType type;
584 
585 	type = rb_metadata_get_field_type (field);
586 	g_return_val_if_fail (type == G_VALUE_TYPE (val), FALSE);
587 
588 	newval = g_slice_new0 (GValue);
589 	g_value_init (newval, type);
590 	g_value_copy (val, newval);
591 
592 	g_hash_table_insert (md->priv->metadata, GINT_TO_POINTER (field),
593 			     newval);
594 	return TRUE;
595 }
596 
597 /**
598  * rb_metadata_can_save:
599  * @md: a #RBMetaData
600  * @media_type: the media type string to check
601  *
602  * Checks if the metadata writer is capable of updating file metadata
603  * for a given media type.
604  *
605  * Return value: TRUE if the file metadata for the given media type can be updated
606  */
607 gboolean
608 rb_metadata_can_save (RBMetaData *md, const char *media_type)
609 {
610 	GError *error = NULL;
611 	gboolean result = FALSE;
612 	int i = 0;
613 
614 	g_static_mutex_lock (&conn_mutex);
615 
616 	if (saveable_types == NULL) {
617 		if (start_metadata_service (&error) == FALSE) {
618 			g_error_free (error);
619 			return FALSE;
620 		}
621 	}
622 
623 	if (saveable_types != NULL) {
624 		for (i = 0; saveable_types[i] != NULL; i++) {
625 			if (g_str_equal (media_type, saveable_types[i])) {
626 				result = TRUE;
627 				break;
628 			}
629 		}
630 	}
631 
632 	g_static_mutex_unlock (&conn_mutex);
633 	return result;
634 }
635 
636 /**
637  * rb_metadata_get_saveable_types:
638  * @md: a #RBMetaData
639  *
640  * Constructs a list of the media types for which the metadata backend
641  * implements tag saving.
642  *
643  * Return value: (transfer full) (array zero-terminated=1): a NULL-terminated
644  * array of media type strings.  Use g_strfreev to free it.
645  */
646 char **
647 rb_metadata_get_saveable_types (RBMetaData *md)
648 {
649 	return g_strdupv (saveable_types);
650 }
651 
652 /**
653  * rb_metadata_save:
654  * @md: a #RBMetaData
655  * @uri: the target URI
656  * @error: returns error information
657  *
658  * Saves all metadata changes made with rb_metadata_set to the
659  * target URI.
660  */
661 void
662 rb_metadata_save (RBMetaData *md, const char *uri, GError **error)
663 {
664 	GVariant *response;
665 	GError *fake_error = NULL;
666 
667 	if (error == NULL)
668 		error = &fake_error;
669 
670 	g_static_mutex_lock (&conn_mutex);
671 
672 	start_metadata_service (error);
673 
674 	if (*error == NULL) {
675 		response = g_dbus_connection_call_sync (dbus_connection,
676 							RB_METADATA_DBUS_NAME,
677 							RB_METADATA_DBUS_OBJECT_PATH,
678 							RB_METADATA_DBUS_INTERFACE,
679 							"save",
680 							g_variant_new ("(sa{iv})",
681 								       uri,
682 								       rb_metadata_dbus_get_variant_builder (md)),
683 							NULL,
684 							G_DBUS_CALL_FLAGS_NONE,
685 							RB_METADATA_SAVE_DBUS_TIMEOUT,
686 							NULL,
687 							error);
688 	}
689 
690 	if (*error == NULL) {
691 		gboolean ok = TRUE;
692 		int error_code;
693 		const char *error_message;
694 
695 		g_variant_get (response, "(bis)", &ok, &error_code, &error_message);
696 		if (ok == FALSE) {
697 			g_set_error (error, RB_METADATA_ERROR,
698 				     error_code,
699 				     "%s", error_message);
700 		}
701 
702 		g_variant_unref (response);
703 	}
704 
705 	if (fake_error)
706 		g_error_free (fake_error);
707 
708 	g_static_mutex_unlock (&conn_mutex);
709 }
710 
711 gboolean
712 rb_metadata_has_audio (RBMetaData *md)
713 {
714 	return md->priv->has_audio;
715 }
716 
717 gboolean
718 rb_metadata_has_video (RBMetaData *md)
719 {
720 	return md->priv->has_video;
721 }
722 
723 gboolean
724 rb_metadata_has_other_data (RBMetaData *md)
725 {
726 	return md->priv->has_other_data;
727 }