2006-12-12 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / metadata / sj-metadata-musicbrainz.c
blob45d82b7fb2e85658921491cab635929b9d5542ca
1 /*
2 * sj-metadata-musicbrainz.c
3 * Copyright (C) 2003 Ross Burton <ross@burtonini.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif /* HAVE_CONFIG_H */
25 #include <string.h>
26 #include <stdio.h>
27 #include <glib-object.h>
28 #include <glib/gi18n.h>
29 #include <glib/gerror.h>
30 #include <glib/glist.h>
31 #include <glib/gstrfuncs.h>
32 #include <glib/gmessages.h>
33 #include <gconf/gconf-client.h>
34 #include <musicbrainz/queries.h>
35 #include <musicbrainz/mb_c.h>
36 #include <nautilus-burn.h>
37 #include <stdlib.h>
38 #include <unistd.h>
40 #include "sj-metadata-musicbrainz.h"
41 #include "sj-structures.h"
42 #include "sj-error.h"
44 struct SjMetadataMusicbrainzPrivate {
45 GError *construct_error;
46 musicbrainz_t mb;
47 char *http_proxy;
48 int http_proxy_port;
49 char *cdrom;
50 /* TODO: remove and use an async queue or something l33t */
51 GList *albums;
52 GError *error;
55 static GError* mb_get_new_error (SjMetadata *metadata);
56 static void mb_set_cdrom (SjMetadata *metadata, const char* device);
57 static void mb_set_proxy (SjMetadata *metadata, const char* proxy);
58 static void mb_set_proxy_port (SjMetadata *metadata, const int port);
59 static void mb_list_albums (SjMetadata *metadata, GError **error);
60 static char *mb_get_submit_url (SjMetadata *metadata);
62 #define GCONF_MUSICBRAINZ_SERVER "/apps/sound-juicer/musicbrainz_server"
63 #define GCONF_PROXY_USE_PROXY "/system/http_proxy/use_http_proxy"
64 #define GCONF_PROXY_HOST "/system/http_proxy/host"
65 #define GCONF_PROXY_PORT "/system/http_proxy/port"
66 #define GCONF_PROXY_USE_AUTHENTICATION "/system/http_proxy/use_authentication"
67 #define GCONF_PROXY_USERNAME "/system/http_proxy/authentication_user"
68 #define GCONF_PROXY_PASSWORD "/system/http_proxy/authentication_password"
70 /**
71 * GObject methods
74 static GObjectClass *parent_class = NULL;
76 static void
77 sj_metadata_musicbrainz_finalize (GObject *object)
79 SjMetadataMusicbrainzPrivate *priv;
80 g_return_if_fail (object != NULL);
81 priv = SJ_METADATA_MUSICBRAINZ (object)->priv;
83 g_free (priv->http_proxy);
84 g_free (priv->cdrom);
85 mb_Delete (priv->mb);
86 g_free (priv);
89 static void
90 sj_metadata_musicbrainz_instance_init (GTypeInstance *instance, gpointer g_class)
92 GConfClient *gconf_client;
93 char *server_name = NULL;
94 SjMetadataMusicbrainz *self = (SjMetadataMusicbrainz*)instance;
95 self->priv = g_new0 (SjMetadataMusicbrainzPrivate, 1);
96 self->priv->construct_error = NULL;
97 self->priv->mb = mb_New ();
98 /* TODO: something. :/ */
99 if (!self->priv->mb) {
100 g_set_error (&self->priv->construct_error,
101 SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
102 _("Cannot create MusicBrainz client"));
103 return;
105 mb_UseUTF8 (self->priv->mb, TRUE);
107 gconf_client = gconf_client_get_default ();
108 server_name = gconf_client_get_string (gconf_client, GCONF_MUSICBRAINZ_SERVER, NULL);
109 if (server_name) {
110 g_strstrip (server_name);
112 if (server_name && strcmp (server_name, "") != 0) {
113 mb_SetServer (self->priv->mb, server_name, 80);
114 g_free (server_name);
117 /* Set the HTTP proxy */
118 if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_PROXY, NULL)) {
119 char *proxy_host = gconf_client_get_string (gconf_client, GCONF_PROXY_HOST, NULL);
120 mb_SetProxy (self->priv->mb, proxy_host,
121 gconf_client_get_int (gconf_client, GCONF_PROXY_PORT, NULL));
122 g_free (proxy_host);
123 if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_AUTHENTICATION, NULL)) {
124 #if HAVE_MB_SETPROXYCREDS
125 char *username = gconf_client_get_string (gconf_client, GCONF_PROXY_USERNAME, NULL);
126 char *password = gconf_client_get_string (gconf_client, GCONF_PROXY_PASSWORD, NULL);
127 mb_SetProxyCreds (self->priv->mb, username, password);
128 g_free (username);
129 g_free (password);
130 #else
131 g_warning ("mb_SetProxyCreds() not found, no proxy authorisation possible.");
132 #endif
136 g_object_unref (gconf_client);
138 if (g_getenv("MUSICBRAINZ_DEBUG")) {
139 mb_SetDebug (self->priv->mb, TRUE);
143 static void
144 metadata_interface_init (gpointer g_iface, gpointer iface_data)
146 SjMetadataClass *klass = (SjMetadataClass*)g_iface;
147 klass->get_new_error = mb_get_new_error;
148 klass->set_cdrom = mb_set_cdrom;
149 klass->set_proxy = mb_set_proxy;
150 klass->set_proxy_port = mb_set_proxy_port;
151 klass->list_albums = mb_list_albums;
152 klass->get_submit_url = mb_get_submit_url;
155 static void
156 sj_metadata_musicbrainz_class_init (SjMetadataMusicbrainzClass *class)
158 GObjectClass *object_class;
159 parent_class = g_type_class_peek_parent (class);
160 object_class = (GObjectClass*) class;
161 object_class->finalize = sj_metadata_musicbrainz_finalize;
164 GType
165 sj_metadata_musicbrainz_get_type (void)
167 static GType type = 0;
168 if (type == 0) {
169 static const GTypeInfo info = {
170 sizeof (SjMetadataMusicbrainzClass),
171 NULL,
172 NULL,
173 (GClassInitFunc)sj_metadata_musicbrainz_class_init,
174 NULL,
175 NULL,
176 sizeof (SjMetadataMusicbrainz),
178 sj_metadata_musicbrainz_instance_init,
179 NULL
181 static const GInterfaceInfo metadata_i_info = {
182 (GInterfaceInitFunc) metadata_interface_init,
183 NULL, NULL
185 type = g_type_register_static (G_TYPE_OBJECT, "SjMetadataMusicBrainzClass", &info, 0);
186 g_type_add_interface_static (type, SJ_TYPE_METADATA, &metadata_i_info);
188 return type;
191 GObject *
192 sj_metadata_musicbrainz_new (void)
194 return g_object_new (sj_metadata_musicbrainz_get_type (), NULL);
198 * Private methods
201 #define BYTES_PER_SECTOR 2352
202 #define BYTES_PER_SECOND (44100 / 8) / 16 / 2
204 static int
205 get_duration_from_sectors (int sectors)
207 return (sectors * BYTES_PER_SECTOR / BYTES_PER_SECOND);
210 static GList*
211 get_offline_track_listing(SjMetadata *metadata, GError **error)
213 SjMetadataMusicbrainzPrivate *priv;
214 GList* list = NULL;
215 AlbumDetails *album;
216 TrackDetails *track;
217 int num_tracks, i;
219 g_return_val_if_fail (metadata != NULL, NULL);
220 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
222 if (!mb_Query (priv->mb, MBQ_GetCDTOC)) {
223 char message[255];
224 mb_GetQueryError (priv->mb, message, 255);
225 g_set_error (error,
226 SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
227 _("Cannot read CD: %s"), message);
228 return NULL;
231 num_tracks = mb_GetResultInt (priv->mb, MBE_TOCGetLastTrack);
233 album = g_new0 (AlbumDetails, 1);
234 album->artist = g_strdup (_("Unknown Artist"));
235 album->title = g_strdup (_("Unknown Title"));
236 album->genre = NULL;
237 for (i = 1; i <= num_tracks; i++) {
238 track = g_new0 (TrackDetails, 1);
239 track->album = album;
240 track->number = i;
241 track->title = g_strdup_printf (_("Track %d"), i);
242 track->artist = g_strdup (album->artist);
243 track->duration = get_duration_from_sectors (mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, i+1));
244 album->tracks = g_list_append (album->tracks, track);
245 album->number++;
247 return g_list_append (list, album);
250 static gboolean
251 fire_signal_idle (SjMetadataMusicbrainz *m)
253 g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (m), FALSE);
254 g_signal_emit_by_name (G_OBJECT (m), "metadata", m->priv->albums, m->priv->error);
255 return FALSE;
259 * Virtual methods
262 static GError*
263 mb_get_new_error (SjMetadata *metadata)
265 GError *error = NULL;
266 if (metadata == NULL || SJ_METADATA_MUSICBRAINZ (metadata)->priv == NULL) {
267 g_set_error (&error,
268 SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
269 _("MusicBrainz metadata object is not valid. This is bad, check your console for errors."));
270 return error;
272 return SJ_METADATA_MUSICBRAINZ (metadata)->priv->construct_error;
275 static void
276 mb_set_cdrom (SjMetadata *metadata, const char* device)
278 SjMetadataMusicbrainzPrivate *priv;
279 g_return_if_fail (metadata != NULL);
280 g_return_if_fail (device != NULL);
281 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
283 if (priv->cdrom) {
284 g_free (priv->cdrom);
286 priv->cdrom = g_strdup (device);
287 mb_SetDevice (priv->mb, priv->cdrom);
290 static void
291 mb_set_proxy (SjMetadata *metadata, const char* proxy)
293 SjMetadataMusicbrainzPrivate *priv;
294 g_return_if_fail (metadata != NULL);
295 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
297 if (proxy == NULL) {
298 proxy = "";
300 if (priv->http_proxy) {
301 g_free (priv->http_proxy);
303 priv->http_proxy = g_strdup (proxy);
304 mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
307 static void
308 mb_set_proxy_port (SjMetadata *metadata, const int port)
310 SjMetadataMusicbrainzPrivate *priv;
311 g_return_if_fail (metadata != NULL);
312 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
314 priv->http_proxy_port = port;
315 mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
318 /* Data imported from FreeDB is horrendeous for compilations,
319 * Try to split the 'Various' artist */
320 static void
321 artist_and_title_from_title (TrackDetails *track, gpointer data)
323 char *slash, **split;
325 if (g_ascii_strncasecmp (MBI_VARIOUS_ARTIST_ID, track->album->artist_id, 64) != 0 && track->album->artist_id[0] != '\0' && track->artist_id[0] != '\0') {
326 track->title = g_strdup (data);
327 return;
330 slash = strstr (data, " / ");
331 if (slash == NULL) {
332 track->title = g_strdup (data);
333 return;
335 split = g_strsplit (data, " / ", 2);
336 track->artist = g_strdup (split[0]);
337 track->title = g_strdup (split[1]);
338 g_strfreev (split);
341 #if WITH_CACHE
343 * Write the RDF in the MusicBrainz object to the file specified.
345 static void
346 cache_rdf (musicbrainz_t mb, const char *filename)
348 GError *error = NULL;
349 int len;
350 char *path, *rdf;
352 g_assert (mb != NULL);
353 g_assert (filename != NULL);
355 /* Create the folder for the file */
356 path = g_path_get_dirname (filename);
357 g_mkdir_with_parents (path, 0755); /* Handle errors in set_contents() */
358 g_free (path);
360 /* How much data is there to save? */
361 len = mb_GetResultRDFLen (mb);
362 rdf = g_malloc0 (len);
364 /* Get the RDF and save it */
365 mb_GetResultRDF (mb, rdf, len);
366 if (!g_file_set_contents (filename, rdf, len, &error)) {
367 g_warning ("Cannot write cache file %s: %s", filename, error->message);
368 g_error_free (error);
371 g_free (rdf);
375 * Load into the MusicBrainz object the RDF from the specified cache file if it
376 * exists and is valid then return TRUE, otherwise return FALSE.
378 static gboolean
379 get_cached_rdf (musicbrainz_t mb, const char *cachepath)
381 gboolean ret = FALSE;
382 GError *error = NULL;
383 char *rdf = NULL;
385 g_assert (mb != NULL);
386 g_assert (cachepath != NULL);
388 if (!g_file_test (cachepath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
389 goto done;
391 /* Cache file exists, open it */
392 if (!g_file_get_contents (cachepath, &rdf, NULL, &error)) {
393 g_warning ("Cannot open cache file %s: %s", cachepath, error->message);
394 g_error_free (error);
395 goto done;
398 /* Set the RDF */
399 if (mb_SetResultRDF (mb, rdf))
400 ret = TRUE;
402 done:
403 g_free (rdf);
404 return ret;
406 #else
407 static gboolean
408 get_cached_rdf (musicbrainz_t mb, const char *cachepath)
410 return FALSE;
412 static void
413 cache_rdf (musicbrainz_t mb, const char *filename) {
415 #endif
418 * Fill the MusicBrainz object with RDF. Basically get the CD Index and check
419 * the local cache, if that fails then lookup the data online.
421 static void
422 get_rdf (SjMetadata *metadata)
424 SjMetadataMusicbrainzPrivate *priv;
425 char data[256];
426 char *cdindex = NULL, *cachepath = NULL;
428 g_assert (metadata != NULL);
430 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
432 #if WITH_CACHE
433 /* Get the Table of Contents */
434 if (!mb_Query (priv->mb, MBQ_GetCDTOC)) {
435 mb_GetQueryError (priv->mb, data, sizeof (data));
436 g_print (_("This CD could not be queried: %s\n"), data);
437 return;
440 /* Extract the CD Index */
441 if (!mb_GetResultData(priv->mb, MBE_TOCGetCDIndexId, data, sizeof (data))) {
442 mb_GetQueryError (priv->mb, data, sizeof (data));
443 g_print (_("This CD could not be queried: %s\n"), data);
444 return;
446 cdindex = g_strdup (data);
448 cachepath = g_build_filename (g_get_home_dir (), ".gnome2", "sound-juicer", "cache", cdindex, NULL);
449 #endif
451 if (!get_cached_rdf (priv->mb, cachepath)) {
452 /* Don't re-use the CD Index as that doesn't send enough data to the server.
453 By doing this we also pass track lengths, which can be proxied to FreeDB
454 if required. */
455 if (!mb_Query (priv->mb, MBQ_GetCDInfo)) {
456 mb_GetQueryError (priv->mb, data, sizeof (data));
457 g_print (_("This CD could not be queried: %s\n"), data);
458 goto done;
460 cache_rdf (priv->mb, cachepath);
463 done:
464 g_free (cachepath);
465 g_free (cdindex);
468 static gpointer
469 lookup_cd (SjMetadata *metadata)
471 /** The size of the buffer used in MusicBrainz lookups */
472 SjMetadataMusicbrainzPrivate *priv;
473 GList *albums = NULL;
474 GList *al, *tl;
475 char data[256];
476 int num_albums, i, j;
477 NautilusBurnMediaType type;
478 NautilusBurnDriveMonitor *monitor;
479 NautilusBurnDrive *drive;
481 /* TODO: fire error signal */
482 g_return_val_if_fail (metadata != NULL, NULL);
483 g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata), NULL);
484 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
485 g_return_val_if_fail (priv->cdrom != NULL, NULL);
486 priv->error = NULL; /* TODO: hack */
488 if (! nautilus_burn_initialized ()) {
489 nautilus_burn_init ();
491 monitor = nautilus_burn_get_drive_monitor ();
492 drive = nautilus_burn_drive_monitor_get_drive_for_device (monitor, priv->cdrom);
493 if (drive == NULL) {
494 return NULL;
496 type = nautilus_burn_drive_get_media_type (drive);
497 nautilus_burn_drive_unref (drive);
499 if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
500 char *msg;
501 SjError err;
503 if (access (priv->cdrom, W_OK) == 0) {
504 msg = g_strdup_printf (_("Device '%s' does not contain any media"), priv->cdrom);
505 err = SJ_ERROR_CD_NO_MEDIA;
506 } else {
507 msg = g_strdup_printf (_("Device '%s' could not be opened. Check the access permissions on the device."), priv->cdrom);
508 err = SJ_ERROR_CD_PERMISSION_ERROR;
510 priv->error = g_error_new (SJ_ERROR, err, _("Cannot read CD: %s"), msg);
511 g_free (msg);
513 priv->albums = NULL;
514 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
515 return NULL;
518 get_rdf (metadata);
520 num_albums = mb_GetResultInt(priv->mb, MBE_GetNumAlbums);
521 if (num_albums < 1) {
522 priv->albums = get_offline_track_listing (metadata, &(priv->error));
523 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
524 return priv->albums;
527 for (i = 1; i <= num_albums; i++) {
528 int num_tracks;
529 AlbumDetails *album;
531 mb_Select1(priv->mb, MBS_SelectAlbum, i);
532 album = g_new0 (AlbumDetails, 1);
534 if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumId, data, sizeof (data))) {
535 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
536 album->album_id = g_strdup (data);
539 if (mb_GetResultData (priv->mb, MBE_AlbumGetAlbumArtistId, data, sizeof (data))) {
540 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
541 album->artist_id = g_strdup (data);
542 if (g_ascii_strncasecmp (MBI_VARIOUS_ARTIST_ID, data, 64) == 0) {
543 album->artist = g_strdup (_("Various"));
544 } else {
545 if (*data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, sizeof (data), 1)) {
546 album->artist = g_strdup (data);
547 } else {
548 album->artist = g_strdup (_("Unknown Artist"));
550 if (*data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistSortName, data, sizeof (data), 1)) {
551 album->artist_sortname = g_strdup (data);
556 if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumName, data, sizeof (data))) {
557 album->title = g_strdup (data);
558 } else {
559 album->title = g_strdup (_("Unknown Title"));
563 int num_releases;
564 num_releases = mb_GetResultInt (priv->mb, MBE_AlbumGetNumReleaseDates);
565 if (num_releases > 0) {
566 mb_Select1(priv->mb, MBS_SelectReleaseDate, 1);
567 if (mb_GetResultData(priv->mb, MBE_ReleaseGetDate, data, sizeof (data))) {
568 int matched, year=1, month=1, day=1;
569 matched = sscanf(data, "%u-%u-%u", &year, &month, &day);
570 if (matched >= 1) {
571 album->release_date = g_date_new_dmy ((day == 0) ? 1 : day, (month == 0) ? 1 : month, year);
574 mb_Select(priv->mb, MBS_Back);
578 num_tracks = mb_GetResultInt(priv->mb, MBE_AlbumGetNumTracks);
579 if (num_tracks < 1) {
580 g_free (album->artist);
581 g_free (album->artist_sortname);
582 g_free (album->title);
583 g_free (album);
584 g_warning (_("Incomplete metadata for this CD"));
585 priv->albums = get_offline_track_listing (metadata, &(priv->error));
586 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
587 return priv->albums;
590 for (j = 1; j <= num_tracks; j++) {
591 TrackDetails *track;
592 track = g_new0 (TrackDetails, 1);
594 track->album = album;
596 track->number = j; /* replace with number lookup? */
598 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackId, data, sizeof (data), j)) {
599 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
600 track->track_id = g_strdup (data);
603 if (mb_GetResultData1(priv->mb, MBE_AlbumGetArtistId, data, sizeof (data), j)) {
604 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
605 track->artist_id = g_strdup (data);
608 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackName, data, sizeof (data), j)) {
609 if (track->artist_id != NULL) {
610 artist_and_title_from_title (track, data);
611 } else {
612 track->title = g_strdup (data);
616 if (track->artist == NULL && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, sizeof (data), j)) {
617 track->artist = g_strdup (data);
620 if (mb_GetResultData1(priv->mb, MBE_AlbumGetArtistSortName, data, sizeof (data), j)) {
621 track->artist_sortname = g_strdup (data);
624 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackDuration, data, sizeof (data), j)) {
625 track->duration = atoi (data) / 1000;
628 album->tracks = g_list_append (album->tracks, track);
629 album->number++;
632 albums = g_list_append (albums, album);
634 mb_Select (priv->mb, MBS_Rewind);
637 /* For each album, we need to insert the duration data if necessary
638 * We need to query this here because otherwise we would flush the
639 * data queried from the server */
640 /* TODO: scan for 0 duration before doing the query to avoid another lookup if
641 we don't need to do it */
642 mb_Query (priv->mb, MBQ_GetCDTOC);
643 for (al = albums; al; al = al->next) {
644 AlbumDetails *album = al->data;
646 j = 1;
647 for (tl = album->tracks; tl; tl = tl->next) {
648 TrackDetails *track = tl->data;
649 int sectors;
651 if (track->duration == 0) {
652 sectors = mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, j+1);
653 track->duration = get_duration_from_sectors (sectors);
655 j++;
659 priv->albums = albums;
660 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
661 return albums;
664 static void
665 mb_list_albums (SjMetadata *metadata, GError **error)
667 GThread *thread;
669 g_return_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata));
671 thread = g_thread_create ((GThreadFunc)lookup_cd, metadata, TRUE, error);
672 if (thread == NULL) {
673 g_set_error (error,
674 SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
675 _("Could not create CD lookup thread"));
676 return;
680 static char *
681 mb_get_submit_url (SjMetadata *metadata)
683 SjMetadataMusicbrainzPrivate *priv;
684 char url[1025];
686 g_return_val_if_fail (metadata != NULL, NULL);
688 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
690 if (mb_GetWebSubmitURL(priv->mb, url, 1024)) {
691 return g_strdup(url);
692 } else {
693 return NULL;