[spotify] Rescan of single playlists if update trigger received from
libspotify (Readds "spotify:savedtracks" playlist to avoid deletion of saved tracks)
This commit is contained in:
parent
adac1d3b5f
commit
99945fa576
215
src/spotify.c
215
src/spotify.c
|
@ -158,6 +158,11 @@ static enum spotify_state g_state;
|
|||
static int spotify_base_plid;
|
||||
// Flag telling us if access to the web api was granted
|
||||
static bool spotify_access_token_valid;
|
||||
// The base playlist id for Spotify saved tracks in the db
|
||||
static int spotify_saved_plid;
|
||||
|
||||
// Flag to avoid triggering playlist change events while the (re)scan is running
|
||||
static bool scanning;
|
||||
|
||||
// Audio fifo
|
||||
static audio_fifo_t *g_audio_fifo;
|
||||
|
@ -414,7 +419,11 @@ fptr_assign_all()
|
|||
|
||||
|
||||
static enum command_state
|
||||
scan_webapi(void *arg, int *ret);
|
||||
webapi_scan(void *arg, int *ret);
|
||||
static enum command_state
|
||||
webapi_pl_save(void *arg, int *ret);
|
||||
static enum command_state
|
||||
webapi_pl_remove(void *arg, int *ret);
|
||||
|
||||
/* ------------------------------- MISC HELPERS ---------------------------- */
|
||||
|
||||
|
@ -947,6 +956,33 @@ uri_register(void *arg, int *retval)
|
|||
return COMMAND_END;
|
||||
}
|
||||
|
||||
static void
|
||||
webapi_playlist_updated(sp_playlist *pl)
|
||||
{
|
||||
sp_link *link;
|
||||
char url[1024];
|
||||
int ret;
|
||||
|
||||
if (!scanning)
|
||||
{
|
||||
// Run playlist save in the library thread
|
||||
link = fptr_sp_link_create_from_playlist(pl);
|
||||
if (!link)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for playlist: '%s'\n", fptr_sp_playlist_name(pl));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = fptr_sp_link_as_string(link, url, sizeof(url));
|
||||
if (ret == sizeof(url))
|
||||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "Spotify link truncated: %s\n", url);
|
||||
}
|
||||
fptr_sp_link_release(link);
|
||||
|
||||
library_exec_async(webapi_pl_save, strdup(url));
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------- PLAYLIST CALLBACKS ------------------------- */
|
||||
/**
|
||||
|
@ -966,8 +1002,14 @@ static void playlist_update_in_progress(sp_playlist *pl, bool done, void *userda
|
|||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "Playlist update (status %d): %s\n", done, fptr_sp_playlist_name(pl));
|
||||
|
||||
if (!spotify_access_token_valid) // TODO Trigger update of library on pl change
|
||||
spotify_playlist_save(pl);
|
||||
if (spotify_access_token_valid)
|
||||
{
|
||||
webapi_playlist_updated(pl);
|
||||
}
|
||||
else
|
||||
{
|
||||
spotify_playlist_save(pl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,8 +1017,15 @@ static void playlist_metadata_updated(sp_playlist *pl, void *userdata)
|
|||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "Playlist metadata updated: %s\n", fptr_sp_playlist_name(pl));
|
||||
|
||||
if (!spotify_access_token_valid) // TODO Trigger update of library on pl change
|
||||
spotify_playlist_save(pl);
|
||||
if (spotify_access_token_valid)
|
||||
{
|
||||
//TODO Update disabled to prevent multiple triggering of updates e. g. on adding a playlist
|
||||
//webapi_playlist_updated(pl);
|
||||
}
|
||||
else
|
||||
{
|
||||
spotify_playlist_save(pl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1006,8 +1055,37 @@ static void playlist_added(sp_playlistcontainer *pc, sp_playlist *pl,
|
|||
|
||||
fptr_sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
|
||||
|
||||
if (!spotify_access_token_valid) // TODO Trigger update of library on pl change
|
||||
spotify_playlist_save(pl);
|
||||
if (spotify_access_token_valid)
|
||||
{
|
||||
webapi_playlist_updated(pl);
|
||||
}
|
||||
else
|
||||
{
|
||||
spotify_playlist_save(pl);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
playlist_remove(const char *uri)
|
||||
{
|
||||
struct playlist_info *pli;
|
||||
int plid;
|
||||
|
||||
pli = db_pl_fetch_bypath(uri);
|
||||
|
||||
if (!pli)
|
||||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "Playlist %s not found, can't delete\n", uri);
|
||||
return -1;
|
||||
}
|
||||
|
||||
plid = pli->id;
|
||||
|
||||
free_pli(pli, 0);
|
||||
|
||||
db_spotify_pl_delete(plid);
|
||||
spotify_cleanup_files();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1023,19 +1101,14 @@ static void playlist_added(sp_playlistcontainer *pc, sp_playlist *pl,
|
|||
static void
|
||||
playlist_removed(sp_playlistcontainer *pc, sp_playlist *pl, int position, void *userdata)
|
||||
{
|
||||
struct playlist_info *pli;
|
||||
sp_link *link;
|
||||
char url[1024];
|
||||
int plid;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_INFO, L_SPOTIFY, "Playlist removed: %s\n", fptr_sp_playlist_name(pl));
|
||||
|
||||
fptr_sp_playlist_remove_callbacks(pl, &pl_callbacks, NULL);
|
||||
|
||||
if (spotify_access_token_valid) // TODO Trigger update of library on pl change
|
||||
return;
|
||||
|
||||
link = fptr_sp_link_create_from_playlist(pl);
|
||||
if (!link)
|
||||
{
|
||||
|
@ -1050,21 +1123,16 @@ playlist_removed(sp_playlistcontainer *pc, sp_playlist *pl, int position, void *
|
|||
}
|
||||
fptr_sp_link_release(link);
|
||||
|
||||
pli = db_pl_fetch_bypath(url);
|
||||
|
||||
if (!pli)
|
||||
if (spotify_access_token_valid)
|
||||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "Playlist %s not found, can't delete\n", url);
|
||||
return;
|
||||
// Run playlist remove in the library thread
|
||||
if (!scanning)
|
||||
library_exec_async(webapi_pl_remove, strdup(url));
|
||||
}
|
||||
else
|
||||
{
|
||||
playlist_remove(url);
|
||||
}
|
||||
|
||||
plid = pli->id;
|
||||
|
||||
free_pli(pli, 0);
|
||||
|
||||
db_spotify_pl_delete(plid);
|
||||
|
||||
spotify_cleanup_files();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1997,7 +2065,7 @@ spotify_oauth_callback(struct evbuffer *evbuf, struct evkeyvalq *param, const ch
|
|||
spotify_access_token_valid = true;
|
||||
|
||||
// Trigger scan after successful access to spotifywebapi
|
||||
library_exec_async(scan_webapi, NULL);
|
||||
library_exec_async(webapi_scan, NULL);
|
||||
|
||||
evbuffer_add_printf(evbuf, "ok, all done</p>\n");
|
||||
|
||||
|
@ -2140,6 +2208,9 @@ scan_saved_albums()
|
|||
cache_artwork_ping(track.uri, album.mtime, 0);
|
||||
|
||||
free_mfi(&mfi, 1);
|
||||
|
||||
if (spotify_saved_plid)
|
||||
db_pl_add_item_bypath(spotify_saved_plid, track.uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2224,8 +2295,10 @@ scan_playlists()
|
|||
|
||||
plid = library_add_playlist_info(playlist.uri, playlist.name, virtual_path, PL_PLAIN, spotify_base_plid, DIR_SPOTIFY);
|
||||
|
||||
if (plid)
|
||||
if (plid > 0)
|
||||
scan_playlisttracks(&playlist, plid);
|
||||
else
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist: '%s' (%s) \n", playlist.name, playlist.uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2235,6 +2308,58 @@ scan_playlists()
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Thread: library */
|
||||
static int
|
||||
scan_playlist(const char *uri)
|
||||
{
|
||||
struct spotify_request request;
|
||||
struct spotify_playlist playlist;
|
||||
char virtual_path[PATH_MAX];
|
||||
int plid;
|
||||
|
||||
db_transaction_begin();
|
||||
|
||||
memset(&request, 0, sizeof(struct spotify_request));
|
||||
|
||||
if (0 == spotifywebapi_playlist_start(&request, uri, &playlist))
|
||||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "Got playlist: '%s' (%s) \n", playlist.name, playlist.uri);
|
||||
|
||||
if (playlist.owner)
|
||||
{
|
||||
snprintf(virtual_path, PATH_MAX, "/spotify:/%s (%s)", playlist.name, playlist.owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(virtual_path, PATH_MAX, "/spotify:/%s", playlist.name);
|
||||
}
|
||||
|
||||
plid = library_add_playlist_info(playlist.uri, playlist.name, virtual_path, PL_PLAIN, spotify_base_plid, DIR_SPOTIFY);
|
||||
|
||||
if (plid > 0)
|
||||
scan_playlisttracks(&playlist, plid);
|
||||
else
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist: '%s' (%s) \n", playlist.name, playlist.uri);
|
||||
}
|
||||
|
||||
spotifywebapi_request_end(&request);
|
||||
db_transaction_end();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
create_saved_tracks_playlist()
|
||||
{
|
||||
spotify_saved_plid = library_add_playlist_info("spotify:savedtracks", "Spotify Saved", "/spotify:/Spotify Saved", PL_PLAIN, spotify_base_plid, DIR_SPOTIFY);
|
||||
|
||||
if (spotify_saved_plid <= 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist for saved tracks\n");
|
||||
spotify_saved_plid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Thread: library */
|
||||
static int
|
||||
initscan()
|
||||
|
@ -2242,6 +2367,8 @@ initscan()
|
|||
cfg_t *spotify_cfg;
|
||||
int ret;
|
||||
|
||||
scanning = true;
|
||||
|
||||
/* Refresh access token for the spotify webapi */
|
||||
spotify_access_token_valid = (0 == spotifywebapi_token_refresh());
|
||||
if (!spotify_access_token_valid)
|
||||
|
@ -2257,6 +2384,7 @@ initscan()
|
|||
* Add playlist folder for all spotify playlists
|
||||
*/
|
||||
spotify_base_plid = 0;
|
||||
spotify_saved_plid = 0;
|
||||
spotify_cfg = cfg_getsec(cfg, "spotify");
|
||||
if (! cfg_getbool(spotify_cfg, "base_playlist_disable"))
|
||||
{
|
||||
|
@ -2278,10 +2406,13 @@ initscan()
|
|||
*/
|
||||
if (spotify_access_token_valid)
|
||||
{
|
||||
create_saved_tracks_playlist();
|
||||
scan_saved_albums();
|
||||
scan_playlists();
|
||||
}
|
||||
|
||||
scanning = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2289,11 +2420,14 @@ initscan()
|
|||
static int
|
||||
rescan()
|
||||
{
|
||||
scanning = true;
|
||||
|
||||
/*
|
||||
* Scan saved tracks from the web api
|
||||
*/
|
||||
if (spotify_access_token_valid)
|
||||
{
|
||||
create_saved_tracks_playlist();
|
||||
scan_saved_albums();
|
||||
scan_playlists();
|
||||
}
|
||||
|
@ -2306,6 +2440,8 @@ rescan()
|
|||
db_transaction_end();
|
||||
}
|
||||
|
||||
scanning = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2316,14 +2452,34 @@ fullrescan()
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Thread: httpd */
|
||||
/* Thread: library */
|
||||
static enum command_state
|
||||
scan_webapi(void *arg, int *ret)
|
||||
webapi_scan(void *arg, int *ret)
|
||||
{
|
||||
*ret = rescan();
|
||||
return COMMAND_END;
|
||||
}
|
||||
|
||||
/* Thread: library */
|
||||
static enum command_state
|
||||
webapi_pl_save(void *arg, int *ret)
|
||||
{
|
||||
const char *uri = arg;
|
||||
|
||||
*ret = scan_playlist(uri);
|
||||
return COMMAND_END;
|
||||
}
|
||||
|
||||
/* Thread: library */
|
||||
static enum command_state
|
||||
webapi_pl_remove(void *arg, int *ret)
|
||||
{
|
||||
const char *uri = arg;
|
||||
|
||||
*ret = playlist_remove(uri);
|
||||
return COMMAND_END;
|
||||
}
|
||||
|
||||
/* Thread: main */
|
||||
int
|
||||
spotify_init(void)
|
||||
|
@ -2334,6 +2490,7 @@ spotify_init(void)
|
|||
int ret;
|
||||
|
||||
spotify_access_token_valid = false;
|
||||
scanning = false;
|
||||
|
||||
/* Initialize libspotify */
|
||||
g_libhandle = dlopen("libspotify.so", RTLD_LAZY);
|
||||
|
|
|
@ -50,6 +50,7 @@ static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789";
|
|||
static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239";
|
||||
static const char *spotify_auth_uri = "https://accounts.spotify.com/authorize";
|
||||
static const char *spotify_token_uri = "https://accounts.spotify.com/api/token";
|
||||
static const char *spotify_playlist_uri = "https://api.spotify.com/v1/users/%s/playlists/%s";
|
||||
|
||||
|
||||
/*--------------------- HELPERS FOR SPOTIFY WEB API -------------------------*/
|
||||
|
@ -690,18 +691,67 @@ spotifywebapi_playlists_fetch(struct spotify_request *request, struct spotify_pl
|
|||
|
||||
if (request->index >= request->count)
|
||||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "All playlists processed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
jsonplaylist = json_object_array_get_idx(request->items, request->index);
|
||||
if (!jsonplaylist)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Error fetching playlist at index '%d'\n", request->index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
playlist_metadata(jsonplaylist, playlist);
|
||||
|
||||
request->index++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extracts the owner and the id from a spotify playlist uri
|
||||
*
|
||||
* Playlist-uri has the following format: spotify:user:[owner]:playlist:[id]
|
||||
* Owner and plid must be freed by the caller.
|
||||
*/
|
||||
static int
|
||||
get_owner_plid_from_uri(const char *uri, char **owner, char **plid)
|
||||
{
|
||||
char *ptr1;
|
||||
char *ptr2;
|
||||
char *tmp;
|
||||
size_t len;
|
||||
|
||||
ptr1 = strchr(uri, ':');
|
||||
if (!ptr1)
|
||||
return -1;
|
||||
ptr1++;
|
||||
ptr1 = strchr(ptr1, ':');
|
||||
if (!ptr1)
|
||||
return -1;
|
||||
ptr1++;
|
||||
ptr2 = strchr(ptr1, ':');
|
||||
|
||||
len = ptr2 - ptr1;
|
||||
|
||||
tmp = malloc(sizeof(char) * (len + 1));
|
||||
strncpy(tmp, ptr1, len);
|
||||
tmp[len] = '\0';
|
||||
*owner = tmp;
|
||||
|
||||
ptr2++;
|
||||
ptr1 = strchr(ptr2, ':');
|
||||
if (!ptr1)
|
||||
{
|
||||
free(tmp);
|
||||
return -1;
|
||||
}
|
||||
ptr1++;
|
||||
*plid = strdup(ptr1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spotify_track *track)
|
||||
{
|
||||
|
@ -731,3 +781,44 @@ spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spoti
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spotifywebapi_playlist_start(struct spotify_request *request, const char *path, struct spotify_playlist *playlist)
|
||||
{
|
||||
char uri[1024];
|
||||
char *owner;
|
||||
char *id;
|
||||
int ret;
|
||||
|
||||
ret = get_owner_plid_from_uri(path, &owner, &id);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Error extracting owner and id from playlist uri '%s'\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = snprintf(uri, sizeof(uri), spotify_playlist_uri, owner, id);
|
||||
if (ret < 0 || ret >= sizeof(uri))
|
||||
{
|
||||
DPRINTF(E_LOG, L_SPOTIFY, "Error creating playlist endpoint uri for playlist '%s'\n", path);
|
||||
free(owner);
|
||||
free(id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = spotifywebapi_request_uri(request, uri);
|
||||
if (ret < 0)
|
||||
{
|
||||
free(owner);
|
||||
free(id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
request->haystack = json_tokener_parse(request->response_body);
|
||||
playlist_metadata(request->haystack, playlist);
|
||||
|
||||
free(owner);
|
||||
free(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ int
|
|||
spotifywebapi_playlists_fetch(struct spotify_request *request, struct spotify_playlist* playlist);
|
||||
int
|
||||
spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spotify_track *track);
|
||||
|
||||
int
|
||||
spotifywebapi_playlist_start(struct spotify_request *request, const char *path, struct spotify_playlist *playlist);
|
||||
|
||||
#endif /* SRC_SPOTIFY_WEBAPI_H_ */
|
||||
|
|
Loading…
Reference in New Issue