2006-12-13 Jonathan Matthew <jonathan@kaolin.wh9.net>
[rhythmbox.git] / metadata / sj-metadata-musicbrainz.c
blob6d34a54367f9ad4715fc4b19978af2af80102996
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 <stdlib.h>
37 #include <unistd.h>
39 #include <nautilus-burn-drive.h>
40 #ifndef NAUTILUS_BURN_CHECK_VERSION
41 #define NAUTILUS_BURN_CHECK_VERSION(a,b,c) FALSE
42 #endif
44 #if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
45 #include <nautilus-burn.h>
46 #endif
48 #ifndef HAVE_BURN_DRIVE_UNREF
49 #define nautilus_burn_drive_unref nautilus_burn_drive_free
50 #endif
52 #include "sj-metadata-musicbrainz.h"
53 #include "sj-structures.h"
54 #include "sj-error.h"
56 struct SjMetadataMusicbrainzPrivate {
57 GError *construct_error;
58 musicbrainz_t mb;
59 char *http_proxy;
60 int http_proxy_port;
61 char *cdrom;
62 /* TODO: remove and use an async queue or something l33t */
63 GList *albums;
64 GError *error;
67 static GError* mb_get_new_error (SjMetadata *metadata);
68 static void mb_set_cdrom (SjMetadata *metadata, const char* device);
69 static void mb_set_proxy (SjMetadata *metadata, const char* proxy);
70 static void mb_set_proxy_port (SjMetadata *metadata, const int port);
71 static void mb_list_albums (SjMetadata *metadata, GError **error);
72 static char *mb_get_submit_url (SjMetadata *metadata);
74 #define GCONF_MUSICBRAINZ_SERVER "/apps/sound-juicer/musicbrainz_server"
75 #define GCONF_PROXY_USE_PROXY "/system/http_proxy/use_http_proxy"
76 #define GCONF_PROXY_HOST "/system/http_proxy/host"
77 #define GCONF_PROXY_PORT "/system/http_proxy/port"
78 #define GCONF_PROXY_USE_AUTHENTICATION "/system/http_proxy/use_authentication"
79 #define GCONF_PROXY_USERNAME "/system/http_proxy/authentication_user"
80 #define GCONF_PROXY_PASSWORD "/system/http_proxy/authentication_password"
82 /**
83 * GObject methods
86 static GObjectClass *parent_class = NULL;
88 static void
89 sj_metadata_musicbrainz_finalize (GObject *object)
91 SjMetadataMusicbrainzPrivate *priv;
92 g_return_if_fail (object != NULL);
93 priv = SJ_METADATA_MUSICBRAINZ (object)->priv;
95 g_free (priv->http_proxy);
96 g_free (priv->cdrom);
97 mb_Delete (priv->mb);
98 g_free (priv);
101 static void
102 sj_metadata_musicbrainz_instance_init (GTypeInstance *instance, gpointer g_class)
104 GConfClient *gconf_client;
105 char *server_name = NULL;
106 SjMetadataMusicbrainz *self = (SjMetadataMusicbrainz*)instance;
107 self->priv = g_new0 (SjMetadataMusicbrainzPrivate, 1);
108 self->priv->construct_error = NULL;
109 self->priv->mb = mb_New ();
110 /* TODO: something. :/ */
111 if (!self->priv->mb) {
112 g_set_error (&self->priv->construct_error,
113 SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
114 _("Cannot create MusicBrainz client"));
115 return;
117 mb_UseUTF8 (self->priv->mb, TRUE);
119 gconf_client = gconf_client_get_default ();
120 server_name = gconf_client_get_string (gconf_client, GCONF_MUSICBRAINZ_SERVER, NULL);
121 if (server_name) {
122 g_strstrip (server_name);
124 if (server_name && strcmp (server_name, "") != 0) {
125 mb_SetServer (self->priv->mb, server_name, 80);
126 g_free (server_name);
129 /* Set the HTTP proxy */
130 if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_PROXY, NULL)) {
131 char *proxy_host = gconf_client_get_string (gconf_client, GCONF_PROXY_HOST, NULL);
132 mb_SetProxy (self->priv->mb, proxy_host,
133 gconf_client_get_int (gconf_client, GCONF_PROXY_PORT, NULL));
134 g_free (proxy_host);
135 if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_AUTHENTICATION, NULL)) {
136 #if HAVE_MB_SETPROXYCREDS
137 char *username = gconf_client_get_string (gconf_client, GCONF_PROXY_USERNAME, NULL);
138 char *password = gconf_client_get_string (gconf_client, GCONF_PROXY_PASSWORD, NULL);
139 mb_SetProxyCreds (self->priv->mb, username, password);
140 g_free (username);
141 g_free (password);
142 #else
143 g_warning ("mb_SetProxyCreds() not found, no proxy authorisation possible.");
144 #endif
148 g_object_unref (gconf_client);
150 if (g_getenv("MUSICBRAINZ_DEBUG")) {
151 mb_SetDebug (self->priv->mb, TRUE);
155 static void
156 metadata_interface_init (gpointer g_iface, gpointer iface_data)
158 SjMetadataClass *klass = (SjMetadataClass*)g_iface;
159 klass->get_new_error = mb_get_new_error;
160 klass->set_cdrom = mb_set_cdrom;
161 klass->set_proxy = mb_set_proxy;
162 klass->set_proxy_port = mb_set_proxy_port;
163 klass->list_albums = mb_list_albums;
164 klass->get_submit_url = mb_get_submit_url;
167 static void
168 sj_metadata_musicbrainz_class_init (SjMetadataMusicbrainzClass *class)
170 GObjectClass *object_class;
171 parent_class = g_type_class_peek_parent (class);
172 object_class = (GObjectClass*) class;
173 object_class->finalize = sj_metadata_musicbrainz_finalize;
176 GType
177 sj_metadata_musicbrainz_get_type (void)
179 static GType type = 0;
180 if (type == 0) {
181 static const GTypeInfo info = {
182 sizeof (SjMetadataMusicbrainzClass),
183 NULL,
184 NULL,
185 (GClassInitFunc)sj_metadata_musicbrainz_class_init,
186 NULL,
187 NULL,
188 sizeof (SjMetadataMusicbrainz),
190 sj_metadata_musicbrainz_instance_init,
191 NULL
193 static const GInterfaceInfo metadata_i_info = {
194 (GInterfaceInitFunc) metadata_interface_init,
195 NULL, NULL
197 type = g_type_register_static (G_TYPE_OBJECT, "SjMetadataMusicBrainzClass", &info, 0);
198 g_type_add_interface_static (type, SJ_TYPE_METADATA, &metadata_i_info);
200 return type;
203 GObject *
204 sj_metadata_musicbrainz_new (void)
206 return g_object_new (sj_metadata_musicbrainz_get_type (), NULL);
210 * Private methods
213 #define BYTES_PER_SECTOR 2352
214 #define BYTES_PER_SECOND (44100 / 8) / 16 / 2
216 static int
217 get_duration_from_sectors (int sectors)
219 return (sectors * BYTES_PER_SECTOR / BYTES_PER_SECOND);
222 static GList*
223 get_offline_track_listing(SjMetadata *metadata, GError **error)
225 SjMetadataMusicbrainzPrivate *priv;
226 GList* list = NULL;
227 AlbumDetails *album;
228 TrackDetails *track;
229 int num_tracks, i;
231 g_return_val_if_fail (metadata != NULL, NULL);
232 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
234 if (!mb_Query (priv->mb, MBQ_GetCDTOC)) {
235 char message[255];
236 mb_GetQueryError (priv->mb, message, 255);
237 g_set_error (error,
238 SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
239 _("Cannot read CD: %s"), message);
240 return NULL;
243 num_tracks = mb_GetResultInt (priv->mb, MBE_TOCGetLastTrack);
245 album = g_new0 (AlbumDetails, 1);
246 album->artist = g_strdup (_("Unknown Artist"));
247 album->title = g_strdup (_("Unknown Title"));
248 album->genre = NULL;
249 for (i = 1; i <= num_tracks; i++) {
250 track = g_new0 (TrackDetails, 1);
251 track->album = album;
252 track->number = i;
253 track->title = g_strdup_printf (_("Track %d"), i);
254 track->artist = g_strdup (album->artist);
255 track->duration = get_duration_from_sectors (mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, i+1));
256 album->tracks = g_list_append (album->tracks, track);
257 album->number++;
259 return g_list_append (list, album);
262 static gboolean
263 fire_signal_idle (SjMetadataMusicbrainz *m)
265 g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (m), FALSE);
266 g_signal_emit_by_name (G_OBJECT (m), "metadata", m->priv->albums, m->priv->error);
267 return FALSE;
271 * Virtual methods
274 static GError*
275 mb_get_new_error (SjMetadata *metadata)
277 GError *error = NULL;
278 if (metadata == NULL || SJ_METADATA_MUSICBRAINZ (metadata)->priv == NULL) {
279 g_set_error (&error,
280 SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
281 _("MusicBrainz metadata object is not valid. This is bad, check your console for errors."));
282 return error;
284 return SJ_METADATA_MUSICBRAINZ (metadata)->priv->construct_error;
287 static void
288 mb_set_cdrom (SjMetadata *metadata, const char* device)
290 SjMetadataMusicbrainzPrivate *priv;
291 g_return_if_fail (metadata != NULL);
292 g_return_if_fail (device != NULL);
293 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
295 if (priv->cdrom) {
296 g_free (priv->cdrom);
298 priv->cdrom = g_strdup (device);
299 mb_SetDevice (priv->mb, priv->cdrom);
302 static void
303 mb_set_proxy (SjMetadata *metadata, const char* proxy)
305 SjMetadataMusicbrainzPrivate *priv;
306 g_return_if_fail (metadata != NULL);
307 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
309 if (proxy == NULL) {
310 proxy = "";
312 if (priv->http_proxy) {
313 g_free (priv->http_proxy);
315 priv->http_proxy = g_strdup (proxy);
316 mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
319 static void
320 mb_set_proxy_port (SjMetadata *metadata, const int port)
322 SjMetadataMusicbrainzPrivate *priv;
323 g_return_if_fail (metadata != NULL);
324 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
326 priv->http_proxy_port = port;
327 mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
330 /* Data imported from FreeDB is horrendeous for compilations,
331 * Try to split the 'Various' artist */
332 static void
333 artist_and_title_from_title (TrackDetails *track, gpointer data)
335 char *slash, **split;
337 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') {
338 track->title = g_strdup (data);
339 return;
342 slash = strstr (data, " / ");
343 if (slash == NULL) {
344 track->title = g_strdup (data);
345 return;
347 split = g_strsplit (data, " / ", 2);
348 track->artist = g_strdup (split[0]);
349 track->title = g_strdup (split[1]);
350 g_strfreev (split);
353 #if WITH_CACHE
355 * Write the RDF in the MusicBrainz object to the file specified.
357 static void
358 cache_rdf (musicbrainz_t mb, const char *filename)
360 GError *error = NULL;
361 int len;
362 char *path, *rdf;
364 g_assert (mb != NULL);
365 g_assert (filename != NULL);
367 /* Create the folder for the file */
368 path = g_path_get_dirname (filename);
369 g_mkdir_with_parents (path, 0755); /* Handle errors in set_contents() */
370 g_free (path);
372 /* How much data is there to save? */
373 len = mb_GetResultRDFLen (mb);
374 rdf = g_malloc0 (len);
376 /* Get the RDF and save it */
377 mb_GetResultRDF (mb, rdf, len);
378 if (!g_file_set_contents (filename, rdf, len, &error)) {
379 g_warning ("Cannot write cache file %s: %s", filename, error->message);
380 g_error_free (error);
383 g_free (rdf);
387 * Load into the MusicBrainz object the RDF from the specified cache file if it
388 * exists and is valid then return TRUE, otherwise return FALSE.
390 static gboolean
391 get_cached_rdf (musicbrainz_t mb, const char *cachepath)
393 gboolean ret = FALSE;
394 GError *error = NULL;
395 char *rdf = NULL;
397 g_assert (mb != NULL);
398 g_assert (cachepath != NULL);
400 if (!g_file_test (cachepath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
401 goto done;
403 /* Cache file exists, open it */
404 if (!g_file_get_contents (cachepath, &rdf, NULL, &error)) {
405 g_warning ("Cannot open cache file %s: %s", cachepath, error->message);
406 g_error_free (error);
407 goto done;
410 /* Set the RDF */
411 if (mb_SetResultRDF (mb, rdf))
412 ret = TRUE;
414 done:
415 g_free (rdf);
416 return ret;
418 #else
419 static gboolean
420 get_cached_rdf (musicbrainz_t mb, const char *cachepath)
422 return FALSE;
424 static void
425 cache_rdf (musicbrainz_t mb, const char *filename) {
427 #endif
430 * Fill the MusicBrainz object with RDF. Basically get the CD Index and check
431 * the local cache, if that fails then lookup the data online.
433 static void
434 get_rdf (SjMetadata *metadata)
436 SjMetadataMusicbrainzPrivate *priv;
437 char data[256];
438 char *cdindex = NULL, *cachepath = NULL;
440 g_assert (metadata != NULL);
442 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
444 #if WITH_CACHE
445 /* Get the Table of Contents */
446 if (!mb_Query (priv->mb, MBQ_GetCDTOC)) {
447 mb_GetQueryError (priv->mb, data, sizeof (data));
448 g_print (_("This CD could not be queried: %s\n"), data);
449 return;
452 /* Extract the CD Index */
453 if (!mb_GetResultData(priv->mb, MBE_TOCGetCDIndexId, data, sizeof (data))) {
454 mb_GetQueryError (priv->mb, data, sizeof (data));
455 g_print (_("This CD could not be queried: %s\n"), data);
456 return;
458 cdindex = g_strdup (data);
460 cachepath = g_build_filename (g_get_home_dir (), ".gnome2", "sound-juicer", "cache", cdindex, NULL);
461 #endif
463 if (!get_cached_rdf (priv->mb, cachepath)) {
464 /* Don't re-use the CD Index as that doesn't send enough data to the server.
465 By doing this we also pass track lengths, which can be proxied to FreeDB
466 if required. */
467 if (!mb_Query (priv->mb, MBQ_GetCDInfo)) {
468 mb_GetQueryError (priv->mb, data, sizeof (data));
469 g_print (_("This CD could not be queried: %s\n"), data);
470 goto done;
472 cache_rdf (priv->mb, cachepath);
475 done:
476 g_free (cachepath);
477 g_free (cdindex);
480 static NautilusBurnMediaType
481 get_drive_media_type (SjMetadata *metadata)
483 SjMetadataMusicbrainzPrivate *priv;
484 NautilusBurnDrive *drive;
485 NautilusBurnMediaType type;
486 #if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
487 NautilusBurnDriveMonitor *monitor;
488 #endif
490 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
491 #if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
492 if (! nautilus_burn_initialized ()) {
493 nautilus_burn_init ();
495 monitor = nautilus_burn_get_drive_monitor ();
496 drive = nautilus_burn_drive_monitor_get_drive_for_device (monitor, priv->cdrom);
497 #else
498 drive = nautilus_burn_drive_new_from_path (priv->cdrom);
499 #endif
501 if (drive == NULL) {
502 return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
504 type = nautilus_burn_drive_get_media_type (drive);
505 nautilus_burn_drive_unref (drive);
506 return type;
509 static gpointer
510 lookup_cd (SjMetadata *metadata)
512 /** The size of the buffer used in MusicBrainz lookups */
513 SjMetadataMusicbrainzPrivate *priv;
514 GList *albums = NULL;
515 GList *al, *tl;
516 char data[256];
517 int num_albums, i, j;
518 NautilusBurnMediaType type;
520 /* TODO: fire error signal */
521 g_return_val_if_fail (metadata != NULL, NULL);
522 g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata), NULL);
523 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
524 g_return_val_if_fail (priv->cdrom != NULL, NULL);
525 priv->error = NULL; /* TODO: hack */
527 type = get_drive_media_type (metadata);
529 if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
530 char *msg;
531 SjError err;
533 if (access (priv->cdrom, W_OK) == 0) {
534 msg = g_strdup_printf (_("Device '%s' does not contain any media"), priv->cdrom);
535 err = SJ_ERROR_CD_NO_MEDIA;
536 } else {
537 msg = g_strdup_printf (_("Device '%s' could not be opened. Check the access permissions on the device."), priv->cdrom);
538 err = SJ_ERROR_CD_PERMISSION_ERROR;
540 priv->error = g_error_new (SJ_ERROR, err, _("Cannot read CD: %s"), msg);
541 g_free (msg);
543 priv->albums = NULL;
544 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
545 return NULL;
548 get_rdf (metadata);
550 num_albums = mb_GetResultInt(priv->mb, MBE_GetNumAlbums);
551 if (num_albums < 1) {
552 priv->albums = get_offline_track_listing (metadata, &(priv->error));
553 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
554 return priv->albums;
557 for (i = 1; i <= num_albums; i++) {
558 int num_tracks;
559 AlbumDetails *album;
561 mb_Select1(priv->mb, MBS_SelectAlbum, i);
562 album = g_new0 (AlbumDetails, 1);
564 if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumId, data, sizeof (data))) {
565 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
566 album->album_id = g_strdup (data);
569 if (mb_GetResultData (priv->mb, MBE_AlbumGetAlbumArtistId, data, sizeof (data))) {
570 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
571 album->artist_id = g_strdup (data);
572 if (g_ascii_strncasecmp (MBI_VARIOUS_ARTIST_ID, data, 64) == 0) {
573 album->artist = g_strdup (_("Various"));
574 } else {
575 if (*data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, sizeof (data), 1)) {
576 album->artist = g_strdup (data);
577 } else {
578 album->artist = g_strdup (_("Unknown Artist"));
580 if (*data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistSortName, data, sizeof (data), 1)) {
581 album->artist_sortname = g_strdup (data);
586 if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumName, data, sizeof (data))) {
587 album->title = g_strdup (data);
588 } else {
589 album->title = g_strdup (_("Unknown Title"));
593 int num_releases;
594 num_releases = mb_GetResultInt (priv->mb, MBE_AlbumGetNumReleaseDates);
595 if (num_releases > 0) {
596 mb_Select1(priv->mb, MBS_SelectReleaseDate, 1);
597 if (mb_GetResultData(priv->mb, MBE_ReleaseGetDate, data, sizeof (data))) {
598 int matched, year=1, month=1, day=1;
599 matched = sscanf(data, "%u-%u-%u", &year, &month, &day);
600 if (matched >= 1) {
601 album->release_date = g_date_new_dmy ((day == 0) ? 1 : day, (month == 0) ? 1 : month, year);
604 mb_Select(priv->mb, MBS_Back);
608 num_tracks = mb_GetResultInt(priv->mb, MBE_AlbumGetNumTracks);
609 if (num_tracks < 1) {
610 g_free (album->artist);
611 g_free (album->artist_sortname);
612 g_free (album->title);
613 g_free (album);
614 g_warning (_("Incomplete metadata for this CD"));
615 priv->albums = get_offline_track_listing (metadata, &(priv->error));
616 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
617 return priv->albums;
620 for (j = 1; j <= num_tracks; j++) {
621 TrackDetails *track;
622 track = g_new0 (TrackDetails, 1);
624 track->album = album;
626 track->number = j; /* replace with number lookup? */
628 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackId, data, sizeof (data), j)) {
629 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
630 track->track_id = g_strdup (data);
633 if (mb_GetResultData1(priv->mb, MBE_AlbumGetArtistId, data, sizeof (data), j)) {
634 mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
635 track->artist_id = g_strdup (data);
638 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackName, data, sizeof (data), j)) {
639 if (track->artist_id != NULL) {
640 artist_and_title_from_title (track, data);
641 } else {
642 track->title = g_strdup (data);
646 if (track->artist == NULL && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, sizeof (data), j)) {
647 track->artist = g_strdup (data);
650 if (mb_GetResultData1(priv->mb, MBE_AlbumGetArtistSortName, data, sizeof (data), j)) {
651 track->artist_sortname = g_strdup (data);
654 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackDuration, data, sizeof (data), j)) {
655 track->duration = atoi (data) / 1000;
658 album->tracks = g_list_append (album->tracks, track);
659 album->number++;
662 albums = g_list_append (albums, album);
664 mb_Select (priv->mb, MBS_Rewind);
667 /* For each album, we need to insert the duration data if necessary
668 * We need to query this here because otherwise we would flush the
669 * data queried from the server */
670 /* TODO: scan for 0 duration before doing the query to avoid another lookup if
671 we don't need to do it */
672 mb_Query (priv->mb, MBQ_GetCDTOC);
673 for (al = albums; al; al = al->next) {
674 AlbumDetails *album = al->data;
676 j = 1;
677 for (tl = album->tracks; tl; tl = tl->next) {
678 TrackDetails *track = tl->data;
679 int sectors;
681 if (track->duration == 0) {
682 sectors = mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, j+1);
683 track->duration = get_duration_from_sectors (sectors);
685 j++;
689 priv->albums = albums;
690 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
691 return albums;
694 static void
695 mb_list_albums (SjMetadata *metadata, GError **error)
697 GThread *thread;
699 g_return_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata));
701 thread = g_thread_create ((GThreadFunc)lookup_cd, metadata, TRUE, error);
702 if (thread == NULL) {
703 g_set_error (error,
704 SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
705 _("Could not create CD lookup thread"));
706 return;
710 static char *
711 mb_get_submit_url (SjMetadata *metadata)
713 SjMetadataMusicbrainzPrivate *priv;
714 char url[1025];
716 g_return_val_if_fail (metadata != NULL, NULL);
718 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
720 if (mb_GetWebSubmitURL(priv->mb, url, 1024)) {
721 return g_strdup(url);
722 } else {
723 return NULL;