webservices: acoustid: move to https
[vlc.git] / modules / misc / webservices / acoustid.c
blobe867327233d55f9938b8099e6715b0d4ccf20f34
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 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_stream.h>
27 #include <limits.h>
28 #include <vlc_memory.h>
30 #include <vlc/vlc.h>
31 #include "acoustid.h"
32 #include "json.h"
34 /*****************************************************************************
35 * Requests lifecycle
36 *****************************************************************************/
37 void free_acoustid_result_t( acoustid_result_t * r )
39 free( r->psz_id );
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;
54 return NULL;
57 static void parse_artists( json_value *node, musicbrainz_recording_t *record )
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 );
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 )
85 size_t i_len = strlen( value->u.string.ptr );
86 i_len = __MIN( i_len, MB_ID_SIZE );
87 memcpy( record->s_musicbrainz_id, value->u.string.ptr, i_len );
89 parse_artists( jsongetbyname( recordnode, "artists" ), record );
90 msg_Dbg( p_obj, "recording %d title %s %36s %s", i, record->psz_title, record->s_musicbrainz_id, record->psz_artist );
94 static bool ParseJson( vlc_object_t *p_obj, char *psz_buffer, acoustid_results_t *p_results )
96 json_settings settings;
97 char psz_error[128];
98 memset (&settings, 0, sizeof (json_settings));
99 json_value *root = json_parse_ex( &settings, psz_buffer, psz_error );
100 if ( root == NULL )
102 msg_Warn( p_obj, "Can't parse json data: %s", psz_error );
103 goto error;
105 if ( root->type != json_object )
107 msg_Warn( p_obj, "wrong json root node" );
108 goto error;
110 json_value *node = jsongetbyname( root, "status" );
111 if ( !node || node->type != json_string )
113 msg_Warn( p_obj, "status node not found or invalid" );
114 goto error;
116 if ( strcmp( node->u.string.ptr, "ok" ) != 0 )
118 msg_Warn( p_obj, "Bad request status" );
119 goto error;
121 node = jsongetbyname( root, "results" );
122 if ( !node || node->type != json_array )
124 msg_Warn( p_obj, "Bad results array or no results" );
125 goto error;
127 p_results->p_results = calloc( node->u.array.length, sizeof(acoustid_result_t) );
128 if ( ! p_results->p_results ) goto error;
129 p_results->count = node->u.array.length;
130 for( unsigned int i=0; i<node->u.array.length; i++ )
132 json_value *resultnode = node->u.array.values[i];
133 if ( resultnode && resultnode->type == json_object )
135 acoustid_result_t *p_result = & p_results->p_results[i];
136 json_value *value = jsongetbyname( resultnode, "score" );
137 if ( value && value->type == json_double )
138 p_result->d_score = value->u.dbl;
139 value = jsongetbyname( resultnode, "id" );
140 if ( value && value->type == json_string )
141 p_result->psz_id = strdup( value->u.string.ptr );
142 parse_recordings( p_obj, jsongetbyname( resultnode, "recordings" ), p_result );
145 json_value_free( root );
146 return true;
148 error:
149 if ( root ) json_value_free( root );
150 return false;
153 int DoAcoustIdWebRequest( vlc_object_t *p_obj, acoustid_fingerprint_t *p_data )
155 if ( !p_data->psz_fingerprint ) return VLC_SUCCESS;
157 char *psz_url;
158 if( unlikely(asprintf( &psz_url, "https://fingerprint.videolan.org/"
159 "acoustid.php?meta=recordings+tracks+usermeta+"
160 "releases&duration=%d&fingerprint=%s",
161 p_data->i_duration, p_data->psz_fingerprint ) < 1 ) )
162 return VLC_EGENERIC;
164 msg_Dbg( p_obj, "Querying AcoustID from %s", psz_url );
165 int i_saved_flags = p_obj->obj.flags;
166 p_obj->obj.flags |= OBJECT_FLAGS_NOINTERACT;
168 stream_t *p_stream = vlc_stream_NewURL( p_obj, psz_url );
170 free( psz_url );
171 p_obj->obj.flags = i_saved_flags;
172 if ( p_stream == NULL )
173 return VLC_EGENERIC;
175 stream_t *p_chain = vlc_stream_FilterNew( p_stream, "inflate" );
176 if( p_chain )
177 p_stream = p_chain;
179 /* read answer */
180 char *p_buffer = NULL;
181 int i_ret = 0;
182 for( ;; )
184 int i_read = 65536;
186 if( i_ret >= INT_MAX - i_read )
187 break;
189 p_buffer = realloc_or_free( p_buffer, 1 + i_ret + i_read );
190 if( unlikely(p_buffer == NULL) )
192 vlc_stream_Delete( p_stream );
193 return VLC_ENOMEM;
196 i_read = vlc_stream_Read( p_stream, &p_buffer[i_ret], i_read );
197 if( i_read <= 0 )
198 break;
200 i_ret += i_read;
202 vlc_stream_Delete( p_stream );
203 p_buffer[i_ret] = 0;
205 if ( ParseJson( p_obj, p_buffer, & p_data->results ) )
206 msg_Dbg( p_obj, "results count == %d", p_data->results.count );
207 else
208 msg_Dbg( p_obj, "No results" );
210 return VLC_SUCCESS;