vlc | branch: master | Francois Cartegnie <[email protected]> | Fri May 31 15:35:56 2019 +0200| [7fde19d9d23a7a075f1e532ef3144dab86d4306b] | committer: Francois Cartegnie
access: cdda: add support for musicbrainz (fix #21796) deprecates CDDB which has too low entropy for lookups > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=7fde19d9d23a7a075f1e532ef3144dab86d4306b --- NEWS | 1 + modules/access/Makefile.am | 8 +- modules/access/cdda.c | 226 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 217 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index 554538d9a0..cf75202611 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,7 @@ Access: * Added support for the RIST (Reliable Internet Stream Transport) Protocol * Added avaudiocapture module as a replacement for qtsound, which is removed now * Audio CD data tracks are now correctly detected and skipped + * Deprecates Audio CD CDDB lookups in favor of more accurate Musicbrainz Access output: * Added support for the RIST (Reliable Internet Stream Transport) Protocol diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am index 9cdf98e0a1..919ac4b408 100644 --- a/modules/access/Makefile.am +++ b/modules/access/Makefile.am @@ -216,7 +216,9 @@ EXTRA_LTLIBRARIES += libvnc_plugin.la ### Optical media ### -libcdda_plugin_la_SOURCES = access/cdda.c access/vcd/cdrom.c access/vcd/cdrom.h access/vcd/cdrom_internals.h +libcdda_plugin_la_SOURCES = access/cdda.c access/vcd/cdrom.c access/vcd/cdrom.h access/vcd/cdrom_internals.h \ + misc/webservices/json.c misc/webservices/json.h misc/webservices/json_helper.h \ + misc/webservices/musicbrainz.c misc/webservices/musicbrainz.h libcdda_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBCDDB_CFLAGS) libcdda_plugin_la_LIBADD = $(LIBCDDB_LIBS) $(LIBM) libcdda_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)' @@ -224,6 +226,10 @@ if HAVE_DARWIN libcdda_plugin_la_LIBADD += -liconv libcdda_plugin_la_LDFLAGS += -Wl,-framework,IOKit,-framework,CoreFoundation endif +if HAVE_GCRYPT +libcdda_plugin_la_CFLAGS += $(GCRYPT_CFLAGS) +libcdda_plugin_la_LIBADD += $(GCRYPT_LIBS) +endif EXTRA_LTLIBRARIES += libcdda_plugin.la access_LTLIBRARIES += $(LTLIBcdda) diff --git a/modules/access/cdda.c b/modules/access/cdda.c index 69e4cb6acb..7fc7af6590 100644 --- a/modules/access/cdda.c +++ b/modules/access/cdda.c @@ -39,6 +39,7 @@ #include <math.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <vlc_common.h> #include <vlc_demux.h> @@ -48,8 +49,15 @@ #include <vlc_meta.h> #include <vlc_charset.h> /* ToLocaleDup */ #include <vlc_url.h> +#include <vlc_strings.h> +#include <vlc_memstream.h> +#ifdef HAVE_GCRYPT +# include <gcrypt.h> +# include <vlc_gcrypt.h> +#endif #include "vcd/cdrom.h" /* For CDDA_DATA_SIZE */ +#include "../misc/webservices/musicbrainz.h" #ifdef HAVE_LIBCDDB #include <cddb/cddb.h> @@ -351,17 +359,157 @@ typedef struct #ifdef HAVE_LIBCDDB cddb_disc_t *cddb; #endif + musicbrainz_recording_t *mbrecord; } access_sys_t; -#ifdef HAVE_LIBCDDB -static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, const vcddev_toc_t *p_toc ) +static inline int LBAPregap( int i_sector ) +{ + /* LBA to sector pregap fixup + see libdiscid/src/toc.c #L80 */ + return i_sector + 150; +} + +#ifdef HAVE_GCRYPT +static char * BuildMusicbrainzDiscID( const vcddev_toc_t *p_toc, + int i_total, int i_first, int i_last ) { - if( !var_InheritBool( obj, "metadata-network-access" ) ) + gcry_md_hd_t hd; + gcry_error_t err = gcry_md_open( &hd, GCRY_MD_SHA1, 0 ); + if(err) + return NULL; + + if(gcry_md_enable( hd, GCRY_MD_SHA1 )) { - msg_Dbg( obj, "album art policy set to manual: not fetching" ); + gcry_md_close( hd ); return NULL; } + char buffer[16]; + + sprintf( buffer, "%02X", i_first ); + gcry_md_write( hd, buffer, 2 ); + sprintf( buffer, "%02X", i_last ); + gcry_md_write( hd, buffer, 2 ); + /* LEAD OUT sector info */ + + /* LEAD OUT sector info + * https://github.com/metabrainz/libdiscid/blob/e46249415eb6d657ecc63667b03d670a4347712f/src/toc.c#L90 */ + int i_last_track_end; + if (p_toc->i_tracks > i_total) + i_last_track_end = LBAPregap(p_toc->p_sectors[i_total].i_lba) - CD_ROM_XA_INTERVAL; + else + i_last_track_end = LBAPregap(p_toc->p_sectors[p_toc->i_tracks].i_lba); + + sprintf( buffer, "%08X", i_last_track_end ); + gcry_md_write( hd, buffer, 8 ); + + for( int i = 0; i<i_total; i++ ) /* skip LEAD OUT */ + { + sprintf( buffer, "%08X", LBAPregap(p_toc->p_sectors[i].i_lba) ); + gcry_md_write( hd, buffer, 8 ); + } + + for( int i = i_total; i<100; i++ ) + { + if( i == p_toc->i_tracks ) + continue; + gcry_md_write( hd, "00000000", 8 ); + } + + gcry_md_final( hd ); + + size_t i_len = gcry_md_get_algo_dlen( GCRY_MD_SHA1 ); + unsigned char *output = gcry_md_read( hd, GCRY_MD_SHA1 ); + char *out = vlc_b64_encode_binary( output, i_len ); + if( out ) + { + i_len = strlen( out ); + for( size_t i=0; i<i_len; i++ ) + { + if( !isalpha(out[i]) ) + { + if( out[i] == '+' ) + out[i] = '.'; + else if( out[i] == '/' ) + out[i] = '_'; + else if( out[i] == '=' ) + out[i] = '-'; + } + } + } + + gcry_md_close( hd ); + + return out; +} +#else +# define BuildMusicbrainzDiscID(a, b, c, d) (NULL) +#endif + +static musicbrainz_recording_t * GetMusicbrainzInfo( vlc_object_t *obj, + const vcddev_toc_t *p_toc, + int i_total, int i_first, int i_last ) +{ + musicbrainz_recording_t *recording = NULL; + + char *psz_mbserver = var_InheritString( obj, "musicbrainz-server" ); + if( !psz_mbserver || !*psz_mbserver ) + return NULL; + + musicbrainz_config_t cfg = { .obj = obj, + .psz_mb_server = psz_mbserver, + .psz_coverart_server = NULL }; + + /* Build DISC ID based on SHA1 */ + char *psz_disc_id = BuildMusicbrainzDiscID( p_toc, + i_total, i_first, i_last ); + if( psz_disc_id ) + { + recording = musicbrainz_lookup_recording_by_discid( &cfg, psz_disc_id ); + } + else /* Fuzzy lookup using TOC */ + { + struct vlc_memstream ms; + if( vlc_memstream_open(&ms) ) + { + free( psz_mbserver ); + return NULL; + } + + vlc_memstream_printf(&ms, "toc=%u+%u", i_first, i_last ); + /* LEAD OUT sector info + * https://github.com/metabrainz/libdiscid/blob/e46249415eb6d657ecc63667b03d670a4347712f/src/toc.c#L90 */ + int i_last_track_end; + if (p_toc->i_tracks > i_total) + i_last_track_end = LBAPregap(p_toc->p_sectors[i_total].i_lba) - CD_ROM_XA_INTERVAL; + else + i_last_track_end = LBAPregap(p_toc->p_sectors[p_toc->i_tracks].i_lba); + vlc_memstream_printf(&ms, "+%u", i_last_track_end ); + for( int i = 0; i<i_total; i++ ) /* skipped LEAD OUT, audio only */ + vlc_memstream_printf(&ms, "+%u", LBAPregap(p_toc->p_sectors[i].i_lba) ); + if( vlc_memstream_flush(&ms) ) + { + if( vlc_memstream_close(&ms) ) + free( ms.ptr ); + free( psz_mbserver ); + return NULL; + } + + recording = musicbrainz_lookup_recording_by_toc( &cfg, ms.ptr ); + + if( vlc_memstream_close(&ms) == 0 ) + free( ms.ptr ); + } + + free( psz_mbserver ); + return recording; +} + +#ifdef HAVE_LIBCDDB +static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, const vcddev_toc_t *p_toc ) +{ + msg_Dbg( obj, "retrieving metadata with CDDB" ); + /* */ cddb_conn_t *p_cddb = cddb_new(); if( !p_cddb ) @@ -448,6 +596,8 @@ static cddb_disc_t *GetCDDBInfo( vlc_object_t *obj, const vcddev_toc_t *p_toc ) cddb_read( p_cddb, p_disc ); + msg_Dbg( obj, "disc ID: 0x%08x", cddb_disc_get_discid(p_disc) ); + cddb_destroy( p_cddb); return p_disc; @@ -455,6 +605,7 @@ error: if( p_disc ) cddb_disc_destroy( p_disc ); cddb_destroy( p_cddb ); + msg_Dbg( obj, "CDDB failure" ); return NULL; } #endif /* HAVE_LIBCDDB */ @@ -624,6 +775,37 @@ static int ReadDir(stream_t *access, input_item_node_t *node) ON_EMPTY(description, vlc_meta_Get(m, vlc_meta_Description)); } + if(sys->mbrecord && sys->mbrecord->i_release) + { + musicbrainz_release_t *r = sys->mbrecord->p_releases; + for(size_t j=0; j<r->i_tracks; j++) + { + if(r->p_tracks[j].i_index == (unsigned)i + 1) + { + if (r->p_tracks[j].psz_title) + { + ON_EMPTY(title, r->p_tracks[j].psz_title); + ON_EMPTY(artist, r->p_tracks[j].psz_artist); + } + break; + } + } + ON_EMPTY(album, r->psz_title); + if(NONEMPTY(r->psz_artist)) + { + artist = r->psz_artist; + input_item_SetAlbumArtist(item, r->psz_artist); + } + if(year == 0 && r->psz_date) + { + unsigned i_year; + if(sscanf(r->psz_date, "%4d", &i_year) == 1) + year = i_year; + } + if(NONEMPTY(r->psz_coverart_url)) + input_item_SetArtworkURL(item, r->psz_coverart_url); + } + if (NONEMPTY(title)) { input_item_SetName(item, title); @@ -651,8 +833,10 @@ static int ReadDir(stream_t *access, input_item_node_t *node) } char num[4]; - snprintf(num, sizeof (num), "%d", i + 1); - input_item_SetTrackNum(item, num); + if(snprintf(num, sizeof (num), "%u", i + 1) < 4) + input_item_SetTrackNum(item, num); + snprintf(num, sizeof (num), "%u", p_toc->i_tracks); + input_item_SetTrackTotal(item, num); input_item_node_AppendItem(node, item); input_item_Release(item); @@ -699,16 +883,6 @@ static int AccessOpen(vlc_object_t *obj, vcddev_t *dev) goto error; } -#ifdef HAVE_LIBCDDB - msg_Dbg(obj, "retrieving metadata with CDDB"); - - sys->cddb = GetCDDBInfo(obj, sys->p_toc); - if (sys->cddb != NULL) - msg_Dbg(obj, "disc ID: 0x%08x", cddb_disc_get_discid(sys->cddb)); - else - msg_Dbg(obj, "CDDB failure"); -#endif - if (ioctl_GetCdText(obj, dev, &sys->cdtextv, &sys->cdtextc)) { msg_Dbg(obj, "CD-TEXT information missing"); @@ -716,6 +890,20 @@ static int AccessOpen(vlc_object_t *obj, vcddev_t *dev) sys->cdtextc = 0; } + sys->mbrecord = NULL; + sys->cddb = NULL; + + if(var_InheritBool(obj, "metadata-network-access")) + { + sys->mbrecord = GetMusicbrainzInfo(obj, sys->p_toc, sys->i_cdda_tracks, + sys->i_cdda_first, sys->i_cdda_last ); +#ifdef HAVE_LIBCDDB + if(!sys->mbrecord) + sys->cddb = GetCDDBInfo(obj, sys->p_toc); +#endif + } + else msg_Dbg(obj, "album art policy set to manual: not fetching"); + access->p_sys = sys; access->pf_read = NULL; access->pf_block = NULL; @@ -743,7 +931,8 @@ static void AccessClose(access_sys_t *sys) if (sys->cddb != NULL) cddb_disc_destroy(sys->cddb); #endif - + if(sys->mbrecord) + musicbrainz_recording_release(sys->mbrecord); vcddev_toc_Free(sys->p_toc); } @@ -815,6 +1004,9 @@ vlc_module_begin () add_integer( "cdda-last-sector", -1, NULL, NULL, true ) change_volatile () + add_string( "musicbrainz-server", MUSICBRAINZ_DEFAULT_SERVER, + N_( "Musicbrainz Server" ), + N_( "Address of the musicbrainz server to use." ), true ) #ifdef HAVE_LIBCDDB add_string( "cddb-server", "freedb.videolan.org", N_( "CDDB Server" ), N_( "Address of the CDDB server to use." ), true ) _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
