1
// -------------------------------------------------------------------------------- //
2
// Copyright (C) 2008-2010 J.Rios
5
// This Program is free software; you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation; either version 2, or (at your option)
10
// This Program 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
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program; see the file LICENSE. If not, write to
17
// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18
// http://www.gnu.org/copyleft/gpl.html
20
// -------------------------------------------------------------------------------- //
24
#include "curl/http.h"
25
#include "MusicBrainz.h"
29
#define guMUSICDNS_CLIENT_ID "ca3d48c7383db1dcf6dccd1f0cab26e5"
30
#define guMUSICDNS_BASEURL "http://ofa.musicdns.org/ofa/1/track"
32
#define guMUSICDNS_REQSTR_FP "cid=%s&cvr=%s&fpt=%s&rmd=%d&" \
33
"brt=%d&fmt=%s&dur=%ld&art=%s&" \
34
"ttl=%s&alb=%s&tnm=%d&gnr=%s&" \
35
"yrr=%s&enc=UTF-8&\r\n"
37
#define guMUSICDNS_REQSTR_PUID "cid=%s&cvr=%s&pid=%s&rmd=%d&" \
38
"brt=%d&fmt=%s&dur=%ld&art=%s&" \
39
"ttl=%s&alb=%s&tnm=%d&gnr=%s&" \
40
"yrr=%s&enc=UTF-8&\r\n"
46
#define GST_TAG_OFA_FINGERPRINT "ofa-fingerprint"
48
//// -------------------------------------------------------------------------------- //
49
//void list_tags( const GstTagList * list, const gchar * tag, gpointer user_data )
51
// printf( "Tag: %s\n", tag );
54
// -------------------------------------------------------------------------------- //
55
static gboolean gst_bus_async_callback( GstBus * bus, GstMessage * message, guMusicDnsThread * pobj )
57
//guLogMessage( wxT( "Got gstreamer message %u" ), GST_MESSAGE_TYPE( message ) );
58
switch( GST_MESSAGE_TYPE( message ) )
60
case GST_MESSAGE_ERROR :
64
gst_message_parse_error( message, &err, &debug );
65
guLogError( wxT( "Gstreamer error '%s'" ), wxString( err->message, wxConvUTF8 ).c_str() );
73
case GST_MESSAGE_STATE_CHANGED:
75
GstState oldstate, newstate, pendingstate;
76
gst_message_parse_state_changed( message, &oldstate, &newstate, &pendingstate );
77
//guLogMessage( wxT( "State changed... %u %u %u" ), oldstate, newstate, pendingstate );
81
// case GST_MESSAGE_BUFFERING :
83
// guLogMessage( wxT( "Buffering..." ) );
87
case GST_MESSAGE_EOS :
89
//guLogMessage( wxT( "EOS Detected..." ) );
94
case GST_MESSAGE_TAG :
96
/* The stream discovered new tags. */
98
gchar * fingerprint = NULL;
99
/* Extract from the message the GstTagList.
100
* This generates a copy, so we must remember to free it.*/
101
gst_message_parse_tag( message, &tags );
103
//gst_tag_list_foreach( tags, ( GstTagForeachFunc ) list_tags, NULL );
105
/* Extract the title and artist tags - if they exist */
106
gst_tag_list_get_string( tags, GST_TAG_OFA_FINGERPRINT, &fingerprint );
110
//guLogMessage( wxT( "Gstreamer got fingerprint '%s'" ), wxString( fingerprint, wxConvUTF8 ).c_str() );
111
pobj->SetFingerprint( fingerprint );
112
g_free( fingerprint );
115
/* Free the tag list */
116
gst_tag_list_free( tags );
127
// -------------------------------------------------------------------------------- //
128
static void on_pad_added( GstElement * decodebin, GstPad * pad, gboolean last, GstElement * conv )
133
// GstElement * conv;
134
// conv = ( GstElemet * ) data;
136
// guLogMessage( wxT( "New pad created..." ) );
138
convpad = gst_element_get_pad( conv, "sink" );
140
/* check media type */
141
caps = gst_pad_get_caps( pad );
142
str = gst_caps_get_structure( caps, 0 );
143
if( !g_strrstr( gst_structure_get_name( str ), "audio" ) )
145
gst_caps_unref( caps );
146
gst_object_unref( convpad );
149
gst_caps_unref( caps );
151
// guLogMessage( wxT( "Linked decoder and converter..." ) );
153
gst_pad_link( pad, convpad );
159
// -------------------------------------------------------------------------------- //
161
// -------------------------------------------------------------------------------- //
162
guMusicDnsThread::guMusicDnsThread( guMusicDns * musicdns, const wxChar * filename )
164
wxASSERT( musicdns );
166
//guLogMessage( wxT( "guMusicDnsThread..." ) );
167
m_MusicDns = musicdns;
168
m_FileName = wxString( filename );
170
int Error = guMDNS_STATUS_ERROR_GSTREAMER;
172
m_Pipeline = gst_pipeline_new( "guPipeline" );
173
if( GST_IS_ELEMENT( m_Pipeline ) )
176
src = gst_element_factory_make( "filesrc", "guSource" );
177
if( GST_IS_ELEMENT( src ) )
179
g_object_set( G_OBJECT( src ), "location", ( const char * ) m_FileName.mb_str(), NULL );
181
dec = gst_element_factory_make( "decodebin", "guDecoder" );
182
if( GST_IS_ELEMENT( dec ) )
185
conv = gst_element_factory_make( "audioconvert", "guConverter" );
186
if( GST_IS_ELEMENT( conv ) )
189
ofa = gst_element_factory_make( "ofa", "guOFA" );
190
if( GST_IS_ELEMENT( ofa ) )
193
fake = gst_element_factory_make( "fakesink", "guFakeSink" );
194
g_object_set( G_OBJECT( fake ), "sync", 0, NULL );
195
if( GST_IS_ELEMENT( fake ) )
197
gst_bin_add_many( GST_BIN( m_Pipeline ), src, dec, conv, ofa, fake, NULL );
199
GstBus * bus = gst_pipeline_get_bus( GST_PIPELINE( m_Pipeline ) );
200
gst_bus_add_watch( bus, ( GstBusFunc ) gst_bus_async_callback, this );
201
gst_object_unref( G_OBJECT( bus ) );
203
if( gst_element_link( src, dec ) )
205
g_signal_connect( dec, "new-decoded-pad", G_CALLBACK( on_pad_added ), conv );
207
if( gst_element_link_many( conv, ofa, fake, NULL ) )
209
gst_element_set_state( m_Pipeline, GST_STATE_PAUSED );
211
//guLogMessage( wxT( "Created the pipeline..." ) );
213
if( Create() == wxTHREAD_NO_ERROR )
215
SetPriority( WXTHREAD_DEFAULT_PRIORITY - 30 );
221
Error = guMDNS_STATUS_ERROR_THREAD;
226
guLogError( wxT( "Error linking the objects conv, ofa, fake" ) );
231
guLogError( wxT( "Error linking the objects src, dec" ) );
233
gst_object_unref( fake );
237
guLogError( wxT( "Error creating the MusicDns fakeout" ) );
239
gst_object_unref( ofa );
243
guLogError( wxT( "Error creating the MusicDns ofa" ) );
245
gst_object_unref( conv );
249
guLogError( wxT( "Error creating the MusicDns converter" ) );
251
gst_object_unref( dec );
255
guLogError( wxT( "Error creating the MusicDns decoder" ) );
257
gst_object_unref( src );
261
guLogError( wxT( "Error creating the MusicDns source" ) );
263
gst_object_unref( m_Pipeline );
267
guLogError( wxT( "Error creating the MusicDns pipeline" ) );
269
m_MusicDns->SetStatus( Error );
272
// -------------------------------------------------------------------------------- //
273
guMusicDnsThread::~guMusicDnsThread()
275
wxASSERT( GST_IS_OBJECT( m_Pipeline ) );
276
if( GST_IS_ELEMENT( m_Pipeline ) )
278
gst_element_set_state( m_Pipeline, GST_STATE_NULL );
279
gst_object_unref( GST_OBJECT( m_Pipeline ) );
281
m_MusicDns->ClearMusicDnsThread();
282
//guLogMessage( wxT( "Destroyed MusicDnsThread..." ) );
285
// -------------------------------------------------------------------------------- //
286
guMusicDnsThread::ExitCode guMusicDnsThread::Entry()
288
gst_element_set_state( m_Pipeline, GST_STATE_PLAYING );
291
while( !TestDestroy() && m_Running )
295
//guLogMessage( wxT( "Finished guMusicDnsThread..." ) );
299
// -------------------------------------------------------------------------------- //
300
void guMusicDnsThread::SetFingerprint( const char * fingerprint )
302
m_MusicDns->SetFingerprint( fingerprint );
306
// -------------------------------------------------------------------------------- //
307
void guMusicDnsThread::Stop( void )
312
// -------------------------------------------------------------------------------- //
314
// -------------------------------------------------------------------------------- //
315
guMusicDns::guMusicDns( guMusicBrainz * musicbrainz )
317
m_MusicDnsThread = NULL;
318
m_MusicBrainz = musicbrainz;
319
m_Status = guMDNS_STATUS_OK;
322
// -------------------------------------------------------------------------------- //
323
guMusicDns::~guMusicDns()
325
if( m_MusicDnsThread )
327
m_MusicDnsThread->Pause();
328
m_MusicDnsThread->Delete();
332
// -------------------------------------------------------------------------------- //
333
void guMusicDns::SetTrack( const guTrack * track )
336
m_Fingerprint = wxT( "" );
341
// -------------------------------------------------------------------------------- //
342
wxString guMusicDns::GetXmlDoc( void )
347
// -------------------------------------------------------------------------------- //
348
wxString guMusicDns::GetFingerprint( void )
350
return m_Fingerprint;
353
// -------------------------------------------------------------------------------- //
354
void guMusicDns::SetFingerprint( const wxString &fingerprint )
356
m_Fingerprint = fingerprint;
357
if( !m_Fingerprint.IsEmpty() )
360
m_Status = guMDNS_STATUS_ERROR_NO_FINGERPRINT;
363
// -------------------------------------------------------------------------------- //
364
void guMusicDns::SetFingerprint( const char * fingerprint )
366
SetFingerprint( wxString( fingerprint, wxConvUTF8 ) );
369
// -------------------------------------------------------------------------------- //
370
void guMusicDns::SetXmlDoc( const wxString &xmldoc )
372
int EndPos = xmldoc.Find( wxT( "</metadata>" ) ) + 11;
373
m_XmlDoc = xmldoc.Mid( 0, EndPos );
374
//guLogMessage( wxT( "XmlDoc:\n%s" ), m_XmlDoc.c_str() );
375
if( !m_XmlDoc.IsEmpty() )
378
m_Status = guMDNS_STATUS_ERROR_NOXMLDATA;
381
// -------------------------------------------------------------------------------- //
382
wxString guMusicDns::GetPUID( void )
387
// -------------------------------------------------------------------------------- //
388
void guMusicDns::SetPUID( const wxString &puid )
391
if( m_MusicDnsThread )
395
//guLogMessage( wxT( "Calling FoundPUID..." ) );
396
m_MusicBrainz->FoundPUID( m_PUID );
399
// -------------------------------------------------------------------------------- //
400
void guMusicDns::SetPUID( const char * puid )
402
SetPUID( wxString( puid, wxConvUTF8 ) );
405
// -------------------------------------------------------------------------------- //
406
void guMusicDns::ClearMusicDnsThread( void )
408
m_MusicDnsThread = NULL;
411
// -------------------------------------------------------------------------------- //
412
bool guMusicDns::IsRunning( void )
414
return m_MusicDnsThread != NULL;
417
// -------------------------------------------------------------------------------- //
418
void guMusicDns::CancelSearch( void )
420
if( m_MusicDnsThread )
422
m_MusicDnsThread->Pause();
423
m_MusicDnsThread->Delete();
424
m_MusicDnsThread = NULL;
428
// -------------------------------------------------------------------------------- //
429
bool guMusicDns::DoGetFingerprint( void )
433
//guLogMessage( wxT( "DoGetFingerprint..." ) );
436
m_MusicDnsThread = new guMusicDnsThread( this, m_Track->m_FileName.c_str() );
438
return m_MusicDnsThread != NULL;
441
// -------------------------------------------------------------------------------- //
442
bool guMusicDns::DoGetMetadata( void )
444
wxString HtmlData = wxString::Format( wxT( guMUSICDNS_REQSTR_FP ),
445
wxT( guMUSICDNS_CLIENT_ID ),
446
wxT( ID_GUAYADEQUE_VERSION ),
447
m_Fingerprint.c_str(),
448
0, // only return PUID
450
m_Track->m_FileName.AfterLast( wxT( '.' ) ).c_str(),
451
m_Track->m_Length * 1000,
452
!m_Track->m_ArtistName.IsEmpty() ? m_Track->m_ArtistName.c_str() : wxT( "unknown" ),
453
!m_Track->m_SongName.IsEmpty() ? m_Track->m_SongName.c_str() : wxT( "unknown" ),
454
!m_Track->m_AlbumName.IsEmpty() ? m_Track->m_AlbumName.c_str() : wxT( "unknown" ),
456
!m_Track->m_GenreName.IsEmpty() ? m_Track->m_GenreName.c_str() : wxT( "unknown" ),
457
wxString::Format( wxT( "%u" ), m_Track->m_Year ).c_str() );
460
//guLogMessage( wxT( guMUSICDNS_BASEURL ) wxT( "%s" ), HtmlData.c_str() );
462
http.AddHeader( wxT( "User-Agent: " ) guDEFAULT_BROWSER_USER_AGENT );
463
http.AddHeader( wxT( "Accept: text/html" ) );
464
http.AddHeader( wxT( "Accept-Charset: utf-8" ) );
465
http.SetOpt( CURLOPT_FOLLOWLOCATION, 1 );
466
if( http.Post( wxCURL_STRING2BUF( HtmlData ), HtmlData.Length(), wxT( guMUSICDNS_BASEURL ) ) )
468
SetXmlDoc( http.GetResponseBody() );
473
m_Status = guMDNS_STATUS_ERROR_HTTP;
478
// -------------------------------------------------------------------------------- //
479
bool guMusicDns::ReadTrackInfo( wxXmlNode * XmlNode )
481
// <?xml version="1.0" encoding="UTF-8"?>
482
// <metadata xmlns="http://musicbrainz.org/ns/mmd-1.0#" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:mip="http://musicip.com/ns/mip-1.0#">
484
// <title>Song Title</title>
486
// <name>Artist Name</name>
489
// <puid id="2c43ec65-b629-f449-3f5e-c0b69527d771"/>
493
if( XmlNode && XmlNode->GetName() == wxT( "track" ) )
495
XmlNode = XmlNode->GetChildren();
498
if( XmlNode->GetName() == wxT( "puid-list" ) )
500
XmlNode = XmlNode->GetChildren();
503
else if( XmlNode->GetName() == wxT( "puid" ) )
506
XmlNode->GetPropVal( wxT( "id" ), &PUId );
510
XmlNode = XmlNode->GetNext();
513
m_Status = guMDNS_STATUS_ERROR_XMLPARSE;
517
// -------------------------------------------------------------------------------- //
518
bool guMusicDns::DoParseXmlDoc( void )
520
wxStringInputStream ins( m_XmlDoc );
521
wxXmlDocument XmlDoc( ins );
522
wxXmlNode * XmlNode = XmlDoc.GetRoot();
523
if( XmlNode && ( XmlNode->GetName() == wxT( "metadata" ) ) )
525
return ReadTrackInfo( XmlNode->GetChildren() );
528
m_Status = guMDNS_STATUS_ERROR_XMLERROR;
532
// -------------------------------------------------------------------------------- //
533
void guMusicDns::SetStatus( const int status )
538
// -------------------------------------------------------------------------------- //
539
int guMusicDns::GetStatus( void )
544
// -------------------------------------------------------------------------------- //
545
bool guMusicDns::IsOk( void )
547
return m_Status == guMDNS_STATUS_OK;
550
// -------------------------------------------------------------------------------- //