Subsonic API: now returns 404 when the requested cover art is not found (but a client list has been added to serve them a default cover art)
This commit is contained in:
@@ -345,9 +345,6 @@ namespace Cover
|
||||
}
|
||||
}
|
||||
|
||||
if (!cover)
|
||||
cover = getDefault(width);
|
||||
|
||||
if (cover)
|
||||
saveToCache(cacheEntryDesc, cover);
|
||||
|
||||
@@ -397,9 +394,6 @@ namespace Cover
|
||||
cover = getFromTrack(session, releaseInfo->firstTrackId, width, false /* no release fallback */);
|
||||
}
|
||||
|
||||
if (!cover)
|
||||
cover = getDefault(width);
|
||||
|
||||
if (cover)
|
||||
saveToCache(cacheEntryDesc, cover);
|
||||
|
||||
|
||||
@@ -35,103 +35,97 @@
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Session;
|
||||
class Session;
|
||||
}
|
||||
|
||||
namespace Av
|
||||
{
|
||||
class IAudioFile;
|
||||
class IAudioFile;
|
||||
}
|
||||
|
||||
namespace Cover
|
||||
{
|
||||
struct CacheEntryDesc
|
||||
{
|
||||
std::variant<Database::TrackId, Database::ReleaseId> id;
|
||||
std::size_t size;
|
||||
|
||||
bool operator==(const CacheEntryDesc& other) const
|
||||
{
|
||||
return id == other.id
|
||||
&& size == other.size;
|
||||
}
|
||||
};
|
||||
struct CacheEntryDesc
|
||||
{
|
||||
std::variant<Database::TrackId, Database::ReleaseId> id;
|
||||
std::size_t size;
|
||||
|
||||
bool operator==(const CacheEntryDesc& other) const
|
||||
{
|
||||
return id == other.id
|
||||
&& size == other.size;
|
||||
}
|
||||
};
|
||||
} // ns Cover
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<>
|
||||
class hash<Cover::CacheEntryDesc>
|
||||
{
|
||||
public:
|
||||
size_t operator()(const Cover::CacheEntryDesc& e) const
|
||||
{
|
||||
size_t h {};
|
||||
std::visit([&](auto id)
|
||||
{
|
||||
using IdType = std::decay_t<decltype(id)>;
|
||||
h ^= std::hash<IdType>()(id);
|
||||
}, e.id);
|
||||
h ^= std::hash<std::size_t>()(e.size) << 1;
|
||||
return h;
|
||||
}
|
||||
};
|
||||
template<>
|
||||
class hash<Cover::CacheEntryDesc>
|
||||
{
|
||||
public:
|
||||
size_t operator()(const Cover::CacheEntryDesc& e) const
|
||||
{
|
||||
size_t h{};
|
||||
std::visit([&](auto id)
|
||||
{
|
||||
using IdType = std::decay_t<decltype(id)>;
|
||||
h ^= std::hash<IdType>()(id);
|
||||
}, e.id);
|
||||
h ^= std::hash<std::size_t>()(e.size) << 1;
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
} // ns std
|
||||
|
||||
namespace Cover
|
||||
{
|
||||
class CoverService : public ICoverService
|
||||
{
|
||||
public:
|
||||
CoverService(Database::Db& db,
|
||||
const std::filesystem::path& execPath,
|
||||
const std::filesystem::path& defaultCoverPath);
|
||||
class CoverService : public ICoverService
|
||||
{
|
||||
public:
|
||||
CoverService(Database::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath);
|
||||
|
||||
CoverService(const CoverService&) = delete;
|
||||
CoverService& operator=(const CoverService&) = delete;
|
||||
CoverService(CoverService&&) = delete;
|
||||
CoverService& operator=(CoverService&&) = delete;
|
||||
CoverService(const CoverService&) = delete;
|
||||
CoverService& operator=(const CoverService&) = delete;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Image::IEncodedImage> getFromTrack(Database::TrackId trackId, Image::ImageSize width) override;
|
||||
std::shared_ptr<Image::IEncodedImage> getFromRelease(Database::ReleaseId releaseId, Image::ImageSize width) override;
|
||||
void flushCache() override;
|
||||
void setJpegQuality(unsigned quality) override;
|
||||
private:
|
||||
std::shared_ptr<Image::IEncodedImage> getFromTrack(Database::TrackId trackId, Image::ImageSize width) override;
|
||||
std::shared_ptr<Image::IEncodedImage> getFromRelease(Database::ReleaseId releaseId, Image::ImageSize width) override;
|
||||
std::shared_ptr<Image::IEncodedImage> getDefault(Image::ImageSize width) override;
|
||||
void flushCache() override;
|
||||
void setJpegQuality(unsigned quality) override;
|
||||
|
||||
std::shared_ptr<Image::IEncodedImage> getFromTrack(Database::Session& dbSession, Database::TrackId trackId, Image::ImageSize width, bool allowReleaseFallback);
|
||||
std::unique_ptr<Image::IEncodedImage> getFromAvMediaFile(const Av::IAudioFile& input, Image::ImageSize width) const;
|
||||
std::unique_ptr<Image::IEncodedImage> getFromCoverFile(const std::filesystem::path& p, Image::ImageSize width) const;
|
||||
std::shared_ptr<Image::IEncodedImage> getFromTrack(Database::Session& dbSession, Database::TrackId trackId, Image::ImageSize width, bool allowReleaseFallback);
|
||||
std::unique_ptr<Image::IEncodedImage> getFromAvMediaFile(const Av::IAudioFile& input, Image::ImageSize width) const;
|
||||
std::unique_ptr<Image::IEncodedImage> getFromCoverFile(const std::filesystem::path& p, Image::ImageSize width) const;
|
||||
|
||||
std::unique_ptr<Image::IEncodedImage> getFromTrack(const std::filesystem::path& path, Image::ImageSize width) const;
|
||||
std::multimap<std::string, std::filesystem::path> getCoverPaths(const std::filesystem::path& directoryPath) const;
|
||||
std::unique_ptr<Image::IEncodedImage> getFromDirectory(const std::filesystem::path& directory, Image::ImageSize width) const;
|
||||
std::unique_ptr<Image::IEncodedImage> getFromSameNamedFile(const std::filesystem::path& filePath, Image::ImageSize width) const;
|
||||
std::shared_ptr<Image::IEncodedImage> getDefault(Image::ImageSize width);
|
||||
std::unique_ptr<Image::IEncodedImage> getFromTrack(const std::filesystem::path& path, Image::ImageSize width) const;
|
||||
std::multimap<std::string, std::filesystem::path> getCoverPaths(const std::filesystem::path& directoryPath) const;
|
||||
std::unique_ptr<Image::IEncodedImage> getFromDirectory(const std::filesystem::path& directory, Image::ImageSize width) const;
|
||||
std::unique_ptr<Image::IEncodedImage> getFromSameNamedFile(const std::filesystem::path& filePath, Image::ImageSize width) const;
|
||||
|
||||
bool checkCoverFile(const std::filesystem::path& directoryPath) const;
|
||||
bool checkCoverFile(const std::filesystem::path& directoryPath) const;
|
||||
|
||||
Database::Db& _db;
|
||||
Database::Db& _db;
|
||||
|
||||
std::shared_mutex _cacheMutex;
|
||||
std::unordered_map<CacheEntryDesc, std::shared_ptr<Image::IEncodedImage>> _cache;
|
||||
std::unordered_map<Image::ImageSize, std::shared_ptr<Image::IEncodedImage>> _defaultCoverCache;
|
||||
std::atomic<std::size_t> _cacheMisses {};
|
||||
std::atomic<std::size_t> _cacheHits {};
|
||||
std::size_t _cacheSize {};
|
||||
std::shared_mutex _cacheMutex;
|
||||
std::unordered_map<CacheEntryDesc, std::shared_ptr<Image::IEncodedImage>> _cache;
|
||||
std::unordered_map<Image::ImageSize, std::shared_ptr<Image::IEncodedImage>> _defaultCoverCache;
|
||||
std::atomic<std::size_t> _cacheMisses{};
|
||||
std::atomic<std::size_t> _cacheHits{};
|
||||
std::size_t _cacheSize{};
|
||||
|
||||
void saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr<Image::IEncodedImage> image);
|
||||
std::shared_ptr<Image::IEncodedImage> loadFromCache(const CacheEntryDesc& entryDesc);
|
||||
void saveToCache(const CacheEntryDesc& entryDesc, std::shared_ptr<Image::IEncodedImage> image);
|
||||
std::shared_ptr<Image::IEncodedImage> loadFromCache(const CacheEntryDesc& entryDesc);
|
||||
|
||||
const std::filesystem::path _defaultCoverPath;
|
||||
const std::size_t _maxCacheSize;
|
||||
static inline const std::vector<std::filesystem::path> _fileExtensions {".jpg", ".jpeg", ".png", ".bmp"}; // TODO parametrize
|
||||
const std::size_t _maxFileSize;
|
||||
const std::vector<std::string> _preferredFileNames;
|
||||
unsigned _jpegQuality;
|
||||
};
|
||||
const std::filesystem::path _defaultCoverPath;
|
||||
const std::size_t _maxCacheSize;
|
||||
static inline const std::vector<std::filesystem::path> _fileExtensions{ ".jpg", ".jpeg", ".png", ".bmp" }; // TODO parametrize
|
||||
const std::size_t _maxFileSize;
|
||||
const std::vector<std::string> _preferredFileNames;
|
||||
unsigned _jpegQuality;
|
||||
};
|
||||
|
||||
} // namespace Cover
|
||||
|
||||
|
||||
@@ -28,27 +28,27 @@
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Db;
|
||||
class Db;
|
||||
}
|
||||
|
||||
namespace Cover
|
||||
{
|
||||
class ICoverService
|
||||
{
|
||||
public:
|
||||
virtual ~ICoverService() = default;
|
||||
class ICoverService
|
||||
{
|
||||
public:
|
||||
virtual ~ICoverService() = default;
|
||||
|
||||
virtual std::shared_ptr<Image::IEncodedImage> getFromTrack(Database::TrackId trackId, Image::ImageSize width) = 0;
|
||||
virtual std::shared_ptr<Image::IEncodedImage> getFromRelease(Database::ReleaseId releaseId, Image::ImageSize width) = 0;
|
||||
virtual std::shared_ptr<Image::IEncodedImage> getFromTrack(Database::TrackId trackId, Image::ImageSize width) = 0;
|
||||
virtual std::shared_ptr<Image::IEncodedImage> getFromRelease(Database::ReleaseId releaseId, Image::ImageSize width) = 0;
|
||||
|
||||
virtual void flushCache() = 0;
|
||||
virtual std::shared_ptr<Image::IEncodedImage> getDefault(Image::ImageSize width) = 0;
|
||||
|
||||
virtual void setJpegQuality(unsigned quality) = 0; // from 1 to 100
|
||||
};
|
||||
virtual void flushCache() = 0;
|
||||
|
||||
std::unique_ptr<ICoverService> createCoverService(Database::Db& db,
|
||||
const std::filesystem::path& execPath,
|
||||
const std::filesystem::path& defaultCoverPath);
|
||||
virtual void setJpegQuality(unsigned quality) = 0; // from 1 to 100
|
||||
};
|
||||
|
||||
std::unique_ptr<ICoverService> createCoverService(Database::Db& db, const std::filesystem::path& execPath, const std::filesystem::path& defaultCoverPath);
|
||||
|
||||
} // namespace CoverArt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user