mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-15 00:35:03 -05:00
[spotify] Remove scanning playlists with libspotify
This commit is contained in:
parent
b428760599
commit
418f808d2e
385
src/spotify.c
385
src/spotify.c
@ -414,83 +414,6 @@ create_base_playlist();
|
|||||||
/* -------------------------- PLAYLIST HELPERS ------------------------- */
|
/* -------------------------- PLAYLIST HELPERS ------------------------- */
|
||||||
/* Should only be called from within the spotify thread */
|
/* Should only be called from within the spotify thread */
|
||||||
|
|
||||||
static int
|
|
||||||
spotify_metadata_get(sp_track *track, struct media_file_info *mfi, const char *pltitle, int time_added)
|
|
||||||
{
|
|
||||||
cfg_t *spotify_cfg;
|
|
||||||
bool artist_override;
|
|
||||||
bool album_override;
|
|
||||||
sp_album *album;
|
|
||||||
sp_artist *artist;
|
|
||||||
sp_albumtype albumtype;
|
|
||||||
bool starred;
|
|
||||||
int compilation;
|
|
||||||
char *albumname;
|
|
||||||
|
|
||||||
spotify_cfg = cfg_getsec(cfg, "spotify");
|
|
||||||
artist_override = cfg_getbool(spotify_cfg, "artist_override");
|
|
||||||
album_override = cfg_getbool(spotify_cfg, "album_override");
|
|
||||||
|
|
||||||
album = fptr_sp_track_album(track);
|
|
||||||
if (!album)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
artist = fptr_sp_album_artist(album);
|
|
||||||
if (!artist)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
albumtype = fptr_sp_album_type(album);
|
|
||||||
starred = fptr_sp_track_is_starred(g_sess, track);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Treat album as compilation if one of the following conditions is true:
|
|
||||||
* - spotfy album type is compilation
|
|
||||||
* - artist_override in config is set to true and track is not part of the starred playlist
|
|
||||||
* - starred_artist_override in config is set to true and track is part of the starred playlist
|
|
||||||
*/
|
|
||||||
compilation = ((albumtype == SP_ALBUMTYPE_COMPILATION)
|
|
||||||
|| artist_override);
|
|
||||||
|
|
||||||
if (album_override && pltitle)
|
|
||||||
albumname = strdup(pltitle);
|
|
||||||
else
|
|
||||||
albumname = strdup(fptr_sp_album_name(album));
|
|
||||||
|
|
||||||
mfi->title = strdup(fptr_sp_track_name(track));
|
|
||||||
mfi->album = albumname;
|
|
||||||
mfi->artist = strdup(fptr_sp_artist_name(artist));
|
|
||||||
mfi->year = fptr_sp_album_year(album);
|
|
||||||
mfi->song_length = fptr_sp_track_duration(track);
|
|
||||||
mfi->track = fptr_sp_track_index(track);
|
|
||||||
mfi->disc = fptr_sp_track_disc(track);
|
|
||||||
mfi->compilation = compilation;
|
|
||||||
mfi->artwork = ARTWORK_SPOTIFY;
|
|
||||||
mfi->type = strdup("spotify");
|
|
||||||
mfi->codectype = strdup("wav");
|
|
||||||
mfi->description = strdup("Spotify audio");
|
|
||||||
mfi->time_added = time_added;
|
|
||||||
|
|
||||||
DPRINTF(E_SPAM, L_SPOTIFY, "Metadata for track:\n"
|
|
||||||
"Title: %s\n"
|
|
||||||
"Album: %s\n"
|
|
||||||
"Artist: %s\n"
|
|
||||||
"Year: %u\n"
|
|
||||||
"Track: %u\n"
|
|
||||||
"Disc: %u\n"
|
|
||||||
"Compilation: %d\n"
|
|
||||||
"Starred: %d\n",
|
|
||||||
mfi->title,
|
|
||||||
mfi->album,
|
|
||||||
mfi->artist,
|
|
||||||
mfi->year,
|
|
||||||
mfi->track,
|
|
||||||
mfi->disc,
|
|
||||||
mfi->compilation,
|
|
||||||
starred);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the directory id for /spotify:/<artist>/<album>, if the directory (or the parent
|
* Returns the directory id for /spotify:/<artist>/<album>, if the directory (or the parent
|
||||||
* directories) does not yet exist, they will be created.
|
* directories) does not yet exist, they will be created.
|
||||||
@ -533,96 +456,6 @@ prepare_directories(const char *artist, const char *album)
|
|||||||
return dir_id;
|
return dir_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
spotify_track_save(int plid, sp_track *track, const char *pltitle, int time_added)
|
|
||||||
{
|
|
||||||
struct media_file_info mfi;
|
|
||||||
sp_link *link;
|
|
||||||
char url[1024];
|
|
||||||
int ret;
|
|
||||||
char virtual_path[PATH_MAX];
|
|
||||||
int dir_id;
|
|
||||||
int id;
|
|
||||||
|
|
||||||
memset(&mfi, 0, sizeof(struct media_file_info));
|
|
||||||
|
|
||||||
if (!fptr_sp_track_is_loaded(track))
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Track appears to no longer have the proper status\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fptr_sp_track_get_availability(g_sess, track) != SP_TRACK_AVAILABILITY_AVAILABLE)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Track not available for playback: '%s'\n", fptr_sp_track_name(track));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
link = fptr_sp_link_create_from_track(track, 0);
|
|
||||||
if (!link)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for track: '%s'\n", fptr_sp_track_name(track));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* Add to playlistitems table */
|
|
||||||
if (plid)
|
|
||||||
{
|
|
||||||
ret = db_pl_add_item_bypath(plid, url);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not save playlist item: '%s'\n", url);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = spotify_metadata_get(track, &mfi, pltitle, time_added);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Metadata missing (but track should be loaded?): '%s'\n", fptr_sp_track_name(track));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
dir_id = prepare_directories(mfi.artist, mfi.album);
|
|
||||||
if (dir_id <= 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not add or update directory for item: '%s'\n", url);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DPRINTF(E_DBG, L_SPOTIFY, "Saving track '%s': '%s' by %s (%s)\n", url, mfi.title, mfi.artist, mfi.album);
|
|
||||||
|
|
||||||
id = db_file_id_bypath(url);
|
|
||||||
if (id)
|
|
||||||
db_file_ping(id);
|
|
||||||
|
|
||||||
mfi.id = id;
|
|
||||||
mfi.path = strdup(url);
|
|
||||||
mfi.fname = strdup(url);
|
|
||||||
mfi.time_modified = time(NULL);
|
|
||||||
mfi.data_kind = DATA_KIND_SPOTIFY;
|
|
||||||
snprintf(virtual_path, PATH_MAX, "/spotify:/%s/%s/%s", mfi.album_artist, mfi.album, mfi.title);
|
|
||||||
mfi.virtual_path = strdup(virtual_path);
|
|
||||||
mfi.directory_id = dir_id;
|
|
||||||
|
|
||||||
library_add_media(&mfi);
|
|
||||||
|
|
||||||
free_mfi(&mfi, 1);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail:
|
|
||||||
free_mfi(&mfi, 1);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
spotify_cleanup_files(void)
|
spotify_cleanup_files(void)
|
||||||
{
|
{
|
||||||
@ -655,165 +488,6 @@ spotify_cleanup_files(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
spotify_playlist_save(sp_playlist *pl)
|
|
||||||
{
|
|
||||||
struct playlist_info *pli;
|
|
||||||
sp_track *track;
|
|
||||||
sp_link *link;
|
|
||||||
sp_user *owner;
|
|
||||||
char url[1024];
|
|
||||||
const char *name;
|
|
||||||
const char *ownername;
|
|
||||||
int plid;
|
|
||||||
int num_tracks;
|
|
||||||
char virtual_path[PATH_MAX];
|
|
||||||
int created;
|
|
||||||
int ret;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!fptr_sp_playlist_is_loaded(pl))
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_SPOTIFY, "Playlist still not loaded - will wait for next callback\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = fptr_sp_playlist_name(pl);
|
|
||||||
num_tracks = fptr_sp_playlist_num_tracks(pl);
|
|
||||||
|
|
||||||
// The starred playlist has an empty name, set it manually to "Starred"
|
|
||||||
if (*name == '\0')
|
|
||||||
name = "Starred";
|
|
||||||
|
|
||||||
for (i = 0; i < num_tracks; i++)
|
|
||||||
{
|
|
||||||
track = fptr_sp_playlist_track(pl, i);
|
|
||||||
|
|
||||||
if (track && !fptr_sp_track_is_loaded(track))
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_SPOTIFY, "All playlist tracks not loaded (will wait for next callback): %s\n", name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Saving playlist (%d tracks): '%s'\n", num_tracks, name);
|
|
||||||
|
|
||||||
// Save playlist (playlists table)
|
|
||||||
link = fptr_sp_link_create_from_playlist(pl);
|
|
||||||
if (!link)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for playlist (wait): '%s'\n", name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
owner = fptr_sp_playlist_owner(pl);
|
|
||||||
if (owner)
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_SPOTIFY, "Playlist '%s' owner: '%s' (canonical) / '%s' (display)\n",
|
|
||||||
name, fptr_sp_user_canonical_name(owner), fptr_sp_user_display_name(owner));
|
|
||||||
|
|
||||||
ownername = fptr_sp_user_canonical_name(owner);
|
|
||||||
|
|
||||||
snprintf(virtual_path, PATH_MAX, "/spotify:/%s (%s)", name, ownername);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(virtual_path, PATH_MAX, "/spotify:/%s", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pli = db_pl_fetch_bypath(url);
|
|
||||||
|
|
||||||
if (pli)
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_SPOTIFY, "Playlist found ('%s', link %s), updating\n", name, url);
|
|
||||||
|
|
||||||
plid = pli->id;
|
|
||||||
|
|
||||||
free(pli->title);
|
|
||||||
pli->title = strdup(name);
|
|
||||||
free(pli->virtual_path);
|
|
||||||
pli->virtual_path = strdup(virtual_path);
|
|
||||||
pli->directory_id = DIR_SPOTIFY;
|
|
||||||
|
|
||||||
ret = db_pl_update(pli);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Error updating playlist ('%s', link %s)\n", name, url);
|
|
||||||
|
|
||||||
free_pli(pli, 0);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
db_pl_clear_items(plid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DPRINTF(E_DBG, L_SPOTIFY, "Adding playlist ('%s', link %s)\n", name, url);
|
|
||||||
|
|
||||||
pli = (struct playlist_info *)malloc(sizeof(struct playlist_info));
|
|
||||||
if (!pli)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SCAN, "Out of memory\n");
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(pli, 0, sizeof(struct playlist_info));
|
|
||||||
|
|
||||||
pli->type = PL_PLAIN;
|
|
||||||
pli->title = strdup(name);
|
|
||||||
pli->path = strdup(url);
|
|
||||||
pli->virtual_path = strdup(virtual_path);
|
|
||||||
pli->parent_id = spotify_base_plid;
|
|
||||||
pli->directory_id = DIR_SPOTIFY;
|
|
||||||
|
|
||||||
ret = db_pl_add(pli, &plid);
|
|
||||||
if ((ret < 0) || (plid < 1))
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist ('%s', link %s, ret %d, plid %d)\n", name, url, ret, plid);
|
|
||||||
|
|
||||||
free_pli(pli, 0);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free_pli(pli, 0);
|
|
||||||
|
|
||||||
// Save tracks and playlistitems (files and playlistitems table)
|
|
||||||
db_transaction_begin();
|
|
||||||
for (i = 0; i < num_tracks; i++)
|
|
||||||
{
|
|
||||||
track = fptr_sp_playlist_track(pl, i);
|
|
||||||
if (!track)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Track %d in playlist '%s' (id %d) is invalid\n", i, name, plid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
created = fptr_sp_playlist_track_create_time(pl, i);
|
|
||||||
|
|
||||||
ret = spotify_track_save(plid, track, name, created);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Error saving track %d to playlist '%s' (id %d)\n", i, name, plid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spotify_cleanup_files();
|
|
||||||
db_transaction_end();
|
|
||||||
|
|
||||||
return plid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registers a track with libspotify, which will make it start loading the track
|
// Registers a track with libspotify, which will make it start loading the track
|
||||||
// metadata. When that is done metadata_updated() is called (but we won't be
|
// metadata. When that is done metadata_updated() is called (but we won't be
|
||||||
// told which track it was...). Note that this function will result in a ref
|
// told which track it was...). Note that this function will result in a ref
|
||||||
@ -903,10 +577,6 @@ static void playlist_update_in_progress(sp_playlist *pl, bool done, void *userda
|
|||||||
{
|
{
|
||||||
webapi_playlist_updated(pl);
|
webapi_playlist_updated(pl);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
spotify_playlist_save(pl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,10 +589,6 @@ static void playlist_metadata_updated(sp_playlist *pl, void *userdata)
|
|||||||
//TODO Update disabled to prevent multiple triggering of updates e. g. on adding a playlist
|
//TODO Update disabled to prevent multiple triggering of updates e. g. on adding a playlist
|
||||||
//webapi_playlist_updated(pl);
|
//webapi_playlist_updated(pl);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
spotify_playlist_save(pl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -956,10 +622,6 @@ static void playlist_added(sp_playlistcontainer *pc, sp_playlist *pl,
|
|||||||
{
|
{
|
||||||
webapi_playlist_updated(pl);
|
webapi_playlist_updated(pl);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
spotify_playlist_save(pl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -1008,6 +670,8 @@ playlist_removed(sp_playlistcontainer *pc, sp_playlist *pl, int position, void *
|
|||||||
|
|
||||||
fptr_sp_playlist_remove_callbacks(pl, &pl_callbacks, NULL);
|
fptr_sp_playlist_remove_callbacks(pl, &pl_callbacks, NULL);
|
||||||
|
|
||||||
|
if (spotify_access_token_valid && !scanning)
|
||||||
|
{
|
||||||
link = fptr_sp_link_create_from_playlist(pl);
|
link = fptr_sp_link_create_from_playlist(pl);
|
||||||
if (!link)
|
if (!link)
|
||||||
{
|
{
|
||||||
@ -1022,16 +686,9 @@ playlist_removed(sp_playlistcontainer *pc, sp_playlist *pl, int position, void *
|
|||||||
}
|
}
|
||||||
fptr_sp_link_release(link);
|
fptr_sp_link_release(link);
|
||||||
|
|
||||||
if (spotify_access_token_valid)
|
|
||||||
{
|
|
||||||
// Run playlist remove in the library thread
|
// Run playlist remove in the library thread
|
||||||
if (!scanning)
|
|
||||||
library_exec_async(webapi_pl_remove, strdup(url));
|
library_exec_async(webapi_pl_remove, strdup(url));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
playlist_remove(url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2329,27 +1986,18 @@ initscan()
|
|||||||
static int
|
static int
|
||||||
rescan()
|
rescan()
|
||||||
{
|
{
|
||||||
|
if (!spotify_access_token_valid)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_SPOTIFY, "No valid web api token, ignoring rescan\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
scanning = true;
|
scanning = true;
|
||||||
|
|
||||||
create_base_playlist();
|
create_base_playlist();
|
||||||
|
|
||||||
/*
|
|
||||||
* Scan saved tracks from the web api
|
|
||||||
*/
|
|
||||||
if (spotify_access_token_valid)
|
|
||||||
{
|
|
||||||
create_saved_tracks_playlist();
|
create_saved_tracks_playlist();
|
||||||
scan_saved_albums();
|
scan_saved_albums();
|
||||||
scan_playlists();
|
scan_playlists();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
db_transaction_begin();
|
|
||||||
db_file_ping_bymatch("spotify:", 0);
|
|
||||||
db_pl_ping_bymatch("spotify:", 0);
|
|
||||||
db_directory_ping_bymatch("/spotify:");
|
|
||||||
db_transaction_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
scanning = false;
|
scanning = false;
|
||||||
|
|
||||||
@ -2360,23 +2008,18 @@ rescan()
|
|||||||
static int
|
static int
|
||||||
fullrescan()
|
fullrescan()
|
||||||
{
|
{
|
||||||
|
if (!spotify_access_token_valid)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_SPOTIFY, "No valid web api token, ignoring fullrescan\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
scanning = true;
|
scanning = true;
|
||||||
|
|
||||||
create_base_playlist();
|
create_base_playlist();
|
||||||
|
|
||||||
/*
|
|
||||||
* Scan saved tracks from the web api
|
|
||||||
*/
|
|
||||||
if (spotify_access_token_valid)
|
|
||||||
{
|
|
||||||
create_saved_tracks_playlist();
|
create_saved_tracks_playlist();
|
||||||
scan_saved_albums();
|
scan_saved_albums();
|
||||||
scan_playlists();
|
scan_playlists();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spotify_login(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
scanning = false;
|
scanning = false;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user