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"));