[vlc-commits] misc: webservices: add musicbrainz

Francois Cartegnie git at videolan.org
Sat Jun 1 00:23:43 CEST 2019


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Mon May 27 17:14:49 2019 +0200| [e8f5c617dfcf6dc571701eaa8c5d41b5a23d57a5] | committer: Francois Cartegnie

misc: webservices: add musicbrainz

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=e8f5c617dfcf6dc571701eaa8c5d41b5a23d57a5
---

 modules/misc/webservices/musicbrainz.c | 349 +++++++++++++++++++++++++++++++++
 modules/misc/webservices/musicbrainz.h |  73 +++++++
 2 files changed, 422 insertions(+)

diff --git a/modules/misc/webservices/musicbrainz.c b/modules/misc/webservices/musicbrainz.c
new file mode 100644
index 0000000000..2393a7320a
--- /dev/null
+++ b/modules/misc/webservices/musicbrainz.c
@@ -0,0 +1,349 @@
+/*****************************************************************************
+ * musicbrainz.c : Musicbrainz API lookup
+ *****************************************************************************
+ * Copyright (C) 2019 VideoLabs, VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <limits.h>
+
+#include "json_helper.h"
+#include "musicbrainz.h"
+
+typedef struct
+{
+    json_value *root;
+} musicbrainz_lookup_t;
+
+static void musicbrainz_lookup_release(musicbrainz_lookup_t *p)
+{
+    if(p && p->root)
+        json_value_free(p->root);
+    free(p);
+}
+
+static musicbrainz_lookup_t * musicbrainz_lookup_new(void)
+{
+    return calloc(1, sizeof(musicbrainz_lookup_t));
+}
+
+static musicbrainz_lookup_t * musicbrainz_lookup(vlc_object_t *p_obj, const char *psz_url)
+{
+    msg_Dbg(p_obj, "Querying MB for %s", psz_url);
+    void *p_buffer = json_retrieve_document(p_obj, psz_url);
+    if(!p_buffer)
+        return NULL;
+
+    musicbrainz_lookup_t *p_lookup = musicbrainz_lookup_new();
+    if(p_lookup)
+    {
+        p_lookup->root = json_parse_document(p_obj, p_buffer);
+        if (!p_lookup->root)
+            msg_Dbg(p_obj, "No results");
+    }
+    free(p_buffer);
+    return p_lookup;
+}
+
+static bool musicbrainz_fill_track(const json_value *tracknode, musicbrainz_track_t *t)
+{
+    t->psz_title = json_dupstring(tracknode, "title");
+
+    const json_value *node = json_getbyname(tracknode, "artist-credit");
+    if (node && node->type == json_array && node->u.array.length)
+        t->psz_artist = json_dupstring(node->u.array.values[0], "name");
+
+    node = json_getbyname(tracknode, "position");
+    if (node && node->type == json_integer)
+        t->i_index = node->u.integer;
+
+    return true;
+}
+
+static bool musicbrainz_has_cover_in_releasegroup(json_value ** const p_nodes,
+                                                  size_t i_nodes,
+                                                  const char *psz_group_id)
+{
+    for(size_t i=0; i<i_nodes; i++)
+    {
+        const json_value *rgnode = json_getbyname(p_nodes[i], "release-group");
+        if(rgnode)
+        {
+            const char *psz_id = jsongetstring(rgnode, "id");
+            if(!psz_id || strcmp(psz_id, psz_group_id))
+                continue;
+
+            const json_value *node = json_getbyname(p_nodes[i], "cover-art-archive");
+            if(!node)
+                continue;
+
+            node = json_getbyname(node, "front");
+            if(!node || node->type != json_boolean || !node->u.boolean)
+                continue;
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static char *musicbrainz_fill_artists(const json_value *arraynode)
+{
+    char *psz = NULL;
+    if(arraynode->type != json_array || arraynode->u.array.length < 1)
+        return psz;
+
+    size_t i_total = 1;
+    for(size_t i=0; i<arraynode->u.array.length; i++)
+    {
+        const json_value *name = json_getbyname(arraynode->u.array.values[i], "name");
+        if(name->type != json_string)
+            continue;
+
+        if(psz == NULL)
+        {
+            psz = strdup(name->u.string.ptr);
+            i_total = name->u.string.length + 1;
+        }
+        else
+        {
+            char *p = realloc(psz, i_total + name->u.string.length + 2);
+            if(p)
+            {
+                psz = p;
+                psz = strcat(psz, ", ");
+                psz = strncat(psz, name->u.string.ptr, name->u.string.length);
+                i_total += name->u.string.length + 2;
+            }
+        }
+    }
+
+    return psz;
+}
+
+static bool musicbrainz_fill_release(const json_value *releasenode, musicbrainz_release_t *r)
+{
+    const json_value *media = json_getbyname(releasenode, "media");
+    if(!media || media->type != json_array ||
+       media->u.array.length == 0)
+        return false;
+    /* we always use first media */
+    media = media->u.array.values[0];
+
+    const json_value *tracks = json_getbyname(media, "tracks");
+    if(!tracks || tracks->type != json_array ||
+       tracks->u.array.length == 0)
+        return false;
+
+    r->p_tracks = calloc(tracks->u.array.length, sizeof(*r->p_tracks));
+    if(!r->p_tracks)
+        return false;
+
+    for(size_t i=0; i<tracks->u.array.length; i++)
+    {
+        if(musicbrainz_fill_track(tracks->u.array.values[i], &r->p_tracks[r->i_tracks]))
+            r->i_tracks++;
+    }
+
+    r->psz_title = json_dupstring(releasenode, "title");
+    r->psz_id = json_dupstring(releasenode, "id");
+
+    const json_value *rgnode = json_getbyname(releasenode, "release-group");
+    if(rgnode)
+    {
+        r->psz_date = json_dupstring(rgnode, "first-release-date");
+        r->psz_group_id = json_dupstring(rgnode, "id");
+
+        const json_value *node = json_getbyname(rgnode, "artist-credit");
+        if(node)
+            r->psz_artist = musicbrainz_fill_artists(node);
+    }
+    else
+    {
+        const json_value *node = json_getbyname(releasenode, "artist-credit");
+        if(node)
+            r->psz_artist = musicbrainz_fill_artists(node);
+
+        node = json_getbyname(releasenode, "release-events");
+        if(node && node->type == json_array && node->u.array.length)
+            r->psz_date = json_dupstring(node->u.array.values[0], "date");
+    }
+
+
+    return true;
+}
+
+void musicbrainz_recording_release(musicbrainz_recording_t *mbr)
+{
+    for(size_t i=0; i<mbr->i_release; i++)
+    {
+        free(mbr->p_releases[i].psz_id);
+        free(mbr->p_releases[i].psz_group_id);
+        free(mbr->p_releases[i].psz_artist);
+        free(mbr->p_releases[i].psz_title);
+        free(mbr->p_releases[i].psz_date);
+        free(mbr->p_releases[i].psz_coverart_url);
+        for(size_t j=0; j<mbr->p_releases[i].i_tracks; j++)
+        {
+            free(mbr->p_releases[i].p_tracks[j].psz_title);
+            free(mbr->p_releases[i].p_tracks[j].psz_artist);
+        }
+        free(mbr->p_releases[i].p_tracks);
+    }
+    free(mbr->p_releases);
+    free(mbr);
+}
+
+static musicbrainz_recording_t *musicbrainz_lookup_recording_by_apiurl(vlc_object_t *obj,
+                                                                       const char *psz_url)
+{
+    musicbrainz_recording_t *r = calloc(1, sizeof(*r));
+    if(!r)
+        return NULL;
+
+    musicbrainz_lookup_t *lookup = musicbrainz_lookup(obj, psz_url);
+    if(!lookup)
+    {
+        free(r);
+        return NULL;
+    }
+
+    const json_value *releases = json_getbyname(lookup->root, "releases");
+    if (releases && releases->type == json_array &&
+        releases->u.array.length)
+    {
+        r->p_releases = calloc(releases->u.array.length, sizeof(*r->p_releases));
+        if(r->p_releases)
+        {
+            for(unsigned i=0; i<releases->u.array.length; i++)
+            {
+                json_value *node = releases->u.array.values[i];
+                musicbrainz_release_t *p_mbrel = &r->p_releases[r->i_release];
+                if (!node || node->type != json_object ||
+                    !musicbrainz_fill_release(node, p_mbrel))
+                    continue;
+
+                /* Try to find cover from other releases from the same group */
+                if(p_mbrel->psz_group_id && !p_mbrel->psz_coverart_url &&
+                   musicbrainz_has_cover_in_releasegroup(releases->u.array.values,
+                                                         releases->u.array.length,
+                                                         p_mbrel->psz_group_id))
+                {
+                    char *psz_art = coverartarchive_make_releasegroup_arturl(
+                                        COVERARTARCHIVE_DEFAULT_SERVER,
+                                        p_mbrel->psz_group_id );
+                    if(psz_art)
+                        p_mbrel->psz_coverart_url = psz_art;
+                }
+
+                r->i_release++;
+            }
+        }
+    }
+
+    musicbrainz_lookup_release(lookup);
+
+    return r;
+}
+
+static char *musicbrainz_build_discid_json_url(const char *psz_server,
+                                               const char *psz_disc_id,
+                                               const char *psz_tail)
+{
+    char *psz_url;
+    if(asprintf(&psz_url,
+                "https://%s/ws/2/discid/%s?"
+                "fmt=json"
+                "&inc=artist-credits+recordings+release-groups"
+                "&cdstubs=no"
+                "%s%s",
+                psz_server ? psz_server : MUSICBRAINZ_DEFAULT_SERVER,
+                psz_disc_id,
+                psz_tail ? "&" : "",
+                psz_tail ? psz_tail : "" ) > -1 )
+    {
+        return psz_url;
+    }
+    return NULL;
+}
+
+musicbrainz_recording_t *musicbrainz_lookup_recording_by_toc(musicbrainz_config_t *cfg,
+                                                             const char *psz_toc)
+{
+    char *psz_url = musicbrainz_build_discid_json_url(cfg->psz_mb_server, "-", psz_toc);
+    if(!psz_url)
+        return NULL;
+
+    musicbrainz_recording_t *r = musicbrainz_lookup_recording_by_apiurl(cfg->obj, psz_url);
+    free(psz_url);
+    return r;
+}
+
+musicbrainz_recording_t *musicbrainz_lookup_recording_by_discid(musicbrainz_config_t *cfg,
+                                                                const char *psz_disc_id)
+{
+    char *psz_url = musicbrainz_build_discid_json_url(cfg->psz_mb_server, psz_disc_id, NULL);
+    if(!psz_url)
+        return NULL;
+
+    musicbrainz_recording_t *r = musicbrainz_lookup_recording_by_apiurl(cfg->obj, psz_url);
+    free(psz_url);
+    return r;
+}
+
+char * coverartarchive_make_releasegroup_arturl(const char *psz_server, const char *psz_group_id)
+{
+    char *psz_art;
+    if(-1 < asprintf(&psz_art, "https://%s/release-group/%s/front",
+                     psz_server ? psz_server : COVERARTARCHIVE_DEFAULT_SERVER,
+                     psz_group_id))
+        return psz_art;
+    return NULL;
+}
+
+void musicbrainz_release_covert_art(coverartarchive_t *c)
+{
+    free(c);
+}
+
+coverartarchive_t * coverartarchive_lookup_releasegroup(musicbrainz_config_t *cfg, const char *psz_id)
+{
+    coverartarchive_t *c = calloc(1, sizeof(*c));
+    if(!c)
+        return NULL;
+
+    char *psz_url;
+    if(0 < asprintf(&psz_url, "https://%s/releasegroup/%s", cfg->psz_coverart_server, psz_id ))
+    {
+        return NULL;
+    }
+
+     musicbrainz_lookup_t *p_lookup = musicbrainz_lookup(cfg->obj, psz_url);
+     free(psz_url);
+
+     if(!p_lookup)
+     {
+         free(c);
+         return NULL;
+     }
+
+    return c;
+}
diff --git a/modules/misc/webservices/musicbrainz.h b/modules/misc/webservices/musicbrainz.h
new file mode 100644
index 0000000000..d30be92d7f
--- /dev/null
+++ b/modules/misc/webservices/musicbrainz.h
@@ -0,0 +1,73 @@
+/*****************************************************************************
+ * musicbrainz.h : Musicbrainz API lookup
+ *****************************************************************************
+ * Copyright (C) 2019 VideoLabs, VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef VLC_MUSICBRAINZ_H_
+#define VLC_MUSICBRAINZ_H_
+
+#define MUSICBRAINZ_DEFAULT_SERVER "musicbrainz.org"
+#define COVERARTARCHIVE_DEFAULT_SERVER "coverartarchive.org"
+
+typedef struct
+{
+    unsigned i_index;
+    char *psz_title;
+    char *psz_artist;
+} musicbrainz_track_t;
+
+typedef struct
+{
+    char *psz_id;
+    char *psz_group_id;
+    char *psz_title;
+    char *psz_artist;
+    /* https://github.com/metabrainz/mmd-schema/blob/master/schema/musicbrainz_mmd-2.0.rng
+       "def_incomplete-date" [0-9]{4}(-[0-9]{2})?(-[0-9]{2})? */
+    char *psz_date;
+    char *psz_coverart_url;
+    size_t i_tracks;
+    musicbrainz_track_t *p_tracks;
+} musicbrainz_release_t;
+
+typedef struct
+{
+    size_t i_release;
+    musicbrainz_release_t *p_releases;
+} musicbrainz_recording_t;
+
+typedef struct
+{
+    vlc_object_t *obj;
+    char *psz_mb_server;
+    char *psz_coverart_server;
+} musicbrainz_config_t;
+
+void musicbrainz_recording_release(musicbrainz_recording_t *);
+musicbrainz_recording_t * musicbrainz_lookup_recording_by_toc(musicbrainz_config_t *, const char *);
+musicbrainz_recording_t * musicbrainz_lookup_recording_by_discid(musicbrainz_config_t *, const char *);
+
+typedef struct
+{
+    char *psz_url;
+} coverartarchive_t;
+
+void musicbrainz_release_covert_art(coverartarchive_t *c);
+coverartarchive_t * coverartarchive_lookup_releasegroup(musicbrainz_config_t *, const char *);
+char * coverartarchive_make_releasegroup_arturl(const char *, const char *);
+
+#endif



More information about the vlc-commits mailing list