From 814eaf0a3e6ed5cfc803dee85a4302ac75a30b0d Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 26 Jan 2025 15:08:31 +0100 Subject: [PATCH] Switched the file added info back to a full date time, ref #601 --- src/libs/core/impl/String.cpp | 18 ++++++++++--- .../core/include/core/PartialDateTime.hpp | 27 +++++++++++-------- src/libs/core/test/String.cpp | 27 ++++++++++++++++--- src/libs/database/impl/Release.cpp | 6 ++--- .../database/include/database/Release.hpp | 4 +-- src/libs/database/include/database/Track.hpp | 8 +++--- src/libs/database/test/Artist.cpp | 10 +++---- src/libs/database/test/Release.cpp | 22 +++++++-------- src/libs/database/test/Track.cpp | 6 ++--- .../impl/scanners/AudioFileScanner.cpp | 19 ++++++++----- src/libs/subsonic/impl/responses/Album.cpp | 2 +- src/libs/subsonic/impl/responses/Song.cpp | 2 +- 12 files changed, 96 insertions(+), 55 deletions(-) diff --git a/src/libs/core/impl/String.cpp b/src/libs/core/impl/String.cpp index 3be9817b..fd437c02 100644 --- a/src/libs/core/impl/String.cpp +++ b/src/libs/core/impl/String.cpp @@ -466,14 +466,24 @@ namespace lms::core::stringUtils std::string toISO8601String(const Wt::WDateTime& dateTime) { - // assume UTC - return dateTime.toString("yyyy-MM-ddThh:mm:ss.zzz", false).toUTF8(); + if (dateTime.isValid()) + { + // assume UTC + return dateTime.toString("yyyy-MM-ddThh:mm:ss.zzz", false).toUTF8(); + } + + return ""; } std::string toISO8601String(const Wt::WDate& date) { - // assume UTC - return date.toString("yyyy-MM-dd").toUTF8(); + if (date.isValid()) + { + // assume UTC + return date.toString("yyyy-MM-dd").toUTF8(); + } + + return ""; } Wt::WDateTime fromISO8601String(std::string_view dateTime) diff --git a/src/libs/core/include/core/PartialDateTime.hpp b/src/libs/core/include/core/PartialDateTime.hpp index 37b05302..05947c2e 100644 --- a/src/libs/core/include/core/PartialDateTime.hpp +++ b/src/libs/core/include/core/PartialDateTime.hpp @@ -43,11 +43,26 @@ namespace lms::core static PartialDateTime fromWtDateTime(const Wt::WDateTime& dateTime); std::string toISO8601String() const; - bool isValid() const { return _precision != Precision::Invalid; } + constexpr bool isValid() const { return _precision != Precision::Invalid; } + + enum class Precision : std::uint8_t + { + Invalid, + Year, + Month, + Day, + Hour, + Min, + Sec, + }; + constexpr Precision getPrecision() const { return _precision; } constexpr std::optional getYear() const { return (_precision >= Precision::Year ? std::make_optional(_year) : std::nullopt); } constexpr std::optional getMonth() const { return (_precision >= Precision::Month ? std::make_optional(_month) : std::nullopt); } constexpr std::optional getDay() const { return (_precision >= Precision::Day ? std::make_optional(_day) : std::nullopt); } + constexpr std::optional getHour() const { return (_precision >= Precision::Hour ? std::make_optional(_hour) : std::nullopt); } + constexpr std::optional getMin() const { return (_precision >= Precision::Min ? std::make_optional(_min) : std::nullopt); } + constexpr std::optional getSec() const { return (_precision >= Precision::Sec ? std::make_optional(_sec) : std::nullopt); } constexpr auto operator<=>(const PartialDateTime& other) const = default; @@ -60,16 +75,6 @@ namespace lms::core std::uint8_t _hour{}; // 0 to 23 std::uint8_t _min{}; // 0 to 59 std::uint8_t _sec{}; // 0 to 59 - enum class Precision : std::uint8_t - { - Invalid, - Year, - Month, - Day, - Hour, - Min, - Sec, - }; Precision _precision{ Precision::Invalid }; }; } // namespace lms::core \ No newline at end of file diff --git a/src/libs/core/test/String.cpp b/src/libs/core/test/String.cpp index a1fa628e..b137854b 100644 --- a/src/libs/core/test/String.cpp +++ b/src/libs/core/test/String.cpp @@ -301,14 +301,33 @@ namespace lms::core::stringUtils::tests TEST(Stringutils, DateToString) { - const Wt::WDate date{ 2020, 01, 03 }; - EXPECT_EQ(toISO8601String(date), "2020-01-03"); + { + const Wt::WDate date{ 2020, 01, 03 }; + EXPECT_EQ(toISO8601String(date), "2020-01-03"); + } + + { + const Wt::WDate date; + EXPECT_EQ(toISO8601String(date), ""); + } } TEST(Stringutils, DateTimeToString) { - const Wt::WDateTime dateTime{ Wt::WDate{ 2020, 01, 03 }, Wt::WTime{ 9, 8, 11, 75 } }; - EXPECT_EQ(toISO8601String(dateTime), "2020-01-03T09:08:11.075"); + { + const Wt::WDateTime dateTime{ Wt::WDate{ 2020, 01, 03 }, Wt::WTime{ 9, 8, 11, 75 } }; + EXPECT_EQ(toISO8601String(dateTime), "2020-01-03T09:08:11.075"); + } + + { + const Wt::WDateTime dateTime{ Wt::WDate{ 2020, 01, 03 } }; + EXPECT_EQ(toISO8601String(dateTime), "2020-01-03T00:00:00.000"); + } + + { + const Wt::WDateTime dateTime; + EXPECT_EQ(toISO8601String(dateTime), ""); + } } TEST(Stringutils, DateTimeFromString) diff --git a/src/libs/database/impl/Release.cpp b/src/libs/database/impl/Release.cpp index b27dd380..89f7a9ed 100644 --- a/src/libs/database/impl/Release.cpp +++ b/src/libs/database/impl/Release.cpp @@ -704,18 +704,18 @@ namespace lms::db return utils::fetchQuerySingleResult(session()->query("SELECT COALESCE(SUM(duration), 0) FROM track t").where("t.release_id = ?").bind(getId())); } - Wt::WDateTime Release::getLastWritten() const + Wt::WDateTime Release::getLastWrittenTime() const { assert(session()); return utils::fetchQuerySingleResult(session()->query("SELECT MAX(file_last_write) FROM track t").where("t.release_id = ?").bind(getId())); } - core::PartialDateTime Release::getAddedTime() const + Wt::WDateTime Release::getAddedTime() const { assert(session()); - return utils::fetchQuerySingleResult(session()->query("SELECT MAX(file_added) FROM track t").where("t.release_id = ?").bind(getId())); + return utils::fetchQuerySingleResult(session()->query("SELECT MAX(file_added) FROM track t").where("t.release_id = ?").bind(getId())); } std::vector> Release::getClusterGroups(const std::vector& clusterTypeIds, std::size_t size) const diff --git a/src/libs/database/include/database/Release.hpp b/src/libs/database/include/database/Release.hpp index d07d300c..22050468 100644 --- a/src/libs/database/include/database/Release.hpp +++ b/src/libs/database/include/database/Release.hpp @@ -244,8 +244,8 @@ namespace lms::db std::size_t getDiscCount() const; // may not be total disc (if incomplete for example) std::vector getDiscs() const; std::chrono::milliseconds getDuration() const; - core::PartialDateTime getAddedTime() const; - Wt::WDateTime getLastWritten() const; + Wt::WDateTime getAddedTime() const; + Wt::WDateTime getLastWrittenTime() const; std::string_view getArtistDisplayName() const { return _artistDisplayName; } bool isCompilation() const { return _isCompilation; } std::size_t getTrackCount() const; diff --git a/src/libs/database/include/database/Track.hpp b/src/libs/database/include/database/Track.hpp index f42089d3..02cca4d0 100644 --- a/src/libs/database/include/database/Track.hpp +++ b/src/libs/database/include/database/Track.hpp @@ -223,8 +223,8 @@ namespace lms::db void setAbsoluteFilePath(const std::filesystem::path& filePath); void setRelativeFilePath(const std::filesystem::path& filePath); void setFileSize(std::size_t fileSize) { _fileSize = fileSize; } - void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; } - void setAddedTime(core::PartialDateTime time) { _fileAdded = time; } + void setLastWriteTime(const Wt::WDateTime& time) { _fileLastWrite = time; } + void setAddedTime(const Wt::WDateTime& time) { _fileAdded = time; } void setBitrate(std::size_t bitrate) { _bitrate = bitrate; } void setBitsPerSample(std::size_t bitsPerSample) { _bitsPerSample = bitsPerSample; } void setDuration(std::chrono::milliseconds duration) { _duration = duration; } @@ -272,7 +272,7 @@ namespace lms::db const core::PartialDateTime& getOriginalDate() const { return _originalDate; } std::optional getOriginalYear() const; const Wt::WDateTime& getLastWriteTime() const { return _fileLastWrite; } - const core::PartialDateTime& getAddedTime() const { return _fileAdded; } + const Wt::WDateTime& getAddedTime() const { return _fileAdded; } bool hasCover() const { return _hasCover; } bool hasLyrics() const; std::optional getTrackMBID() const { return core::UUID::fromString(_trackMBID); } @@ -367,7 +367,7 @@ namespace lms::db std::filesystem::path _fileName; long long _fileSize{}; Wt::WDateTime _fileLastWrite; - core::PartialDateTime _fileAdded; + Wt::WDateTime _fileAdded; bool _hasCover{}; std::string _trackMBID; std::string _recordingMBID; diff --git a/src/libs/database/test/Artist.cpp b/src/libs/database/test/Artist.cpp index 0ee1c8da..04395c36 100644 --- a/src/libs/database/test/Artist.cpp +++ b/src/libs/database/test/Artist.cpp @@ -751,11 +751,11 @@ namespace lms::db::tests { auto transaction{ session.createWriteTransaction() }; - trackA1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2 }); - trackB1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 1 }); - trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2, 15, 36, 24 }); - trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 3 }); - trackA2.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 4 }); + trackA1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 } }); + trackB1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 1 } }); + trackD1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 }, Wt::WTime{ 15, 36, 24 } }); + trackD1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 3 } }); + trackA2.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 4 } }); TrackArtistLink::create(session, trackA1.get(), artistA.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, trackA2.get(), artistA.get(), TrackArtistLinkType::Producer); diff --git a/src/libs/database/test/Release.cpp b/src/libs/database/test/Release.cpp index dce0a8d8..f90e7a61 100644 --- a/src/libs/database/test/Release.cpp +++ b/src/libs/database/test/Release.cpp @@ -1138,11 +1138,11 @@ namespace lms::db::tests { auto transaction{ session.createWriteTransaction() }; - trackA1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2 }); - trackB1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 1 }); - trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2, 15, 36, 24 }); - trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 3 }); - trackA2.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 4 }); + trackA1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 } }); + trackB1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 1 } }); + trackD1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 }, Wt::WTime{ 15, 36, 24 } }); + trackD1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 3 } }); + trackA2.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 4 } }); trackA1.get().modify()->setRelease(releaseA.get()); trackA2.get().modify()->setRelease(releaseA.get()); @@ -1212,7 +1212,7 @@ namespace lms::db::tests { auto transaction{ session.createReadTransaction() }; - Wt::WDateTime lastWritten{ release.get()->getLastWritten() }; + Wt::WDateTime lastWritten{ release.get()->getLastWrittenTime() }; EXPECT_FALSE(lastWritten.isValid()); } @@ -1227,7 +1227,7 @@ namespace lms::db::tests { auto transaction{ session.createReadTransaction() }; - Wt::WDateTime lastWritten{ release.get()->getLastWritten() }; + Wt::WDateTime lastWritten{ release.get()->getLastWrittenTime() }; ASSERT_TRUE(lastWritten.isValid()); EXPECT_EQ(lastWritten, track2.get()->getLastWriteTime()); } @@ -1243,14 +1243,14 @@ namespace lms::db::tests { auto transaction{ session.createReadTransaction() }; - core::PartialDateTime addedTime{ release.get()->getAddedTime() }; + const Wt::WDateTime addedTime{ release.get()->getAddedTime() }; EXPECT_FALSE(addedTime.isValid()); } { auto transaction{ session.createWriteTransaction() }; - track1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2 }); - track2.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2, 15, 36, 24 }); + track1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 } }); + track2.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 }, Wt::WTime{ 15, 36, 24 } }); track1.get().modify()->setRelease(release.get()); track2.get().modify()->setRelease(release.get()); } @@ -1258,7 +1258,7 @@ namespace lms::db::tests { auto transaction{ session.createReadTransaction() }; - core::PartialDateTime addedTime{ release.get()->getAddedTime() }; + const Wt::WDateTime addedTime{ release.get()->getAddedTime() }; ASSERT_TRUE(addedTime.isValid()); EXPECT_EQ(addedTime, track2.get()->getAddedTime()); } diff --git a/src/libs/database/test/Track.cpp b/src/libs/database/test/Track.cpp index 67be5338..392c2df0 100644 --- a/src/libs/database/test/Track.cpp +++ b/src/libs/database/test/Track.cpp @@ -399,9 +399,9 @@ namespace lms::db::tests { auto transaction{ session.createWriteTransaction() }; - track1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2 }); - track2.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 1 }); - track4.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2, 15, 36, 24 }); + track1.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 } }); + track2.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 1 } }); + track4.get().modify()->setAddedTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 }, Wt::WTime{ 15, 36, 24 } }); } { diff --git a/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp b/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp index 5899e4d4..fa814041 100644 --- a/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp +++ b/src/libs/services/scanner/impl/scanners/AudioFileScanner.cpp @@ -482,11 +482,7 @@ namespace lms::scanner added = true; track.modify()->setAbsoluteFilePath(_file); - if (!_parsedTrack->encodingTime.isValid()) - { - const core::PartialDateTime addedTime{ core::PartialDateTime::fromWtDateTime(_mediaLibrary.firstScan ? fileInfo->lastWriteTime : Wt::WDateTime::currentDateTime()) }; - track.modify()->setAddedTime(addedTime); // may be erased by encodingTime - } + track.modify()->setAddedTime(_mediaLibrary.firstScan ? fileInfo->lastWriteTime : Wt::WDateTime::currentDateTime()); // may be erased by encodingTime } // Track related data @@ -504,7 +500,18 @@ namespace lms::scanner track.modify()->setLastWriteTime(fileInfo->lastWriteTime); if (_parsedTrack->encodingTime.isValid()) - track.modify()->setAddedTime(_parsedTrack->encodingTime); + { + const core::PartialDateTime& encodingTime{ _parsedTrack->encodingTime }; + Wt::WDate date; + Wt::WTime time; + if (encodingTime.getPrecision() >= core::PartialDateTime::Precision::Day) + date = Wt::WDate{ *encodingTime.getYear(), *encodingTime.getMonth(), *encodingTime.getDay() }; + if (encodingTime.getPrecision() >= core::PartialDateTime::Precision::Sec) + time = Wt::WTime{ *encodingTime.getHour(), *encodingTime.getMin(), *encodingTime.getSec() }; + + if (date.isValid()) + track.modify()->setAddedTime(Wt::WDateTime{ date, time }); + } db::MediaLibrary::pointer mediaLibrary{ db::MediaLibrary::find(dbSession, _mediaLibrary.id) }; // may be null if settings are updated in // => next scan will correct this track.modify()->setMediaLibrary(mediaLibrary); diff --git a/src/libs/subsonic/impl/responses/Album.cpp b/src/libs/subsonic/impl/responses/Album.cpp index 6b019879..7132e011 100644 --- a/src/libs/subsonic/impl/responses/Album.cpp +++ b/src/libs/subsonic/impl/responses/Album.cpp @@ -85,7 +85,7 @@ namespace lms::api::subsonic albumNode.setAttribute("isDir", true); } - albumNode.setAttribute("created", release->getAddedTime().toISO8601String()); + albumNode.setAttribute("created", core::stringUtils::toISO8601String(release->getAddedTime())); if (const auto image{ release->getImage() }) { const CoverArtId coverArtId{ image->getId(), image->getLastWriteTime().toTime_t() }; diff --git a/src/libs/subsonic/impl/responses/Song.cpp b/src/libs/subsonic/impl/responses/Song.cpp index 6149589f..ee1cc5c2 100644 --- a/src/libs/subsonic/impl/responses/Song.cpp +++ b/src/libs/subsonic/impl/responses/Song.cpp @@ -145,7 +145,7 @@ namespace lms::api::subsonic trackResponse.setAttribute("duration", std::chrono::duration_cast(track->getDuration()).count()); trackResponse.setAttribute("bitRate", (track->getBitrate() / 1000)); trackResponse.setAttribute("type", "music"); - trackResponse.setAttribute("created", track->getAddedTime().toISO8601String()); + trackResponse.setAttribute("created", core::stringUtils::toISO8601String(track->getAddedTime())); trackResponse.setAttribute("contentType", av::getMimeType(track->getAbsoluteFilePath().extension())); if (const auto rating{ core::Service::get()->getRating(context.user->getId(), track->getId()) }) trackResponse.setAttribute("userRating", *rating);