Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc-gpu
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc-gpu
Commits
246a4e23
Commit
246a4e23
authored
Sep 17, 2006
by
Antoine Cellerier
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New audioscrobbler/last.fm plugin by Rafaël Carré and Kenneth Ostby.
parent
a972b8b7
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1153 additions
and
1 deletion
+1153
-1
NEWS
NEWS
+3
-0
THANKS
THANKS
+2
-0
configure.ac
configure.ac
+8
-1
modules/misc/Modules.am
modules/misc/Modules.am
+1
-0
modules/misc/audioscrobbler.c
modules/misc/audioscrobbler.c
+1139
-0
No files found.
NEWS
View file @
246a4e23
...
...
@@ -18,6 +18,9 @@ Interfaces:
- right/ctrl-click menu in video outputs
- support for Apple's remote (courtesy Martin Kahr)
Other plugins:
* Audioscrobbler / last.fm plugin
Nix Port:
* Notification plugin.
...
...
THANKS
View file @
246a4e23
...
...
@@ -89,6 +89,7 @@ John Paul Lorenti <jpl31 at columbia.edu> - ALSA device selection patch
Jonas Larsen <jonas at vrt.dk> - Danish translation
Julien Blache <jb at technologeek.org> - disc ejection code
kty0ne - WinAmp 5 skin for VLC
Kenneth Ostby <kenneo -at- idi -dot- ntnu -dot- no> - Audioscrobbler plugin
Kevin H. Patterson <kevpatt at khptech dot com> - Theora framesize calculation patch by Kevin H. Patterson (kevpatt at khptech dot com
K. Staring <qdk at quickdekay dot net> - RTSP rewind and fast-forward support
Laurent Jonqueres <laurent_jonqueres at yahoo.fr> - Occitan localization
...
...
@@ -124,6 +125,7 @@ Pavlov Konstantin “thresh” - several Linux build system fixes
Petr Vacek - FTP cleartext authentication
Philippe Van Hecke <philippe at belnet dot be> - SAP header hash patch
Pierre Marc Dumuid <pierre.dumuid at adelaide dot edu dot au> - Playlist patches
Rafaël Carré <rafael -dot- carre -at- wanadoo -dot- fr> - Audioscrobbler plugin
Régis Duchesne <regis at via.ecp.fr> - original VLC code
Remco Poortinga <poortinga at telin.nl> - IPv6 multicast patch
Rene Gollent <rgollent at u.arizona.edu> - BeOS interface fix
...
...
configure.ac
View file @
246a4e23
...
...
@@ -1549,8 +1549,15 @@ AS_IF([test "${enable_notify}" != "no"], [
])
])
dnl
dnl Audioscrobbler plugin
dnl
AC_ARG_ENABLE(audioscrobbler,
[ --enable-audioscrobbler Last.fm submission plugin (default enabled)])
AS_IF([test "${enable_audioscrobbler}" != "no"], [
VLC_ADD_PLUGINS([audioscrobbler])
])
dnl
dnl Input plugins
...
...
modules/misc/Modules.am
View file @
246a4e23
...
...
@@ -11,3 +11,4 @@ SOURCES_vod_rtsp = rtsp.c
SOURCES_gnutls = gnutls.c
SOURCES_svg = svg.c
SOURCES_profile_parser = profile_parser.c
SOURCES_audioscrobbler = audioscrobbler.c
modules/misc/audioscrobbler.c
0 → 100644
View file @
246a4e23
/*****************************************************************************
* audioscrobbler.c : audioscrobbler submission plugin
*****************************************************************************
* Copyright (C) 2006 the VideoLAN team
* $Id$
*
* Authors: Rafal Carr <rafael -dot- carre -at- wanadoo -dot- fr>
* Kenneth Ostby <kenneo -at- idi -dot- ntnu -dot- no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#define _GNU_SOURCE
#include <string.h>
#if !defined( strlwr ) && !defined( WIN32 )
#include <ctype.h>
#endif
#if defined( WIN32 )
#include <time.h>
#endif
/*
* TODO :
* review/replace :
* hexa()
* md5 to char[]
*/
#include <vlc/vlc.h>
#include <vlc/intf.h>
#include <vlc_meta.h>
#include <vlc_md5.h>
#include <vlc_block.h>
#include <vlc_stream.h>
#include <vlc_url.h>
#include <network.h>
/*****************************************************************************
* Local prototypes
*****************************************************************************/
/* Keeps track of metadata to be submitted, and if song has been submitted */
typedef
struct
audioscrobbler_song_t
{
char
*
psz_a
;
/* track artist */
char
*
psz_t
;
/* track title */
char
*
psz_b
;
/* track album */
int
i_l
;
/* track length */
/* vlc can't retrieve musicbrainz id, so let's ignore it */
/* int i_m; */
/* musicbrainz id */
char
*
psz_i
;
/* date */
time_t
time_playing
;
/* date (epoch) */
}
audioscrobbler_song_t
;
/* Queue to be submitted to server, 10 songs max */
typedef
struct
audioscrobbler_queue_t
{
audioscrobbler_song_t
**
p_queue
;
/* contains up to 10 songs */
int
i_songs_nb
;
/* number of songs */
void
*
p_next_queue
;
/* if queue full, pointer to next */
}
audioscrobbler_queue_t
;
struct
intf_sys_t
{
audioscrobbler_queue_t
*
p_first_queue
;
/* 1st queue */
vlc_mutex_t
lock
;
/* p_sys mutex */
/* data about audioscrobbler session */
int
i_interval
;
/* last interval recorded */
time_t
time_last_interval
;
/* when was it recorded ? */
char
*
psz_submit_host
;
/* where to submit data ? */
int
i_submit_port
;
/* at which port ? */
char
*
psz_submit_file
;
/* in which file ? */
char
*
psz_username
;
/* last.fm username */
vlc_bool_t
b_handshaked
;
/* did we handshake ? */
int
i_post_socket
;
/* socket for submission */
char
*
psz_response_md5
;
/* md5 response to use */
/* data about input elements */
input_thread_t
*
p_input
;
/* previous p_input */
audioscrobbler_song_t
*
p_current_song
;
/* song being played */
time_t
time_pause
;
/* time when vlc paused */
time_t
time_total_pauses
;
/* sum of time in pause */
vlc_bool_t
b_queued
;
/* has it been queud ? */
vlc_bool_t
b_metadata_read
;
/* did we read metadata ? */
vlc_bool_t
b_paused
;
/* are we playing ? */
};
intf_sys_t
*
p_sys_global
;
/* for use same p_sys in all threads */
static
int
Open
(
vlc_object_t
*
);
static
void
Close
(
vlc_object_t
*
);
static
void
Run
(
intf_thread_t
*
);
static
int
ItemChange
(
vlc_object_t
*
,
const
char
*
,
vlc_value_t
,
vlc_value_t
,
void
*
);
static
int
PlayingChange
(
vlc_object_t
*
,
const
char
*
,
vlc_value_t
,
vlc_value_t
,
void
*
);
static
int
AddToQueue
(
intf_thread_t
*
p_this
);
static
int
Handshake
(
intf_thread_t
*
p_sd
);
static
int
ReadMetaData
(
intf_thread_t
*
p_this
);
void
DeleteQueue
(
audioscrobbler_queue_t
*
p_queue
);
char
*
hexa
(
short
int
i
);
#if !defined(strlwr) && !defined( WIN32 )
char
*
strlwr
(
char
*
psz_string
);
#endif
/*****************************************************************************
* Module descriptor
****************************************************************************/
#define APPLICATION_NAME "VLC media player"
#define USERNAME_TEXT N_("Username")
#define USERNAME_LONGTEXT N_("Audioscrobbler username")
#define PASSWORD_TEXT N_("Password")
#define PASSWORD_LONGTEXT N_("Audioscrobbler password")
#define DEFAULT_INTERVAL 60
#define CLIENT_NAME PACKAGE
#define CLIENT_VERSION VERSION
/* HTTP POST request : to submit data */
#define POST_REQUEST "POST /%s HTTP/1.1\n" \
"Accept-Encoding: identity\n" \
"Content-length: %d\n" \
"Connection: close\n" \
"Content-type: application/x-www-form-urlencoded\n" \
"Host: %s\n" \
"User-agent: VLC Media Player/%s\r\n" \
"\r\n" \
"%s\r\n" \
"\r\n"
/* data to submit */
#define POST_DATA "u=%s&s=%s&a%%5B%d%%5D=%s&t%%5B%d%%5D=%s" \
"&b%%5B%d%%5D=%s&m%%5B%d%%5D=&l%%5B%d%%5D=%d&i%%5B%d%%5D=%s"
vlc_module_begin
();
set_category
(
CAT_INTERFACE
);
set_subcategory
(
SUBCAT_INTERFACE_CONTROL
);
set_shortname
(
N_
(
"Audioscrobbler"
)
);
set_description
(
_
(
"Audioscrobbler submission Plugin"
)
);
add_string
(
"lastfm-username"
,
""
,
NULL
,
USERNAME_TEXT
,
USERNAME_LONGTEXT
,
VLC_TRUE
);
add_string
(
"lastfm-password"
,
""
,
NULL
,
PASSWORD_TEXT
,
PASSWORD_LONGTEXT
,
VLC_TRUE
);
set_capability
(
"interface"
,
0
);
set_callbacks
(
Open
,
Close
);
vlc_module_end
();
/*****************************************************************************
* Open: initialize and create stuff
*****************************************************************************/
static
int
Open
(
vlc_object_t
*
p_this
)
{
intf_thread_t
*
p_intf
;
intf_sys_t
*
p_sys
;
playlist_t
*
p_playlist
;
p_intf
=
(
intf_thread_t
*
)
p_this
;
p_sys
=
malloc
(
sizeof
(
intf_sys_t
)
);
if
(
!
p_sys
)
{
goto
error
;
}
vlc_mutex_init
(
p_this
,
&
p_sys
->
lock
);
p_sys_global
=
p_sys
;
p_sys
->
psz_submit_host
=
NULL
;
p_sys
->
psz_submit_file
=
NULL
;
p_sys
->
b_handshaked
=
VLC_FALSE
;
p_sys
->
i_interval
=
0
;
p_sys
->
time_last_interval
=
time
(
NULL
);
p_sys
->
psz_username
=
NULL
;
p_sys
->
p_input
=
NULL
;
p_sys
->
b_paused
=
VLC_FALSE
;
/* md5 response is 32 chars, + final \0 */
p_sys
->
psz_response_md5
=
malloc
(
sizeof
(
char
)
*
33
);
if
(
!
p_sys
->
psz_response_md5
)
{
vlc_mutex_destroy
(
&
p_sys
->
lock
);
goto
error
;
}
p_sys
->
p_first_queue
=
malloc
(
sizeof
(
audioscrobbler_queue_t
)
);
if
(
!
p_sys
->
p_first_queue
)
{
vlc_mutex_destroy
(
&
p_sys
->
lock
);
goto
error
;
}
p_sys
->
p_current_song
=
malloc
(
sizeof
(
audioscrobbler_song_t
)
);
if
(
!
p_sys
->
p_current_song
)
{
vlc_mutex_destroy
(
&
p_sys
->
lock
);
goto
error
;
}
/* queues can't contain more than 10 songs */
p_sys
->
p_first_queue
->
p_queue
=
malloc
(
10
*
sizeof
(
audioscrobbler_song_t
)
);
if
(
!
p_sys
->
p_current_song
)
{
vlc_mutex_destroy
(
&
p_sys
->
lock
);
goto
error
;
}
p_sys
->
p_first_queue
->
i_songs_nb
=
0
;
p_sys
->
p_first_queue
->
p_next_queue
=
NULL
;
p_playlist
=
pl_Yield
(
p_intf
);
var_AddCallback
(
p_playlist
,
"playlist-current"
,
ItemChange
,
p_intf
);
pl_Release
(
p_playlist
);
p_intf
->
pf_run
=
Run
;
return
VLC_SUCCESS
;
error:
free
(
p_sys
->
p_current_song
);
free
(
p_sys
->
p_first_queue
);
free
(
p_sys
->
psz_response_md5
);
free
(
p_sys
);
return
VLC_ENOMEM
;
}
/*****************************************************************************
* Close: destroy interface stuff
*****************************************************************************/
static
void
Close
(
vlc_object_t
*
p_this
)
{
intf_thread_t
*
p_intf
;
intf_sys_t
*
p_sys
;
audioscrobbler_queue_t
*
p_current_queue
,
*
p_next_queue
;
playlist_t
*
p_playlist
;
p_intf
=
(
intf_thread_t
*
)
p_this
;
p_sys
=
p_intf
->
p_sys
;
p_playlist
=
pl_Yield
(
p_intf
);
var_DelCallback
(
p_playlist
,
"playlist-current"
,
ItemChange
,
p_intf
);
pl_Release
(
p_playlist
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_current_queue
=
p_sys
->
p_first_queue
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
while
(
(
p_current_queue
->
i_songs_nb
==
10
)
&&
(
p_current_queue
->
p_next_queue
!=
NULL
)
)
{
p_next_queue
=
p_current_queue
->
p_next_queue
;
DeleteQueue
(
p_current_queue
);
free
(
p_current_queue
);
p_current_queue
=
p_next_queue
;
}
DeleteQueue
(
p_current_queue
);
free
(
p_current_queue
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
if
(
p_sys
->
psz_username
)
{
free
(
p_sys
->
psz_username
);
}
free
(
p_sys
->
p_current_song
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
vlc_mutex_destroy
(
&
p_sys
->
lock
);
free
(
p_sys
);
}
/*****************************************************************************
* Run : Handshake with audioscrobbler, then submit songs
*****************************************************************************/
static
void
Run
(
intf_thread_t
*
p_this
)
{
char
*
psz_submit_string
=
NULL
;
int
i_handshake
;
int
i_netprintf
;
int
i_song
;
playlist_t
*
p_playlist
;
uint8_t
*
p_buffer
=
NULL
;
char
*
p_buffer_pos
=
NULL
;
time_t
played_time
;
audioscrobbler_queue_t
*
p_first_queue
;
intf_sys_t
*
p_sys
;
p_this
->
p_sys
=
p_sys_global
;
p_sys
=
p_this
->
p_sys
;
while
(
!
p_this
->
b_die
)
{
if
(
p_sys
->
b_handshaked
==
VLC_FALSE
)
{
if
(
time
(
NULL
)
>=
(
p_sys
->
time_last_interval
+
p_sys
->
i_interval
)
)
{
msg_Dbg
(
p_this
,
"Handshaking with last.fm ..."
);
i_handshake
=
Handshake
(
p_this
);
if
(
i_handshake
==
VLC_ENOMEM
)
{
msg_Err
(
p_this
,
"Out of memory"
);
return
;
}
else
if
(
i_handshake
==
VLC_ENOVAR
)
/* username not set */
{
msg_Dbg
(
p_this
,
"Set an username then restart vlc"
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
;
}
else
if
(
i_handshake
==
VLC_SUCCESS
)
{
msg_Dbg
(
p_this
,
"Handshake successfull :)"
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_sys
->
b_handshaked
=
VLC_TRUE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
else
{
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_sys
->
i_interval
=
DEFAULT_INTERVAL
;
time
(
&
p_sys
->
time_last_interval
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
}
}
else
{
if
(
(
p_sys
->
p_first_queue
->
i_songs_nb
>
0
)
&&
(
time
(
NULL
)
>=
(
p_sys
->
time_last_interval
+
p_sys
->
i_interval
)
)
)
{
msg_Dbg
(
p_this
,
"Going to submit some data..."
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
psz_submit_string
=
malloc
(
2048
*
sizeof
(
char
)
);
if
(
!
psz_submit_string
)
{
msg_Err
(
p_this
,
"Out of memory"
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
;
}
for
(
i_song
=
0
;
i_song
<
p_sys
->
p_first_queue
->
i_songs_nb
;
i_song
++
)
{
snprintf
(
psz_submit_string
,
2048
,
POST_DATA
,
p_sys
->
psz_username
,
p_sys
->
psz_response_md5
,
i_song
,
p_sys
->
p_first_queue
->
p_queue
[
i_song
]
->
psz_a
,
i_song
,
p_sys
->
p_first_queue
->
p_queue
[
i_song
]
->
psz_t
,
i_song
,
p_sys
->
p_first_queue
->
p_queue
[
i_song
]
->
psz_b
,
i_song
,
i_song
,
p_sys
->
p_first_queue
->
p_queue
[
i_song
]
->
i_l
,
i_song
,
p_sys
->
p_first_queue
->
p_queue
[
i_song
]
->
psz_i
);
}
p_sys
->
i_post_socket
=
net_ConnectTCP
(
p_this
,
p_sys
->
psz_submit_host
,
p_sys
->
i_submit_port
);
i_netprintf
=
net_Printf
(
VLC_OBJECT
(
p_this
),
p_sys
->
i_post_socket
,
NULL
,
POST_REQUEST
,
p_sys
->
psz_submit_file
,
strlen
(
psz_submit_string
),
p_sys
->
psz_submit_file
,
VERSION
,
psz_submit_string
);
if
(
i_netprintf
==
-
1
)
{
/* If connection fails, we assume we must handshake again */
p_sys
->
i_interval
=
DEFAULT_INTERVAL
;
time
(
&
p_sys
->
time_last_interval
);
p_sys
->
b_handshaked
=
VLC_FALSE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
;
}
p_buffer
=
(
uint8_t
*
)
calloc
(
1
,
1024
);
if
(
!
p_buffer
)
{
msg_Err
(
p_this
,
"Out of memory"
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
;
}
net_Read
(
p_this
,
p_sys
->
i_post_socket
,
NULL
,
p_buffer
,
1024
,
VLC_FALSE
);
net_Close
(
p_sys
->
i_post_socket
);
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"INTERVAL"
);
if
(
p_buffer_pos
)
{
p_sys
->
i_interval
=
atoi
(
p_buffer_pos
+
strlen
(
"INTERVAL "
)
);
time
(
&
p_sys
->
time_last_interval
);
}
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"FAILED"
);
if
(
p_buffer_pos
)
{
msg_Err
(
p_this
,
p_buffer_pos
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
;
}
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"BADAUTH"
);
if
(
p_buffer_pos
)
{
msg_Err
(
p_this
,
"Authentification failed, handshaking again"
);
p_sys
->
b_handshaked
=
VLC_FALSE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
;
}
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"OK"
);
if
(
p_buffer_pos
)
{
if
(
p_sys
->
p_first_queue
->
i_songs_nb
==
10
)
{
p_first_queue
=
p_sys
->
p_first_queue
->
p_next_queue
;
DeleteQueue
(
p_sys
->
p_first_queue
);
free
(
p_sys
->
p_first_queue
);
p_sys
->
p_first_queue
=
p_first_queue
;
}
else
{
DeleteQueue
(
p_sys
->
p_first_queue
);
p_sys
->
p_first_queue
->
i_songs_nb
=
0
;
}
msg_Dbg
(
p_this
,
"Submission successfull!"
);
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
}
msleep
(
INTF_IDLE_SLEEP
);
p_playlist
=
pl_Yield
(
p_this
);
PL_LOCK
;
if
(
p_playlist
->
request
.
i_status
==
PLAYLIST_STOPPED
)
{
PL_UNLOCK
;
/* if we stopped, we won't submit playing song */
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_sys
->
b_queued
=
VLC_TRUE
;
p_sys
->
b_metadata_read
=
VLC_TRUE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
pl_Release
(
p_playlist
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
if
(
p_sys
->
b_metadata_read
==
VLC_FALSE
)
{
time
(
&
played_time
);
played_time
-=
p_sys
->
p_current_song
->
time_playing
;
played_time
-=
p_sys
->
time_total_pauses
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
/* ok now we can read meta data */
if
(
played_time
>
10
)
{
ReadMetaData
(
p_this
);
}
}
else
{
if
(
(
p_sys
->
b_queued
==
VLC_FALSE
)
&&
(
p_sys
->
b_paused
==
VLC_FALSE
)
)
{
vlc_mutex_unlock
(
&
p_sys
->
lock
);
if
(
AddToQueue
(
p_this
)
==
VLC_ENOMEM
)
{
msg_Err
(
p_this
,
"Out of memory"
);
return
;
}
}
else
{
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
}
}
}
/*****************************************************************************
* PlayingChange: Playing status change callback
*****************************************************************************/
static
int
PlayingChange
(
vlc_object_t
*
p_this
,
const
char
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
p_data
)
{
intf_thread_t
*
p_intf
;
intf_sys_t
*
p_sys
;
p_intf
=
(
intf_thread_t
*
)
p_data
;
p_sys
=
p_intf
->
p_sys
;
(
void
)
p_this
;
(
void
)
psz_var
;
(
void
)
oldval
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
if
(
newval
.
i_int
==
PAUSE_S
)
{
time
(
&
p_sys
->
time_pause
);
p_sys
->
b_paused
=
VLC_TRUE
;
}
if
(
newval
.
i_int
==
PLAYING_S
)
{
p_sys
->
time_total_pauses
+=
time
(
NULL
)
-
p_sys
->
time_pause
;
p_sys
->
b_paused
=
VLC_TRUE
;
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
}
/*****************************************************************************
* ItemChange: Playlist item change callback
*****************************************************************************/
static
int
ItemChange
(
vlc_object_t
*
p_this
,
const
char
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
p_data
)
{
playlist_t
*
p_playlist
;
input_thread_t
*
p_input
=
NULL
;
intf_thread_t
*
p_intf
;
intf_sys_t
*
p_sys
;
time_t
epoch
;
struct
tm
*
epoch_tm
;
char
psz_date
[
20
];
(
void
)
p_this
;
(
void
)
psz_var
;
(
void
)
oldval
;
(
void
)
newval
;
p_intf
=
(
intf_thread_t
*
)
p_data
;
p_sys
=
p_intf
->
p_sys
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
if
(
p_sys
->
p_input
)
{
/* we delete the callback for the old p_input */
var_DelCallback
(
p_sys
->
p_input
,
"state"
,
PlayingChange
,
p_intf
);
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
p_playlist
=
pl_Yield
(
p_intf
);
PL_LOCK
;
p_input
=
p_playlist
->
p_input
;
if
(
!
p_input
)
{
PL_UNLOCK
;
pl_Release
(
p_playlist
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
/* we won't read p_input */
p_sys
->
b_queued
=
VLC_TRUE
;
p_sys
->
b_metadata_read
=
VLC_TRUE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
}
vlc_object_yield
(
p_input
);
PL_UNLOCK
;
pl_Release
(
p_playlist
);
var_AddCallback
(
p_input
,
"state"
,
PlayingChange
,
p_intf
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
/* reset pause counter */
p_sys
->
time_total_pauses
=
0
;
/* we save the p_input value to delete the callback later */
p_sys
->
p_input
=
p_input
;
/* we'll read after to be sure it's present */
p_sys
->
b_metadata_read
=
VLC_FALSE
;
p_sys
->
b_queued
=
VLC_TRUE
;
time
(
&
epoch
);
epoch_tm
=
gmtime
(
&
epoch
);
snprintf
(
psz_date
,
20
,
"%.4d-%.2d-%.2d %.2d:%.2d:%.2d"
,
epoch_tm
->
tm_year
+
1900
,
epoch_tm
->
tm_mon
+
1
,
epoch_tm
->
tm_mday
,
epoch_tm
->
tm_hour
,
epoch_tm
->
tm_min
,
epoch_tm
->
tm_sec
);
p_sys
->
p_current_song
->
psz_i
=
encode_URI_component
(
psz_date
);
p_sys
->
p_current_song
->
time_playing
=
epoch
;
p_sys
->
b_paused
=
(
p_input
->
b_dead
||
!
p_input
->
input
.
p_item
->
psz_name
)
?
VLC_TRUE
:
VLC_FALSE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
vlc_object_release
(
p_input
);
return
VLC_SUCCESS
;
}
/*****************************************************************************
* AddToQueue: Add the played song to the queue to be submitted
*****************************************************************************/
static
int
AddToQueue
(
intf_thread_t
*
p_this
)
{
int
i_songs_nb
;
time_t
played_time
;
audioscrobbler_queue_t
*
p_queue
=
NULL
,
*
p_next_queue
=
NULL
;
intf_sys_t
*
p_sys
;
p_sys
=
p_this
->
p_sys
;
/* has it been queued already ? is it long enough ? */
vlc_mutex_lock
(
&
p_sys
->
lock
);
if
(
p_sys
->
p_current_song
->
i_l
<
30
)
{
msg_Dbg
(
p_this
,
"Song too short (< 30s) -> not submitting"
);
p_sys
->
b_queued
=
VLC_TRUE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
}
/* wait for the user to listen enough before submitting */
time
(
&
played_time
);
played_time
-=
p_sys
->
p_current_song
->
time_playing
;
played_time
-=
p_sys
->
time_total_pauses
;
if
(
(
played_time
<
240
)
&&
(
played_time
<
(
p_sys
->
p_current_song
->
i_l
/
2
)
)
)
{
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
}
msg_Dbg
(
p_this
,
"Ok. We'll put it in the queue for submission"
);
p_queue
=
p_sys
->
p_first_queue
;
while
(
(
p_queue
->
i_songs_nb
==
10
)
&&
(
p_queue
->
p_next_queue
!=
NULL
)
)
{
p_queue
=
p_queue
->
p_next_queue
;
}
i_songs_nb
=
p_queue
->
i_songs_nb
;
if
(
i_songs_nb
==
10
)
{
p_next_queue
=
malloc
(
sizeof
(
audioscrobbler_queue_t
)
);
if
(
!
p_next_queue
)
{
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_ENOMEM
;
}
p_queue
->
p_next_queue
=
p_next_queue
;
i_songs_nb
=
0
;
p_queue
=
p_next_queue
;
p_queue
->
i_songs_nb
=
i_songs_nb
;
}
p_queue
->
p_queue
[
i_songs_nb
]
=
malloc
(
sizeof
(
audioscrobbler_song_t
)
);
p_queue
->
p_queue
[
i_songs_nb
]
->
i_l
=
p_sys
->
p_current_song
->
i_l
;
p_queue
->
p_queue
[
i_songs_nb
]
->
psz_a
=
strdup
(
p_sys
->
p_current_song
->
psz_a
);
p_queue
->
p_queue
[
i_songs_nb
]
->
psz_t
=
strdup
(
p_sys
->
p_current_song
->
psz_t
);
p_queue
->
p_queue
[
i_songs_nb
]
->
psz_b
=
strdup
(
p_sys
->
p_current_song
->
psz_b
);
p_queue
->
p_queue
[
i_songs_nb
]
->
psz_i
=
strdup
(
p_sys
->
p_current_song
->
psz_i
);
p_queue
->
i_songs_nb
++
;
p_sys
->
b_queued
=
VLC_TRUE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
}
/*****************************************************************************
* Handshake : Init audioscrobbler connection
*****************************************************************************/
static
int
Handshake
(
intf_thread_t
*
p_this
)
{
char
*
psz_password
=
NULL
;
struct
md5_s
*
p_struct_md5
=
NULL
;
char
*
psz_password_md5
=
NULL
;
char
*
ps_challenge_md5
=
NULL
;
stream_t
*
p_stream
;
char
*
psz_handshake_url
=
NULL
;
uint8_t
*
p_buffer
=
NULL
;
char
*
p_buffer_pos
=
NULL
;
char
*
psz_buffer_substring
=
NULL
;
char
*
psz_url_parser
=
NULL
;
int
i_url_pos
,
i
;
char
*
b1
,
*
b2
,
*
b3
,
*
b4
;
intf_thread_t
*
p_intf
;
intf_sys_t
*
p_sys
;
p_intf
=
(
intf_thread_t
*
)
p_this
;
p_sys
=
p_this
->
p_sys
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_sys
->
psz_username
=
config_GetPsz
(
p_this
,
"lastfm-username"
);
if
(
!
p_sys
->
psz_username
)
{
goto
memerror
;
}
if
(
!*
p_sys
->
psz_username
)
{
msg_Info
(
p_this
,
"You have to set an username! "
"Visit https://www.last.fm/join/"
);
return
VLC_ENOVAR
;
}
psz_handshake_url
=
malloc
(
1024
);
if
(
!
psz_handshake_url
)
{
goto
memerror
;
}
snprintf
(
psz_handshake_url
,
1024
,
"http://post.audioscrobbler.com/?hs=true&p=1.1&c=%s&v=%s&u=%s"
,
CLIENT_NAME
,
CLIENT_VERSION
,
p_sys
->
psz_username
);
p_stream
=
stream_UrlNew
(
p_intf
,
psz_handshake_url
);
free
(
psz_handshake_url
);
if
(
!
p_stream
)
{
p_sys
->
i_interval
=
DEFAULT_INTERVAL
;
time
(
&
p_sys
->
time_last_interval
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_EGENERIC
;
}
p_buffer
=
(
uint8_t
*
)
calloc
(
1
,
1024
);
if
(
!
p_buffer
)
{
stream_Delete
(
p_stream
);
goto
memerror
;
}
if
(
stream_Read
(
p_stream
,
p_buffer
,
1024
)
==
0
)
{
stream_Delete
(
p_stream
);
free
(
p_buffer
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_EGENERIC
;
}
stream_Delete
(
p_stream
);
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"INTERVAL"
);
if
(
p_buffer_pos
)
{
p_sys
->
i_interval
=
atoi
(
p_buffer_pos
+
strlen
(
"INTERVAL "
)
);
time
(
&
p_sys
->
time_last_interval
);
}
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"FAILED"
);
if
(
p_buffer_pos
)
{
msg_Info
(
p_this
,
p_buffer_pos
);
free
(
p_buffer
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_EGENERIC
;
}
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"BADUSER"
);
if
(
p_buffer_pos
)
{
msg_Info
(
p_intf
,
"Username is incorrect"
);
free
(
p_buffer
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_EGENERIC
;
}
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"UPDATE"
);
if
(
p_buffer_pos
)
{
msg_Dbg
(
p_intf
,
"Protocol updated"
);
msg_Dbg
(
p_intf
,
p_buffer_pos
);
}
else
{
p_buffer_pos
=
strstr
(
(
char
*
)
p_buffer
,
"UPTODATE"
);
if
(
!
p_buffer_pos
)
{
msg_Dbg
(
p_intf
,
"Protocol error"
);
free
(
p_buffer
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_EGENERIC
;
}
}
psz_buffer_substring
=
strndup
(
strstr
(
p_buffer_pos
,
"
\n
"
)
+
1
,
32
);
if
(
!
psz_buffer_substring
)
{
goto
memerror
;
}
else
{
ps_challenge_md5
=
malloc
(
sizeof
(
char
)
*
32
);
if
(
!
ps_challenge_md5
)
{
goto
memerror
;
}
memcpy
(
ps_challenge_md5
,
psz_buffer_substring
,
32
);
free
(
psz_buffer_substring
);
}
p_buffer_pos
=
(
void
*
)
strstr
(
(
char
*
)
p_buffer
,
"http://"
);
if
(
p_sys
->
psz_submit_host
!=
NULL
)
{
free
(
p_sys
->
psz_submit_host
);
}
if
(
p_sys
->
psz_submit_file
!=
NULL
)
{
free
(
p_sys
->
psz_submit_file
);
}
psz_url_parser
=
p_buffer_pos
+
strlen
(
"http://"
);
i_url_pos
=
strcspn
(
psz_url_parser
,
":"
);
p_sys
->
psz_submit_host
=
strndup
(
psz_url_parser
,
i_url_pos
);
p_sys
->
i_submit_port
=
atoi
(
psz_url_parser
+
i_url_pos
+
1
);
psz_url_parser
+=
strcspn
(
psz_url_parser
,
"/"
)
+
1
;
i_url_pos
=
strcspn
(
psz_url_parser
,
"
\n
"
);
p_sys
->
psz_submit_file
=
strndup
(
psz_url_parser
,
i_url_pos
);
free
(
p_buffer
);
p_struct_md5
=
malloc
(
sizeof
(
struct
md5_s
)
);
if
(
!
p_struct_md5
)
{
goto
memerror
;
}
psz_password
=
config_GetPsz
(
p_this
,
"lastfm-password"
);
if
(
!
psz_password
)
{
goto
memerror
;
}
InitMD5
(
p_struct_md5
);
AddMD5
(
p_struct_md5
,
(
uint8_t
*
)
psz_password
,
strlen
(
psz_password
)
);
EndMD5
(
p_struct_md5
);
free
(
psz_password
);
psz_password_md5
=
malloc
(
33
*
sizeof
(
char
)
);
if
(
!
psz_password_md5
)
{
goto
memerror
;
}
for
(
i
=
0
;
i
<
4
;
i
++
)
{
/* TODO check that this works on every arch/platform (uint32_t to char) */
b1
=
hexa
(
p_struct_md5
->
p_digest
[
i
]
%
256
);
b2
=
hexa
(
(
p_struct_md5
->
p_digest
[
i
]
/
256
)
%
256
);
b3
=
hexa
(
(
p_struct_md5
->
p_digest
[
i
]
/
65536
)
%
256
);
b4
=
hexa
(
p_struct_md5
->
p_digest
[
i
]
/
16777216
);
sprintf
(
&
psz_password_md5
[
8
*
i
],
"%s%s%s%s"
,
b1
,
b2
,
b3
,
b4
);
free
(
b1
);
free
(
b2
);
free
(
b3
);
free
(
b4
);
}
strlwr
(
psz_password_md5
);
InitMD5
(
p_struct_md5
);
AddMD5
(
p_struct_md5
,
(
uint8_t
*
)
psz_password_md5
,
32
);
AddMD5
(
p_struct_md5
,
(
uint8_t
*
)
ps_challenge_md5
,
32
);
EndMD5
(
p_struct_md5
);
free
(
ps_challenge_md5
);
free
(
psz_password_md5
);
for
(
i
=
0
;
i
<
4
;
i
++
)
{
b1
=
hexa
(
p_struct_md5
->
p_digest
[
i
]
%
256
);
b2
=
hexa
(
(
p_struct_md5
->
p_digest
[
i
]
/
256
)
%
256
);
b3
=
hexa
(
(
p_struct_md5
->
p_digest
[
i
]
/
65536
)
%
256
);
b4
=
hexa
(
p_struct_md5
->
p_digest
[
i
]
/
16777216
);
sprintf
(
&
p_sys
->
psz_response_md5
[
8
*
i
],
"%s%s%s%s"
,
b1
,
b2
,
b3
,
b4
);
free
(
b1
);
free
(
b2
);
free
(
b3
);
free
(
b4
);
}
p_sys
->
psz_response_md5
[
32
]
=
0
;
strlwr
(
p_sys
->
psz_response_md5
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
memerror:
free
(
p_buffer
);
free
(
p_struct_md5
);
free
(
psz_buffer_substring
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_ENOMEM
;
}
/*****************************************************************************
* hexa : Converts a byte to a string in its hexadecimal value
*****************************************************************************/
char
*
hexa
(
short
int
i
)
{
char
*
res
=
calloc
(
3
,
sizeof
(
char
)
);
((
i
/
16
)
<
10
)
?
res
[
0
]
=
(
i
/
16
)
+
'0'
:
(
res
[
0
]
=
(
i
/
16
)
+
'a'
-
10
);
((
i
%
16
)
<
10
)
?
res
[
1
]
=
(
i
%
16
)
+
'0'
:
(
res
[
1
]
=
(
i
%
16
)
+
'a'
-
10
);
return
res
;
}
/*****************************************************************************
* strlwr : Converts a string to lower case
*****************************************************************************/
#if !defined(strlwr) && !defined( WIN32 )
char
*
strlwr
(
char
*
psz_string
)
{
while
(
*
psz_string
)
{
*
psz_string
++
=
tolower
(
*
psz_string
);
}
return
psz_string
;
}
#endif
/*****************************************************************************
* DeleteQueue : Free all songs from an audioscrobbler_queue_t
*****************************************************************************/
void
DeleteQueue
(
audioscrobbler_queue_t
*
p_queue
)
{
int
i
;
for
(
i
=
0
;
i
<
p_queue
->
i_songs_nb
;
i
++
)
{
free
(
p_queue
->
p_queue
[
i
]
->
psz_a
);
free
(
p_queue
->
p_queue
[
i
]
->
psz_b
);
free
(
p_queue
->
p_queue
[
i
]
->
psz_t
);
free
(
p_queue
->
p_queue
[
i
]
->
psz_i
);
free
(
p_queue
->
p_queue
[
i
]
);
}
}
/*****************************************************************************
* ReadMetaData : Puts current song's meta data in p_sys->p_current_song
*****************************************************************************/
static
int
ReadMetaData
(
intf_thread_t
*
p_this
)
{
playlist_t
*
p_playlist
;
char
*
psz_title
=
NULL
;
char
*
psz_artist
=
NULL
;
char
*
psz_album
=
NULL
;
int
i_length
=
-
1
;
input_thread_t
*
p_input
=
NULL
;
vlc_value_t
video_val
;
intf_sys_t
*
p_sys
;
p_sys
=
p_this
->
p_sys
;
p_playlist
=
pl_Yield
(
p_this
);
PL_LOCK
;
p_input
=
p_playlist
->
p_input
;
if
(
!
p_input
)
{
return
VLC_SUCCESS
;
}
vlc_object_yield
(
p_input
);
PL_UNLOCK
;
pl_Release
(
p_playlist
);
if
(
p_input
->
input
.
p_item
->
i_type
==
ITEM_TYPE_NET
)
{
msg_Dbg
(
p_this
,
"We play a stream -> no submission"
);
goto
no_submission
;
}
var_Change
(
p_input
,
"video-es"
,
VLC_VAR_CHOICESCOUNT
,
&
video_val
,
NULL
);
if
(
video_val
.
i_int
>
0
)
{
msg_Dbg
(
p_this
,
"We play a video -> no submission"
);
goto
no_submission
;
}
if
(
p_input
->
input
.
p_item
->
p_meta
->
psz_artist
)
{
psz_artist
=
encode_URI_component
(
p_input
->
input
.
p_item
->
p_meta
->
psz_artist
);
if
(
!
psz_artist
)
{
goto
error
;
}
}
else
{
msg_Dbg
(
p_this
,
"No artist.."
);
goto
no_submission
;
}
if
(
p_input
->
input
.
p_item
->
p_meta
->
psz_album
)
{
psz_album
=
encode_URI_component
(
p_input
->
input
.
p_item
->
p_meta
->
psz_album
);
if
(
!
psz_album
)
{
goto
error
;
}
}
else
{
msg_Dbg
(
p_this
,
"No album.."
);
goto
no_submission
;
}
if
(
p_input
->
input
.
p_item
->
psz_name
)
{
psz_title
=
encode_URI_component
(
p_input
->
input
.
p_item
->
psz_name
);
if
(
!
psz_title
)
{
goto
error
;
}
}
else
{
msg_Dbg
(
p_this
,
"No track name.."
);
goto
no_submission
;
}
i_length
=
p_input
->
input
.
p_item
->
i_duration
/
1000000
;
vlc_object_release
(
p_input
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_sys
->
p_current_song
->
psz_a
=
strdup
(
psz_artist
);
p_sys
->
p_current_song
->
psz_t
=
strdup
(
psz_title
);
p_sys
->
p_current_song
->
psz_b
=
strdup
(
psz_album
);
p_sys
->
p_current_song
->
i_l
=
i_length
;
p_sys
->
b_queued
=
VLC_FALSE
;
p_sys
->
b_metadata_read
=
VLC_TRUE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
msg_Dbg
(
p_this
,
"Meta data registered, waiting to be queued"
);
free
(
psz_title
);
free
(
psz_artist
);
free
(
psz_album
);
return
VLC_SUCCESS
;
error:
free
(
psz_artist
);
free
(
psz_album
);
free
(
psz_title
);
return
VLC_ENOMEM
;
no_submission:
vlc_object_release
(
p_input
);
free
(
psz_title
);
free
(
psz_artist
);
free
(
psz_album
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_sys
->
b_queued
=
VLC_TRUE
;
p_sys
->
b_metadata_read
=
VLC_TRUE
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment