diff --git a/src/libs/database/impl/objects/Release.cpp b/src/libs/database/impl/objects/Release.cpp index dd9cf913..fab11b60 100644 --- a/src/libs/database/impl/objects/Release.cpp +++ b/src/libs/database/impl/objects/Release.cpp @@ -679,6 +679,14 @@ namespace lms::db return utils::fetchQuerySingleResult(session()->query("SELECT COALESCE(AVG(t.bitrate), 0) FROM track t").where("release_id = ?").bind(getId()).where("bitrate > 0")); } + std::vector Release::getCodecs() const + { + assert(session()); + + // Get the codec ordered by frequency + return utils::fetchQueryResults(session()->query("SELECT t.codec FROM track t").where("release_id = ?").bind(getId()).groupBy("t.codec").orderBy("COUNT(t.id) DESC")); + } + std::vector Release::getArtists(TrackArtistLinkType linkType) const { assert(session()); diff --git a/src/libs/database/impl/objects/Types.cpp b/src/libs/database/impl/objects/Types.cpp index df159c91..32388860 100644 --- a/src/libs/database/impl/objects/Types.cpp +++ b/src/libs/database/impl/objects/Types.cpp @@ -73,4 +73,95 @@ namespace lms::db return allowedAudioBitrates.find(bitrate) != std::cend(allowedAudioBitrates); } + core::LiteralString containerTypeToString(ContainerType type) + { + switch (type) + { + case ContainerType::Unknown: + break; + + case ContainerType::AIFF: + return "AIFF"; + case ContainerType::APE: + return "APE"; + case ContainerType::ASF: + return "ASF"; + case ContainerType::DSF: + return "DSF"; + case ContainerType::FLAC: + return "FLAC"; + case ContainerType::MP4: + return "MP4"; + case ContainerType::MPC: + return "MPC"; + case ContainerType::MPEG: + return "MPEG"; + case ContainerType::Ogg: + return "Ogg"; + case ContainerType::Shorten: + return "Shorten"; + case ContainerType::TrueAudio: + return "TrueAudio"; + case ContainerType::WAV: + return "WAV"; + case ContainerType::WavPack: + return "WavPack"; + } + + return "Unknown"; + } + + core::LiteralString codecTypeToString(CodecType type) + { + switch (type) + { + case CodecType::Unknown: + break; + + case CodecType::AAC: + return "AAC"; + case CodecType::AC3: + return "AC3"; + case CodecType::ALAC: + return "ALAC"; + case CodecType::APE: + return "APE"; + case CodecType::DSD: + return "DSD"; + case CodecType::EAC3: + return "EAC3"; + case CodecType::FLAC: + return "FLAC"; + case CodecType::MP3: + return "MP3"; + case CodecType::MP4ALS: + return "MP4ALS"; + case CodecType::MPC7: + return "MPC7"; + case CodecType::MPC8: + return "MPC8"; + case CodecType::Opus: + return "Opus"; + case CodecType::PCM: + return "PCM"; + case CodecType::Shorten: + return "Shorten"; + case CodecType::TrueAudio: + return "TrueAudio"; + case CodecType::Vorbis: + return "Vorbis"; + case CodecType::WavPack: + return "WavPack"; + case CodecType::WMA1: + return "WMA1"; + case CodecType::WMA2: + return "WMA2"; + case CodecType::WMA9Pro: + return "WMA9Pro"; + case CodecType::WMA9Lossless: + return "WMA9Lossless"; + } + + return "Unknown"; + } } // namespace lms::db diff --git a/src/libs/database/include/database/objects/Release.hpp b/src/libs/database/include/database/objects/Release.hpp index 18999963..f8b8a3d8 100644 --- a/src/libs/database/include/database/objects/Release.hpp +++ b/src/libs/database/include/database/objects/Release.hpp @@ -279,6 +279,7 @@ namespace lms::db std::optional getCopyright() const; std::optional getCopyrightURL() const; std::size_t getMeanBitrate() const; + std::vector getCodecs() const; // Accessors std::string_view getName() const { return _name; } diff --git a/src/libs/database/include/database/objects/Types.hpp b/src/libs/database/include/database/objects/Types.hpp index 966083be..6cdbf15a 100644 --- a/src/libs/database/include/database/objects/Types.hpp +++ b/src/libs/database/include/database/objects/Types.hpp @@ -286,6 +286,8 @@ namespace lms::db WavPack = 13, }; + core::LiteralString containerTypeToString(ContainerType type); + enum class CodecType { Unknown = 0, @@ -312,4 +314,6 @@ namespace lms::db WMA9Pro = 20, WMA9Lossless = 21, }; + + core::LiteralString codecTypeToString(CodecType type); } // namespace lms::db diff --git a/src/libs/database/test/Release.cpp b/src/libs/database/test/Release.cpp index 07e18749..54f1419c 100644 --- a/src/libs/database/test/Release.cpp +++ b/src/libs/database/test/Release.cpp @@ -1214,6 +1214,40 @@ namespace lms::db::tests checkExpectedBitrate(192); // 0 should not be taken into account } + TEST_F(DatabaseFixture, Release_codec) + { + ScopedRelease release1{ session, "MyRelease1" }; + ScopedTrack track1{ session }; + ScopedTrack track2{ session }; + ScopedTrack track3{ session }; + ScopedTrack track4{ session }; + + { + auto transaction{ session.createReadTransaction() }; + ASSERT_EQ(release1->getCodecs().size(), 0); + } + + { + auto transaction{ session.createWriteTransaction() }; + track1.get().modify()->setCodec(CodecType::FLAC); + track1.get().modify()->setRelease(release1.get()); + + track2.get().modify()->setCodec(CodecType::MP3); + track2.get().modify()->setRelease(release1.get()); + + track3.get().modify()->setCodec(CodecType::MP3); + track3.get().modify()->setRelease(release1.get()); + + track4.get().modify()->setCodec(CodecType::AAC); + track4.get().modify()->setRelease(release1.get()); + } + + { + auto transaction{ session.createReadTransaction() }; + EXPECT_EQ(release1->getCodecs(), (std::vector{ CodecType::MP3, CodecType::FLAC, CodecType::AAC })); + } + } + TEST_F(DatabaseFixture, Release_trackCount) { ScopedRelease release1{ session, "MyRelease1" }; diff --git a/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp index 13c84402..91ff9f36 100644 --- a/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/endpoints/MediaRetrieval.cpp @@ -117,6 +117,7 @@ namespace lms::api::subsonic bool isOutputFormatCompatible(const std::filesystem::path& trackPath, audio::OutputFormat outputFormat) { // TODO: put this information in db during scan + // It is in base only for tracks, not yet for podcasts try { const auto parser{ audio::createAudioFileInfoParser(audio::AudioFileInfoParserBackend::FFmpeg) }; diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index cf001f96..3f6e0bb2 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -19,20 +19,17 @@ #include "ReleaseView.hpp" -#include #include +#include #include +#include #include #include #include #include "core/String.hpp" -#include "audio/AudioTypes.hpp" -#include "audio/Exception.hpp" -#include "audio/IAudioFileInfo.hpp" -#include "audio/IAudioFileInfoParser.hpp" #include "database/Session.hpp" #include "database/Types.hpp" #include "database/objects/Artist.hpp" @@ -142,28 +139,20 @@ namespace lms::ui releaseInfo->bindString("release-labels", core::stringUtils::joinStrings(labels, " · ")); } - // TODO: save in DB and aggregate all this - for (const db::Track::pointer& track : db::Track::find(LmsApp->getDbSession(), db::Track::FindParameters{}.setRelease(releaseId).setRange(db::Range{ 0, 1 })).results) + // Codecs { - try + std::string codecStr; + for (db::CodecType codec : release->getCodecs()) { - const auto parser{ audio::createAudioFileInfoParser(audio::AudioFileInfoParserBackend::FFmpeg) }; - - audio::AudioFileInfoParseOptions parseOptions; - parseOptions.audioPropertiesReadStyle = audio::AudioFileInfoParseOptions::AudioPropertiesReadStyle::Fast; // only coded needed - parseOptions.readImages = false; - parseOptions.readTags = false; - const auto audioFile{ parser->parse(track->getAbsoluteFilePath(), parseOptions) }; - - if (audioFile->getAudioProperties()) - { - releaseInfo->setCondition("if-has-codec", true); - releaseInfo->bindString("codec", audio::codecTypeToString(audioFile->getAudioProperties()->codec).c_str(), Wt::TextFormat::Plain); - break; - } + if (!codecStr.empty()) + codecStr += " · "; + codecStr += db::codecTypeToString(codec).str(); } - catch (const audio::Exception& e) + + if (!codecStr.empty()) { + releaseInfo->setCondition("if-has-codec", true); + releaseInfo->bindString("codec", codecStr, Wt::TextFormat::Plain); } } diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index 3591fe46..6bead9d7 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -25,10 +25,6 @@ #include "core/Service.hpp" -#include "audio/AudioTypes.hpp" -#include "audio/Exception.hpp" -#include "audio/IAudioFileInfo.hpp" -#include "audio/IAudioFileInfoParser.hpp" #include "database/Session.hpp" #include "database/Types.hpp" #include "database/objects/Artist.hpp" @@ -131,25 +127,8 @@ namespace lms::ui::TrackListHelpers } } - try - { - const auto parser{ audio::createAudioFileInfoParser(audio::AudioFileInfoParserBackend::FFmpeg) }; - - audio::AudioFileInfoParseOptions parseOptions; - parseOptions.audioPropertiesReadStyle = audio::AudioFileInfoParseOptions::AudioPropertiesReadStyle::Fast; // only coded needed - parseOptions.readImages = false; - parseOptions.readTags = false; - const auto audioFile{ parser->parse(track->getAbsoluteFilePath(), parseOptions) }; - - if (audioFile->getAudioProperties()) - { - trackInfo->setCondition("if-has-codec", true); - trackInfo->bindString("codec", audio::codecTypeToString(audioFile->getAudioProperties()->codec).c_str(), Wt::TextFormat::Plain); - } - } - catch (const audio::Exception& e) - { - } + trackInfo->setCondition("if-has-codec", true); + trackInfo->bindString("codec", db::codecTypeToString(track->getCodec()).c_str(), Wt::TextFormat::Plain); trackInfo->bindString("duration", utils::durationToString(track->getDuration())); if (track->getBitrate())