<!DOCTYPE html><html><head><title></title><style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}</style></head><body><div><br></div><div>On Wed, Mar 25, 2020, at 21:20, Kartik Ohri wrote:<br></div><blockquote type="cite" id="qt"><div dir="ltr">Hi! I wanted to ask if any other changes are required from my side or is this patch ready to be merged ?<br></div></blockquote><div><br></div><div>I first need to test the execution of this module<br></div><div> - with a valid/invalid ListenBrainz account</div><div> - with valid/invalid ListenBrainz url<br></div><div> - test interruption<br></div><div> - check with asan<br></div><div><br></div><div>Maybe, I will have time to do it tomorrow.<br></div><div><br></div><blockquote type="cite" id="qt"><div><br></div><div class="qt-gmail_quote"><div dir="ltr" class="qt-gmail_attr">On Tue, Mar 24, 2020 at 4:29 PM Kartik Ohri <<a href="mailto:kartikohri13@gmail.com">kartikohri13@gmail.com</a>> wrote:<br></div><blockquote class="qt-gmail_quote" style="margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0.8ex;border-left-color:rgb(204, 204, 204);border-left-style:solid;border-left-width:1px;padding-left:1ex;"><div>From: "Kartik Ohri <a href="mailto:kartikohri13@gmail.com" target="_blank">kartikohri13@gmail.com</a>" <<a href="mailto:kartikohri13@gmail.com" target="_blank">kartikohri13@gmail.com</a>><br></div><div> <br></div><div> VLC already has the audioscrobbler module to submit scrobbles to<br></div><div> <a href="http://last.fm" rel="noreferrer" target="_blank">last.fm</a> and other services with a similar API. This module extends<br></div><div> that functionality to allow submission of listens to ListenBrainz.<br></div><div> The existing audioscrobbler module is incompatible with ListenBrainz<br></div><div> due to difference in authentication procedures and REST API for<br></div><div> submissions.<br></div><div> <br></div><div> The term scrobble is a trademarked term by Last.fm, therefore the<br></div><div> term listen used instead. More information about ListenBrainz is<br></div><div> available at listenbrainz [dot] org.<br></div><div> ---<br></div><div>  modules/misc/Makefile.am    |   4 +<br></div><div>  modules/misc/listenbrainz.c | 544 ++++++++++++++++++++++++++++++++++++<br></div><div>  2 files changed, 548 insertions(+)<br></div><div>  create mode 100755 modules/misc/listenbrainz.c<br></div><div> <br></div><div> diff --git a/modules/misc/Makefile.am b/modules/misc/Makefile.am<br></div><div> index 78f9b09710..ed3ef24ee6 100644<br></div><div> --- a/modules/misc/Makefile.am<br></div><div> +++ b/modules/misc/Makefile.am<br></div><div> @@ -8,6 +8,10 @@ libaudioscrobbler_plugin_la_SOURCES = misc/audioscrobbler.c<br></div><div>  libaudioscrobbler_plugin_la_LIBADD = $(SOCKET_LIBS)<br></div><div>  misc_LTLIBRARIES += <a href="http://libaudioscrobbler_plugin.la" rel="noreferrer" target="_blank">libaudioscrobbler_plugin.la</a><br></div><div> <br></div><div> +liblistenbrainz_plugin_la_SOURCES = misc/listenbrainz.c<br></div><div> +liblistenbrainz_plugin_la_LIBADD = $(SOCKET_LIBS)<br></div><div> +misc_LTLIBRARIES += <a href="http://liblistenbrainz_plugin.la" rel="noreferrer" target="_blank">liblistenbrainz_plugin.la</a><br></div><div> +<br></div><div>  libexport_plugin_la_SOURCES = \<br></div><div>         misc/playlist/html.c \<br></div><div>         misc/playlist/m3u.c \<br></div><div> diff --git a/modules/misc/listenbrainz.c b/modules/misc/listenbrainz.c<br></div><div> new file mode 100755<br></div><div> index 0000000000..1e070358fb<br></div><div> --- /dev/null<br></div><div> +++ b/modules/misc/listenbrainz.c<br></div><div> @@ -0,0 +1,544 @@<br></div><div> +/*****************************************************************************<br></div><div> + * listenbrainz.c : ListenBrainz submission plugin<br></div><div> + * ListenBrainz Submit Listens API 1<br></div><div> + * <a href="https://api.listenbrainz.org/1/submit-listens" rel="noreferrer" target="_blank">https://api.listenbrainz.org/1/submit-listens</a><br></div><div> + *****************************************************************************<br></div><div> + * Copyright (C) 2020 VLC authors and VideoLAN<br></div><div> + *<br></div><div> + * Author: Kartik Ohri <kartikohri13 at gmail dot com><br></div><div> + *<br></div><div> + * This program is free software; you can redistribute it and/or modify it<br></div><div> + * under the terms of the GNU Lesser General Public License as published by<br></div><div> + * the Free Software Foundation; either version 2.1 of the License, or<br></div><div> + * (at your option) any later version.<br></div><div> + *<br></div><div> + * This program is distributed in the hope that it will be useful,<br></div><div> + * but WITHOUT ANY WARRANTY; without even the implied warranty of<br></div><div> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br></div><div> + * GNU Lesser General Public License for more details.<br></div><div> + *<br></div><div> + * You should have received a copy of the GNU Lesser General Public License<br></div><div> + * along with this program; if not, write to the Free Software Foundation,<br></div><div> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.<br></div><div> + *****************************************************************************/<br></div><div> +<br></div><div> +#ifdef HAVE_CONFIG_H<br></div><div> +#include "config.h"<br></div><div> +#endif<br></div><div> +<br></div><div> +#include <assert.h><br></div><div> +#include <time.h><br></div><div> +<br></div><div> +#include <vlc_common.h><br></div><div> +#include <vlc_plugin.h><br></div><div> +#include <vlc_interface.h><br></div><div> +#include <vlc_input_item.h><br></div><div> +#include <vlc_dialog.h><br></div><div> +#include <vlc_meta.h><br></div><div> +#include <vlc_memstream.h><br></div><div> +#include <vlc_stream.h><br></div><div> +#include <vlc_url.h><br></div><div> +#include <vlc_tls.h><br></div><div> +#include <vlc_player.h><br></div><div> +#include <vlc_playlist.h><br></div><div> +#include <vlc_vector.h><br></div><div> +#include <vlc_interrupt.h><br></div><div> +<br></div><div> +typedef struct listen_t {<br></div><div> +    char *psz_artist;<br></div><div> +    char *psz_title;<br></div><div> +    char *psz_album;<br></div><div> +    char *psz_track_number;<br></div><div> +    int i_length;<br></div><div> +    char *psz_musicbrainz_recording_id;<br></div><div> +    time_t date;<br></div><div> +} listen_t;<br></div><div> +<br></div><div> +typedef struct VLC_VECTOR (listen_t) vlc_vector_listen_t;<br></div><div> +<br></div><div> +struct intf_sys_t {<br></div><div> +    vlc_vector_listen_t queue;<br></div><div> +<br></div><div> +    vlc_player_t *player;<br></div><div> +    struct vlc_player_listener_id *player_listener;<br></div><div> +    struct vlc_player_timer_id *timer_listener;<br></div><div> +<br></div><div> +    vlc_mutex_t lock;<br></div><div> +    vlc_cond_t wait;                // song to submit event<br></div><div> +    vlc_thread_t thread;            // thread to submit song<br></div><div> +    vlc_interrupt_t *interrupt;<br></div><div> +    bool live;<br></div><div> +<br></div><div> +    vlc_url_t p_submit_url;         // where to submit data<br></div><div> +    char *psz_user_token;           // authentication token<br></div><div> +<br></div><div> +    listen_t p_current_song;<br></div><div> +    bool b_meta_read;               // check if song metadata is already read<br></div><div> +    vlc_tick_t time_played;<br></div><div> +};<br></div><div> +<br></div><div> +static int Open (vlc_object_t *);<br></div><div> +<br></div><div> +static void Close (vlc_object_t *);<br></div><div> +<br></div><div> +static void *Run (void *);<br></div><div> +<br></div><div> +#define USER_TOKEN_TEXT      N_("User token")<br></div><div> +#define USER_TOKEN_LONGTEXT  N_("The user token of your ListenBrainz account")<br></div><div> +#define URL_TEXT             N_("Submission URL")<br></div><div> +#define URL_LONGTEXT         N_("The URL set for an alternative ListenBrainz instance")<br></div><div> +<br></div><div> +/****************************************************************************<br></div><div> + * Module descriptor<br></div><div> + ****************************************************************************/<br></div><div> +<br></div><div> +vlc_module_begin ()<br></div><div> +    set_category(CAT_INTERFACE)<br></div><div> +    set_subcategory(SUBCAT_INTERFACE_CONTROL)<br></div><div> +    set_shortname (N_ ("ListenBrainz"))<br></div><div> +    set_description (N_ ("Submit listens to ListenBrainz"))<br></div><div> +    add_string("listenbrainz_user_token" , "" , USER_TOKEN_TEXT , USER_TOKEN_LONGTEXT , false)<br></div><div> +    add_string("listenbrainz_submission_url" , "<a href="http://api.listenbrainz.org" rel="noreferrer" target="_blank">api.listenbrainz.org</a>" , URL_TEXT , URL_LONGTEXT , false)<br></div><div> +    set_capability("interface" , 0)<br></div><div> +    set_callbacks(Open , Close)<br></div><div> +vlc_module_end ()<br></div><div> +<br></div><div> +static void DeleteSong (listen_t *p_song)<br></div><div> +{<br></div><div> +    FREENULL (p_song->psz_artist);<br></div><div> +    FREENULL (p_song->psz_album);<br></div><div> +    FREENULL (p_song->psz_title);<br></div><div> +    FREENULL (p_song->psz_musicbrainz_recording_id);<br></div><div> +    FREENULL (p_song->psz_track_number);<br></div><div> +    p_song->date = 0;<br></div><div> +}<br></div><div> +<br></div><div> +static void ReadMetaData (intf_thread_t *p_this , input_item_t *item)<br></div><div> +{<br></div><div> +    intf_sys_t *p_sys = p_this->p_sys;<br></div><div> +<br></div><div> +    if (item == NULL)<br></div><div> +        return;<br></div><div> +<br></div><div> +    vlc_mutex_lock (&p_sys->lock);<br></div><div> +<br></div><div> +    p_sys->b_meta_read = true;<br></div><div> +    time (&p_sys->p_current_song.date);<br></div><div> +<br></div><div> +#define RETRIEVE_METADATA(a , b) do {                                   \<br></div><div> +        char *psz_data = input_item_Get##b(item);                       \<br></div><div> +        if (psz_data && *psz_data)                                      \<br></div><div> +            a = vlc_uri_encode(psz_data);                               \<br></div><div> +        else                                                            \<br></div><div> +            msg_Dbg (p_this, #b" missing.");                            \<br></div><div> +        free(psz_data);                                                 \<br></div><div> +    } while (0)<br></div><div> +<br></div><div> +    RETRIEVE_METADATA(p_sys->p_current_song.psz_artist , Artist);<br></div><div> +    if (!p_sys->p_current_song.psz_artist)<br></div><div> +    {<br></div><div> +        DeleteSong (&p_sys->p_current_song);<br></div><div> +        goto error;<br></div><div> +    }<br></div><div> +<br></div><div> +    RETRIEVE_METADATA(p_sys->p_current_song.psz_title , Title);<br></div><div> +    if (!p_sys->p_current_song.psz_title)<br></div><div> +    {<br></div><div> +        DeleteSong (&p_sys->p_current_song);<br></div><div> +        goto error;<br></div><div> +    }<br></div><div> +<br></div><div> +    RETRIEVE_METADATA(p_sys->p_current_song.psz_album , Album);<br></div><div> +    RETRIEVE_METADATA(p_sys->p_current_song.psz_musicbrainz_recording_id , TrackID);<br></div><div> +    RETRIEVE_METADATA(p_sys->p_current_song.psz_track_number , TrackNum);<br></div><div> +    p_sys->p_current_song.i_length = SEC_FROM_VLC_TICK (input_item_GetDuration (item));<br></div><div> +    msg_Dbg (p_this , "Meta data registered");<br></div><div> +    vlc_cond_signal (&p_sys->wait);<br></div><div> +<br></div><div> +error:<br></div><div> +    vlc_mutex_unlock (&p_sys->lock);<br></div><div> +<br></div><div> +#undef RETRIEVE_METADATA<br></div><div> +}<br></div><div> +<br></div><div> +static void Enqueue (intf_thread_t *p_this)<br></div><div> +{<br></div><div> +    intf_sys_t *p_sys = p_this->p_sys;<br></div><div> +<br></div><div> +    p_sys->b_meta_read = false;<br></div><div> +    vlc_mutex_lock (&p_sys->lock);<br></div><div> +<br></div><div> +    if (EMPTY_STR(p_sys->p_current_song.psz_artist) ||<br></div><div> +        EMPTY_STR(p_sys->p_current_song.psz_title))<br></div><div> +    {<br></div><div> +        msg_Dbg (p_this , "Missing artist or title, not submitting");<br></div><div> +        goto error;<br></div><div> +    }<br></div><div> +<br></div><div> +    if (p_sys->p_current_song.i_length == 0)<br></div><div> +        p_sys->p_current_song.i_length = p_sys->time_played;<br></div><div> +<br></div><div> +    if (p_sys->time_played < 30)<br></div><div> +    {<br></div><div> +        msg_Dbg (p_this , "Song not listened long enough, not submitting");<br></div><div> +        goto error;<br></div><div> +    }<br></div><div> +<br></div><div> +    msg_Dbg (p_this , "Song will be submitted.");<br></div><div> +    vlc_vector_push (&p_sys->queue , p_sys->p_current_song);<br></div><div> +    vlc_cond_signal (&p_sys->wait);<br></div><div> +    vlc_mutex_unlock (&p_sys->lock);<br></div><div> +    return;<br></div><div> +<br></div><div> +error:<br></div><div> +    DeleteSong (&p_sys->p_current_song);<br></div><div> +    vlc_mutex_unlock (&p_sys->lock);<br></div><div> +}<br></div><div> +<br></div><div> +static void PlayerStateChanged (vlc_player_t *player , enum vlc_player_state state , void *data)<br></div><div> +{<br></div><div> +    intf_thread_t *intf = data;<br></div><div> +    intf_sys_t *p_sys = intf->p_sys;<br></div><div> +<br></div><div> +    if (vlc_player_GetVideoTrackCount (player))<br></div><div> +        return;<br></div><div> +<br></div><div> +    if (!p_sys->b_meta_read && state >= VLC_PLAYER_STATE_PLAYING)<br></div><div> +    {<br></div><div> +        input_item_t *item = vlc_player_GetCurrentMedia (p_sys->player);<br></div><div> +        ReadMetaData (intf , item);<br></div><div> +        return;<br></div><div> +    }<br></div><div> +<br></div><div> +    if (state == VLC_PLAYER_STATE_STOPPED)<br></div><div> +        Enqueue (intf);<br></div><div> +}<br></div><div> +<br></div><div> +static void OnTimerUpdate (const struct vlc_player_timer_point *value , void *data)<br></div><div> +{<br></div><div> +    intf_thread_t *intf = data;<br></div><div> +    intf_sys_t *p_sys = intf->p_sys;<br></div><div> +    p_sys->time_played = SEC_FROM_VLC_TICK (value->ts - VLC_TICK_0);<br></div><div> +}<br></div><div> +<br></div><div> +static void OnTimerStopped (vlc_tick_t system_date , void *data) { }<br></div><div> +<br></div><div> +static void OnCurrentMediaChanged (vlc_player_t *player , input_item_t *new_media , void *data)<br></div><div> +{<br></div><div> +    intf_thread_t *intf = data;<br></div><div> +    Enqueue (intf);<br></div><div> +<br></div><div> +    intf_sys_t *p_sys = intf->p_sys;<br></div><div> +    p_sys->b_meta_read = false;<br></div><div> +<br></div><div> +    if (!new_media || vlc_player_GetVideoTrackCount (player))<br></div><div> +        return;<br></div><div> +<br></div><div> +    p_sys->time_played = 0;<br></div><div> +    if (input_item_IsPreparsed (new_media))<br></div><div> +        ReadMetaData (intf , new_media);<br></div><div> +}<br></div><div> +<br></div><div> +static char *PreparePayload (intf_thread_t *p_this)<br></div><div> +{<br></div><div> +    intf_sys_t *p_sys = p_this->p_sys;<br></div><div> +    struct vlc_memstream payload;<br></div><div> +    vlc_memstream_open (&payload);<br></div><div> +<br></div><div> +    if (p_sys->queue.size == 1)<br></div><div> +        vlc_memstream_printf (&payload , "{\"listen_type\":\"single\",\"payload\":[");<br></div><div> +    else<br></div><div> +        vlc_memstream_printf (&payload , "{\"listen_type\":\"import\",\"payload\":[");<br></div><div> +<br></div><div> +    for (int i_song = 0 ; i_song < ( int ) p_sys->queue.size ; i_song++)<br></div><div> +    {<br></div><div> +        listen_t *p_song = &p_sys->queue.data[ i_song ];<br></div><div> +<br></div><div> +        vlc_memstream_printf (&payload , "{\"listened_at\": %"PRIu64 , ( uint64_t ) p_song->date);<br></div><div> +        vlc_memstream_printf (&payload , ", \"track_metadata\": {\"artist_name\": \"%s\", " ,<br></div><div> +                              vlc_uri_decode (p_song->psz_artist));<br></div><div> +        vlc_memstream_printf (&payload , " \"track_name\": \"%s\", " , vlc_uri_decode (p_song->psz_title));<br></div><div> +        if (!EMPTY_STR (p_song->psz_album))<br></div><div> +            vlc_memstream_printf (&payload , " \"release_name\": \"%s\"" , vlc_uri_decode (p_song->psz_album));<br></div><div> +        if (!EMPTY_STR (p_song->psz_musicbrainz_recording_id))<br></div><div> +            vlc_memstream_printf (&payload , ", \"additional_info\": {\"recording_mbid\":\"%s\"} " ,<br></div><div> +                                  vlc_uri_decode (p_song->psz_musicbrainz_recording_id));<br></div><div> +        vlc_memstream_printf (&payload , "}}");<br></div><div> +    }<br></div><div> +<br></div><div> +    vlc_memstream_printf (&payload , "]}");<br></div><div> +<br></div><div> +    int i_status = vlc_memstream_close (&payload);<br></div><div> +    if (!i_status)<br></div><div> +    {<br></div><div> +        msg_Dbg (p_this , "Payload: %s" , payload.ptr);<br></div><div> +        return payload.ptr;<br></div><div> +    }<br></div><div> +    else<br></div><div> +        return NULL;<br></div><div> +}<br></div><div> +<br></div><div> +static char *PrepareRequest (intf_thread_t *p_this , char *payload)<br></div><div> +{<br></div><div> +    intf_sys_t *p_sys = p_this->p_sys;<br></div><div> +    struct vlc_memstream request;<br></div><div> +<br></div><div> +    vlc_memstream_open (&request);<br></div><div> +    vlc_memstream_printf (&request , "POST %s HTTP/1.1\r\n" , p_sys->p_submit_url.psz_path);<br></div><div> +    vlc_memstream_printf (&request , "Host: %s\r\n" , p_sys->p_submit_url.psz_host);<br></div><div> +    vlc_memstream_printf (&request , "Authorization: Token %s\r\n" , p_sys->psz_user_token);<br></div><div> +    vlc_memstream_puts (&request , "User-Agent: "PACKAGE"/"VERSION"\r\n");<br></div><div> +    vlc_memstream_puts (&request , "Connection: close\r\n");<br></div><div> +    vlc_memstream_puts (&request , "Accept-Encoding: identity\r\n");<br></div><div> +    vlc_memstream_printf (&request , "Content-Length: %zu\r\n" , strlen (payload));<br></div><div> +    vlc_memstream_puts (&request , "\r\n");<br></div><div> +    vlc_memstream_puts (&request , payload);<br></div><div> +    vlc_memstream_puts (&request , "\r\n\r\n");<br></div><div> +<br></div><div> +    free (payload);<br></div><div> +<br></div><div> +    int i_status = vlc_memstream_close (&request);<br></div><div> +    if (!i_status)<br></div><div> +        return request.ptr;<br></div><div> +    else<br></div><div> +        return NULL;<br></div><div> +}<br></div><div> +<br></div><div> +static int SendRequest (intf_thread_t *p_this , char *request)<br></div><div> +{<br></div><div> +    uint8_t p_buffer[1024];<br></div><div> +    int i_ret;<br></div><div> +<br></div><div> +    intf_sys_t *p_sys = p_this->p_sys;<br></div><div> +    vlc_tls_client_t *creds = vlc_tls_ClientCreate (VLC_OBJECT (p_this));<br></div><div> +    vlc_tls_t *sock = vlc_tls_SocketOpenTLS (creds , p_sys->p_submit_url.psz_host , 443 , NULL , NULL , NULL);<br></div><div> +<br></div><div> +    if (sock == NULL)<br></div><div> +    {<br></div><div> +        free (request);<br></div><div> +        return VLC_EGENERIC;<br></div><div> +    }<br></div><div> +<br></div><div> +    i_ret = vlc_tls_Write (sock , request , strlen (request));<br></div><div> +    free (request);<br></div><div> +<br></div><div> +    if (i_ret == -1)<br></div><div> +    {<br></div><div> +        vlc_tls_Close (sock);<br></div><div> +        return VLC_EGENERIC;<br></div><div> +    }<br></div><div> +<br></div><div> +    i_ret = vlc_tls_Read (sock , p_buffer , sizeof (p_buffer) - 1 , false);<br></div><div> +    msg_Dbg (p_this , "Response: %s" , ( char * ) p_buffer);<br></div><div> +    vlc_tls_Close (sock);<br></div><div> +    if (i_ret <= 0)<br></div><div> +    {<br></div><div> +        msg_Warn (p_this , "No response");<br></div><div> +        return VLC_EGENERIC;<br></div><div> +    }<br></div><div> +    p_buffer[ i_ret ] = '\0';<br></div><div> +<br></div><div> +    if (strstr (( char * ) p_buffer , "200"))<br></div><div> +    {<br></div><div> +        vlc_vector_clear (&p_sys->queue);<br></div><div> +        msg_Dbg (p_this , "Submission successful!");<br></div><div> +        return 1;<br></div><div> +    }<br></div><div> +    else if (strstr (( char * ) p_buffer , "401"))<br></div><div> +        msg_Warn (p_this , "Authentication Error");<br></div><div> +    else<br></div><div> +        msg_Warn (p_this , "Invalid Request");<br></div><div> +<br></div><div> +    return VLC_EGENERIC;<br></div><div> +}<br></div><div> +<br></div><div> +static int Configure (intf_thread_t *p_intf)<br></div><div> +{<br></div><div> +    int i_ret;<br></div><div> +    char *psz_submission_url , *psz_url;<br></div><div> +    intf_sys_t *p_sys = p_intf->p_sys;<br></div><div> +<br></div><div> +    p_sys->psz_user_token = var_InheritString (p_intf , "listenbrainz_user_token");<br></div><div> +    if (EMPTY_STR (p_sys->psz_user_token))<br></div><div> +    {<br></div><div> +        free (p_sys->psz_user_token);<br></div><div> +        vlc_dialog_display_error (p_intf ,<br></div><div> +                                  _ ("ListenBrainz User Token not set") , "%s" ,<br></div><div> +                                  _ ("Please set a user token or disable the ListenBrainz plugin, and restart VLC.\n"<br></div><div> +                                     " Visit <a href="https://listenbrainz.org/profile/" rel="noreferrer" target="_blank">https://listenbrainz.org/profile/</a> to get a user token."));<br></div><div> +        free (p_sys);<br></div><div> +        return 0;<br></div><div> +    }<br></div><div> +<br></div><div> +    psz_submission_url = var_InheritString (p_intf , "listenbrainz_submission_url");<br></div><div> +    if (psz_submission_url)<br></div><div> +    {<br></div><div> +        i_ret = asprintf (&psz_url , "https://%s/1/submit-listens" , psz_submission_url);<br></div><div> +        free (psz_submission_url);<br></div><div> +        if (i_ret > 0)<br></div><div> +        {<br></div><div> +            vlc_UrlParse (&p_sys->p_submit_url , psz_url);<br></div><div> +            free (psz_url);<br></div><div> +            free (p_sys);<br></div><div> +            return 1;<br></div><div> +        }<br></div><div> +    }<br></div><div> +<br></div><div> +    vlc_dialog_display_error (p_intf ,<br></div><div> +                              _ ("ListenBrainz API URL Invalid") , "%s" ,<br></div><div> +                              _ ("Please set a valid endpoint URL. The default value is <a href="http://api.listenbrainz.org" rel="noreferrer" target="_blank">api.listenbrainz.org</a> ."));<br></div><div> +    free (p_sys);<br></div><div> +    return 0;<br></div><div> +}<br></div><div> +<br></div><div> +static int Open (vlc_object_t *p_this)<br></div><div> +{<br></div><div> +    intf_thread_t *p_intf = ( intf_thread_t * ) p_this;<br></div><div> +    intf_sys_t *p_sys = calloc (1 , sizeof (intf_sys_t));<br></div><div> +<br></div><div> +    if (!p_sys)<br></div><div> +        return VLC_ENOMEM;<br></div><div> +<br></div><div> +    p_intf->p_sys = p_sys;<br></div><div> +    p_sys->live = true;<br></div><div> +<br></div><div> +    if (!Configure (p_intf))<br></div><div> +        return VLC_EGENERIC;<br></div><div> +<br></div><div> +    static struct vlc_player_cbs const player_cbs =<br></div><div> +        {<br></div><div> +            .on_state_changed = PlayerStateChanged ,<br></div><div> +            .on_current_media_changed = OnCurrentMediaChanged ,<br></div><div> +        };<br></div><div> +    static struct vlc_player_timer_cbs const timer_cbs =<br></div><div> +        {<br></div><div> +            .on_update = OnTimerUpdate ,<br></div><div> +            .on_discontinuity = OnTimerStopped ,<br></div><div> +        };<br></div><div> +<br></div><div> +    vlc_playlist_t *playlist = vlc_intf_GetMainPlaylist (p_intf);<br></div><div> +    p_sys->player = vlc_playlist_GetPlayer (playlist);<br></div><div> +<br></div><div> +    vlc_player_Lock (p_sys->player);<br></div><div> +    p_sys->player_listener = vlc_player_AddListener (p_sys->player , &player_cbs , p_intf);<br></div><div> +    vlc_player_Unlock (p_sys->player);<br></div><div> +<br></div><div> +    if (!p_sys->player_listener)<br></div><div> +        goto error;<br></div><div> +<br></div><div> +    p_sys->timer_listener = vlc_player_AddTimer (p_sys->player , VLC_TICK_FROM_SEC (1) , &timer_cbs , p_intf);<br></div><div> +    if (!p_sys->timer_listener)<br></div><div> +        goto error;<br></div><div> +<br></div><div> +    p_sys->interrupt = vlc_interrupt_create();<br></div><div> +    if (unlikely(p_sys->interrupt == NULL))<br></div><div> +        goto error;<br></div><div> +<br></div><div> +    vlc_mutex_init (&p_sys->lock);<br></div><div> +    vlc_cond_init (&p_sys->wait);<br></div><div> +<br></div><div> +    if (vlc_clone (&p_sys->thread , Run , p_intf , VLC_THREAD_PRIORITY_LOW))<br></div><div> +    {<br></div><div> +        vlc_interrupt_destroy(p_sys->interrupt);<br></div><div> +        goto error;<br></div><div> +    }<br></div><div> +<br></div><div> +    return VLC_SUCCESS;<br></div><div> +<br></div><div> +error:<br></div><div> +    vlc_player_Lock (p_sys->player);<br></div><div> +    if (p_sys->player_listener)<br></div><div> +        vlc_player_RemoveListener (p_sys->player , p_sys->player_listener);<br></div><div> +    if (p_sys->timer_listener)<br></div><div> +        vlc_player_RemoveTimer (p_sys->player , p_sys->timer_listener);<br></div><div> +    vlc_player_Unlock (p_sys->player);<br></div><div> +    free (p_sys);<br></div><div> +    return VLC_EGENERIC;<br></div><div> +}<br></div><div> +<br></div><div> +static void Close (vlc_object_t *p_this)<br></div><div> +{<br></div><div> +    intf_thread_t *p_intf = ( intf_thread_t * ) p_this;<br></div><div> +    intf_sys_t *p_sys = p_intf->p_sys;<br></div><div> +<br></div><div> +    vlc_mutex_lock(&p_sys->lock);<br></div><div> +    p_sys->live = false;<br></div><div> +    vlc_cond_signal (&p_sys->wait);<br></div><div> +    vlc_mutex_unlock(&p_sys->lock);<br></div><div> +<br></div><div> +    vlc_interrupt_kill(p_sys->interrupt);<br></div><div> +    vlc_join (p_sys->thread , NULL);<br></div><div> +    vlc_interrupt_destroy(p_sys->interrupt);<br></div><div> +<br></div><div> +    vlc_vector_clear (&p_sys->queue);<br></div><div> +    vlc_UrlClean (&p_sys->p_submit_url);<br></div><div> +<br></div><div> +    vlc_player_Lock (p_sys->player);<br></div><div> +    vlc_player_RemoveListener (p_sys->player , p_sys->player_listener);<br></div><div> +    vlc_player_Unlock (p_sys->player);<br></div><div> +<br></div><div> +    vlc_player_RemoveTimer (p_sys->player , p_sys->timer_listener);<br></div><div> +<br></div><div> +    free (p_sys);<br></div><div> +}<br></div><div> +<br></div><div> +static void *Run (void *data)<br></div><div> +{<br></div><div> +    intf_thread_t *p_intf = data;<br></div><div> +    bool b_wait = 0;<br></div><div> +    char *request , *payload;<br></div><div> +<br></div><div> +    intf_sys_t *p_sys = p_intf->p_sys;<br></div><div> +<br></div><div> +    vlc_interrupt_set(p_sys->interrupt);<br></div><div> +<br></div><div> +    for (;;)<br></div><div> +    {<br></div><div> +        vlc_mutex_lock (&p_sys->lock);<br></div><div> +        if (b_wait)<br></div><div> +        {<br></div><div> +            vlc_tick_t deadline = vlc_tick_now() + VLC_TICK_FROM_SEC (60);<br></div><div> +            int ret = 0;<br></div><div> +            while (p_sys->live && ret == 0) // wait for 1 min<br></div><div> +                ret = vlc_cond_timedwait (&p_sys->wait , &p_sys->lock, deadline);<br></div><div> +        }<br></div><div> +<br></div><div> +        while (p_sys->live && p_sys->queue.size == 0)<br></div><div> +            vlc_cond_wait (&p_sys->wait , &p_sys->lock);<br></div><div> +<br></div><div> +        if (!p_sys->live)<br></div><div> +        {<br></div><div> +            vlc_mutex_unlock (&p_sys->lock);<br></div><div> +            return NULL;<br></div><div> +        }<br></div><div> +<br></div><div> +        payload = PreparePayload (p_intf);<br></div><div> +        vlc_mutex_unlock (&p_sys->lock);<br></div><div> +<br></div><div> +        if (!payload)<br></div><div> +        {<br></div><div> +            msg_Warn (p_intf , "Error: Unable to generate payload");<br></div><div> +            break;<br></div><div> +        }<br></div><div> +<br></div><div> +        request = PrepareRequest (p_intf , payload);<br></div><div> +        if (!request)<br></div><div> +        {<br></div><div> +            msg_Warn (p_intf , "Error: Unable to generate request body");<br></div><div> +            break;<br></div><div> +        }<br></div><div> +<br></div><div> +        int i_status = SendRequest (p_intf , request);<br></div><div> +        if (i_status)<br></div><div> +        {<br></div><div> +            msg_Warn (p_intf , "Error: Could not transmit request");<br></div><div> +            b_wait = 1;<br></div><div> +            continue;<br></div><div> +        }<br></div><div> +<br></div><div> +        b_wait = 0;<br></div><div> +    }<br></div><div> +<br></div><div> +    return NULL;<br></div><div> +}<br></div><div> +<br></div><div> -- <br></div><div> 2.20.1<br></div><div> <br></div></blockquote></div><div>_______________________________________________<br></div><div>vlc-devel mailing list<br></div><div>To unsubscribe or modify your subscription options:<br></div><div>https://mailman.videolan.org/listinfo/vlc-devel<br></div></blockquote><div><br></div></body></html>