]> git.sesse.net Git - vlc/blob - modules/misc/webservices/acoustid.c
live555: retrieve RTSP track languages from SDP
[vlc] / modules / misc / webservices / acoustid.c
1 /*****************************************************************************
2  * acoustid.c: AcoustId webservice parser
3  *****************************************************************************
4  * Copyright (C) 2012 VLC authors and VideoLAN
5  *
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.
10  *
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.
15  *
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  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <vlc_common.h>
26 #include <vlc_stream.h>
27 #include <limits.h>
28 #include <vlc_memory.h>
29
30 #include <vlc/vlc.h>
31 #include "acoustid.h"
32 #include "json.h"
33
34 /*****************************************************************************
35  * Requests lifecycle
36  *****************************************************************************/
37 void free_acoustid_result_t( acoustid_result_t * r )
38 {
39     free( r->psz_id );
40     for ( unsigned int i=0; i<r->recordings.count; i++ )
41     {
42         free( r->recordings.p_recordings[ i ].psz_artist );
43         free( r->recordings.p_recordings[ i ].psz_title );
44     }
45     free( r->recordings.p_recordings );
46 }
47
48 static json_value * jsongetbyname( json_value *object, const char *psz_name )
49 {
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;
54     return NULL;
55 }
56
57 static void parse_artists( json_value *node, musicbrainz_recording_t *record )
58 {
59     /* take only main */
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 );
65 }
66
67 static void parse_recordings( vlc_object_t *p_obj, json_value *node, acoustid_result_t *p_result )
68 {
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;
73
74     for( unsigned int i=0; i<node->u.array.length; i++ )
75     {
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 );
87     }
88 }
89
90 static bool ParseJson( vlc_object_t *p_obj, char *psz_buffer, acoustid_results_t *p_results )
91 {
92     json_settings settings;
93     char psz_error[128];
94     memset (&settings, 0, sizeof (json_settings));
95     json_value *root = json_parse_ex( &settings, psz_buffer, psz_error );
96     if ( root == NULL )
97     {
98         msg_Warn( p_obj, "Can't parse json data: %s", psz_error );
99         goto error;
100     }
101     if ( root->type != json_object )
102     {
103         msg_Warn( p_obj, "wrong json root node" );
104         goto error;
105     }
106     json_value *node = jsongetbyname( root, "status" );
107     if ( !node || node->type != json_string )
108     {
109         msg_Warn( p_obj, "status node not found or invalid" );
110         goto error;
111     }
112     if ( strcmp( node->u.string.ptr, "ok" ) != 0 )
113     {
114         msg_Warn( p_obj, "Bad request status" );
115         goto error;
116     }
117     node = jsongetbyname( root, "results" );
118     if ( !node || node->type != json_array )
119     {
120         msg_Warn( p_obj, "Bad results array or no results" );
121         goto error;
122     }
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++ )
127     {
128         json_value *resultnode = node->u.array.values[i];
129         if ( resultnode && resultnode->type == json_object )
130         {
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 );
139         }
140     }
141     json_value_free( root );
142     return true;
143
144 error:
145     if ( root ) json_value_free( root );
146     return false;
147 }
148
149 struct webrequest_t
150 {
151     stream_t *p_stream;
152     char *psz_url;
153     char *p_buffer;
154 };
155
156 static void cancelDoAcoustIdWebRequest( void *p_arg )
157 {
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 );
165 }
166
167 int DoAcoustIdWebRequest( vlc_object_t *p_obj, acoustid_fingerprint_t *p_data )
168 {
169     int i_ret;
170     int i_status;
171     struct webrequest_t request = { NULL, NULL, NULL };
172
173     if ( !p_data->psz_fingerprint ) return VLC_SUCCESS;
174
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;
179
180     vlc_cleanup_push( cancelDoAcoustIdWebRequest, &request );
181
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 )
185     {
186         i_status = VLC_EGENERIC;
187         goto cleanup;
188     }
189
190     /* read answer */
191     i_ret = 0;
192     for( ;; )
193     {
194         int i_read = 65536;
195
196         if( i_ret >= INT_MAX - i_read )
197             break;
198
199         request.p_buffer = realloc_or_free( request.p_buffer, 1 + i_ret + i_read );
200         if( !request.p_buffer )
201         {
202             i_status = VLC_ENOMEM;
203             goto cleanup;
204         }
205
206         i_read = stream_Read( request.p_stream, &request.p_buffer[i_ret], i_read );
207         if( i_read <= 0 )
208             break;
209
210         i_ret += i_read;
211     }
212     stream_Delete( request.p_stream );
213     request.p_stream = NULL;
214     request.p_buffer[ i_ret ] = 0;
215
216     int i_canc = vlc_savecancel();
217     if ( ParseJson( p_obj, request.p_buffer, & p_data->results ) )
218     {
219         msg_Dbg( p_obj, "results count == %d", p_data->results.count );
220     } else {
221         msg_Dbg( p_obj, "No results" );
222     }
223     vlc_restorecancel( i_canc );
224     i_status = VLC_SUCCESS;
225
226 cleanup:
227     vlc_cleanup_run( );
228     return i_status;
229 }