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.
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>
35
#include "eel-gconf-extensions.h"
36
#include "sj-metadata-musicbrainz.h"
37
#include "sj-structures.h"
41
#define GCONF_MUSICBRAINZ_SERVER "/apps/sound-juicer" "/musicbrainz_server"
44
struct SjMetadataMusicbrainzPrivate {
45
GError *construct_error;
50
/* TODO: remove and use an async queue or something l33t */
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);
65
static GObjectClass *parent_class = NULL;
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);
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"));
95
mb_UseUTF8 (self->priv->mb, TRUE);
97
server_name = eel_gconf_get_string (GCONF_MUSICBRAINZ_SERVER);
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);
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;
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;
133
sj_metadata_musicbrainz_get_type (void)
135
static GType type = 0;
137
static const GTypeInfo info = {
138
sizeof (SjMetadataMusicbrainzClass),
141
(GClassInitFunc)sj_metadata_musicbrainz_class_init,
144
sizeof (SjMetadataMusicbrainz),
146
sj_metadata_musicbrainz_instance_init,
149
static const GInterfaceInfo metadata_i_info = {
150
(GInterfaceInitFunc) metadata_interface_init,
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);
160
sj_metadata_musicbrainz_new (void)
162
return g_object_new (sj_metadata_musicbrainz_get_type (), NULL);
169
#define BYTES_PER_SECTOR 2352
170
#define BYTES_PER_SECOND (44100 / 8) / 16 / 2
173
get_duration_from_sectors (int sectors)
175
return (sectors * BYTES_PER_SECTOR / BYTES_PER_SECOND);
179
get_offline_track_listing(SjMetadata *metadata, GError **error)
181
SjMetadataMusicbrainzPrivate *priv;
187
g_return_val_if_fail (metadata != NULL, NULL);
188
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
190
if (!mb_Query (priv->mb, MBQ_GetCDTOC)) {
192
mb_GetQueryError (priv->mb, message, 255);
194
SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
195
_("Cannot read CD: %s"), message);
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"));
205
for (i = 1; i <= num_tracks; i++) {
206
track = g_new0 (TrackDetails, 1);
207
track->album = album;
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);
215
album->tracks = g_list_reverse (album->tracks);
217
return g_list_append (list, album);
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);
233
mb_get_new_error (SjMetadata *metadata)
235
GError *error = NULL;
236
if (metadata == NULL || SJ_METADATA_MUSICBRAINZ (metadata)->priv == NULL) {
238
SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
239
_("MusicBrainz metadata object is not valid. This is bad, check your console for errors."));
242
return SJ_METADATA_MUSICBRAINZ (metadata)->priv->construct_error;
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;
254
g_free (priv->cdrom);
256
priv->cdrom = g_strdup (device);
257
mb_SetDevice (priv->mb, priv->cdrom);
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;
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);
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);
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;
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) {
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;
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);
323
g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
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);
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);
343
for (i = 1; i <= num_albums; i++) {
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"));
361
if (data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, MB_BUFFER_SIZE, 1)) {
362
album->artist = g_strdup (data);
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);
372
album->title = g_strdup (_("Unknown Title"));
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);
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);
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);
402
for (j = 1; j <= num_tracks; j++) {
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);
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;
448
for (tl = album->tracks; tl; tl = tl->next) {
449
TrackDetails *track = tl->data;
452
if (track->duration == 0) {
453
sectors = mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, j+1);
454
track->duration = get_duration_from_sectors (sectors);
459
priv->albums = albums;
460
g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
465
mb_list_albums (SjMetadata *metadata, GError **error)
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) {
474
SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
475
_("Could not create CD lookup thread"));