diff --git a/src/cover/CoverArtGrabber.cpp b/src/cover/CoverArtGrabber.cpp index fee2167f..b6485364 100644 --- a/src/cover/CoverArtGrabber.cpp +++ b/src/cover/CoverArtGrabber.cpp @@ -43,12 +43,15 @@ isFileSupported(const boost::filesystem::path& file, const std::vectordocRoot() + "/images/unknown-cover.jpg")) + throw LmsException("Cannot read default cover file"); } Grabber& @@ -58,48 +61,42 @@ Grabber::instance() return instance; } -static std::vector -getFromAvMediaFile(const Av::MediaFile& input, std::size_t nbMaxCovers) +static boost::optional +getFromAvMediaFile(const Av::MediaFile& input) { std::vector res; - for (Av::Picture& picture : input.getAttachedPictures(nbMaxCovers)) + for (auto& picture : input.getAttachedPictures(2)) { Image::Image image; if (image.load(picture.data)) - res.push_back( image ); + return image; else LMS_LOG(COVER, ERROR) << "Cannot load embedded cover file in '" << input.getPath().string() << "'"; } - return res; + return boost::none; } -std::vector -Grabber::getFromDirectory(const boost::filesystem::path& p, std::size_t nbMaxCovers) const +boost::optional +Grabber::getFromDirectory(const boost::filesystem::path& p) const { - std::vector res; - - std::vector coverPathes = getCoverPaths(p, nbMaxCovers); - for (auto coverPath : coverPathes) + for (auto coverPath : getCoverPaths(p)) { - if (res.size() >= nbMaxCovers) - break; - Image::Image image; if (image.load(coverPath)) - res.push_back(image); + return image; else LMS_LOG(COVER, ERROR) << "Cannot load image in file '" << coverPath.string() << "'"; } - return res; + return boost::none; } std::vector -Grabber::getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers) const +Grabber::getCoverPaths(const boost::filesystem::path& directoryPath) const { std::vector res; boost::system::error_code ec; @@ -126,86 +123,94 @@ Grabber::getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t } res.push_back(path); - if (res.size() >= nbMaxCovers) - break; } + return res; } -std::vector -Grabber::getFromTrack(const boost::filesystem::path& p, std::size_t nbMaxCovers) const +boost::optional +Grabber::getFromTrack(const boost::filesystem::path& p) const { try { Av::MediaFile input(p); - return getFromAvMediaFile(input, nbMaxCovers); + return getFromAvMediaFile(input); } catch (Av::MediaFileException& e) { LMS_LOG(COVER, ERROR) << "Cannot get covers from track " << p.string() << ": " << e.what(); + return boost::none; } - - return std::vector(); } -std::vector -Grabber::getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t nbMaxCovers) const +Image::Image +Grabber::getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t size) const { using namespace Database; - Wt::Dbo::Transaction transaction(session); + boost::optional cover; - Track::pointer track = Track::getById(session, trackId); - if (!track) - return std::vector(); - - Track::CoverType coverType = track->getCoverType(); - boost::filesystem::path trackPath = track->getPath(); - - transaction.commit(); - - switch (coverType) { - case Track::CoverType::Embedded: - return Grabber::getFromTrack(trackPath, nbMaxCovers); - case Track::CoverType::None: - return Grabber::getFromDirectory(trackPath.parent_path(), nbMaxCovers); + Wt::Dbo::Transaction transaction(session); + + Track::pointer track = Track::getById(session, trackId); + if (track) + { + Track::CoverType coverType = track->getCoverType(); + boost::filesystem::path trackPath = track->getPath(); + + transaction.commit(); + + switch (coverType) + { + case Track::CoverType::Embedded: + cover = getFromTrack(trackPath); + case Track::CoverType::None: + cover = getFromDirectory(trackPath.parent_path()); + } + } } - return std::vector(); + if (!cover) + cover = _defaultCover; + + cover->scale(size); + + return *cover; } -std::vector -Grabber::getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t nbMaxCovers) const +Image::Image +Grabber::getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t size) const { using namespace Database; - Wt::Dbo::Transaction transaction(session); + boost::optional cover; - // If the release does not exist, do nothing - Release::pointer release = Release::getById(session, releaseId); - if (!release) - return std::vector(); + { + Wt::Dbo::Transaction transaction(session); - std::vector tracks = release->getTracks(); - if (tracks.empty()) - return std::vector(); + auto release = Release::getById(session, releaseId); + if (release) + { + auto tracks = release->getTracks(); + if (!tracks.empty()) + { + auto trackId = tracks.front().id(); + transaction.commit(); - boost::filesystem::path firstTrackPath = tracks.front()->getPath(); - bool embeddedCover = (tracks.front()->getCoverType() == Track::CoverType::Embedded); + return getFromTrack(session, trackId, size); + } + } + } - transaction.commit(); + if (!cover) + cover = _defaultCover; - // First, try to get covers from the directory of the release - std::vector res = getFromDirectory( firstTrackPath.parent_path(), nbMaxCovers); + cover->scale(size); - // Fallback on the embedded cover of the first track - if (res.empty() && embeddedCover) - res = getFromTrack( firstTrackPath, nbMaxCovers); - - return res; + return *cover; } } // namespace CoverArt diff --git a/src/cover/CoverArtGrabber.hpp b/src/cover/CoverArtGrabber.hpp index 43753c4f..b62c22e0 100644 --- a/src/cover/CoverArtGrabber.hpp +++ b/src/cover/CoverArtGrabber.hpp @@ -35,18 +35,25 @@ class Grabber static Grabber& instance(); - std::vector getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers = 1) const; - std::vector getFromDirectory(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const; - std::vector getFromTrack(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const; - std::vector getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t nbMaxCovers = 1) const; - std::vector getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t nbMaxCovers = 1) const; + Image::Image getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t size) const; + Image::Image getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t size) const; private: + Grabber(); + boost::optional getFromTrack(const boost::filesystem::path& path) const; + std::vector getCoverPaths(const boost::filesystem::path& directoryPath) const; + boost::optional getFromDirectory(const boost::filesystem::path& path) const; + + + Image::Image _defaultCover; + std::vector _fileExtensions = {".jpg", ".jpeg", ".png", ".bmp"}; // TODO parametrize + std::size_t _maxFileSize = 5000000; + std::vector _preferredFileNames = {"cover", "front"}; // TODO parametrize }; diff --git a/src/ui/LmsApplication.cpp b/src/ui/LmsApplication.cpp index 354ceeb4..9e6f3b40 100644 --- a/src/ui/LmsApplication.cpp +++ b/src/ui/LmsApplication.cpp @@ -328,8 +328,8 @@ LmsApplication::createHome() _isAdmin = LmsApp->getUser()->isAdmin(); } - _imageResource = std::make_shared(_db); - _transcodeResource = std::make_shared(_db); + _imageResource = std::make_shared(); + _transcodeResource = std::make_shared(); setConfirmCloseMessage(Wt::WString::tr("Lms.quit-confirm")); diff --git a/src/ui/resource/ImageResource.cpp b/src/ui/resource/ImageResource.cpp index dc45b339..a41c9f8d 100644 --- a/src/ui/resource/ImageResource.cpp +++ b/src/ui/resource/ImageResource.cpp @@ -37,61 +37,11 @@ namespace UserInterface { static const std::string unknownCoverPath = "/images/unknown-cover.jpg"; static const std::string unknownArtistImagePath = "/images/unknown-artist.jpg"; -ImageResource::ImageResource(Database::Handler& db) -: _db(db) -{ -} - ImageResource::~ImageResource() { beingDeleted(); } -Image::Image -ImageResource::getDefaultCover(std::size_t size) -{ - std::unique_lock lock(_mutex); - - auto itCover = _defaultCovers.find(size); - if (itCover == _defaultCovers.end()) - { - // Load default cover art for this size - Image::Image image; - - if (!image.load( Wt::WApplication::instance()->docRoot() + unknownCoverPath )) - throw LmsException("Cannot read default cover file"); - - image.scale(size); - - auto res = _defaultCovers.insert(std::make_pair(size, image)); - itCover = res.first; - } - - return itCover->second; -} - -Image::Image -ImageResource::getDefaultArtistImage(std::size_t size) -{ - std::unique_lock lock(_mutex); - - auto itCover = _defaultArtistImages.find(size); - if (itCover == _defaultArtistImages.end()) - { - Image::Image image; - - if (!image.load( Wt::WApplication::instance()->docRoot() + unknownArtistImagePath)) - throw LmsException("Cannot read default artist image file"); - - image.scale(size); - - auto res = _defaultArtistImages.insert(std::make_pair(size, image)); - itCover = res.first; - } - - return itCover->second; -} - std::string ImageResource::getReleaseUrl(Database::IdType releaseId, std::size_t size) const { @@ -127,29 +77,12 @@ ImageResource::putImage(Wt::Http::Response& response, Image::Image cover) response.out().write(reinterpret_cast(&data[0]), data.size()); } -void -ImageResource::putCover(Wt::Http::Response& response, std::vector covers, size_t size) -{ - for (Image::Image cover : covers) - { - if (cover.scale(size)) - { - putImage(response, cover); - return; - } - } - - // If no cover found, just send default one - putImage(response, getDefaultCover(size)); -} - void ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response) { // Retrieve parameters const std::string *trackIdStr = request.getParameter("trackid"); const std::string *releaseIdStr = request.getParameter("releaseid"); - const std::string *artistIdStr = request.getParameter("artistid"); const std::string *sizeStr = request.getParameter("size"); // Mandatory parameter size @@ -160,48 +93,19 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons if (!size || *size > maxSize) return; + Image::Image cover; + if (trackIdStr) { auto trackId = readAs(*trackIdStr); if (!trackId) return; - boost::filesystem::path path; - Database::Track::CoverType coverType = Database::Track::CoverType::None; + // transactions are not thread safe { - // transactions are not thread safe - Wt::WApplication::UpdateLock lock(LmsApplication::instance()); - - Wt::Dbo::Transaction transaction(_db.getSession()); - - Database::Track::pointer track = Database::Track::getById(_db.getSession(), *trackId); - if (track) - { - coverType = track->getCoverType(); - path = track->getPath(); - } + Wt::WApplication::UpdateLock lock(LmsApp); + cover = CoverArt::Grabber::instance().getFromTrack(LmsApp->getDboSession(), *trackId, *size); } - - if (!path.empty()) - { - std::vector covers; - - switch (coverType) - { - case Database::Track::CoverType::Embedded: - covers = CoverArt::Grabber::instance().getFromTrack(path); - break; - - case Database::Track::CoverType::None: - covers = CoverArt::Grabber::instance().getFromDirectory(path.parent_path()); - break; - } - - putCover(response, covers, *size); - return; - } - - putImage(response, getDefaultCover(*size)); } else if (releaseIdStr) { @@ -209,24 +113,16 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons if (!releaseId) return; - std::vector covers; - // transactions are not thread safe { - Wt::WApplication::UpdateLock lock(LmsApplication::instance()); - covers = CoverArt::Grabber::instance().getFromRelease(_db.getSession(), *releaseId); + Wt::WApplication::UpdateLock lock(LmsApp); + cover = CoverArt::Grabber::instance().getFromRelease(LmsApp->getDboSession(), *releaseId, *size); } - - putCover(response, covers, *size); - } - else if (artistIdStr) - { - putImage(response, getDefaultArtistImage(*size)); } else - { - putImage(response, getDefaultCover(*size)); - } + return; + + putImage(response, cover); } } // namespace UserInterface diff --git a/src/ui/resource/ImageResource.hpp b/src/ui/resource/ImageResource.hpp index aa01ce2c..608e4c17 100644 --- a/src/ui/resource/ImageResource.hpp +++ b/src/ui/resource/ImageResource.hpp @@ -37,7 +37,6 @@ class ImageResource : public Wt::WResource public: static const std::size_t maxSize = 512; - ImageResource(Database::Handler& db); ~ImageResource(); std::string getReleaseUrl(Database::IdType releaseId, size_t size) const; @@ -49,22 +48,9 @@ class ImageResource : public Wt::WResource private: - Image::Image getDefaultCover(std::size_t size); - Image::Image getDefaultArtistImage(std::size_t size); void putImage(Wt::Http::Response& response, Image::Image image); void putCover(Wt::Http::Response& response, std::vector covers, std::size_t size); - // Used to protect transactions since they are not thread safe - std::mutex _mutex; - Database::Handler& _db; - - // Default cover for different sizes - std::map _defaultCovers; - - // Default artist images for different sizes - std::map _defaultArtistImages; - - // TODO construct a cache for covers? }; } // namespace UserInterface diff --git a/src/ui/resource/TranscodeResource.cpp b/src/ui/resource/TranscodeResource.cpp index c27e2355..82bda742 100644 --- a/src/ui/resource/TranscodeResource.cpp +++ b/src/ui/resource/TranscodeResource.cpp @@ -29,15 +29,9 @@ namespace UserInterface { -TranscodeResource::TranscodeResource(Database::Handler& db) -: _db(db) -{ - LMS_LOG(UI, DEBUG) << "CONSTRUCTING RESOURCE"; -} TranscodeResource:: ~TranscodeResource() { - LMS_LOG(UI, DEBUG) << "DESTRUCTING RESOURCE"; beingDeleted(); } @@ -100,12 +94,11 @@ TranscodeResource::handleRequest(const Wt::Http::Request& request, // transactions are not thread safe { - Wt::WApplication::UpdateLock lock(LmsApplication::instance()); + Wt::WApplication::UpdateLock lock(LmsApp); - Wt::Dbo::Transaction transaction(_db.getSession()); + Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); - Database::User::pointer user = _db.getCurrentUser(); - Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId); + Database::Track::pointer track = Database::Track::getById(LmsApp->getDboSession(), trackId); if (!track) { @@ -113,7 +106,7 @@ TranscodeResource::handleRequest(const Wt::Http::Request& request, return; } - parameters.bitrate = user->getAudioBitrate(); + parameters.bitrate = LmsApp->getUser()->getAudioBitrate(); transcoder = std::make_shared(track->getPath(), parameters); } diff --git a/src/ui/resource/TranscodeResource.hpp b/src/ui/resource/TranscodeResource.hpp index ead39acc..6867db91 100644 --- a/src/ui/resource/TranscodeResource.hpp +++ b/src/ui/resource/TranscodeResource.hpp @@ -34,7 +34,6 @@ namespace UserInterface { class TranscodeResource : public Wt::WResource { public: - TranscodeResource(Database::Handler& db); ~TranscodeResource(); std::string getUrl(Database::IdType trackId, Av::Encoding encoding) const; @@ -44,8 +43,6 @@ class TranscodeResource : public Wt::WResource private: - Database::Handler& _db; - static const std::size_t _chunkSize = 65536*4; };