23
23
#endif /* HAVE_CONFIG_H */
25
25
#include <string.h>
27
26
#include <glib-object.h>
28
27
#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
29
#include <gconf/gconf-client.h>
34
30
#include <musicbrainz/queries.h>
35
31
#include <musicbrainz/mb_c.h>
36
32
#include <stdlib.h>
39
#include <nautilus-burn-drive.h>
40
#ifndef NAUTILUS_BURN_CHECK_VERSION
41
#define NAUTILUS_BURN_CHECK_VERSION(a,b,c) FALSE
44
#if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
45
#include <nautilus-burn.h>
48
#ifndef HAVE_BURN_DRIVE_UNREF
49
#define nautilus_burn_drive_unref nautilus_burn_drive_free
52
34
#include "sj-metadata-musicbrainz.h"
53
35
#include "sj-structures.h"
54
36
#include "sj-error.h"
56
struct SjMetadataMusicbrainzPrivate {
57
GError *construct_error;
62
/* TODO: remove and use an async queue or something l33t */
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
38
#define GCONF_MUSICBRAINZ_SERVER "/apps/sound-juicer/musicbrainz_server"
75
39
#define GCONF_PROXY_USE_PROXY "/system/http_proxy/use_http_proxy"
76
40
#define GCONF_PROXY_HOST "/system/http_proxy/host"
79
43
#define GCONF_PROXY_USERNAME "/system/http_proxy/authentication_user"
80
44
#define GCONF_PROXY_PASSWORD "/system/http_proxy/authentication_password"
86
static GObjectClass *parent_class = NULL;
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);
100
parent_class->finalize (object);
104
sj_metadata_musicbrainz_instance_init (GTypeInstance *instance, gpointer g_class)
106
GConfClient *gconf_client;
107
char *server_name = NULL;
108
SjMetadataMusicbrainz *self = (SjMetadataMusicbrainz*)instance;
109
self->priv = g_new0 (SjMetadataMusicbrainzPrivate, 1);
110
self->priv->construct_error = NULL;
111
self->priv->mb = mb_New ();
112
/* TODO: something. :/ */
113
if (!self->priv->mb) {
114
g_set_error (&self->priv->construct_error,
115
SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
116
_("Cannot create MusicBrainz client"));
119
mb_UseUTF8 (self->priv->mb, TRUE);
121
gconf_client = gconf_client_get_default ();
122
server_name = gconf_client_get_string (gconf_client, GCONF_MUSICBRAINZ_SERVER, NULL);
124
g_strstrip (server_name);
126
if (server_name && strcmp (server_name, "") != 0) {
127
mb_SetServer (self->priv->mb, server_name, 80);
128
g_free (server_name);
131
/* Set the HTTP proxy */
132
if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_PROXY, NULL)) {
133
char *proxy_host = gconf_client_get_string (gconf_client, GCONF_PROXY_HOST, NULL);
134
mb_SetProxy (self->priv->mb, proxy_host,
135
gconf_client_get_int (gconf_client, GCONF_PROXY_PORT, NULL));
137
if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_AUTHENTICATION, NULL)) {
138
#if HAVE_MB_SETPROXYCREDS
139
char *username = gconf_client_get_string (gconf_client, GCONF_PROXY_USERNAME, NULL);
140
char *password = gconf_client_get_string (gconf_client, GCONF_PROXY_PASSWORD, NULL);
141
mb_SetProxyCreds (self->priv->mb, username, password);
145
g_warning ("mb_SetProxyCreds() not found, no proxy authorisation possible.");
150
g_object_unref (gconf_client);
152
if (g_getenv("MUSICBRAINZ_DEBUG")) {
153
mb_SetDebug (self->priv->mb, TRUE);
158
metadata_interface_init (gpointer g_iface, gpointer iface_data)
160
SjMetadataClass *klass = (SjMetadataClass*)g_iface;
161
klass->get_new_error = mb_get_new_error;
162
klass->set_cdrom = mb_set_cdrom;
163
klass->set_proxy = mb_set_proxy;
164
klass->set_proxy_port = mb_set_proxy_port;
165
klass->list_albums = mb_list_albums;
166
klass->get_submit_url = mb_get_submit_url;
170
sj_metadata_musicbrainz_class_init (SjMetadataMusicbrainzClass *class)
172
GObjectClass *object_class;
173
parent_class = g_type_class_peek_parent (class);
174
object_class = (GObjectClass*) class;
175
object_class->finalize = sj_metadata_musicbrainz_finalize;
179
sj_metadata_musicbrainz_get_type (void)
181
static GType type = 0;
183
static const GTypeInfo info = {
184
sizeof (SjMetadataMusicbrainzClass),
187
(GClassInitFunc)sj_metadata_musicbrainz_class_init,
190
sizeof (SjMetadataMusicbrainz),
192
sj_metadata_musicbrainz_instance_init,
195
static const GInterfaceInfo metadata_i_info = {
196
(GInterfaceInitFunc) metadata_interface_init,
199
type = g_type_register_static (G_TYPE_OBJECT, "SjMetadataMusicBrainzClass", &info, 0);
200
g_type_add_interface_static (type, SJ_TYPE_METADATA, &metadata_i_info);
206
sj_metadata_musicbrainz_new (void)
208
return g_object_new (sj_metadata_musicbrainz_get_type (), NULL);
47
struct SjMetadataMusicbrainzPrivate {
55
#define GET_PRIVATE(o) \
56
(G_TYPE_INSTANCE_GET_PRIVATE ((o), SJ_TYPE_METADATA_MUSICBRAINZ, SjMetadataMusicbrainzPrivate))
66
static void metadata_interface_init (gpointer g_iface, gpointer iface_data);
68
G_DEFINE_TYPE_WITH_CODE (SjMetadataMusicbrainz,
69
sj_metadata_musicbrainz,
71
G_IMPLEMENT_INTERFACE (SJ_TYPE_METADATA,
72
metadata_interface_init));
80
get_duration_from_sectors (int sectors)
215
82
#define BYTES_PER_SECTOR 2352
216
83
#define BYTES_PER_SECOND (44100 / 8) / 16 / 2
219
get_duration_from_sectors (int sectors)
221
85
return (sectors * BYTES_PER_SECTOR / BYTES_PER_SECOND);
225
get_offline_track_listing(SjMetadata *metadata, GError **error)
227
SjMetadataMusicbrainzPrivate *priv;
233
g_return_val_if_fail (metadata != NULL, NULL);
234
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
236
if (!mb_Query (priv->mb, MBQ_GetCDTOC)) {
238
mb_GetQueryError (priv->mb, message, 255);
240
SJ_ERROR, SJ_ERROR_CD_LOOKUP_ERROR,
241
_("Cannot read CD: %s"), message);
245
num_tracks = mb_GetResultInt (priv->mb, MBE_TOCGetLastTrack);
247
album = g_new0 (AlbumDetails, 1);
248
album->artist = g_strdup (_("Unknown Artist"));
249
album->title = g_strdup (_("Unknown Title"));
251
for (i = 1; i <= num_tracks; i++) {
252
track = g_new0 (TrackDetails, 1);
253
track->album = album;
255
track->title = g_strdup_printf (_("Track %d"), i);
256
track->artist = g_strdup (album->artist);
257
track->duration = get_duration_from_sectors (mb_GetResultInt1 (priv->mb, MBE_TOCGetTrackNumSectors, i+1));
258
album->tracks = g_list_append (album->tracks, track);
261
return g_list_append (list, album);
265
fire_signal_idle (SjMetadataMusicbrainz *m)
267
g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (m), FALSE);
268
g_signal_emit_by_name (G_OBJECT (m), "metadata", m->priv->albums, m->priv->error);
277
mb_get_new_error (SjMetadata *metadata)
279
GError *error = NULL;
280
if (metadata == NULL || SJ_METADATA_MUSICBRAINZ (metadata)->priv == NULL) {
282
SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
283
_("MusicBrainz metadata object is not valid. This is bad, check your console for errors."));
286
return SJ_METADATA_MUSICBRAINZ (metadata)->priv->construct_error;
290
mb_set_cdrom (SjMetadata *metadata, const char* device)
292
SjMetadataMusicbrainzPrivate *priv;
293
g_return_if_fail (metadata != NULL);
294
g_return_if_fail (device != NULL);
295
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
298
g_free (priv->cdrom);
300
priv->cdrom = g_strdup (device);
301
mb_SetDevice (priv->mb, priv->cdrom);
305
mb_set_proxy (SjMetadata *metadata, const char* proxy)
307
SjMetadataMusicbrainzPrivate *priv;
308
g_return_if_fail (metadata != NULL);
309
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
314
if (priv->http_proxy) {
315
g_free (priv->http_proxy);
317
priv->http_proxy = g_strdup (proxy);
318
mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
322
mb_set_proxy_port (SjMetadata *metadata, const int port)
324
SjMetadataMusicbrainzPrivate *priv;
325
g_return_if_fail (metadata != NULL);
326
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
328
priv->http_proxy_port = port;
329
mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
332
92
/* Data imported from FreeDB is horrendeous for compilations,
333
93
* Try to split the 'Various' artist */
479
239
g_free (cdindex);
482
static NautilusBurnMediaType
483
get_drive_media_type (SjMetadata *metadata)
243
* Magic character set encoding to try and repair brain-dead FreeDB encoding,
244
* converting it to the current locale's encoding (which is likely to be the
245
* intended encoding).
248
convert_encoding(char **str)
485
SjMetadataMusicbrainzPrivate *priv;
486
NautilusBurnDrive *drive;
487
NautilusBurnMediaType type;
488
#if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
489
NautilusBurnDriveMonitor *monitor;
492
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
493
#if NAUTILUS_BURN_CHECK_VERSION(2,15,3)
494
if (! nautilus_burn_initialized ()) {
495
nautilus_burn_init ();
497
monitor = nautilus_burn_get_drive_monitor ();
498
drive = nautilus_burn_drive_monitor_get_drive_for_device (monitor, priv->cdrom);
500
drive = nautilus_burn_drive_new_from_path (priv->cdrom);
504
return NAUTILUS_BURN_MEDIA_TYPE_ERROR;
506
type = nautilus_burn_drive_get_media_type (drive);
507
nautilus_burn_drive_unref (drive);
253
if (str == NULL || *str == NULL)
256
iso8859 = g_convert (*str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL);
259
converted = g_locale_to_utf8 (iso8859, -1, NULL, NULL, NULL);
512
lookup_cd (SjMetadata *metadata)
271
mb_list_albums (SjMetadata *metadata, char **url, GError **error)
514
/** The size of the buffer used in MusicBrainz lookups */
273
/* The size of the buffer used in MusicBrainz lookups */
515
274
SjMetadataMusicbrainzPrivate *priv;
516
275
GList *albums = NULL;
519
278
int num_albums, i, j;
520
NautilusBurnMediaType type;
522
/* TODO: fire error signal */
523
280
g_return_val_if_fail (metadata != NULL, NULL);
524
281
g_return_val_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata), NULL);
525
282
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
526
283
g_return_val_if_fail (priv->cdrom != NULL, NULL);
527
priv->error = NULL; /* TODO: hack */
529
type = get_drive_media_type (metadata);
531
if (type == NAUTILUS_BURN_MEDIA_TYPE_ERROR) {
535
if (access (priv->cdrom, W_OK) == 0) {
536
msg = g_strdup_printf (_("Device '%s' does not contain any media"), priv->cdrom);
537
err = SJ_ERROR_CD_NO_MEDIA;
539
msg = g_strdup_printf (_("Device '%s' could not be opened. Check the access permissions on the device."), priv->cdrom);
540
err = SJ_ERROR_CD_PERMISSION_ERROR;
542
priv->error = g_error_new (SJ_ERROR, err, _("Cannot read CD: %s"), msg);
285
if (sj_metadata_helper_check_media (priv->cdrom, error) == FALSE) {
545
286
priv->albums = NULL;
546
g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
550
290
get_rdf (metadata);
293
mb_GetWebSubmitURL(priv->mb, data, sizeof(data));
294
*url = g_strdup(data);
552
297
num_albums = mb_GetResultInt(priv->mb, MBE_GetNumAlbums);
553
298
if (num_albums < 1) {
554
priv->albums = get_offline_track_listing (metadata, &(priv->error));
555
g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
559
303
for (i = 1; i <= num_albums; i++) {
561
305
AlbumDetails *album;
306
gboolean from_freedb = FALSE;
563
308
mb_Select1(priv->mb, MBS_SelectAlbum, i);
564
309
album = g_new0 (AlbumDetails, 1);
566
311
if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumId, data, sizeof (data))) {
312
from_freedb = strstr(data, "freedb:") == data;
567
314
mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
568
315
album->album_id = g_strdup (data);
571
318
if (mb_GetResultData (priv->mb, MBE_AlbumGetAlbumArtistId, data, sizeof (data))) {
572
319
mb_GetIDFromURL (priv->mb, data, data, sizeof (data));
573
320
album->artist_id = g_strdup (data);
574
if (g_ascii_strncasecmp (MBI_VARIOUS_ARTIST_ID, data, 64) == 0) {
575
album->artist = g_strdup (_("Various"));
322
if (mb_GetResultData (priv->mb, MBE_AlbumGetAlbumArtistName, data, sizeof (data))) {
323
album->artist = g_strdup (data);
577
if (*data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistName, data, sizeof (data), 1)) {
578
album->artist = g_strdup (data);
325
if (g_ascii_strcasecmp (MBI_VARIOUS_ARTIST_ID, album->artist_id) == 0) {
326
album->artist = g_strdup (_("Various"));
580
328
album->artist = g_strdup (_("Unknown Artist"));
582
if (*data && mb_GetResultData1(priv->mb, MBE_AlbumGetArtistSortName, data, sizeof (data), 1)) {
583
album->artist_sortname = g_strdup (data);
331
if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumArtistSortName, data, sizeof (data))) {
332
album->artist_sortname = g_strdup (data);
588
336
if (mb_GetResultData(priv->mb, MBE_AlbumGetAlbumName, data, sizeof (data))) {
589
album->title = g_strdup (data);
338
new_title = sj_metadata_helper_scan_disc_number (data, &album->disc_number);
340
album->title = new_title;
342
album->title = g_strdup (data);
591
344
album->title = g_strdup (_("Unknown Title"));
347
if (mb_GetResultData(priv->mb, MBE_AlbumGetAmazonAsin, data, sizeof (data))) {
348
album->asin = g_strdup (data);
595
352
int num_releases;
596
353
num_releases = mb_GetResultInt (priv->mb, MBE_AlbumGetNumReleaseDates);
597
354
if (num_releases > 0) {
598
355
mb_Select1(priv->mb, MBS_SelectReleaseDate, 1);
599
356
if (mb_GetResultData(priv->mb, MBE_ReleaseGetDate, data, sizeof (data))) {
600
int matched, year=1, month=1, day=1;
601
matched = sscanf(data, "%u-%u-%u", &year, &month, &day);
603
album->release_date = g_date_new_dmy ((day == 0) ? 1 : day, (month == 0) ? 1 : month, year);
357
album->release_date = sj_metadata_helper_scan_date (data);
606
359
mb_Select(priv->mb, MBS_Back);
691
priv->albums = albums;
692
g_idle_add ((GSourceFunc)fire_signal_idle, metadata);
697
mb_list_albums (SjMetadata *metadata, GError **error)
701
g_return_if_fail (SJ_IS_METADATA_MUSICBRAINZ (metadata));
703
thread = g_thread_create ((GThreadFunc)lookup_cd, metadata, TRUE, error);
704
if (thread == NULL) {
706
SJ_ERROR, SJ_ERROR_INTERNAL_ERROR,
707
_("Could not create CD lookup thread"));
713
mb_get_submit_url (SjMetadata *metadata)
471
metadata_interface_init (gpointer g_iface, gpointer iface_data)
473
SjMetadataClass *klass = (SjMetadataClass*)g_iface;
474
klass->list_albums = mb_list_albums;
478
sj_metadata_musicbrainz_init (SjMetadataMusicbrainz *self)
480
GConfClient *gconf_client;
481
char *server_name = NULL;
483
self->priv = GET_PRIVATE (self);
484
self->priv->mb = mb_New ();
485
mb_UseUTF8 (self->priv->mb, TRUE);
487
gconf_client = gconf_client_get_default ();
488
server_name = gconf_client_get_string (gconf_client, GCONF_MUSICBRAINZ_SERVER, NULL);
490
g_strstrip (server_name);
492
if (server_name && strcmp (server_name, "") != 0) {
493
mb_SetServer (self->priv->mb, server_name, 80);
494
g_free (server_name);
497
/* Set the HTTP proxy */
498
if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_PROXY, NULL)) {
499
char *proxy_host = gconf_client_get_string (gconf_client, GCONF_PROXY_HOST, NULL);
500
mb_SetProxy (self->priv->mb, proxy_host,
501
gconf_client_get_int (gconf_client, GCONF_PROXY_PORT, NULL));
503
if (gconf_client_get_bool (gconf_client, GCONF_PROXY_USE_AUTHENTICATION, NULL)) {
504
#if HAVE_MB_SETPROXYCREDS
505
char *username = gconf_client_get_string (gconf_client, GCONF_PROXY_USERNAME, NULL);
506
char *password = gconf_client_get_string (gconf_client, GCONF_PROXY_PASSWORD, NULL);
507
mb_SetProxyCreds (self->priv->mb, username, password);
511
g_warning ("mb_SetProxyCreds() not found, no proxy authorisation possible.");
516
g_object_unref (gconf_client);
518
if (g_getenv("MUSICBRAINZ_DEBUG")) {
519
mb_SetDebug (self->priv->mb, TRUE);
524
sj_metadata_musicbrainz_get_property (GObject *object, guint property_id,
525
GValue *value, GParamSpec *pspec)
527
SjMetadataMusicbrainzPrivate *priv = SJ_METADATA_MUSICBRAINZ (object)->priv;
530
switch (property_id) {
532
g_value_set_string (value, priv->cdrom);
534
case PROP_PROXY_HOST:
535
g_value_set_string (value, priv->http_proxy);
537
case PROP_PROXY_PORT:
538
g_value_set_int (value, priv->http_proxy_port);
541
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
546
sj_metadata_musicbrainz_set_property (GObject *object, guint property_id,
547
const GValue *value, GParamSpec *pspec)
549
SjMetadataMusicbrainzPrivate *priv = SJ_METADATA_MUSICBRAINZ (object)->priv;
552
switch (property_id) {
555
g_free (priv->cdrom);
556
priv->cdrom = g_value_dup_string (value);
558
mb_SetDevice (priv->mb, priv->cdrom);
560
case PROP_PROXY_HOST:
561
if (priv->http_proxy) {
562
g_free (priv->http_proxy);
564
priv->http_proxy = g_value_dup_string (value);
565
/* TODO: check this unsets the proxy if NULL, or should we pass "" ? */
566
mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
568
case PROP_PROXY_PORT:
569
priv->http_proxy_port = g_value_get_int (value);
570
mb_SetProxy (priv->mb, priv->http_proxy, priv->http_proxy_port);
573
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
578
sj_metadata_musicbrainz_finalize (GObject *object)
715
580
SjMetadataMusicbrainzPrivate *priv;
718
g_return_val_if_fail (metadata != NULL, NULL);
720
priv = SJ_METADATA_MUSICBRAINZ (metadata)->priv;
722
if (mb_GetWebSubmitURL(priv->mb, url, 1024)) {
723
return g_strdup(url);
582
priv = SJ_METADATA_MUSICBRAINZ (object)->priv;
584
g_free (priv->http_proxy);
585
g_free (priv->cdrom);
586
mb_Delete (priv->mb);
588
G_OBJECT_CLASS (sj_metadata_musicbrainz_parent_class)->finalize (object);
592
sj_metadata_musicbrainz_class_init (SjMetadataMusicbrainzClass *class)
594
GObjectClass *object_class = (GObjectClass*)class;
596
g_type_class_add_private (class, sizeof (SjMetadataMusicbrainzPrivate));
598
object_class->get_property = sj_metadata_musicbrainz_get_property;
599
object_class->set_property = sj_metadata_musicbrainz_set_property;
600
object_class->finalize = sj_metadata_musicbrainz_finalize;
602
g_object_class_override_property (object_class, PROP_DEVICE, "device");
603
g_object_class_override_property (object_class, PROP_PROXY_HOST, "proxy-host");
604
g_object_class_override_property (object_class, PROP_PROXY_PORT, "proxy-port");
613
sj_metadata_musicbrainz_new (void)
615
return g_object_new (SJ_TYPE_METADATA_MUSICBRAINZ, NULL);