1 /*****************************************************************************
2 * acoustid.c: AcoustId webservice parser
3 *****************************************************************************
4 * Copyright (C) 2012 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include <vlc_common.h>
26 #include <vlc_stream.h>
28 #include <vlc_memory.h>
34 /*****************************************************************************
36 *****************************************************************************/
37 void free_acoustid_result_t( acoustid_result_t * r )
40 for ( unsigned int i=0; i<r->recordings.count; i++ )
42 free( r->recordings.p_recordings[ i ].psz_artist );
43 free( r->recordings.p_recordings[ i ].psz_title );
45 free( r->recordings.p_recordings );
48 static json_value * jsongetbyname( json_value *object, const char *psz_name )
50 if ( object->type != json_object ) return NULL;
51 for ( unsigned int i=0; i < object->u.object.length; i++ )
52 if ( strcmp( object->u.object.values[i].name, psz_name ) == 0 )
53 return object->u.object.values[i].value;
57 static void parse_artists( json_value *node, musicbrainz_recording_t *record )
60 if ( !node || node->type != json_array || node->u.array.length < 1 ) return;
61 json_value *artistnode = node->u.array.values[ 0 ];
62 json_value *value = jsongetbyname( artistnode, "name" );
63 if ( value && value->type == json_string )
64 record->psz_artist = strdup( value->u.string.ptr );
67 static void parse_recordings( vlc_object_t *p_obj, json_value *node, acoustid_result_t *p_result )
69 if ( !node || node->type != json_array ) return;
70 p_result->recordings.p_recordings = calloc( node->u.array.length, sizeof(musicbrainz_recording_t) );
71 if ( ! p_result->recordings.p_recordings ) return;
72 p_result->recordings.count = node->u.array.length;
74 for( unsigned int i=0; i<node->u.array.length; i++ )
76 musicbrainz_recording_t *record = & p_result->recordings.p_recordings[ i ];
77 json_value *recordnode = node->u.array.values[ i ];
78 if ( !recordnode || recordnode->type != json_object ) break;
79 json_value *value = jsongetbyname( recordnode, "title" );
80 if ( value && value->type == json_string )
81 record->psz_title = strdup( value->u.string.ptr );
82 value = jsongetbyname( recordnode, "id" );
83 if ( value && value->type == json_string )
84 strncpy( record->sz_musicbrainz_id, value->u.string.ptr, MB_ID_SIZE );
85 parse_artists( jsongetbyname( recordnode, "artists" ), record );
86 msg_Dbg( p_obj, "recording %d title %s %36s %s", i, record->psz_title, record->sz_musicbrainz_id, record->psz_artist );
90 static bool ParseJson( vlc_object_t *p_obj, char *psz_buffer, acoustid_results_t *p_results )
92 json_settings settings;
94 memset (&settings, 0, sizeof (json_settings));
95 json_value *root = json_parse_ex( &settings, psz_buffer, psz_error );
98 msg_Warn( p_obj, "Can't parse json data: %s", psz_error );
101 if ( root->type != json_object )
103 msg_Warn( p_obj, "wrong json root node" );
106 json_value *node = jsongetbyname( root, "status" );
107 if ( !node || node->type != json_string )
109 msg_Warn( p_obj, "status node not found or invalid" );
112 if ( strcmp( node->u.string.ptr, "ok" ) != 0 )
114 msg_Warn( p_obj, "Bad request status" );
117 node = jsongetbyname( root, "results" );
118 if ( !node || node->type != json_array )
120 msg_Warn( p_obj, "Bad results array or no results" );
123 p_results->p_results = calloc( node->u.array.length, sizeof(acoustid_result_t) );
124 if ( ! p_results->p_results ) goto error;
125 p_results->count = node->u.array.length;
126 for( unsigned int i=0; i<node->u.array.length; i++ )
128 json_value *resultnode = node->u.array.values[i];
129 if ( resultnode && resultnode->type == json_object )
131 acoustid_result_t *p_result = & p_results->p_results[i];
132 json_value *value = jsongetbyname( resultnode, "score" );
133 if ( value && value->type == json_double )
134 p_result->d_score = value->u.dbl;
135 value = jsongetbyname( resultnode, "id" );
136 if ( value && value->type == json_string )
137 p_result->psz_id = strdup( value->u.string.ptr );
138 parse_recordings( p_obj, jsongetbyname( resultnode, "recordings" ), p_result );
141 json_value_free( root );
145 if ( root ) json_value_free( root );
156 static void cancelDoAcoustIdWebRequest( void *p_arg )
158 struct webrequest_t *p_request = (struct webrequest_t *) p_arg;
159 if ( p_request->p_stream )
160 stream_Delete( p_request->p_stream );
161 if ( p_request->psz_url )
162 free( p_request->psz_url );
163 if ( p_request->p_buffer )
164 free( p_request->p_buffer );
167 int DoAcoustIdWebRequest( vlc_object_t *p_obj, acoustid_fingerprint_t *p_data )
171 struct webrequest_t request = { NULL, NULL, NULL };
173 if ( !p_data->psz_fingerprint ) return VLC_SUCCESS;
175 i_ret = asprintf( & request.psz_url,
176 "http://fingerprint.videolan.org/acoustid.php?meta=recordings+tracks+usermeta+releases&duration=%d&fingerprint=%s",
177 p_data->i_duration, p_data->psz_fingerprint );
178 if ( i_ret < 1 ) return VLC_EGENERIC;
180 vlc_cleanup_push( cancelDoAcoustIdWebRequest, &request );
182 msg_Dbg( p_obj, "Querying AcoustID from %s", request.psz_url );
183 request.p_stream = stream_UrlNew( p_obj, request.psz_url );
184 if ( !request.p_stream )
186 i_status = VLC_EGENERIC;
196 if( i_ret >= INT_MAX - i_read )
199 request.p_buffer = realloc_or_free( request.p_buffer, 1 + i_ret + i_read );
200 if( !request.p_buffer )
202 i_status = VLC_ENOMEM;
206 i_read = stream_Read( request.p_stream, &request.p_buffer[i_ret], i_read );
212 stream_Delete( request.p_stream );
213 request.p_stream = NULL;
214 request.p_buffer[ i_ret ] = 0;
216 int i_canc = vlc_savecancel();
217 if ( ParseJson( p_obj, request.p_buffer, & p_data->results ) )
219 msg_Dbg( p_obj, "results count == %d", p_data->results.count );
221 msg_Dbg( p_obj, "No results" );
223 vlc_restorecancel( i_canc );
224 i_status = VLC_SUCCESS;