1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  *  Copyright (C) 2010  Jonathan Matthew  <jonathan@d14n.org>
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 of the License, or
8  *  (at your option) 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 <string.h>
32 
33 #include <glib/gi18n.h>
34 
35 #include "rhythmdb-entry-type.h"
36 #include "rhythmdb-metadata-cache.h"
37 #include "rhythmdb-private.h"
38 
39 #include "rb-util.h"
40 
41 
42 enum
43 {
44 	PROP_0,
45 	PROP_DB,
46 	PROP_NAME,
47 	PROP_SAVE_TO_DISK,
48 	PROP_TYPE_DATA_SIZE,
49 	PROP_CATEGORY,
50 	PROP_CACHE_NAME
51 };
52 
53 static void rhythmdb_entry_type_class_init (RhythmDBEntryTypeClass *klass);
54 static void rhythmdb_entry_type_init (RhythmDBEntryType *etype);
55 
56 struct _RhythmDBEntryTypePrivate
57 {
58 	RhythmDB *db;
59 
60 	char *name;
61 	gboolean save_to_disk;
62 	guint entry_type_data_size;
63 	RhythmDBEntryCategory category;
64 
65 	char *cache_name;
66 
67 	RhythmDBMetadataCache *cache;
68 };
69 
70 
G_DEFINE_TYPE(RhythmDBEntryType,rhythmdb_entry_type,G_TYPE_OBJECT)71 G_DEFINE_TYPE (RhythmDBEntryType, rhythmdb_entry_type, G_TYPE_OBJECT)
72 
73 /**
74  * SECTION:rhythmdb-entry-type
75  * @short_description: Database entry type base class
76  *
77  * This is the base class for database entry type classes, which provide
78  * some aspects of the behaviour of database entry types.  There are different
79  * entry types for songs, radio streams, podcast feeds and episodes, and so on.
80  *
81  * Plugins written in Python or Vala can create new entry types by subclassing
82  * and overriding any methods required.  Plugins written in C can create a new
83  * instance of the RhythmDBEntryType base class and use its function pointer
84  * members rather than subclassing.
85  */
86 
87 /**
88  * rhythmdb_entry_type_get_name:
89  * @etype: a #RhythmDBEntryType
90  *
91  * Returns the name of the entry type
92  *
93  * Return value: entry type name
94  */
95 const char *
96 rhythmdb_entry_type_get_name (RhythmDBEntryType *etype)
97 {
98 	return etype->priv->name;
99 }
100 
101 /**
102  * rhythmdb_entry_get_playback_uri:
103  * @entry: a #RhythmDBEntry
104  *
105  * Returns an allocated string containing the playback URI for @entry,
106  * or NULL if the entry cannot be played.
107  *
108  * Return value: playback URI or NULL
109  */
110 char *
rhythmdb_entry_get_playback_uri(RhythmDBEntry * entry)111 rhythmdb_entry_get_playback_uri (RhythmDBEntry *entry)
112 {
113 	RhythmDBEntryType *etype = rhythmdb_entry_get_entry_type (entry);
114 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
115 
116 	if (klass->get_playback_uri) {
117 		return (klass->get_playback_uri) (etype, entry);
118 	} else {
119 		return rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_LOCATION);
120 	}
121 }
122 
123 /**
124  * rhythmdb_entry_update_availability:
125  * @entry: a #RhythmDBEntry
126  * @avail: an availability event
127  *
128  * Updates @entry to reflect its new availability.
129  */
130 void
rhythmdb_entry_update_availability(RhythmDBEntry * entry,RhythmDBEntryAvailability avail)131 rhythmdb_entry_update_availability (RhythmDBEntry *entry, RhythmDBEntryAvailability avail)
132 {
133 	RhythmDBEntryType *etype = rhythmdb_entry_get_entry_type (entry);
134 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
135 
136 	if (klass->update_availability) {
137 		(klass->update_availability) (etype, entry, avail);
138 	} else {
139 		/* do nothing? */
140 	}
141 }
142 
143 /**
144  * rhythmdb_entry_created:
145  * @entry: a newly created #RhythmDBEntry
146  *
147  * Calls the entry type's post-creation method for @entry.
148  */
149 void
rhythmdb_entry_created(RhythmDBEntry * entry)150 rhythmdb_entry_created (RhythmDBEntry *entry)
151 {
152 	RhythmDBEntryType *etype;
153 	RhythmDBEntryTypeClass *klass;
154 
155 	etype = rhythmdb_entry_get_entry_type (entry);
156 	klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
157 
158 	if (klass->entry_created) {
159 		klass->entry_created (etype, entry);
160 	}
161 }
162 
163 /**
164  * rhythmdb_entry_pre_destroy:
165  * @entry: a #RhythmDBEntry
166  *
167  * Calls the entry type's pre-deletion method for @entry.
168  */
169 void
rhythmdb_entry_pre_destroy(RhythmDBEntry * entry)170 rhythmdb_entry_pre_destroy (RhythmDBEntry *entry)
171 {
172 	RhythmDBEntryType *etype = rhythmdb_entry_get_entry_type (entry);
173 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
174 	if (klass->destroy_entry) {
175 		klass->destroy_entry (etype, entry);
176 	}
177 }
178 
179 /**
180  * rhythmdb_entry_can_sync_metadata:
181  * @entry: a #RhythmDBEntry
182  *
183  * Calls the entry type's method to check if it can sync metadata for @entry.
184  * Usually this is only true for entries backed by files, where tag-writing is
185  * enabled, and the appropriate tag-writing facilities are available.
186  *
187  * Return value: %TRUE if the entry can be synced
188  */
189 gboolean
rhythmdb_entry_can_sync_metadata(RhythmDBEntry * entry)190 rhythmdb_entry_can_sync_metadata (RhythmDBEntry *entry)
191 {
192 	RhythmDBEntryType *etype = rhythmdb_entry_get_entry_type (entry);
193 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
194 	if (klass->can_sync_metadata) {
195 		return klass->can_sync_metadata (etype, entry);
196 	} else {
197 		return FALSE;
198 	}
199 }
200 
201 /**
202  * rhythmdb_entry_sync_metadata:
203  * @entry: a #RhythmDBEntry
204  * @changes: (element-type RB.RhythmDBEntryChange): a list of #RhythmDBEntryChange structures
205  * @error: returns error information
206  *
207  * Calls the entry type's method to sync metadata changes for @entry.
208  */
209 void
rhythmdb_entry_sync_metadata(RhythmDBEntry * entry,GSList * changes,GError ** error)210 rhythmdb_entry_sync_metadata (RhythmDBEntry *entry, GSList *changes, GError **error)
211 {
212 	RhythmDBEntryType *etype = rhythmdb_entry_get_entry_type (entry);
213 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
214 	if (klass->sync_metadata) {
215 		klass->sync_metadata (etype, entry, changes, error);
216 	} else {
217 		/* default implementation? */
218 	}
219 }
220 
221 /**
222  * rhythmdb_entry_type_fetch_metadata:
223  * @etype: a #RhythmDBEntryType
224  * @uri: uri of the item to fetch
225  * @metadata: (element-type RhythmDBEntryChange): returns fetched metadata
226  *
227  * Fetches metadata for a URI (not an entry yet, at this point) from a cache, if possible.
228  *
229  * The @metadata array contains RhythmDBEntryChange items with just the 'new' value set.
230  *
231  * Return value: %TRUE if metadata is returned
232  */
233 gboolean
rhythmdb_entry_type_fetch_metadata(RhythmDBEntryType * etype,const char * uri,GArray * metadata)234 rhythmdb_entry_type_fetch_metadata (RhythmDBEntryType *etype, const char *uri, GArray *metadata)
235 {
236 	char *key;
237 	gboolean result;
238 
239 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
240 	if (klass->uri_to_cache_key == NULL) {
241 		return FALSE;
242 	}
243 
244 	key = klass->uri_to_cache_key (etype, uri);
245 	if (key == NULL)
246 		return FALSE;
247 
248 	result = rhythmdb_metadata_cache_load (etype->priv->cache, key, metadata);
249 	g_free (key);
250 	return result;
251 }
252 
253 /**
254  * rhythmdb_entry_cache_metadata:
255  * @entry: a #RhythmDBEntry
256  *
257  * Stores metadata for @entry in the metadata cache (if any) for its entry type.
258  */
259 void
rhythmdb_entry_cache_metadata(RhythmDBEntry * entry)260 rhythmdb_entry_cache_metadata (RhythmDBEntry *entry)
261 {
262 	RhythmDBEntryType *etype = rhythmdb_entry_get_entry_type (entry);
263 	char *key;
264 
265 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
266 	if (klass->uri_to_cache_key == NULL) {
267 		return;
268 	}
269 
270 	key = klass->uri_to_cache_key (etype, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
271 	if (key == NULL)
272 		return;
273 
274 	rhythmdb_metadata_cache_store (etype->priv->cache, key, entry);
275 }
276 
277 static RhythmDBPropType default_unknown_properties[] = {
278 	RHYTHMDB_PROP_GENRE,
279 	RHYTHMDB_PROP_ARTIST,
280 	RHYTHMDB_PROP_ALBUM,
281 	RHYTHMDB_PROP_COMPOSER
282 };
283 
284 /**
285  * rhythmdb_entry_apply_cached_metadata:
286  * @entry: a #RhythmDBEntry
287  * @metadata: (element-type RhythmDBEntryChange): cached metadata to apply
288  *
289  * Applies a set of metadata properties to @entry.  The metadata should be in the
290  * form returned by @rhythmdb_entry_type_fetch_metadata.
291  */
292 void
rhythmdb_entry_apply_cached_metadata(RhythmDBEntry * entry,GArray * metadata)293 rhythmdb_entry_apply_cached_metadata (RhythmDBEntry *entry, GArray *metadata)
294 {
295 	RhythmDBEntryType *etype = rhythmdb_entry_get_entry_type (entry);
296 	RhythmDBEntryChange *fields;
297 	GValue unknown = {0,};
298 	int i;
299 
300 	g_value_init (&unknown, G_TYPE_STRING);
301 	g_value_set_string (&unknown, _("Unknown"));
302 	for (i = 0; i < G_N_ELEMENTS(default_unknown_properties); i++) {
303 		rhythmdb_entry_set_internal (etype->priv->db, entry, TRUE, default_unknown_properties[i], &unknown);
304 	}
305 	g_value_unset (&unknown);
306 
307 	fields = (RhythmDBEntryChange *)metadata->data;
308 	for (i = 0; i < metadata->len; i++) {
309 		rhythmdb_entry_set_internal (etype->priv->db, entry, TRUE, fields[i].prop, &fields[i].new);
310 	}
311 	rhythmdb_commit (etype->priv->db);
312 }
313 
314 static gboolean
metadata_key_valid_cb(const char * key,RhythmDBEntryType * etype)315 metadata_key_valid_cb (const char *key, RhythmDBEntryType *etype)
316 {
317 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
318 	char *uri;
319 	gboolean result = FALSE;	/* or maybe true? */
320 
321 	uri = klass->cache_key_to_uri (etype, key);
322 	if (uri != NULL) {
323 		RhythmDBEntry *entry;
324 		entry = rhythmdb_entry_lookup_by_location (etype->priv->db, uri);
325 		result = (entry != NULL);
326 	}
327 
328 	g_free (uri);
329 	return result;
330 }
331 
332 /**
333  * rhythmdb_entry_type_purge_metadata_cache:
334  * @etype: a #RhythmDBEntryType
335  * @prefix: a cache key prefix to scan
336  * @max_age: maximum age of missing entries to keep
337  */
338 void
rhythmdb_entry_type_purge_metadata_cache(RhythmDBEntryType * etype,const char * prefix,guint64 max_age)339 rhythmdb_entry_type_purge_metadata_cache (RhythmDBEntryType *etype, const char *prefix, guint64 max_age)
340 {
341 	RhythmDBEntryTypeClass *klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
342 	g_assert (klass->cache_key_to_uri != NULL);
343 	g_assert (etype->priv->cache != NULL);
344 
345 	rhythmdb_metadata_cache_purge (etype->priv->cache,
346 				       prefix,
347 				       max_age,
348 				       (RhythmDBMetadataCacheValidFunc) metadata_key_valid_cb,
349 				       etype,
350 				       NULL);
351 }
352 
353 static void
rhythmdb_entry_type_init(RhythmDBEntryType * etype)354 rhythmdb_entry_type_init (RhythmDBEntryType *etype)
355 {
356 	etype->priv = G_TYPE_INSTANCE_GET_PRIVATE (etype,
357 						   RHYTHMDB_TYPE_ENTRY_TYPE,
358 						   RhythmDBEntryTypePrivate);
359 }
360 
361 static void
impl_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)362 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
363 {
364 	RhythmDBEntryType *etype = RHYTHMDB_ENTRY_TYPE (object);
365 
366 	switch (prop_id) {
367 	case PROP_DB:
368 		etype->priv->db = g_value_get_object (value);
369 		break;
370 	case PROP_NAME:
371 		etype->priv->name = g_value_dup_string (value);
372 		break;
373 	case PROP_SAVE_TO_DISK:
374 		etype->priv->save_to_disk = g_value_get_boolean (value);
375 		break;
376 	case PROP_TYPE_DATA_SIZE:
377 		etype->priv->entry_type_data_size = g_value_get_uint (value);
378 		break;
379 	case PROP_CATEGORY:
380 		etype->priv->category = g_value_get_enum (value);
381 		break;
382 	case PROP_CACHE_NAME:
383 		etype->priv->cache_name = g_value_dup_string (value);
384 		break;
385 	default:
386 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
387 		break;
388 	}
389 }
390 
391 static void
impl_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)392 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
393 {
394 	RhythmDBEntryType *etype = RHYTHMDB_ENTRY_TYPE (object);
395 
396 	switch (prop_id) {
397 	case PROP_DB:
398 		g_value_set_object (value, etype->priv->db);
399 		break;
400 	case PROP_NAME:
401 		g_value_set_string (value, etype->priv->name);
402 		break;
403 	case PROP_SAVE_TO_DISK:
404 		g_value_set_boolean (value, etype->priv->save_to_disk);
405 		break;
406 	case PROP_TYPE_DATA_SIZE:
407 		g_value_set_uint (value, etype->priv->entry_type_data_size);
408 		break;
409 	case PROP_CATEGORY:
410 		g_value_set_enum (value, etype->priv->category);
411 		break;
412 	case PROP_CACHE_NAME:
413 		g_value_set_string (value, etype->priv->cache_name);
414 		break;
415 	default:
416 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 		break;
418 	}
419 }
420 
421 static void
impl_constructed(GObject * object)422 impl_constructed (GObject *object)
423 {
424 	RhythmDBEntryType *etype;
425 	RhythmDBEntryTypeClass *klass;
426 
427 	RB_CHAIN_GOBJECT_METHOD (rhythmdb_entry_type_parent_class, constructed, object);
428 
429 	etype = RHYTHMDB_ENTRY_TYPE (object);
430 	klass = RHYTHMDB_ENTRY_TYPE_GET_CLASS (etype);
431 
432 	if (etype->priv->cache_name) {
433 		g_assert (klass->uri_to_cache_key != NULL);
434 
435 		etype->priv->cache = rhythmdb_metadata_cache_get (etype->priv->db, etype->priv->cache_name);
436 	}
437 }
438 
439 static void
impl_dispose(GObject * object)440 impl_dispose (GObject *object)
441 {
442 	RhythmDBEntryType *etype = RHYTHMDB_ENTRY_TYPE (object);
443 
444 	g_clear_object (&etype->priv->cache);
445 
446 	G_OBJECT_CLASS (rhythmdb_entry_type_parent_class)->dispose (object);
447 }
448 
449 static void
impl_finalize(GObject * object)450 impl_finalize (GObject *object)
451 {
452 	RhythmDBEntryType *etype = RHYTHMDB_ENTRY_TYPE (object);
453 
454 	g_free (etype->priv->name);
455 	g_free (etype->priv->cache_name);
456 
457 	G_OBJECT_CLASS (rhythmdb_entry_type_parent_class)->finalize (object);
458 }
459 
460 static void
rhythmdb_entry_type_class_init(RhythmDBEntryTypeClass * klass)461 rhythmdb_entry_type_class_init (RhythmDBEntryTypeClass *klass)
462 {
463 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
464 
465 	object_class->set_property = impl_set_property;
466 	object_class->get_property = impl_get_property;
467 	object_class->constructed = impl_constructed;
468 	object_class->dispose = impl_dispose;
469 	object_class->finalize = impl_finalize;
470 
471 	/**
472 	 * RhythmDBEntryType:db:
473 	 *
474 	 * The #RhythmDB instance.
475 	 */
476 	g_object_class_install_property (object_class,
477 					 PROP_DB,
478 					 g_param_spec_object ("db",
479 							      "RhythmDB",
480 							      "RhythmDB instance",
481 							      RHYTHMDB_TYPE,
482 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
483 
484 	/**
485 	 * RhythmDBEntryType:name:
486 	 *
487 	 * Entry type name.  This must be unique.
488 	 */
489 	g_object_class_install_property (object_class,
490 					 PROP_NAME,
491 					 g_param_spec_string ("name",
492 							      "name",
493 							      "entry type name",
494 							      NULL,
495 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
496 
497 	/**
498 	 * RhythmDBEntryType:save-to-disk:
499 	 *
500 	 * If %TRUE, entries of this type should be written to the
501 	 * on-disk database.
502 	 */
503 	g_object_class_install_property (object_class,
504 					 PROP_SAVE_TO_DISK,
505 					 g_param_spec_boolean ("save-to-disk",
506 							       "save to disk",
507 							       "whether to save this type of entry to disk",
508 							       FALSE,
509 							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
510 	/**
511 	 * RhythmDBEntryType:type-data-size:
512 	 *
513 	 * The size of the type-specific data structure to allocate for each
514 	 * entry of this type.
515 	 */
516 	g_object_class_install_property (object_class,
517 					 PROP_TYPE_DATA_SIZE,
518 					 g_param_spec_uint ("type-data-size",
519 							    "type data size",
520 							    "size of entry type specific data",
521 							    0, G_MAXUINT, 0,
522 							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
523 	/**
524 	 * RhythmDBEntryType:category:
525 	 *
526 	 * The #RhythmDBEntryCategory that this entry type fits into.
527 	 */
528 	g_object_class_install_property (object_class,
529 					 PROP_CATEGORY,
530 					 g_param_spec_enum ("category",
531 							    "category",
532 							    "RhythmDBEntryCategory for the entry type",
533 							    RHYTHMDB_TYPE_ENTRY_CATEGORY,
534 							    RHYTHMDB_ENTRY_NORMAL,
535 							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
536 	/**
537 	 * RhythmDBEntryType:cache-name:
538 	 *
539 	 * Metadata cache name.  For entry types created by a plugin, should match the plugin name.
540 	 * If this is set, the entry type must also implement the uri_to_cache_key method.
541 	 */
542 	g_object_class_install_property (object_class,
543 					 PROP_CACHE_NAME,
544 					 g_param_spec_string ("cache-name",
545 							      "cache name",
546 							      "metadata cache name",
547 							      NULL,
548 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
549 
550 	g_type_class_add_private (klass, sizeof (RhythmDBEntryTypePrivate));
551 }
552 
553 
554 
555 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
556 
557 /**
558  * RhythmDBEntryCategory:
559  * @RHYTHMDB_ENTRY_NORMAL: Normal files on disk
560  * @RHYTHMDB_ENTRY_STREAM: Endless streams (eg shoutcast)
561  * @RHYTHMDB_ENTRY_CONTAINER: Containers for other entries (eg podcast feeds)
562  * @RHYTHMDB_ENTRY_VIRTUAL: Things Rhythmbox shouldn't normally deal with
563  *
564  * Categories used to group entry types.  These are used in a few places to control
565  * handling of entries.
566  */
567 
568 GType
rhythmdb_entry_category_get_type(void)569 rhythmdb_entry_category_get_type (void)
570 {
571 	static GType etype = 0;
572 
573 	if (etype == 0)
574 	{
575 		static const GEnumValue values[] =
576 		{
577 			ENUM_ENTRY (RHYTHMDB_ENTRY_NORMAL, "normal"),
578 			ENUM_ENTRY (RHYTHMDB_ENTRY_STREAM, "stream"),
579 			ENUM_ENTRY (RHYTHMDB_ENTRY_CONTAINER, "container"),
580 			ENUM_ENTRY (RHYTHMDB_ENTRY_VIRTUAL, "virtual"),
581 			{ 0, 0, 0 }
582 		};
583 
584 		etype = g_enum_register_static ("RhythmDBEntryCategory", values);
585 	}
586 
587 	return etype;
588 }
589 
590 /**
591  * RhythmDBEntryAvailability:
592  * @RHYTHMDB_ENTRY_AVAIL_CHECKED: File was checked and found present
593  * @RHYTHMDB_ENTRY_AVAIL_MOUNTED: Filesystem holding the file was mounted
594  * @RHYTHMDB_ENTRY_AVAIL_UNMOUNTED: Filesystem holding the file was unmounted
595  * @RHYTHMDB_ENTRY_AVAIL_NOT_FOUND: File was checked or played and could not be found
596  *
597  * Various events that can result in changes to the entry's availability.
598  */
599 
600 GType
rhythmdb_entry_availability_get_type(void)601 rhythmdb_entry_availability_get_type (void)
602 {
603 	static GType etype = 0;
604 
605 	if (etype == 0)
606 	{
607 		static const GEnumValue values[] =
608 		{
609 			ENUM_ENTRY (RHYTHMDB_ENTRY_AVAIL_CHECKED, "checked"),
610 			ENUM_ENTRY (RHYTHMDB_ENTRY_AVAIL_MOUNTED, "mounted"),
611 			ENUM_ENTRY (RHYTHMDB_ENTRY_AVAIL_UNMOUNTED, "unmounted"),
612 			ENUM_ENTRY (RHYTHMDB_ENTRY_AVAIL_NOT_FOUND, "not-found"),
613 			{ 0, 0, 0 }
614 		};
615 
616 		etype = g_enum_register_static ("RhythmDBEntryAvailability", values);
617 	}
618 
619 	return etype;
620 }
621