[vlc-commits] access: cdda: add support for musicbrainz (fix #21796)

Francois Cartegnie git at videolan.org
Sat Jun 1 00:27:53 CEST 2019


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | 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 )



More information about the vlc-commits mailing list