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
)
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
;
98 memset (&settings
, 0, sizeof (json_settings
));
99 json_value
*root
= json_parse_ex( &settings
, psz_buffer
, psz_error
);
102 msg_Warn( p_obj
, "Can't parse json data: %s", psz_error
);
105 if ( root
->type
!= json_object
)
107 msg_Warn( p_obj
, "wrong json root node" );
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" );
116 if ( strcmp( node
->u
.string
.ptr
, "ok" ) != 0 )
118 msg_Warn( p_obj
, "Bad request status" );
121 node
= jsongetbyname( root
, "results" );
122 if ( !node
|| node
->type
!= json_array
)
124 msg_Warn( p_obj
, "Bad results array or no results" );
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
);
149 if ( root
) json_value_free( root
);
153 int DoAcoustIdWebRequest( vlc_object_t
*p_obj
, acoustid_fingerprint_t
*p_data
)
155 if ( !p_data
->psz_fingerprint
) return VLC_SUCCESS
;
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 ) )
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
);
171 p_obj
->obj
.flags
= i_saved_flags
;
172 if ( p_stream
== NULL
)
175 stream_t
*p_chain
= vlc_stream_FilterNew( p_stream
, "inflate" );
180 char *p_buffer
= NULL
;
186 if( i_ret
>= INT_MAX
- i_read
)
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
);
196 i_read
= vlc_stream_Read( p_stream
, &p_buffer
[i_ret
], i_read
);
202 vlc_stream_Delete( p_stream
);
205 if ( ParseJson( p_obj
, p_buffer
, & p_data
->results
) )
206 msg_Dbg( p_obj
, "results count == %d", p_data
->results
.count
);
208 msg_Dbg( p_obj
, "No results" );