Updated Macedonian Translation <arangela@cvs.gnome.org>
[rhythmbox.git] / metadata / sj-metadata-musicbrainz.c
blob8a6f5622abb1a89d8bd7da2ea41e4e7424456664
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 #include <string.h>
22 #include <glib-object.h>
23 #include <glib/gi18n.h>
24 #include <glib/gerror.h>
25 #include <glib/glist.h>
26 #include <glib/gstrfuncs.h>
27 #include <glib/gmessages.h>
28 #include <musicbrainz/queries.h>
29 #include <musicbrainz/mb_c.h>
30 #include <nautilus-burn-drive.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <stdio.h>
35 #include "eel-gconf-extensions.h"
36 #include "sj-metadata-musicbrainz.h"
37 #include "sj-structures.h"
38 #include "sj-error.h"
41 #define GCONF_MUSICBRAINZ_SERVER "/apps/sound-juicer" "/musicbrainz_server"
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);
61 /**
62 * GObject methods
65 static GObjectClass *parent_class = NULL;
67 static void
68 sj_metadata_musicbrainz_finalize (GObject *object)
70 SjMetadataMusicbrainzPrivate *priv;
71 g_return_if_fail (object != NULL);
72 priv = SJ_METADATA_MUSICBRAINZ (object)->priv;
74 g_free (priv->http_proxy);
75 g_free (priv->cdrom);
76 mb_Delete (priv->mb);
77 g_free (priv);
80 static void
81 sj_metadata_musicbrainz_instance_init (GTypeInstance *instance, gpointer g_class)
83 char *server_name = NULL;
84 SjMetadataMusicbrainz *self = (SjMetadataMusicbrainz*)instance;
85 self->priv = g_new0 (SjMetadataMusicbrainzPrivate, 1);
86 self->priv->construct_error = NULL;
87 self->priv->mb = mb_New ();
88 /* TODO: something. :/ */
89 if (!self->priv->mb) {
90 g_set_error (&self->priv->construct_error,
91 SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
92 _("Cannot create MusicBrainz client"));
93 return;
95 mb_UseUTF8 (self->priv->mb, TRUE);
97 server_name = eel_gconf_get_string (GCONF_MUSICBRAINZ_SERVER);
98 if (server_name) {
99 g_strstrip (server_name);
101 if (server_name && strcmp (server_name, "") != 0) {
102 mb_SetServer (self->priv->mb, server_name, 80);
103 g_free (server_name);
106 /* TODO: optimal setting? mb_SetDepth (self->priv->mb, 1); */
107 if (g_getenv("MUSICBRAINZ_DEBUG")) {
108 mb_SetDebug (self->priv->mb, TRUE);
112 static void
113 metadata_interface_init (gpointer g_iface, gpointer iface_data)
115 SjMetadataClass *klass = (SjMetadataClass*)g_iface;
116 klass->get_new_error = mb_get_new_error;
117 klass->set_cdrom = mb_set_cdrom;
118 klass->set_proxy = mb_set_proxy;
119 klass->set_proxy_port = mb_set_proxy_port;
120 klass->list_albums = mb_list_albums;
123 static void
124 sj_metadata_musicbrainz_class_init (SjMetadataMusicbrainzClass *class)
126 GObjectClass *object_class;
127 parent_class = g_type_class_peek_parent (class);
128 object_class = (GObjectClass*) class;
129 object_class->finalize = sj_metadata_musicbrainz_finalize;
132 GType
133 sj_metadata_musicbrainz_get_type (void)
135 static GType type = 0;
136 if (type == 0) {
137 static const GTypeInfo info = {
138 sizeof (SjMetadataMusicbrainzClass),
139 NULL,
140 NULL,
141 (GClassInitFunc)sj_metadata_musicbrainz_class_init,
142 NULL,
143 NULL,
144 sizeof (SjMetadataMusicbrainz),
146 sj_metadata_musicbrainz_instance_init,
147 NULL
149 static const GInterfaceInfo metadata_i_info = {
150 (GInterfaceInitFunc) metadata_interface_init,
151 NULL, NULL
153 type = g_type_register_static (G_TYPE_OBJECT, "SjMetadataMusicBrainzClass", &info, 0);
154 g_type_add_interface_static (type, SJ_TYPE_METADATA, &metadata_i_info);
156 return type;
159 GObject *
160 sj_metadata_musicbrainz_new (void)
162 return g_object_new (sj_metadata_musicbrainz_get_type (), NULL);
166 * Private methods
169 #define BYTES_PER_SECTOR 2352
170 #define BYTES_PER_SECOND (44100 / 8) / 16 / 2
172 static int
173 get_duration_from_sectors (int sectors)
175 return (sectors * BYTES_PER_SECTOR / BYTES_PER_SECOND);
178 static GList*
179 get_offline_track_listing(SjMetadata *metadata, GError **error)
181 SjMetadataMusicbrainzPrivate *priv;
182 GList* list = NULL;
183 AlbumDetails *album;
184 TrackDetails *track;
185 int num_tracks, i;
187 g_return_val_if_fail (metadata != NULL, NULL);
188 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
190 if (!mb_Query (priv->mb, MBQ_GetCDTOC)) {
191 char message[255];
192 mb_GetQueryError (priv->mb, message, 255);
193 g_set_error (error,
194 SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
195 _("Cannot read CD: %s"), message);
196 return NULL;
199 num_tracks = mb_GetResultInt (priv->mb, MBE_TOCGetLastTrack);
201 album = g_new0 (AlbumDetails, 1);
202 album->artist = g_strdup (_("Unknown Artist"));
203 album->title = g_strdup (_("Unknown Title"));
204 album->genre = NULL;
205 for (i = 1; i <= num_tracks; i++) {
206 track = g_new0 (TrackDetails, 1);
207 track->album = album;
208 track->number = i;
209 track->title = g_strdup_printf (_("Track %d"), i);
210 track->artist = g_strdup (album->artist);
211 track->duration = get_duration_from_sectors (mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, i+1));
212 album->tracks = g_list_prepend (album->tracks, track);
213 album->number++;
215 album->tracks = g_list_reverse (album->tracks);
217 return g_list_append (list, album);
220 static gboolean
221 fire_signal_idle (SjMetadataMusicbrainz *m)
223 g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (m), FALSE);
224 g_signal_emit_by_name (G_OBJECT (m), "metadata", m->priv->albums, m->priv->error);
225 return FALSE;
229 * Virtual methods
232 static GError*
233 mb_get_new_error (SjMetadata *metadata)
235 GError *error = NULL;
236 if (metadata == NULL || SJ_METADATA_MUSICBRAINZ (metadata)->priv == NULL) {
237 g_set_error (&error,
238 SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
239 _("MusicBrainz metadata object is not valid. This is bad, check your console for errors."));
240 return error;
242 return SJ_METADATA_MUSICBRAINZ (metadata)->priv->construct_error;
245 static void
246 mb_set_cdrom (SjMetadata *metadata, const char* device)
248 SjMetadataMusicbrainzPrivate *priv;
249 g_return_if_fail (metadata != NULL);
250 g_return_if_fail (device != NULL);
251 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
253 if (priv->cdrom) {
254 g_free (priv->cdrom);
256 priv->cdrom = g_strdup (device);
257 mb_SetDevice (priv->mb, priv->cdrom);
260 static void
261 mb_set_proxy (SjMetadata *metadata, const char* proxy)
263 SjMetadataMusicbrainzPrivate *priv;
264 g_return_if_fail (metadata != NULL);
265 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
267 if (proxy == NULL) {
268 proxy = "";
270 if (priv->http_proxy) {
271 g_free (priv->http_proxy);
273 priv->http_proxy = g_strdup (proxy);
274 mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
277 static void
278 mb_set_proxy_port (SjMetadata *metadata, const int port)
280 SjMetadataMusicbrainzPrivate *priv;
281 g_return_if_fail (metadata != NULL);
282 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
284 priv->http_proxy_port = port;
285 mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
288 static gpointer
289 lookup_cd (SjMetadata *metadata)
291 /** The size of the buffer used in MusicBrainz lookups */
292 #define MB_BUFFER_SIZE 256
293 SjMetadataMusicbrainzPrivate *priv;
294 GList *albums = NULL;
295 GList *al, *tl;
296 char data[MB_BUFFER_SIZE];
297 int num_albums, i, j;
298 NautilusBurnMediaType type;
300 /* TODO: fire error signal */
301 g_return_val_if_fail (metadata != NULL, NULL);
302 g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata), NULL);
303 priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
304 g_return_val_if_fail (priv->cdrom != NULL, NULL);
305 priv->error = NULL; /* TODO: hack */
307 type = nautilus_burn_drive_get_media_type_from_path (priv->cdrom);
308 if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
309 char *msg;
310 SjError err;
312 if (access (priv->cdrom, W_OK) == 0) {
313 msg = g_strdup_printf (_("Device '%s' does not contain any media"), priv->cdrom);
314 err = SJ_ERROR_CD_NO_MEDIA;
315 } else {
316 msg = g_strdup_printf (_("Device '%s' could not be opened. Check the access permissions on the device."), priv->cdrom);
317 err = SJ_ERROR_CD_PERMISSION_ERROR;
319 priv->error = g_error_new (SJ_ERROR, err, _("Cannot read CD: %s"), msg);
320 g_free (msg);
322 priv->albums = NULL;
323 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
324 return NULL;
327 if (!mb_Query (priv->mb, MBQ_GetCDInfo)) {
328 mb_GetQueryError (priv->mb, data, MB_BUFFER_SIZE);
329 g_print (_("This CD could not be queried: %s\n"), data);
330 priv->albums = get_offline_track_listing (metadata, &(priv->error));
331 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
332 return priv->albums;
335 num_albums = mb_GetResultInt(priv->mb, MBE_GetNumAlbums);
336 if (num_albums < 1) {
337 g_print (_("This CD was not found.\n"));
338 priv->albums = get_offline_track_listing (metadata, &(priv->error));
339 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
340 return priv->albums;
343 for (i = 1; i <= num_albums; i++) {
344 int num_tracks;
345 AlbumDetails *album;
347 mb_Select1(priv->mb, MBS_SelectAlbum, i);
348 album = g_new0 (AlbumDetails, 1);
350 if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumId, data, MB_BUFFER_SIZE)) {
351 mb_GetIDFromURL (priv->mb, data, data, MB_BUFFER_SIZE);
352 album->album_id = g_strdup (data);
355 if (mb_GetResultData (priv->mb, MBE_AlbumGetAlbumArtistId, data, MB_BUFFER_SIZE)) {
356 mb_GetIDFromURL (priv->mb, data, data, MB_BUFFER_SIZE);
357 album->artist_id = g_strdup (data);
358 if (g_ascii_strncasecmp (MBI_VARIOUS_ARTIST_ID, data, 64) == 0) {
359 album->artist = g_strdup (_("Various"));
360 } else {
361 if (data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, MB_BUFFER_SIZE, 1)) {
362 album->artist = g_strdup (data);
363 } else {
364 album->artist = g_strdup (_("Unknown Artist"));
369 if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumName, data, MB_BUFFER_SIZE)) {
370 album->title = g_strdup (data);
371 } else {
372 album->title = g_strdup (_("Unknown Title"));
376 int num_releases;
377 num_releases = mb_GetResultInt (priv->mb, MBE_AlbumGetNumReleaseDates);
378 if (num_releases > 0) {
379 mb_Select1(priv->mb, MBS_SelectReleaseDate, 1);
380 if (mb_GetResultData(priv->mb, MBE_ReleaseGetDate, data, MB_BUFFER_SIZE)) {
381 int matched, year=1, month=1, day=1;
382 matched = sscanf(data, "%u-%u-%u", &year, &month, &day);
383 if (matched > 1) {
384 album->release_date = g_date_new_dmy (day, month, year);
387 mb_Select(priv->mb, MBS_Back);
391 num_tracks = mb_GetResultInt(priv->mb, MBE_AlbumGetNumTracks);
392 if (num_tracks < 1) {
393 g_free (album->artist);
394 g_free (album->title);
395 g_free (album);
396 g_print (_("Incomplete metadata for this CD.\n"));
397 priv->albums = get_offline_track_listing (metadata, &(priv->error));
398 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
399 return priv->albums;
402 for (j = 1; j <= num_tracks; j++) {
403 TrackDetails *track;
404 track = g_new0 (TrackDetails, 1);
406 track->album = album;
408 track->number = j; /* replace with number lookup? */
410 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackId, data, MB_BUFFER_SIZE, j)) {
411 mb_GetIDFromURL (priv->mb, data, data, MB_BUFFER_SIZE);
412 track->track_id = g_strdup (data);
415 if (mb_GetResultData1(priv->mb, MBE_AlbumGetArtistId, data, MB_BUFFER_SIZE, j)) {
416 mb_GetIDFromURL (priv->mb, data, data, MB_BUFFER_SIZE);
417 track->artist_id = g_strdup (data);
420 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackName, data, MB_BUFFER_SIZE, j)) {
421 track->title = g_strdup (data);
424 if (mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, MB_BUFFER_SIZE, j)) {
425 track->artist = g_strdup (data);
428 if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackDuration, data, MB_BUFFER_SIZE, j)) {
429 track->duration = atoi (data) / 1000;
432 album->tracks = g_list_prepend (album->tracks, track);
433 album->number++;
435 album->tracks = g_list_reverse (album->tracks);
437 albums = g_list_append (albums, album);
440 /* For each album, we need to insert the duration data if necessary
441 * We need to query this here because otherwise we would flush the
442 * data queried from the server */
443 mb_Query (priv->mb, MBQ_GetCDTOC);
444 for (al = albums; al; al = al->next) {
445 AlbumDetails *album = al->data;
447 j = 1;
448 for (tl = album->tracks; tl; tl = tl->next) {
449 TrackDetails *track = tl->data;
450 int sectors;
452 if (track->duration == 0) {
453 sectors = mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, j+1);
454 track->duration = get_duration_from_sectors (sectors);
456 j++;
459 priv->albums = albums;
460 g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
461 return albums;
464 static void
465 mb_list_albums (SjMetadata *metadata, GError **error)
467 GThread *thread;
469 g_return_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata));
471 thread = g_thread_create ((GThreadFunc)lookup_cd, metadata, TRUE, error);
472 if (thread == NULL) {
473 g_set_error (error,
474 SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
475 _("Could not create CD lookup thread"));
476 return;