~ubuntu-branches/ubuntu/utopic/rhythmbox/utopic-proposed

« back to all changes in this revision

Viewing changes to metadata/sj-metadata-musicbrainz.c

Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * sj-metadata-musicbrainz.c
 
3
 * Copyright (C) 2003 Ross Burton <ross@burtonini.com>
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
19
 */
 
20
 
 
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>
 
34
 
 
35
#include "eel-gconf-extensions.h"
 
36
#include "sj-metadata-musicbrainz.h"
 
37
#include "sj-structures.h"
 
38
#include "sj-error.h"
 
39
 
 
40
 
 
41
#define GCONF_MUSICBRAINZ_SERVER "/apps/sound-juicer" "/musicbrainz_server"
 
42
 
 
43
 
 
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;
 
53
};
 
54
 
 
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
 
 
61
/**
 
62
 * GObject methods
 
63
 */
 
64
 
 
65
static GObjectClass *parent_class = NULL;
 
66
 
 
67
static void
 
68
sj_metadata_musicbrainz_finalize (GObject *object)
 
69
{
 
70
  SjMetadataMusicbrainzPrivate *priv;
 
71
  g_return_if_fail (object != NULL);
 
72
  priv = SJ_METADATA_MUSICBRAINZ (object)->priv;
 
73
 
 
74
  g_free (priv->http_proxy);
 
75
  g_free (priv->cdrom);
 
76
  mb_Delete (priv->mb);
 
77
  g_free (priv);
 
78
}
 
79
 
 
80
static void
 
81
sj_metadata_musicbrainz_instance_init (GTypeInstance *instance, gpointer g_class)
 
82
{
 
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;
 
94
  }
 
95
  mb_UseUTF8 (self->priv->mb, TRUE);
 
96
 
 
97
  server_name = eel_gconf_get_string (GCONF_MUSICBRAINZ_SERVER);
 
98
  if (server_name) {
 
99
    g_strstrip (server_name);
 
100
  }
 
101
  if (server_name && strcmp (server_name, "") != 0) {
 
102
    mb_SetServer (self->priv->mb, server_name, 80);
 
103
    g_free (server_name);
 
104
  }
 
105
 
 
106
  /* TODO: optimal setting? mb_SetDepth (self->priv->mb, 1); */
 
107
  if (g_getenv("MUSICBRAINZ_DEBUG")) {
 
108
    mb_SetDebug (self->priv->mb, TRUE);
 
109
  }
 
110
}
 
111
 
 
112
static void
 
113
metadata_interface_init (gpointer g_iface, gpointer iface_data)
 
114
{
 
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;
 
121
}
 
122
 
 
123
static void
 
124
sj_metadata_musicbrainz_class_init (SjMetadataMusicbrainzClass *class)
 
125
{
 
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;
 
130
}
 
131
 
 
132
GType
 
133
sj_metadata_musicbrainz_get_type (void)
 
134
{
 
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),
 
145
      0,
 
146
      sj_metadata_musicbrainz_instance_init,
 
147
      NULL
 
148
    };
 
149
    static const GInterfaceInfo metadata_i_info = {
 
150
      (GInterfaceInitFunc) metadata_interface_init,
 
151
      NULL, NULL
 
152
    };
 
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);
 
155
  }
 
156
  return type;
 
157
}
 
158
 
 
159
GObject *
 
160
sj_metadata_musicbrainz_new (void)
 
161
{
 
162
  return g_object_new (sj_metadata_musicbrainz_get_type (), NULL);
 
163
}
 
164
 
 
165
/**
 
166
 * Private methods
 
167
 */
 
168
 
 
169
#define BYTES_PER_SECTOR 2352
 
170
#define BYTES_PER_SECOND (44100 / 8) / 16 / 2
 
171
 
 
172
static int
 
173
get_duration_from_sectors (int sectors)
 
174
{
 
175
  return (sectors * BYTES_PER_SECTOR / BYTES_PER_SECOND);
 
176
}
 
177
 
 
178
static GList*
 
179
get_offline_track_listing(SjMetadata *metadata, GError **error)
 
180
{
 
181
  SjMetadataMusicbrainzPrivate *priv;
 
182
  GList* list = NULL;
 
183
  AlbumDetails *album;
 
184
  TrackDetails *track;
 
185
  int num_tracks, i;
 
186
 
 
187
  g_return_val_if_fail (metadata != NULL, NULL);
 
188
  priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
 
189
 
 
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;
 
197
  }
 
198
  
 
199
  num_tracks = mb_GetResultInt (priv->mb, MBE_TOCGetLastTrack);
 
200
 
 
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++;
 
214
  }
 
215
  album->tracks = g_list_reverse (album->tracks);
 
216
  
 
217
  return g_list_append (list, album);
 
218
}
 
219
 
 
220
static gboolean
 
221
fire_signal_idle (SjMetadataMusicbrainz *m)
 
222
{
 
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;
 
226
}
 
227
 
 
228
/**
 
229
 * Virtual methods
 
230
 */
 
231
 
 
232
static GError*
 
233
mb_get_new_error (SjMetadata *metadata)
 
234
{
 
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;
 
241
  }
 
242
  return SJ_METADATA_MUSICBRAINZ (metadata)->priv->construct_error;
 
243
}
 
244
 
 
245
static void
 
246
mb_set_cdrom (SjMetadata *metadata, const char* device)
 
247
{
 
248
  SjMetadataMusicbrainzPrivate *priv;
 
249
  g_return_if_fail (metadata != NULL);
 
250
  g_return_if_fail (device != NULL);
 
251
  priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
 
252
 
 
253
  if (priv->cdrom) {
 
254
    g_free (priv->cdrom);
 
255
  }
 
256
  priv->cdrom = g_strdup (device);
 
257
  mb_SetDevice (priv->mb, priv->cdrom);
 
258
}
 
259
 
 
260
static void
 
261
mb_set_proxy (SjMetadata *metadata, const char* proxy)
 
262
{
 
263
  SjMetadataMusicbrainzPrivate *priv;
 
264
  g_return_if_fail (metadata != NULL);
 
265
  priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
 
266
 
 
267
  if (proxy == NULL) {
 
268
    proxy = "";
 
269
  }
 
270
  if (priv->http_proxy) {
 
271
    g_free (priv->http_proxy);
 
272
  }
 
273
  priv->http_proxy = g_strdup (proxy);
 
274
  mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
 
275
}
 
276
 
 
277
static void
 
278
mb_set_proxy_port (SjMetadata *metadata, const int port)
 
279
{
 
280
  SjMetadataMusicbrainzPrivate *priv;
 
281
  g_return_if_fail (metadata != NULL);
 
282
  priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
 
283
 
 
284
  priv->http_proxy_port = port;
 
285
  mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
 
286
}
 
287
 
 
288
static gpointer
 
289
lookup_cd (SjMetadata *metadata)
 
290
{
 
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;
 
299
 
 
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 */
 
306
 
 
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;
 
311
 
 
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;
 
318
    }
 
319
    priv->error = g_error_new (SJ_ERROR, err, _("Cannot read CD: %s"), msg);
 
320
    g_free (msg);
 
321
 
 
322
    priv->albums = NULL;
 
323
    g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
 
324
    return NULL;
 
325
  }
 
326
 
 
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;
 
333
  }
 
334
 
 
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;
 
341
  }
 
342
 
 
343
  for (i = 1; i <= num_albums; i++) {
 
344
    int num_tracks;
 
345
    AlbumDetails *album;
 
346
 
 
347
    mb_Select1(priv->mb, MBS_SelectAlbum, i);
 
348
    album = g_new0 (AlbumDetails, 1);
 
349
 
 
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);
 
353
    }
 
354
 
 
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"));
 
365
        }
 
366
      }
 
367
    }
 
368
 
 
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"));
 
373
    }
 
374
 
 
375
    {
 
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);
 
385
          }
 
386
        }
 
387
        mb_Select(priv->mb, MBS_Back);
 
388
      }
 
389
    }
 
390
 
 
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;
 
400
    }
 
401
 
 
402
    for (j = 1; j <= num_tracks; j++) {
 
403
      TrackDetails *track;
 
404
      track = g_new0 (TrackDetails, 1);
 
405
 
 
406
      track->album = album;
 
407
 
 
408
      track->number = j; /* replace with number lookup? */
 
409
 
 
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);
 
413
      }
 
414
 
 
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);
 
418
      }
 
419
 
 
420
      if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackName, data, MB_BUFFER_SIZE, j)) {
 
421
        track->title = g_strdup (data);
 
422
      }
 
423
 
 
424
      if (mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, MB_BUFFER_SIZE, j)) {
 
425
        track->artist = g_strdup (data);
 
426
      }
 
427
 
 
428
      if (mb_GetResultData1(priv->mb, MBE_AlbumGetTrackDuration, data, MB_BUFFER_SIZE, j)) {
 
429
        track->duration = atoi (data) / 1000;
 
430
      }
 
431
      
 
432
      album->tracks = g_list_prepend (album->tracks, track);
 
433
      album->number++;
 
434
    }
 
435
    album->tracks = g_list_reverse (album->tracks);
 
436
 
 
437
    albums = g_list_append (albums, album);
 
438
  }
 
439
 
 
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;
 
446
    
 
447
    j = 1;
 
448
    for (tl = album->tracks; tl; tl = tl->next) {
 
449
      TrackDetails *track = tl->data;
 
450
      int sectors;
 
451
      
 
452
      if (track->duration == 0) {
 
453
        sectors = mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, j+1);
 
454
        track->duration = get_duration_from_sectors (sectors);
 
455
      }
 
456
      j++;
 
457
    }
 
458
  }
 
459
  priv->albums = albums;
 
460
  g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
 
461
  return albums;
 
462
}
 
463
 
 
464
static void
 
465
mb_list_albums (SjMetadata *metadata, GError **error)
 
466
{
 
467
  GThread *thread;
 
468
 
 
469
  g_return_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata));
 
470
 
 
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;
 
477
  }
 
478
}