diff --git a/forked-daapd.conf b/forked-daapd.conf index b5133588..0dc1a623 100644 --- a/forked-daapd.conf +++ b/forked-daapd.conf @@ -91,6 +91,11 @@ library { # forked-daapd will look for jpg and png files with these base names # artwork_basenames = { "artwork", "cover", "Folder" } + # Enable searching for artwork corresponding to each individual media + # file instead of only looking for album artwork. This is disabled by + # default to reduce cache size. +# artwork_individual = false + # File types the scanner should ignore # Non-audio files will never be added to the database, but here you # can prevent the scanner from even probing them. This might improve diff --git a/src/artwork.c b/src/artwork.c index 94e04ff3..de8b4110 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -58,17 +58,17 @@ static const char *cover_extension[] = }; static int -artwork_read(char *filename, struct evbuffer *evbuf) +artwork_read(char *path, struct evbuffer *evbuf) { uint8_t buf[4096]; struct stat sb; int fd; int ret; - fd = open(filename, O_RDONLY); + fd = open(path, O_RDONLY); if (fd < 0) { - DPRINTF(E_WARN, L_ART, "Could not open artwork file '%s': %s\n", filename, strerror(errno)); + DPRINTF(E_WARN, L_ART, "Could not open artwork file '%s': %s\n", path, strerror(errno)); return -1; } @@ -76,7 +76,7 @@ artwork_read(char *filename, struct evbuffer *evbuf) ret = fstat(fd, &sb); if (ret < 0) { - DPRINTF(E_WARN, L_ART, "Could not stat() artwork file '%s': %s\n", filename, strerror(errno)); + DPRINTF(E_WARN, L_ART, "Could not stat() artwork file '%s': %s\n", path, strerror(errno)); goto out_fail; } @@ -621,7 +621,7 @@ artwork_rescale(AVFormatContext *src_ctx, int s, int out_w, int out_h, struct ev } static int -artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf) +artwork_get(char *path, int max_w, int max_h, struct evbuffer *evbuf) { AVFormatContext *src_ctx; int s; @@ -635,13 +635,13 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf) src_ctx = NULL; #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 3) - ret = avformat_open_input(&src_ctx, filename, NULL, NULL); + ret = avformat_open_input(&src_ctx, path, NULL, NULL); #else - ret = av_open_input_file(&src_ctx, filename, NULL, 0, NULL); + ret = av_open_input_file(&src_ctx, path, NULL, 0, NULL); #endif if (ret < 0) { - DPRINTF(E_WARN, L_ART, "Cannot open artwork file '%s': %s\n", filename, strerror(AVUNERROR(ret))); + DPRINTF(E_WARN, L_ART, "Cannot open artwork file '%s': %s\n", path, strerror(AVUNERROR(ret))); return -1; } @@ -688,7 +688,7 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf) if (s == src_ctx->nb_streams) { - DPRINTF(E_LOG, L_ART, "Artwork file '%s' not a PNG or JPEG file\n", filename); + DPRINTF(E_LOG, L_ART, "Artwork file '%s' not a PNG or JPEG file\n", path); #if LIBAVFORMAT_VERSION_MAJOR >= 54 || (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR >= 21) avformat_close_input(&src_ctx); @@ -703,7 +703,7 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf) /* Fastpath */ if (!ret && format_ok) { - ret = artwork_read(filename, evbuf); + ret = artwork_read(path, evbuf); if (ret == 0) ret = format_ok; } @@ -727,7 +727,7 @@ artwork_get(char *filename, int max_w, int max_h, struct evbuffer *evbuf) #if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 6) static int -artwork_get_embedded_image(char *filename, int max_w, int max_h, struct evbuffer *evbuf) +artwork_get_embedded_image(char *path, int max_w, int max_h, struct evbuffer *evbuf) { AVFormatContext *src_ctx; AVStream *src_st; @@ -737,14 +737,14 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, struct evbuffer int format_ok; int ret; - DPRINTF(E_SPAM, L_ART, "Trying embedded artwork in %s\n", filename); + DPRINTF(E_SPAM, L_ART, "Trying embedded artwork in %s\n", path); src_ctx = NULL; - ret = avformat_open_input(&src_ctx, filename, NULL, NULL); + ret = avformat_open_input(&src_ctx, path, NULL, NULL); if (ret < 0) { - DPRINTF(E_WARN, L_ART, "Cannot open media file '%s': %s\n", filename, strerror(AVUNERROR(ret))); + DPRINTF(E_WARN, L_ART, "Cannot open media file '%s': %s\n", path, strerror(AVUNERROR(ret))); return -1; } @@ -786,13 +786,13 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, struct evbuffer if (s == src_ctx->nb_streams) { - DPRINTF(E_SPAM, L_ART, "Did not find embedded artwork in '%s'\n", filename); + DPRINTF(E_SPAM, L_ART, "Did not find embedded artwork in '%s'\n", path); avformat_close_input(&src_ctx); return -1; } else - DPRINTF(E_DBG, L_ART, "Found embedded artwork in '%s'\n", filename); + DPRINTF(E_DBG, L_ART, "Found embedded artwork in '%s'\n", path); src_st = src_ctx->streams[s]; @@ -839,10 +839,88 @@ artwork_get_embedded_image(char *filename, int max_w, int max_h, struct evbuffer } #endif +/* + * Looks for basename(in_path).{png,jpg}, so if is in_path is /foo/bar.mp3 it + * will look for /foo/bar.png and /foo/bar.jpg + * + * @param in_path path to the item we are getting artwork for + * @param max_w maximum image width + * @param max_h maximum image height + * @param out_path path to artwork, input must be either NULL or char[PATH_MAX] + * @param evbuf the event buffer that will contain the (scaled) image + * @return ART_FMT_* on success, 0 on nothing found, -1 on error + */ static int -artwork_get_dir_image(char *path, int max_w, int max_h, char *filename, struct evbuffer *evbuf) +artwork_get_own_image(char *in_path, int max_w, int max_h, char *out_path, struct evbuffer *evbuf) { - char artwork[PATH_MAX]; + char path[PATH_MAX]; + char *ptr; + int len; + int nextensions; + int i; + int ret; + + ret = snprintf(path, sizeof(path), "%s", in_path); + if ((ret < 0) || (ret >= sizeof(path))) + { + DPRINTF(E_LOG, L_ART, "Artwork path exceeds PATH_MAX (%s)\n", in_path); + return -1; + } + + ptr = strrchr(path, '.'); + if (ptr) + *ptr = '\0'; + + len = strlen(path); + + nextensions = sizeof(cover_extension) / sizeof(cover_extension[0]); + + for (i = 0; i < nextensions; i++) + { + ret = snprintf(path + len, sizeof(path) - len, ".%s", cover_extension[i]); + if ((ret < 0) || (ret >= sizeof(path) - len)) + { + DPRINTF(E_LOG, L_ART, "Artwork path will exceed PATH_MAX (%s)\n", in_path); + continue; + } + + DPRINTF(E_SPAM, L_ART, "Trying own artwork file %s\n", path); + + ret = access(path, F_OK); + if (ret < 0) + continue; + + break; + } + + if (i == nextensions) + return 0; + + DPRINTF(E_DBG, L_ART, "Found own artwork file %s\n", path); + + if (out_path) + strcpy(out_path, path); + + return artwork_get(path, max_w, max_h, evbuf); +} + +/* + * Looks for cover files in a directory, so if dir is /foo/bar and the user has + * configured the cover file names "cover" and "artwork" it will look for + * /foo/bar/cover.{png,jpg}, /foo/bar/artwork.{png,jpg} and also + * /foo/bar/bar.{png,jpg} (so called parentdir artwork) + * + * @param dir the directory to search + * @param max_w maximum image width + * @param max_h maximum image height + * @param out_path path to artwork, input must be either NULL or char[PATH_MAX] + * @param evbuf the event buffer that will contain the (scaled) image + * @return ART_FMT_* on success, 0 on nothing found, -1 on error + */ +static int +artwork_get_dir_image(char *dir, int max_w, int max_h, char *out_path, struct evbuffer *evbuf) +{ + char path[PATH_MAX]; char parentdir[PATH_MAX]; int i; int j; @@ -853,21 +931,20 @@ artwork_get_dir_image(char *path, int max_w, int max_h, char *filename, struct e int nextensions; char *ptr; - ret = snprintf(artwork, sizeof(artwork), "%s", path); - if ((ret < 0) || (ret >= sizeof(artwork))) + ret = snprintf(path, sizeof(path), "%s", dir); + if ((ret < 0) || (ret >= sizeof(path))) { - DPRINTF(E_INFO, L_ART, "Artwork path exceeds PATH_MAX\n"); - + DPRINTF(E_LOG, L_ART, "Artwork path exceeds PATH_MAX (%s)\n", dir); return -1; } - len = strlen(artwork); + len = strlen(path); lib = cfg_getsec(cfg, "library"); nbasenames = cfg_size(lib, "artwork_basenames"); if (nbasenames == 0) - return -1; + return 0; nextensions = sizeof(cover_extension) / sizeof(cover_extension[0]); @@ -875,17 +952,16 @@ artwork_get_dir_image(char *path, int max_w, int max_h, char *filename, struct e { for (j = 0; j < nextensions; j++) { - ret = snprintf(artwork + len, sizeof(artwork) - len, "/%s.%s", cfg_getnstr(lib, "artwork_basenames", i), cover_extension[j]); - if ((ret < 0) || (ret >= sizeof(artwork) - len)) + ret = snprintf(path + len, sizeof(path) - len, "/%s.%s", cfg_getnstr(lib, "artwork_basenames", i), cover_extension[j]); + if ((ret < 0) || (ret >= sizeof(path) - len)) { - DPRINTF(E_INFO, L_ART, "Artwork path exceeds PATH_MAX (%s.%s)\n", cfg_getnstr(lib, "artwork_basenames", i), cover_extension[j]); - + DPRINTF(E_LOG, L_ART, "Artwork path will exceed PATH_MAX (%s/%s)\n", dir, cfg_getnstr(lib, "artwork_basenames", i)); continue; } - DPRINTF(E_SPAM, L_ART, "Trying directory artwork file %s\n", artwork); + DPRINTF(E_SPAM, L_ART, "Trying directory artwork file %s\n", path); - ret = access(artwork, F_OK); + ret = access(path, F_OK); if (ret < 0) continue; @@ -901,29 +977,32 @@ artwork_get_dir_image(char *path, int max_w, int max_h, char *filename, struct e // If the loop for directory artwork did not exit early, look for parent directory artwork if (i == nbasenames) { - ptr = strrchr(artwork, '/'); + ptr = strrchr(path, '/'); if (ptr) *ptr = '\0'; - ptr = strrchr(artwork, '/'); + ptr = strrchr(path, '/'); if ((!ptr) || (strlen(ptr) <= 1)) - return -1; + { + DPRINTF(E_LOG, L_ART, "Could not find parent dir name (%s)\n", path); + return -1; + } strcpy(parentdir, ptr + 1); - len = strlen(artwork); + len = strlen(path); for (i = 0; i < nextensions; i++) { - ret = snprintf(artwork + len, sizeof(artwork) - len, "/%s.%s", parentdir, cover_extension[i]); - if ((ret < 0) || (ret >= sizeof(artwork) - len)) + ret = snprintf(path + len, sizeof(path) - len, "/%s.%s", parentdir, cover_extension[i]); + if ((ret < 0) || (ret >= sizeof(path) - len)) { - DPRINTF(E_INFO, L_ART, "Artwork path exceeds PATH_MAX (%s.%s)\n", parentdir, cover_extension[i]); + DPRINTF(E_LOG, L_ART, "Artwork path will exceed PATH_MAX (%s)\n", parentdir); continue; } - DPRINTF(E_SPAM, L_ART, "Trying parent directory artwork file %s\n", artwork); + DPRINTF(E_SPAM, L_ART, "Trying parent directory artwork file %s\n", path); - ret = access(artwork, F_OK); + ret = access(path, F_OK); if (ret < 0) continue; @@ -931,43 +1010,100 @@ artwork_get_dir_image(char *path, int max_w, int max_h, char *filename, struct e } if (i == nextensions) - return -1; + return 0; } - DPRINTF(E_DBG, L_ART, "Found directory artwork file %s\n", artwork); - strcpy(filename, artwork); + DPRINTF(E_DBG, L_ART, "Found directory artwork file %s\n", path); - return artwork_get(artwork, max_w, max_h, evbuf); + if (out_path) + strcpy(out_path, path); + + return artwork_get(path, max_w, max_h, evbuf); } +/* + * Given an artwork type (eg embedded, Spotify, own) this function will direct + * to the appropriate handler + * + * @param in_path path to the item we are getting artwork for + * @param artwork type of the artwork + * @param max_w maximum image width + * @param max_h maximum image height + * @param out_path path to artwork, input must be either NULL or char[PATH_MAX] + * @param evbuf the event buffer that will contain the (scaled) image + * @return ART_FMT_* on success, 0 on nothing found, -1 on error + */ static int -artwork_get_item_path(char *path, int artwork, int max_w, int max_h, struct evbuffer *evbuf) +artwork_get_item_path(char *in_path, int artwork, int max_w, int max_h, char *out_path, struct evbuffer *evbuf) { int ret; ret = 0; + if (out_path) + strcpy(out_path, in_path); + switch (artwork) { case ARTWORK_NONE: break; + case ARTWORK_UNKNOWN: + case ARTWORK_OWN: + if (cfg_getbool(cfg_getsec(cfg, "library"), "artwork_individual")) + ret = artwork_get_own_image(in_path, max_w, max_h, out_path, evbuf); + break; #ifdef HAVE_SPOTIFY_H case ARTWORK_SPOTIFY: - ret = spotify_artwork_get(evbuf, path, max_w, max_h); + ret = spotify_artwork_get(evbuf, in_path, max_w, max_h); (ret < 0) ? (ret = 0) : (ret = ART_FMT_JPEG); break; #endif #if LIBAVFORMAT_VERSION_MAJOR >= 55 || (LIBAVFORMAT_VERSION_MAJOR == 54 && LIBAVFORMAT_VERSION_MINOR >= 6) case ARTWORK_EMBEDDED: - ret = artwork_get_embedded_image(path, max_w, max_h, evbuf); - + ret = artwork_get_embedded_image(in_path, max_w, max_h, evbuf); break; #endif } - if (ret > 0) - return ret; - else - return -1; + return ret; +} + +/* + * Get the artwork for the given media file and the given maxiumum width/height + + * @param mfi the media file structure for the file whose image should be returned + * @param max_w maximum image width + * @param max_h maximum image height + * @param evbuf the event buffer that will contain the (scaled) image + * @return ART_FMT_* on success, 0 on nothing found, -1 on error + */ +static int +artwork_get_item_mfi(struct media_file_info *mfi, int max_w, int max_h, struct evbuffer *evbuf) +{ + char path[PATH_MAX]; + int cached; + int format; + int ret; + + DPRINTF(E_DBG, L_ART, "Looking for artwork for item with id %d\n", mfi->id); + + ret = cache_artwork_get(CACHE_ARTWORK_INDIVIDUAL, mfi->id, max_w, max_h, &cached, &format, evbuf); + if ((ret == 0) && cached) + { + DPRINTF(E_DBG, L_ART, "Item %d found in cache with format %d\n", mfi->id, format); + return format; + } + + if (mfi->data_kind == 0) + { + format = artwork_get_item_path(mfi->path, mfi->artwork, max_w, max_h, path, evbuf); + + if (format > 0) + cache_artwork_add(CACHE_ARTWORK_INDIVIDUAL, mfi->id, max_w, max_h, format, path, evbuf); + + return format; + } + + return -1; } /* @@ -980,46 +1116,34 @@ artwork_get_item_path(char *path, int artwork, int max_w, int max_h, struct evbu * @param max_w maximum image width * @param max_h maximum image height * @param evbuf the event buffer that will contain the (scaled) image + * @return ART_FMT_* on success, 0 on nothing found, -1 on error */ static int artwork_get_group_persistentid(int64_t persistentid, int max_w, int max_h, struct evbuffer *evbuf) { struct query_params qp; struct db_media_file_info dbmfi; + char path[PATH_MAX]; char *dir; - int got_art; int cached; int format; - char filename[PATH_MAX]; int ret; int artwork; int got_spotifyitem; uint32_t data_kind; - DPRINTF(E_DBG, L_ART, "Artwork request for group %" PRIi64 "\n", persistentid); + DPRINTF(E_DBG, L_ART, "Looking for artwork for group with persistentid %" PRIi64 "\n", persistentid); - ret = 0; - format = 0; got_spotifyitem = 0; /* * First check if the artwork cache has a cached entry for the given persistent id and requested width/height */ - ret = cache_artwork_get(persistentid, max_w, max_h, &cached, &format, evbuf); - if (ret == 0 && cached) + ret = cache_artwork_get(CACHE_ARTWORK_GROUP, persistentid, max_w, max_h, &cached, &format, evbuf); + if ((ret == 0) && cached) { - if (format > 0) - { - // Artwork found in cache "ret" contains the format of the image - DPRINTF(E_DBG, L_ART, "Artwork found in cache for group %" PRIi64 "\n", persistentid); - return format; - } - else if (format == 0) - { - // Entry found in cache but there is not artwork available - DPRINTF(E_DBG, L_ART, "Artwork found in cache but no image available for group %" PRIi64 "\n", persistentid); - return -1; - } + DPRINTF(E_DBG, L_ART, "Group %" PRIi64 " found in cache with format %d\n", persistentid, format); + return format; } /* Image is not in the artwork cache. Try directory artwork first */ @@ -1037,28 +1161,31 @@ artwork_get_group_persistentid(int64_t persistentid, int max_w, int max_h, struc goto files_art; } - got_art = 0; - while (!got_art && ((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir)) + format = 0; + while (((ret = db_query_fetch_string(&qp, &dir)) == 0) && (dir)) { - /* If item is an internet stream don't look for artwork */ - if (strncmp(dir, "http://", strlen("http://")) == 0) + /* The db query may return non-directories (eg if item is an internet stream or Spotify) */ + if (access(dir, F_OK) < 0) continue; - /* If Spotify item don't look for files artwork */ - if (strncmp(dir, "spotify:", strlen("spotify:")) == 0) - continue; + format = artwork_get_dir_image(dir, max_w, max_h, path, evbuf); - format = artwork_get_dir_image(dir, max_w, max_h, filename, evbuf); - got_art = (format > 0); + if (format > 0) + break; } db_query_end(&qp); if (ret < 0) - DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_DIRS results\n"); - else if (got_art > 0) + { + DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_DIRS results\n"); + goto files_art; + } + + /* Found artwork, cache it and return */ + if (format > 0) { - cache_artwork_add(persistentid, max_w, max_h, format, filename, evbuf); + cache_artwork_add(CACHE_ARTWORK_GROUP, persistentid, max_w, max_h, format, path, evbuf); return format; } @@ -1074,24 +1201,22 @@ artwork_get_group_persistentid(int64_t persistentid, int max_w, int max_h, struc if (ret < 0) { DPRINTF(E_LOG, L_ART, "Could not start Q_GROUP_ITEMS query\n"); - return -1; } - got_art = 0; - while (!got_art && ((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) + format = 0; + while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) { if ((safe_atoi32(dbmfi.artwork, &artwork) != 0) && (safe_atou32(dbmfi.data_kind, &data_kind) != 0)) continue; - format = artwork_get_item_path(dbmfi.path, artwork, max_w, max_h, evbuf); - got_art = (format > 0); + format = artwork_get_item_path(dbmfi.path, artwork, max_w, max_h, path, evbuf); if (artwork == ARTWORK_SPOTIFY) got_spotifyitem = 1; - if (got_art) - strcpy(filename, dbmfi.path); + if (format > 0) + break; } db_query_end(&qp); @@ -1101,75 +1226,104 @@ artwork_get_group_persistentid(int64_t persistentid, int max_w, int max_h, struc DPRINTF(E_LOG, L_ART, "Error fetching Q_GROUP_ITEMS results\n"); return -1; } - else if (format < 0) + + /* Found artwork, cache it and return */ + if (format > 0) { - DPRINTF(E_LOG, L_ART, "Error reading artwork\n"); - return -1; - } - else if (got_art > 0) - { - cache_artwork_add(persistentid, max_w, max_h, format, filename, evbuf); + cache_artwork_add(CACHE_ARTWORK_GROUP, persistentid, max_w, max_h, format, path, evbuf); return format; } - - /* Add cache entry for no artwork available */ - if (!got_spotifyitem) - cache_artwork_add(persistentid, max_w, max_h, 0, "", evbuf); + else if (format < 0) + { + DPRINTF(E_WARN, L_ART, "Error getting artwork for group %" PRIi64 "\n", persistentid); + return -1; + } DPRINTF(E_DBG, L_ART, "No artwork found for group %" PRIi64 "\n", persistentid); - return -1; + /* Add cache entry for no artwork available */ + if (!got_spotifyitem) + cache_artwork_add(CACHE_ARTWORK_GROUP, persistentid, max_w, max_h, 0, "", evbuf); + + return 0; } +/* + * Get the artwork image for the given item id and the given maximum width/height + * + * @param id the mfi item id + * @param max_w maximum image width + * @param max_h maximum image height + * @param evbuf the event buffer that will contain the (scaled) image + * @return ART_FMT_* on success, -1 on error or no artwork found + */ int artwork_get_item(int id, int max_w, int max_h, struct evbuffer *evbuf) { struct media_file_info *mfi; - int ret; - - DPRINTF(E_DBG, L_ART, "Artwork request for item %d\n", id); + int format; mfi = db_file_fetch_byid(id); if (!mfi) - return -1; + { + DPRINTF(E_LOG, L_ART, "Artwork request for item %d, but no such item in database\n", id); + return -1; + } - /* - * Load artwork image for the persistent id - */ - ret = artwork_get_group_persistentid(mfi->songalbumid, max_w, max_h, evbuf); - if (ret < 0) - DPRINTF(E_DBG, L_ART, "No artwork found for item id %d (%s)\n", id, mfi->fname); + DPRINTF(E_DBG, L_ART, "Artwork request for item %d (%s)\n", id, mfi->fname); + + format = 0; + if (cfg_getbool(cfg_getsec(cfg, "library"), "artwork_individual")) + format = artwork_get_item_mfi(mfi, max_w, max_h, evbuf); + + /* No individual artwork or individual artwork disabled, try group artwork */ + if (format <= 0) + format = artwork_get_group_persistentid(mfi->songalbumid, max_w, max_h, evbuf); free_mfi(mfi, 0); - return ret; + if (format <= 0) + { + DPRINTF(E_DBG, L_ART, "No artwork found for item %d\n", id); + return -1; + } + + return format; } +/* + * Get the artwork image for the given group id and the given maximum width/height + * + * @param id the group id (not the persistent id) + * @param max_w maximum image width + * @param max_h maximum image height + * @param evbuf the event buffer that will contain the (scaled) image + * @return ART_FMT_* on success, -1 on error or no artwork found + */ int artwork_get_group(int id, int max_w, int max_h, struct evbuffer *evbuf) { int64_t persistentid; - int ret; + int format; DPRINTF(E_DBG, L_ART, "Artwork request for group %d\n", id); - /* - * Get the persistent id for the given group id - */ - ret = db_group_persistentid_byid(id, &persistentid); - if (ret < 0) { - DPRINTF(E_LOG, L_ART, "Error fetching persistent id for group id %d\n", id); - return -1; - } + /* Get the persistent id for the given group id */ + if (db_group_persistentid_byid(id, &persistentid) < 0) + { + DPRINTF(E_LOG, L_ART, "Error fetching persistent id for group id %d\n", id); + return -1; + } - /* - * Load artwork image for the persistent id - */ - ret = artwork_get_group_persistentid(persistentid, max_w, max_h, evbuf); - if (ret < 0) - DPRINTF(E_DBG, L_ART, "No artwork found for group id %d\n", id); + /* Load artwork image for the persistent id */ + format = artwork_get_group_persistentid(persistentid, max_w, max_h, evbuf); + if (format <= 0) + { + DPRINTF(E_DBG, L_ART, "No artwork found for group %d\n", id); + return -1; + } - return ret; + return format; } /* Checks if the file is an artwork file */ diff --git a/src/cache.c b/src/cache.c index 213b4292..3353b81f 100644 --- a/src/cache.c +++ b/src/cache.c @@ -38,7 +38,7 @@ #include "cache.h" -#define CACHE_VERSION 1 +#define CACHE_VERSION 2 /* The DAAP cache will cache raw daap replies for queries added with * cache_add(). Only some query types are supported. @@ -66,6 +66,7 @@ struct cache_command int msec; char *path; // artwork path + int type; // individual or group artwork int64_t peristentid; int max_w; int max_h; @@ -236,6 +237,7 @@ cache_create_tables(void) #define T_ARTWORK \ "CREATE TABLE IF NOT EXISTS artwork (" \ " id INTEGER PRIMARY KEY NOT NULL,"\ + " type INTEGER NOT NULL DEFAULT 0," \ " persistentid INTEGER NOT NULL," \ " max_w INTEGER NOT NULL," \ " max_h INTEGER NOT NULL," \ @@ -245,7 +247,7 @@ cache_create_tables(void) " data BLOB" \ ");" #define I_ARTWORK_ID \ - "CREATE INDEX IF NOT EXISTS idx_persistentidwh ON artwork(persistentid, max_w, max_h);" + "CREATE INDEX IF NOT EXISTS idx_persistentidwh ON artwork(type, persistentid, max_w, max_h);" #define I_ARTWORK_PATH \ "CREATE INDEX IF NOT EXISTS idx_pathtime ON artwork(filepath, db_timestamp);" #define T_ADMIN_CACHE \ @@ -1055,7 +1057,7 @@ cache_artwork_add_impl(struct cache_command *cmd) int datalen; int ret; - query = "INSERT INTO artwork (id, persistentid, max_w, max_h, format, filepath, db_timestamp, data) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?);"; + query = "INSERT INTO artwork (id, persistentid, max_w, max_h, format, filepath, db_timestamp, data, type) VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?);"; ret = sqlite3_prepare_v2(g_db_hdl, query, -1, &stmt, 0); if (ret != SQLITE_OK) @@ -1079,6 +1081,7 @@ cache_artwork_add_impl(struct cache_command *cmd) sqlite3_bind_text(stmt, 5, cmd->arg.path, -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 6, (uint64_t)time(NULL)); sqlite3_bind_blob(stmt, 7, data, datalen, SQLITE_STATIC); + sqlite3_bind_int(stmt, 8, cmd->arg.type); ret = sqlite3_step(stmt); if (ret != SQLITE_DONE) @@ -1104,7 +1107,8 @@ cache_artwork_add_impl(struct cache_command *cmd) * If there is a cached entry for the given id and width/height, the parameter cached is set to 1. * In this case format and data contain the cached values. * - * @param cmd->arg.persistentid persistent songalbumid or songartistid + * @param cmd->arg.type individual or group artwork + * @param cmd->arg.persistentid persistent itemid, songalbumid or songartistid * @param cmd->arg.max_w maximum image width * @param cmd->arg.max_h maximum image height * @param cmd->arg.cached set by this function to 0 if no cache entry exists, otherwise 1 @@ -1115,13 +1119,13 @@ cache_artwork_add_impl(struct cache_command *cmd) static int cache_artwork_get_impl(struct cache_command *cmd) { -#define Q_TMPL "SELECT a.format, a.data FROM artwork a WHERE a.persistentid = %" PRIi64 " AND a.max_w = %d AND a.max_h = %d;" +#define Q_TMPL "SELECT a.format, a.data FROM artwork a WHERE a.type = %d AND a.persistentid = %" PRIi64 " AND a.max_w = %d AND a.max_h = %d;" sqlite3_stmt *stmt; char *query; int datalen; int ret; - query = sqlite3_mprintf(Q_TMPL, cmd->arg.peristentid, cmd->arg.max_w, cmd->arg.max_h); + query = sqlite3_mprintf(Q_TMPL, cmd->arg.type, cmd->arg.peristentid, cmd->arg.max_w, cmd->arg.max_h); if (!query) { DPRINTF(E_LOG, L_CACHE, "Out of memory for query string\n"); @@ -1453,7 +1457,8 @@ cache_artwork_purge_cruft(time_t ref) /* * Adds the given (scaled) artwork image to the artwork cache * - * @param persistentid persistent songalbumid or songartistid + * @param type individual or group artwork + * @param persistentid persistent itemid, songalbumid or songartistid * @param max_w maximum image width * @param max_h maximum image height * @param format ART_FMT_PNG for png, ART_FMT_JPEG for jpeg or 0 if no artwork available @@ -1462,7 +1467,7 @@ cache_artwork_purge_cruft(time_t ref) * @return 0 if successful, -1 if an error occurred */ int -cache_artwork_add(int64_t persistentid, int max_w, int max_h, int format, char *filename, struct evbuffer *evbuf) +cache_artwork_add(int type, int64_t persistentid, int max_w, int max_h, int format, char *filename, struct evbuffer *evbuf) { struct cache_command cmd; int ret; @@ -1473,6 +1478,7 @@ cache_artwork_add(int64_t persistentid, int max_w, int max_h, int format, char * command_init(&cmd); cmd.func = cache_artwork_add_impl; + cmd.arg.type = type; cmd.arg.peristentid = persistentid; cmd.arg.max_w = max_w; cmd.arg.max_h = max_h; @@ -1502,7 +1508,7 @@ cache_artwork_add(int64_t persistentid, int max_w, int max_h, int format, char * * @return 0 if successful, -1 if an error occurred */ int -cache_artwork_get(int64_t persistentid, int max_w, int max_h, int *cached, int *format, struct evbuffer *evbuf) +cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cached, int *format, struct evbuffer *evbuf) { struct cache_command cmd; int ret; @@ -1513,6 +1519,7 @@ cache_artwork_get(int64_t persistentid, int max_w, int max_h, int *cached, int * command_init(&cmd); cmd.func = cache_artwork_get_impl; + cmd.arg.type = type; cmd.arg.peristentid = persistentid; cmd.arg.max_w = max_w; cmd.arg.max_h = max_h; diff --git a/src/cache.h b/src/cache.h index c9055ee7..eded991d 100644 --- a/src/cache.h +++ b/src/cache.h @@ -27,6 +27,9 @@ cache_daap_threshold(void); /* ---------------------------- Artwork cache API --------------------------- */ +#define CACHE_ARTWORK_GROUP 0 +#define CACHE_ARTWORK_INDIVIDUAL 1 + int cache_artwork_ping(char *path, time_t mtime); @@ -37,10 +40,10 @@ int cache_artwork_purge_cruft(time_t ref); int -cache_artwork_add(int64_t persistentid, int max_w, int max_h, int format, char *filename, struct evbuffer *evbuf); +cache_artwork_add(int type, int64_t persistentid, int max_w, int max_h, int format, char *filename, struct evbuffer *evbuf); int -cache_artwork_get(int64_t persistentid, int max_w, int max_h, int *cached, int *format, struct evbuffer *evbuf); +cache_artwork_get(int type, int64_t persistentid, int max_w, int max_h, int *cached, int *format, struct evbuffer *evbuf); /* ---------------------------- Cache API --------------------------- */ diff --git a/src/conffile.c b/src/conffile.c index e55a0b04..dbad5ea3 100644 --- a/src/conffile.c +++ b/src/conffile.c @@ -76,6 +76,7 @@ static cfg_opt_t sec_library[] = CFG_STR("name_podcasts", "Podcasts", CFGF_NONE), CFG_STR("name_audiobooks", "Audiobooks", CFGF_NONE), CFG_STR_LIST("artwork_basenames", "{artwork,cover,Folder}", CFGF_NONE), + CFG_BOOL("artwork_individual", cfg_false, CFGF_NONE), CFG_STR_LIST("filetypes_ignore", "{.db,.ini,.db-journal,.pdf}", CFGF_NONE), CFG_BOOL("filescan_disable", cfg_false, CFGF_NONE), CFG_BOOL("itunes_overrides", cfg_false, CFGF_NONE),