Subsonic API: added bitrate support, ref #363

This commit is contained in:
emeric
2023-11-11 15:13:46 +01:00
parent afcb4d96ca
commit 1d21ba41b7
16 changed files with 1063 additions and 1053 deletions
@@ -234,6 +234,15 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
ScanSettings::get(session).modify()->incScanVersion();
}
static void migrateFromV44(Session& session)
{
// add bitrate
session.getDboSession().execute("ALTER TABLE track ADD bitrate INTEGER");
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
ScanSettings::get(session).modify()->incScanVersion();
}
void doDbMigration(Session& session)
{
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
@@ -256,6 +265,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
{41, migrateFromV41},
{42, migrateFromV42},
{43, migrateFromV43},
{44, migrateFromV44},
};
{
@@ -26,7 +26,7 @@ namespace Database
class Session;
using Version = std::size_t;
static constexpr Version LMS_DATABASE_VERSION{ 44 };
static constexpr Version LMS_DATABASE_VERSION{ 45 };
class VersionInfo
{
public:
+12 -3
View File
@@ -387,9 +387,8 @@ namespace Database
Wt::Dbo::collection<std::string> copyrights = session()->query<std::string>
("SELECT copyright_url FROM track t INNER JOIN release r ON r.id = t.release_id")
.where("r.id = ?")
.groupBy("copyright_url")
.bind(getId());
.where("r.id = ?").bind(getId())
.groupBy("copyright_url");
std::vector<std::string> values(copyrights.begin(), copyrights.end());
@@ -400,6 +399,16 @@ namespace Database
return values.front();
}
std::size_t Release::getMeanBitrate() const
{
assert(session());
return session()->query<int>("SELECT COALESCE(AVG(t.bitrate), 0) FROM track t")
.where("release_id = ?").bind(getId())
.where("bitrate > 0")
.resultValue();
}
std::vector<Artist::pointer> Release::getArtists(TrackArtistLinkType linkType) const
{
assert(session());
@@ -104,6 +104,7 @@ namespace Database
Wt::WDate getOriginalReleaseDate() const;
std::optional<std::string> getCopyright() const;
std::optional<std::string> getCopyrightURL() const;
std::size_t getMeanBitrate() const;
// Accessors
const std::string& getName() const { return _name; }
@@ -128,6 +128,7 @@ namespace Database {
void setName(const std::string& name) { _name = std::string(name, 0, _maxNameLength); }
void setPath(const std::filesystem::path& filePath) { _filePath = filePath; }
void setDuration(std::chrono::milliseconds duration) { _duration = duration; }
void setBitrate(std::size_t bitrate) { _bitrate = bitrate; }
void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
void setAddedTime(Wt::WDateTime time) { _fileAdded = time; }
void setDate(const Wt::WDate& date) { _date = date; }
@@ -153,6 +154,7 @@ namespace Database {
std::string getName() const { return _name; }
std::filesystem::path getPath() const { return _filePath; }
std::chrono::milliseconds getDuration() const { return _duration; }
std::size_t getBitrate() const { return _bitrate; }
const Wt::WDateTime& getLastWritten() const { return _fileLastWrite; }
std::optional<int> getYear() const;
std::optional<int> getOriginalYear() const;
@@ -186,6 +188,7 @@ namespace Database {
Wt::Dbo::field(a, _discSubtitle, "disc_subtitle"); // here in Track since Release does not have concept of "disc" (yet?)
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::field(a, _duration, "duration");
Wt::Dbo::field(a, _bitrate, "bitrate");
Wt::Dbo::field(a, _date, "date");
Wt::Dbo::field(a, _originalDate, "original_date");
Wt::Dbo::field(a, _filePath, "file_path");
@@ -220,6 +223,7 @@ namespace Database {
std::string _discSubtitle;
std::string _name;
std::chrono::duration<int, std::milli> _duration{};
int _bitrate; // in bps
Wt::WDate _date;
Wt::WDate _originalDate;
std::string _filePath;
+40 -1
View File
@@ -547,7 +547,7 @@ TEST_F(DatabaseFixture, Release_releaseType)
}
}
TEST_F(DatabaseFixture, ReleaseSortOrder)
TEST_F(DatabaseFixture, Release_sortMethod)
{
ScopedRelease release1{ session, "MyRelease1" };
const Wt::WDate release1Date{ Wt::WDate {2000, 2, 3} };
@@ -616,3 +616,42 @@ TEST_F(DatabaseFixture, ReleaseSortOrder)
}
}
TEST_F(DatabaseFixture, Release_meanBitrate)
{
ScopedRelease release1{ session, "MyRelease1" };
ScopedTrack track1{ session, "MyTrack1" };
ScopedTrack track2{ session, "MyTrack2" };
ScopedTrack track3{ session, "MyTrack3" };
auto checkExpectedBitrate = [&](std::size_t bitrate)
{
auto transaction{ session.createSharedTransaction() };
EXPECT_EQ(release1->getMeanBitrate(), bitrate);
};
checkExpectedBitrate(0);
{
auto transaction{ session.createUniqueTransaction() };
track1.get().modify()->setBitrate(128);
track1.get().modify()->setRelease(release1.get());
}
checkExpectedBitrate(128);
{
auto transaction{ session.createUniqueTransaction() };
track2.get().modify()->setBitrate(256);
track2.get().modify()->setRelease(release1.get());
}
checkExpectedBitrate(192);
{
auto transaction{ session.createUniqueTransaction() };
track3.get().modify()->setBitrate(0);
track3.get().modify()->setRelease(release1.get());
}
checkExpectedBitrate(192); // 0 should not be taken into account
}
@@ -405,22 +405,7 @@ namespace Scanner
}
}
// We estimate this is an audio file if:
// - we found a least one audio stream
// - the duration is not null
if (trackInfo->audioStreams.empty())
{
LMS_LOG(DBUPDATER, DEBUG) << "Skipped '" << file.string() << "' (no audio stream found)";
// If Track exists here, delete it!
if (track)
{
track.remove();
stats.deletions++;
}
stats.errors.emplace_back(ScanError{ file, ScanErrorType::NoAudioTrack });
return;
}
// We estimate this is an audio file if the duration is not null
if (trackInfo->duration == std::chrono::milliseconds::zero())
{
LMS_LOG(DBUPDATER, DEBUG) << "Skipped '" << file.string() << "' (duration is 0)";
@@ -513,6 +498,7 @@ namespace Scanner
track.modify()->setLastWriteTime(lastWriteTime);
track.modify()->setName(title);
track.modify()->setDuration(trackInfo->duration);
track.modify()->setBitrate(trackInfo->bitrate);
track.modify()->setAddedTime(Wt::WDateTime::currentDateTime());
track.modify()->setTrackNumber(trackInfo->position);
track.modify()->setDiscNumber(trackInfo->medium ? trackInfo->medium->position : std::nullopt);