From dadba23efc28ad5b950329eff8b30ddb1ccdf044 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 29 Jan 2017 10:01:05 +0100 Subject: [PATCH] Refactor library scan of media files Remove the data_kind specific parts out of the general library functions and into their (library) source specific functions. --- src/library.c | 128 +------------- src/library.h | 2 +- src/library/filescanner.c | 266 ++++++++++++++++------------- src/library/filescanner.h | 8 - src/library/filescanner_playlist.c | 32 +++- src/spotify.c | 76 ++++++++- 6 files changed, 257 insertions(+), 255 deletions(-) diff --git a/src/library.c b/src/library.c index 413e3ca3..1b1cbb24 100644 --- a/src/library.c +++ b/src/library.c @@ -42,7 +42,6 @@ #include "commands.h" #include "conffile.h" #include "db.h" -#include "library/filescanner.h" #include "logger.h" #include "misc.h" #include "listener.h" @@ -345,109 +344,20 @@ fixup_tags(struct media_file_info *mfi) } void -library_process_media(const char *path, time_t mtime, off_t size, enum data_kind data_kind, enum media_kind force_media_kind, bool force_compilation, struct media_file_info *external_mfi, int dir_id) +library_process_media(struct media_file_info *mfi) { - struct media_file_info *mfi; - const char *filename; - time_t stamp; - int id; - char virtual_path[PATH_MAX]; - int ret; - - filename = strrchr(path, '/'); - if ((!filename) || (strlen(filename) == 1)) - filename = path; - else - filename++; - - db_file_stamp_bypath(path, &stamp, &id); - - if (stamp && (stamp >= mtime)) + if (!mfi->path || !mfi->fname || !mfi->data_kind) { - db_file_ping(id); + DPRINTF(E_LOG, L_LIB, "Ignoring media file with missing values (path='%s', fname='%s', data_kind='%d')\n", + mfi->path, mfi->fname, mfi->data_kind); return; } - if (!external_mfi) + if (!mfi->directory_id || mfi->virtual_path) { - mfi = (struct media_file_info*)malloc(sizeof(struct media_file_info)); - if (!mfi) - { - DPRINTF(E_LOG, L_LIB, "Out of memory for mfi\n"); - return; - } - - memset(mfi, 0, sizeof(struct media_file_info)); - } - else - mfi = external_mfi; - - if (stamp) - mfi->id = id; - - mfi->fname = strdup(filename); - if (!mfi->fname) - { - DPRINTF(E_LOG, L_LIB, "Out of memory for fname\n"); - goto out; - } - - mfi->path = strdup(path); - if (!mfi->path) - { - DPRINTF(E_LOG, L_LIB, "Out of memory for path\n"); - goto out; - } - - mfi->time_modified = mtime; - mfi->file_size = size; - - if (force_compilation) - mfi->compilation = 1; - if (force_media_kind) - mfi->media_kind = force_media_kind; - - if (data_kind == DATA_KIND_FILE) - { - mfi->data_kind = DATA_KIND_FILE; - ret = scan_metadata_ffmpeg(path, mfi); - } - else if (data_kind == DATA_KIND_HTTP) - { - mfi->data_kind = DATA_KIND_HTTP; - ret = scan_metadata_ffmpeg(path, mfi); - if (ret < 0) - { - DPRINTF(E_LOG, L_LIB, "Playlist URL '%s' is unavailable for probe/metadata, assuming MP3 encoding\n", path); - mfi->type = strdup("mp3"); - mfi->codectype = strdup("mpeg"); - mfi->description = strdup("MPEG audio file"); - ret = 1; - } - } - else if (data_kind == DATA_KIND_SPOTIFY) - { - mfi->data_kind = DATA_KIND_SPOTIFY; - ret = mfi->artist && mfi->album && mfi->title; - } - else if (data_kind == DATA_KIND_PIPE) - { - mfi->data_kind = DATA_KIND_PIPE; - mfi->type = strdup("wav"); - mfi->codectype = strdup("wav"); - mfi->description = strdup("PCM16 pipe"); - ret = 1; - } - else - { - DPRINTF(E_LOG, L_LIB, "Unknown scan type for '%s', this error should not occur\n", path); - ret = -1; - } - - if (ret < 0) - { - DPRINTF(E_INFO, L_LIB, "Could not extract metadata for '%s'\n", path); - goto out; + // Missing informations for virtual_path and directory_id (may) lead to misplaced appearance in mpd clients + DPRINTF(E_WARN, L_LIB, "Media file with missing values (path='%s', directory='%d', virtual_path='%s')\n", + mfi->path, mfi->directory_id, mfi->virtual_path); } if (!mfi->item_kind) @@ -459,32 +369,10 @@ library_process_media(const char *path, time_t mtime, off_t size, enum data_kind fixup_tags(mfi); - if (data_kind == DATA_KIND_HTTP) - { - snprintf(virtual_path, PATH_MAX, "/http:/%s", mfi->title); - mfi->virtual_path = strdup(virtual_path); - } - else if (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); - } - else - { - snprintf(virtual_path, PATH_MAX, "/file:%s", mfi->path); - mfi->virtual_path = strdup(virtual_path); - } - - mfi->directory_id = dir_id; - if (mfi->id == 0) db_file_add(mfi); else db_file_update(mfi); - - out: - if (!external_mfi) - free_mfi(mfi, 0); } int diff --git a/src/library.h b/src/library.h index 740c7540..f2929cf6 100644 --- a/src/library.h +++ b/src/library.h @@ -66,7 +66,7 @@ struct library_source void -library_process_media(const char *path, time_t mtime, off_t size, enum data_kind data_kind, enum media_kind force_media_kind, bool force_compilation, struct media_file_info *external_mfi, int dir_id); +library_process_media(struct media_file_info *mfi); int library_add_playlist_info(const char *path, const char *title, const char *virtual_path, enum pl_type type, int parent_pl_id, int dir_id); diff --git a/src/library/filescanner.c b/src/library/filescanner.c index e8a7a5bb..896a4533 100644 --- a/src/library/filescanner.c +++ b/src/library/filescanner.c @@ -77,6 +77,12 @@ #define F_SCAN_FAST (1 << 2) #define F_SCAN_MOVED (1 << 3) +#define F_SCAN_TYPE_FILE (1 << 0) +#define F_SCAN_TYPE_PODCAST (1 << 1) +#define F_SCAN_TYPE_AUDIOBOOK (1 << 2) +#define F_SCAN_TYPE_COMPILATION (1 << 3) + + enum file_type { FILE_UNKNOWN = 0, FILE_IGNORE, @@ -389,37 +395,83 @@ process_deferred_playlists(void) } } +static void +process_regular_file(char *file, struct stat *sb, int type, int flags, int dir_id) +{ + time_t stamp; + int id; + bool is_bulkscan = (flags & F_SCAN_BULK); + struct media_file_info mfi; + char virtual_path[PATH_MAX]; + int ret; + + // Do not rescan metadata if file did not change since last scan + db_file_stamp_bypath(file, &stamp, &id); + if (stamp && (stamp >= sb->st_mtime)) + { + db_file_ping(id); + return; + } + + // File is new or modified - (re)scan metadata and update file in library + memset(&mfi, 0, sizeof(struct media_file_info)); + + mfi.id = id; + mfi.fname = strdup(basename(file)); + mfi.path = strdup(file); + + mfi.time_modified = sb->st_mtime; + mfi.file_size = sb->st_size; + + snprintf(virtual_path, PATH_MAX, "/file:%s", file); + mfi.virtual_path = strdup(virtual_path); + + mfi.directory_id = dir_id; + + if (S_ISFIFO(sb->st_mode)) + { + mfi.data_kind = DATA_KIND_PIPE; + mfi.type = strdup("wav"); + mfi.codectype = strdup("wav"); + mfi.description = strdup("PCM16 pipe"); + mfi.media_kind = MEDIA_KIND_MUSIC; + } + else + { + mfi.data_kind = DATA_KIND_FILE; + + if (type & F_SCAN_TYPE_AUDIOBOOK) + mfi.media_kind = MEDIA_KIND_AUDIOBOOK; + else if (type & F_SCAN_TYPE_PODCAST) + mfi.media_kind = MEDIA_KIND_PODCAST; + + mfi.compilation = (type & F_SCAN_TYPE_COMPILATION); + mfi.file_size = sb->st_size; + + ret = scan_metadata_ffmpeg(file, &mfi); + if (ret < 0) + { + free_mfi(&mfi, 1); + return; + } + } + + library_process_media(&mfi); + + cache_artwork_ping(file, sb->st_mtime, !is_bulkscan); + // TODO [artworkcache] If entry in artwork cache exists for no artwork available, delete the entry if media file has embedded artwork + + free_mfi(&mfi, 1); +} + /* Thread: scan */ static void -process_file(char *file, time_t mtime, off_t size, int type, int flags, int dir_id) +process_file(char *file, struct stat *sb, int type, int flags, int dir_id) { - bool is_bulkscan; - bool is_type_compilation; - enum media_kind media_kind; - enum data_kind data_kind; - - is_bulkscan = (flags & F_SCAN_BULK); - switch (file_type_get(file)) { case FILE_REGULAR: - is_type_compilation = type & F_SCAN_TYPE_COMPILATION; - if (type & F_SCAN_TYPE_AUDIOBOOK) - media_kind = MEDIA_KIND_AUDIOBOOK; - else if (type & F_SCAN_TYPE_PODCAST) - media_kind = MEDIA_KIND_PODCAST; - else - media_kind = 0; - - if (F_SCAN_TYPE_PIPE & type) - data_kind = DATA_KIND_PIPE; - else - data_kind = DATA_KIND_FILE; - - library_process_media(file, mtime, size, data_kind, media_kind, is_type_compilation, NULL, dir_id); - - cache_artwork_ping(file, mtime, !is_bulkscan); - // TODO [artworkcache] If entry in artwork cache exists for no artwork available, delete the entry if media file has embedded artwork + process_regular_file(file, sb, type, flags, dir_id); counter++; @@ -435,19 +487,19 @@ process_file(char *file, time_t mtime, off_t size, int type, int flags, int dir_ case FILE_PLAYLIST: case FILE_ITUNES: if (flags & F_SCAN_BULK) - defer_playlist(file, mtime, dir_id); + defer_playlist(file, sb->st_mtime, dir_id); else - process_playlist(file, mtime, dir_id); + process_playlist(file, sb->st_mtime, dir_id); break; case FILE_SMARTPL: DPRINTF(E_DBG, L_SCAN, "Smart playlist file: %s\n", file); - scan_smartpl(file, mtime, dir_id); + scan_smartpl(file, sb->st_mtime, dir_id); break; case FILE_ARTWORK: DPRINTF(E_DBG, L_SCAN, "Artwork file: %s\n", file); - cache_artwork_ping(file, mtime, !is_bulkscan); + cache_artwork_ping(file, sb->st_mtime, !(flags & F_SCAN_BULK)); // TODO [artworkcache] If entry in artwork cache exists for no artwork available for a album with files in the same directory, delete the entry @@ -539,13 +591,58 @@ create_virtual_path(char *path, char *virtual_path, int virtual_path_len) return 0; } +/* + * Returns informations about the attributes of the file at the given 'path' in the structure + * pointed to by 'sb'. + * + * If path is a symbolic link, the attributes in sb describe the file that the link points to + * and resolved_path contains the resolved path (resolved_path must be of length PATH_MAX). + * If path is not a symbolic link, resolved_path holds the same value as path. + * + * The return value is 0 if the operation is successful, or -1 on failure. In addition + */ +static int +read_attributes(const char *path, struct stat *sb, char *resolved_path) +{ + int ret; + + ret = lstat(path, sb); + if (ret < 0) + { + DPRINTF(E_LOG, L_SCAN, "Skipping %s, lstat() failed: %s\n", path, strerror(errno)); + return -1; + } + + if (S_ISLNK(sb->st_mode)) + { + if (!realpath(path, resolved_path)) + { + DPRINTF(E_LOG, L_SCAN, "Skipping %s, could not dereference symlink: %s\n", path, strerror(errno)); + return -1; + } + + ret = stat(resolved_path, sb); + if (ret < 0) + { + DPRINTF(E_LOG, L_SCAN, "Skipping %s, stat() failed: %s\n", resolved_path, strerror(errno)); + return -1; + } + } + else + { + strcpy(resolved_path, path); + } + + return 0; +} + static void process_directory(char *path, int parent_id, int flags) { DIR *dirp; struct dirent *de; char entry[PATH_MAX]; - char *deref; + char resolved_path[PATH_MAX]; struct stat sb; struct watch_info wi; int type; @@ -612,59 +709,25 @@ process_directory(char *path, int parent_id, int flags) continue; } - ret = lstat(entry, &sb); + ret = read_attributes(entry, &sb, resolved_path); if (ret < 0) { - DPRINTF(E_LOG, L_SCAN, "Skipping %s, lstat() failed: %s\n", entry, strerror(errno)); + DPRINTF(E_LOG, L_SCAN, "Skipping %s, read_attributes() failed\n", entry); continue; } - if (S_ISLNK(sb.st_mode)) + if (S_ISDIR(sb.st_mode)) { - deref = m_realpath(entry); - if (!deref) - { - DPRINTF(E_LOG, L_SCAN, "Skipping %s, could not dereference symlink: %s\n", entry, strerror(errno)); - - continue; - } - - ret = stat(deref, &sb); - if (ret < 0) - { - DPRINTF(E_LOG, L_SCAN, "Skipping %s, stat() failed: %s\n", deref, strerror(errno)); - - free(deref); - continue; - } - - ret = snprintf(entry, sizeof(entry), "%s", deref); - if ((ret < 0) || (ret >= sizeof(entry))) - { - DPRINTF(E_LOG, L_SCAN, "Skipping %s, PATH_MAX exceeded\n", entry); - - free(deref); - continue; - } - - free(deref); + push_dir(&dirstack, resolved_path, dir_id); } - - if (S_ISREG(sb.st_mode)) + else if (!(flags & F_SCAN_FAST)) { - if (!(flags & F_SCAN_FAST)) - process_file(entry, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, flags, dir_id); + if (S_ISREG(sb.st_mode) || S_ISFIFO(sb.st_mode)) + process_file(resolved_path, &sb, type, flags, dir_id); + else + DPRINTF(E_LOG, L_SCAN, "Skipping %s, not a directory, symlink, pipe nor regular file\n", entry); } - else if (S_ISFIFO(sb.st_mode)) - { - if (!(flags & F_SCAN_FAST)) - process_file(entry, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, flags, dir_id); - } - else if (S_ISDIR(sb.st_mode)) - push_dir(&dirstack, entry, dir_id); - else - DPRINTF(E_LOG, L_SCAN, "Skipping %s, not a directory, symlink, pipe nor regular file\n", entry); } closedir(dirp); @@ -1040,8 +1103,8 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie { struct stat sb; uint32_t path_hash; - char *deref = NULL; char *file = path; + char resolved_path[PATH_MAX]; char *dir; char dir_vpath[PATH_MAX]; int type; @@ -1177,44 +1240,14 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie incomingfiles_buffer[i] = 0; } - ret = lstat(path, &sb); + ret = read_attributes(path, &sb, resolved_path); if (ret < 0) - { - DPRINTF(E_LOG, L_SCAN, "Could not lstat() '%s': %s\n", path, strerror(errno)); + { + DPRINTF(E_LOG, L_SCAN, "Skipping %s, read_attributes() failed\n", path); return; } - if (S_ISLNK(sb.st_mode)) - { - deref = m_realpath(path); - if (!deref) - { - DPRINTF(E_LOG, L_SCAN, "Could not dereference symlink '%s': %s\n", path, strerror(errno)); - - return; - } - - file = deref; - - ret = stat(deref, &sb); - if (ret < 0) - { - DPRINTF(E_LOG, L_SCAN, "Could not stat() '%s': %s\n", file, strerror(errno)); - - free(deref); - return; - } - - if (S_ISDIR(sb.st_mode)) - { - process_inotify_dir(wi, deref, ie); - - free(deref); - return; - } - } - type = 0; if (check_speciallib(path, "compilations")) type |= F_SCAN_TYPE_COMPILATION; @@ -1225,15 +1258,18 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie dir_id = get_parent_dir_id(file); - if (S_ISREG(sb.st_mode)) - { - process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_FILE | type, 0, dir_id); - } - else if (S_ISFIFO(sb.st_mode)) - process_file(file, sb.st_mtime, sb.st_size, F_SCAN_TYPE_PIPE | type, 0, dir_id); + if (S_ISDIR(sb.st_mode)) + { + process_inotify_dir(wi, resolved_path, ie); - if (deref) - free(deref); + return; + } + else if (S_ISREG(sb.st_mode) || S_ISFIFO(sb.st_mode)) + { + process_file(resolved_path, &sb, type, 0, dir_id); + } + else + DPRINTF(E_LOG, L_SCAN, "Skipping %s, not a directory, symlink, pipe nor regular file\n", resolved_path); } } diff --git a/src/library/filescanner.h b/src/library/filescanner.h index 222052d9..9e9b89e8 100644 --- a/src/library/filescanner.h +++ b/src/library/filescanner.h @@ -4,14 +4,6 @@ #include "db.h" -#define F_SCAN_TYPE_FILE (1 << 0) -#define F_SCAN_TYPE_PODCAST (1 << 1) -#define F_SCAN_TYPE_AUDIOBOOK (1 << 2) -#define F_SCAN_TYPE_COMPILATION (1 << 3) -#define F_SCAN_TYPE_URL (1 << 4) -#define F_SCAN_TYPE_SPOTIFY (1 << 5) -#define F_SCAN_TYPE_PIPE (1 << 6) - /* Actual scanners */ int diff --git a/src/library/filescanner_playlist.c b/src/library/filescanner_playlist.c index 345b05c1..10538e57 100644 --- a/src/library/filescanner_playlist.c +++ b/src/library/filescanner_playlist.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "logger.h" #include "db.h" @@ -94,6 +95,8 @@ scan_playlist(char *file, time_t mtime, int dir_id) int ret; char virtual_path[PATH_MAX]; int i; + time_t stamp; + int id; DPRINTF(E_LOG, L_SCAN, "Processing static playlist: %s\n", file); @@ -226,7 +229,14 @@ scan_playlist(char *file, time_t mtime, int dir_id) /* Check if line is an URL, will be added to library */ if (strncasecmp(path, "http://", strlen("http://")) == 0) { - DPRINTF(E_DBG, L_SCAN, "Playlist contains URL entry\n"); + DPRINTF(E_DBG, L_SCAN, "Playlist contains URL entry: '%s'\n", path); + + db_file_stamp_bypath(path, &stamp, &id); + if (stamp && (stamp >= sb.st_mtime)) + { + db_file_ping(id); + continue; + } filename = strdup(path); if (!filename) @@ -239,7 +249,25 @@ scan_playlist(char *file, time_t mtime, int dir_id) if (extinf) DPRINTF(E_INFO, L_SCAN, "Playlist has EXTINF metadata, artist is '%s', title is '%s'\n", mfi.artist, mfi.title); - library_process_media(filename, mtime, 0, DATA_KIND_HTTP, 0, false, &mfi, DIR_HTTP); + mfi.id = id; + mfi.fname = strdup(basename(filename)); + mfi.path = strdup(filename); + mfi.data_kind = DATA_KIND_HTTP; + mfi.time_modified = mtime; + mfi.directory_id = DIR_HTTP; + + ret = scan_metadata_ffmpeg(path, &mfi); + if (ret < 0) + { + DPRINTF(E_LOG, L_SCAN, "Playlist URL '%s' is unavailable for probe/metadata, assuming MP3 encoding\n", path); + mfi.type = strdup("mp3"); + mfi.codectype = strdup("mpeg"); + mfi.description = strdup("MPEG audio file"); + } + + snprintf(virtual_path, PATH_MAX, "/http:/%s", mfi.title); //TODO can title be null at this point? + mfi.virtual_path = strdup(virtual_path); + library_process_media(&mfi); } /* Regular file, should already be in library */ else diff --git a/src/spotify.c b/src/spotify.c index 60579966..624b7f83 100644 --- a/src/spotify.c +++ b/src/spotify.c @@ -640,7 +640,10 @@ spotify_track_save(int plid, sp_track *track, const char *pltitle, int time_adde sp_link *link; char url[1024]; int ret; + char virtual_path[PATH_MAX]; int dir_id; + time_t stamp; + int id; memset(&mfi, 0, sizeof(struct media_file_info)); @@ -697,7 +700,18 @@ spotify_track_save(int plid, sp_track *track, const char *pltitle, int time_adde // DPRINTF(E_DBG, L_SPOTIFY, "Saving track '%s': '%s' by %s (%s)\n", url, mfi.title, mfi.artist, mfi.album); - library_process_media(url, time(NULL), 0, DATA_KIND_SPOTIFY, 0, false, &mfi, dir_id); + db_file_stamp_bypath(url, &stamp, &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_process_media(&mfi); free_mfi(&mfi, 1); @@ -2008,6 +2022,9 @@ map_track_to_mfi(const struct spotify_track *track, struct media_file_info* mfi) mfi->type = strdup("spotify"); mfi->codectype = strdup("wav"); mfi->description = strdup("Spotify audio"); + + mfi->path = strdup(track->uri); + mfi->fname = strdup(track->uri); } static void @@ -2018,6 +2035,7 @@ map_album_to_mfi(const struct spotify_album *album, struct media_file_info* mfi) mfi->genre = safe_strdup(album->genre); mfi->compilation = album->is_compilation; mfi->year = album->release_year; + mfi->time_modified = album->mtime; } /* Thread: library */ @@ -2030,7 +2048,10 @@ scan_saved_albums() struct spotify_album album; struct spotify_track track; struct media_file_info mfi; + char virtual_path[PATH_MAX]; int dir_id; + time_t stamp; + int id; int i; int count; int ret; @@ -2054,17 +2075,34 @@ scan_saved_albums() ret = spotifywebapi_album_track_fetch(jsontracks, i, &track); if (ret == 0 && track.uri) { - memset(&mfi, 0, sizeof(struct media_file_info)); - map_track_to_mfi(&track, &mfi); - map_album_to_mfi(&album, &mfi); + db_file_stamp_bypath(track.uri, &stamp, &id); + if (stamp && (stamp >= track.mtime)) + { + db_file_ping(id); + } + else + { + memset(&mfi, 0, sizeof(struct media_file_info)); + + mfi.id = id; + + map_track_to_mfi(&track, &mfi); + map_album_to_mfi(&album, &mfi); + + 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_process_media(&mfi); + + free_mfi(&mfi, 1); + } - library_process_media(track.uri, album.mtime, 0, DATA_KIND_SPOTIFY, 0, album.is_compilation, &mfi, dir_id); spotify_uri_register(track.uri); 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); } @@ -2093,7 +2131,10 @@ scan_playlisttracks(struct spotify_playlist *playlist, int plid) struct spotify_request request; struct spotify_track track; struct media_file_info mfi; + char virtual_path[PATH_MAX]; int dir_id; + time_t stamp; + int id; memset(&request, 0, sizeof(struct spotify_request)); @@ -2121,9 +2162,19 @@ scan_playlisttracks(struct spotify_playlist *playlist, int plid) if (track.linked_from_uri) DPRINTF(E_DBG, L_SPOTIFY, "Track '%s' (%s) linked from %s\n", track.name, track.uri, track.linked_from_uri); - dir_id = prepare_directories(track.album_artist, track.album); + db_file_stamp_bypath(track.uri, &stamp, &id); + if (stamp) + { + db_file_ping(id); + continue; + } memset(&mfi, 0, sizeof(struct media_file_info)); + + mfi.id = id; + + dir_id = prepare_directories(track.album_artist, track.album); + map_track_to_mfi(&track, &mfi); mfi.compilation = (track.is_compilation || artist_override); @@ -2133,7 +2184,14 @@ scan_playlisttracks(struct spotify_playlist *playlist, int plid) mfi.album = strdup(playlist->name); } - library_process_media(track.uri, 1 /* TODO passing one prevents overwriting existing entries */, 0, DATA_KIND_SPOTIFY, 0, track.is_compilation, &mfi, dir_id); + 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_process_media(&mfi); + spotify_uri_register(track.uri); cache_artwork_ping(track.uri, 1, 0);