Simplified artwork handling: create a brand new artwork when an image changed. Simplifies subsonic element retrievals
This commit is contained in:
@@ -34,7 +34,6 @@ add_library(lmssubsonic STATIC
|
||||
impl/responses/ReplayGain.cpp
|
||||
impl/responses/Song.cpp
|
||||
impl/responses/User.cpp
|
||||
impl/CoverArtId.cpp
|
||||
impl/RequestContext.cpp
|
||||
impl/ResponseFormat.cpp
|
||||
impl/ProtocolVersion.cpp
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "core/String.hpp"
|
||||
#include "database/objects/ArtworkId.hpp"
|
||||
|
||||
namespace lms::api::subsonic
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr char timestampSeparatorChar{ '-' };
|
||||
|
||||
std::string idToString(db::ArtworkId id)
|
||||
{
|
||||
return "art-" + id.toString();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string idToString(CoverArtId coverId)
|
||||
{
|
||||
std::string res;
|
||||
|
||||
// produce "art-id-timestamp"
|
||||
res = idToString(coverId.id);
|
||||
res += timestampSeparatorChar;
|
||||
res += std::to_string(coverId.timestamp);
|
||||
|
||||
assert(!res.empty());
|
||||
return res;
|
||||
}
|
||||
} // namespace lms::api::subsonic
|
||||
|
||||
// Used to parse parameters
|
||||
namespace lms::core::stringUtils
|
||||
{
|
||||
template<>
|
||||
std::optional<api::subsonic::CoverArtId> readAs(std::string_view str)
|
||||
{
|
||||
std::optional<api::subsonic::CoverArtId> res;
|
||||
|
||||
std::vector<std::string_view> values{ core::stringUtils::splitString(str, '-') };
|
||||
|
||||
// expect "art-id-timestamp"
|
||||
if (values.size() != 3)
|
||||
return res;
|
||||
|
||||
if (values[0] != "art")
|
||||
return res;
|
||||
|
||||
const auto value{ core::stringUtils::readAs<db::ArtworkId::ValueType>(values[1]) };
|
||||
const auto timestamp{ core::stringUtils::readAs<std::time_t>(values[2]) };
|
||||
if (!value || !timestamp)
|
||||
return std::nullopt;
|
||||
|
||||
res.emplace(api::subsonic::CoverArtId{ *value, *timestamp });
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace lms::core::stringUtils
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "core/String.hpp"
|
||||
#include "database/objects/ArtworkId.hpp"
|
||||
|
||||
namespace lms::api::subsonic
|
||||
{
|
||||
struct CoverArtId
|
||||
{
|
||||
db::ArtworkId id;
|
||||
std::time_t timestamp;
|
||||
};
|
||||
|
||||
std::string idToString(CoverArtId coverId);
|
||||
} // namespace lms::api::subsonic
|
||||
|
||||
// Used to parse parameters
|
||||
namespace lms::core::stringUtils
|
||||
{
|
||||
template<>
|
||||
std::optional<api::subsonic::CoverArtId> readAs(std::string_view str);
|
||||
} // namespace lms::core::stringUtils
|
||||
@@ -26,6 +26,11 @@ namespace lms::api::subsonic
|
||||
return "ar-" + id.toString();
|
||||
}
|
||||
|
||||
std::string idToString(db::ArtworkId id)
|
||||
{
|
||||
return "art-" + id.toString();
|
||||
}
|
||||
|
||||
std::string idToString(db::DirectoryId id)
|
||||
{
|
||||
return "dir-" + id.toString();
|
||||
@@ -59,6 +64,19 @@ namespace lms::api::subsonic
|
||||
|
||||
namespace lms::core::stringUtils
|
||||
{
|
||||
template<>
|
||||
std::optional<db::ArtworkId> readAs(std::string_view str)
|
||||
{
|
||||
std::vector<std::string_view> values{ core::stringUtils::splitString(str, '-') };
|
||||
if (values.size() != 2 || values[0] != "art")
|
||||
return std::nullopt;
|
||||
|
||||
if (const auto value{ core::stringUtils::readAs<db::ArtworkId::ValueType>(values[1]) })
|
||||
return db::ArtworkId{ *value };
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::optional<db::ArtistId> readAs(std::string_view str)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/String.hpp"
|
||||
|
||||
#include "database/objects/ArtistId.hpp"
|
||||
#include "database/objects/ArtworkId.hpp"
|
||||
#include "database/objects/DirectoryId.hpp"
|
||||
#include "database/objects/MediaLibraryId.hpp"
|
||||
#include "database/objects/PodcastEpisodeId.hpp"
|
||||
@@ -32,6 +34,7 @@
|
||||
namespace lms::api::subsonic
|
||||
{
|
||||
std::string idToString(db::ArtistId id);
|
||||
std::string idToString(db::ArtworkId id);
|
||||
std::string idToString(db::DirectoryId id);
|
||||
std::string idToString(db::PodcastEpisodeId id);
|
||||
std::string idToString(db::PodcastId id);
|
||||
@@ -46,6 +49,9 @@ namespace lms::core::stringUtils
|
||||
template<>
|
||||
std::optional<db::ArtistId> readAs(std::string_view str);
|
||||
|
||||
template<>
|
||||
std::optional<db::ArtworkId> readAs(std::string_view str);
|
||||
|
||||
template<>
|
||||
std::optional<db::DirectoryId> readAs(std::string_view str);
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "services/artwork/IArtworkService.hpp"
|
||||
#include "services/transcoding/ITranscodeService.hpp"
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
#include "ParameterParsing.hpp"
|
||||
#include "RequestContext.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
@@ -359,13 +358,13 @@ namespace lms::api::subsonic
|
||||
void handleGetCoverArt(RequestContext& context, const Wt::Http::Request& /*request*/, Wt::Http::Response& response)
|
||||
{
|
||||
// Mandatory params
|
||||
const CoverArtId coverArtId{ getMandatoryParameterAs<CoverArtId>(context.getParameters(), "id") };
|
||||
const db::ArtworkId artworkId{ getMandatoryParameterAs<db::ArtworkId>(context.getParameters(), "id") };
|
||||
|
||||
std::optional<std::size_t> size{ getParameterAs<std::size_t>(context.getParameters(), "size") };
|
||||
if (size)
|
||||
*size = std::clamp(*size, std::size_t{ 32 }, std::size_t{ 2048 });
|
||||
|
||||
std::shared_ptr<image::IEncodedImage> image{ core::Service<artwork::IArtworkService>::get()->getImage(coverArtId.id, size) };
|
||||
std::shared_ptr<image::IEncodedImage> image{ core::Service<artwork::IArtworkService>::get()->getImage(artworkId, size) };
|
||||
if (!image)
|
||||
{
|
||||
response.setStatus(404);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "core/String.hpp"
|
||||
#include "database/Types.hpp"
|
||||
#include "database/objects/Artist.hpp"
|
||||
#include "database/objects/Artwork.hpp"
|
||||
#include "database/objects/Directory.hpp"
|
||||
#include "database/objects/Genre.hpp"
|
||||
#include "database/objects/Grouping.hpp"
|
||||
@@ -37,7 +36,6 @@
|
||||
#include "services/feedback/IFeedbackService.hpp"
|
||||
#include "services/scrobbling/IScrobblingService.hpp"
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
#include "RequestContext.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
#include "responses/Artist.hpp"
|
||||
@@ -91,11 +89,8 @@ namespace lms::api::subsonic
|
||||
|
||||
albumNode.setAttribute("created", core::stringUtils::toISO8601String(release->getAddedTime()));
|
||||
|
||||
if (const auto artwork{ release->getPreferredArtwork() })
|
||||
{
|
||||
CoverArtId coverArtId{ artwork->getId(), artwork->getLastWrittenTime().toTime_t() };
|
||||
albumNode.setAttribute("coverArt", idToString(coverArtId));
|
||||
}
|
||||
if (const db::ArtworkId artworkId{ release->getPreferredArtworkId() }; artworkId.isValid())
|
||||
albumNode.setAttribute("coverArt", idToString(artworkId));
|
||||
|
||||
if (const auto originalYear{ release->getOriginalYear() })
|
||||
albumNode.setAttribute("year", *originalYear);
|
||||
|
||||
@@ -24,14 +24,12 @@
|
||||
#include "core/String.hpp"
|
||||
|
||||
#include "database/objects/Artist.hpp"
|
||||
#include "database/objects/Artwork.hpp"
|
||||
#include "database/objects/Release.hpp"
|
||||
#include "database/objects/ReleaseArtistLink.hpp"
|
||||
#include "database/objects/TrackArtistLink.hpp"
|
||||
#include "database/objects/User.hpp"
|
||||
#include "services/feedback/IFeedbackService.hpp"
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
#include "RequestContext.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
|
||||
@@ -69,11 +67,8 @@ namespace lms::api::subsonic
|
||||
|
||||
Response::Node artistNode{ createMinimalArtistNode(artist) };
|
||||
|
||||
if (const auto artwork{ artist->getPreferredArtwork() })
|
||||
{
|
||||
CoverArtId coverArtId{ artwork->getId(), artwork->getLastWrittenTime().toTime_t() };
|
||||
artistNode.setAttribute("coverArt", idToString(coverArtId));
|
||||
}
|
||||
if (const db::ArtworkId artworkId{ artist->getPreferredArtworkId() }; artworkId.isValid())
|
||||
artistNode.setAttribute("coverArt", idToString(artworkId));
|
||||
|
||||
bool hasAlbums{};
|
||||
{
|
||||
|
||||
@@ -19,10 +19,9 @@
|
||||
|
||||
#include "responses/DiscTitle.hpp"
|
||||
|
||||
#include "database/objects/Artwork.hpp"
|
||||
#include "database/objects/Medium.hpp"
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
|
||||
namespace lms::api::subsonic
|
||||
{
|
||||
@@ -32,11 +31,8 @@ namespace lms::api::subsonic
|
||||
|
||||
discTitleNode.setAttribute("disc", medium->getPosition() ? *medium->getPosition() : 0);
|
||||
discTitleNode.setAttribute("title", medium->getName());
|
||||
if (const auto artwork{ medium->getPreferredArtwork() })
|
||||
{
|
||||
CoverArtId coverArtId{ artwork->getId(), artwork->getLastWrittenTime().toTime_t() };
|
||||
discTitleNode.setAttribute("coverArt", idToString(coverArtId));
|
||||
}
|
||||
if (const db::ArtworkId artworkId{ medium->getPreferredArtworkId() }; artworkId.isValid())
|
||||
discTitleNode.setAttribute("coverArt", idToString(artworkId));
|
||||
|
||||
return discTitleNode;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,10 @@
|
||||
|
||||
#include "core/Service.hpp"
|
||||
#include "core/String.hpp"
|
||||
#include "database/objects/Artwork.hpp"
|
||||
#include "database/objects/TrackList.hpp"
|
||||
#include "database/objects/User.hpp"
|
||||
#include "services/artwork/IArtworkService.hpp"
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
#include "RequestContext.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
|
||||
@@ -49,13 +47,7 @@ namespace lms::api::subsonic
|
||||
playlistNode.setAttribute("owner", trackListUser->getLoginName());
|
||||
|
||||
if (const db::ArtworkId artworkId{ core::Service<artwork::IArtworkService>::get()->findTrackListImage(tracklist->getId()) }; artworkId.isValid())
|
||||
{
|
||||
if (const auto artwork{ db::Artwork::find(context.getDbSession(), artworkId) })
|
||||
{
|
||||
CoverArtId coverArtId{ artwork->getId(), artwork->getLastWrittenTime().toTime_t() };
|
||||
playlistNode.setAttribute("coverArt", idToString(coverArtId));
|
||||
}
|
||||
}
|
||||
playlistNode.setAttribute("coverArt", idToString(artworkId));
|
||||
|
||||
if (context.isOpenSubsonicEnabled())
|
||||
{
|
||||
|
||||
@@ -24,11 +24,9 @@
|
||||
#include <Wt/WDate.h>
|
||||
|
||||
#include "core/String.hpp"
|
||||
#include "database/objects/Artwork.hpp"
|
||||
#include "database/objects/Podcast.hpp"
|
||||
#include "database/objects/PodcastEpisode.hpp"
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
#include "RequestContext.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
|
||||
@@ -69,11 +67,8 @@ namespace lms::api::subsonic
|
||||
// estimated bitrate
|
||||
if (episode->getEnclosureLength() > 0 && episode->getDuration() > std::chrono::milliseconds::zero())
|
||||
episodeNode.setAttribute("bitrate", episode->getEnclosureLength() * 8 / std::chrono::duration_cast<std::chrono::milliseconds>(episode->getDuration()).count());
|
||||
if (const auto artwork{ episode->getArtwork() })
|
||||
{
|
||||
CoverArtId coverArtId{ artwork->getId(), artwork->getLastWrittenTime().toTime_t() };
|
||||
episodeNode.setAttribute("coverArt", idToString(coverArtId));
|
||||
}
|
||||
if (const db::ArtworkId artworkId{ episode->getArtworkId() }; artworkId.isValid())
|
||||
episodeNode.setAttribute("coverArt", idToString(artworkId));
|
||||
|
||||
// Podcast specific attributes
|
||||
// Expose the streamId only if the episode is actually downloaded
|
||||
@@ -111,11 +106,8 @@ namespace lms::api::subsonic
|
||||
|
||||
podcastNode.setAttribute("status", getStatus(podcast));
|
||||
|
||||
if (const auto artwork{ podcast->getArtwork() })
|
||||
{
|
||||
CoverArtId coverArtId{ artwork->getId(), artwork->getLastWrittenTime().toTime_t() };
|
||||
podcastNode.setAttribute("coverArt", idToString(coverArtId));
|
||||
}
|
||||
if (const db::ArtworkId artworkId{ podcast->getArtworkId() }; artworkId.isValid())
|
||||
podcastNode.setAttribute("coverArt", idToString(artworkId));
|
||||
|
||||
if (includeEpisodes)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
#include "database/Types.hpp"
|
||||
#include "database/objects/Artist.hpp"
|
||||
#include "database/objects/Artwork.hpp"
|
||||
#include "database/objects/Directory.hpp"
|
||||
#include "database/objects/Genre.hpp"
|
||||
#include "database/objects/Grouping.hpp"
|
||||
@@ -45,7 +44,6 @@
|
||||
#include "services/feedback/IFeedbackService.hpp"
|
||||
#include "services/scrobbling/IScrobblingService.hpp"
|
||||
|
||||
#include "CoverArtId.hpp"
|
||||
#include "RequestContext.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
#include "responses/Artist.hpp"
|
||||
@@ -124,15 +122,9 @@ namespace lms::api::subsonic
|
||||
trackResponse.setAttribute("transcodedContentType", core::getMimeType(std::filesystem::path{ "." + fileSuffix }));
|
||||
}
|
||||
|
||||
auto artwork{ track->getPreferredMediaArtwork() };
|
||||
if (!artwork)
|
||||
artwork = track->getPreferredArtwork();
|
||||
|
||||
if (artwork)
|
||||
{
|
||||
CoverArtId coverArtId{ artwork->getId(), artwork->getLastWrittenTime().toTime_t() };
|
||||
trackResponse.setAttribute("coverArt", idToString(coverArtId));
|
||||
}
|
||||
const db::ArtworkId artworkId{ track->getPreferredMediaArtworkId().isValid() ? track->getPreferredMediaArtworkId() : track->getPreferredArtworkId() };
|
||||
if (artworkId.isValid())
|
||||
trackResponse.setAttribute("coverArt", idToString(artworkId));
|
||||
|
||||
std::vector<db::TrackArtistLink::pointer> artistLinks;
|
||||
std::vector<db::TrackArtistLink::pointer> trackArtistLinks;
|
||||
|
||||
Reference in New Issue
Block a user