Use ENCODINGTIME to set the added time, if present, otherwise use the last modified time of the file (only during the first import), fixes #595
This commit is contained in:
@@ -476,6 +476,12 @@ namespace lms::core::stringUtils
|
|||||||
return date.toString("yyyy-MM-dd").toUTF8();
|
return date.toString("yyyy-MM-dd").toUTF8();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Wt::WDateTime fromISO8601String(std::string_view dateTime)
|
||||||
|
{
|
||||||
|
// assume UTC
|
||||||
|
return Wt::WDateTime::fromString(Wt::WString{ std::string{ dateTime } }, "yyyy-MM-ddThh:mm:ss.zzz");
|
||||||
|
}
|
||||||
|
|
||||||
std::string formatTimestamp(std::chrono::milliseconds timestamp)
|
std::string formatTimestamp(std::chrono::milliseconds timestamp)
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ namespace lms::core::stringUtils
|
|||||||
[[nodiscard]] std::string toISO8601String(const Wt::WDateTime& dateTime);
|
[[nodiscard]] std::string toISO8601String(const Wt::WDateTime& dateTime);
|
||||||
[[nodiscard]] std::string toISO8601String(const Wt::WDate& date);
|
[[nodiscard]] std::string toISO8601String(const Wt::WDate& date);
|
||||||
|
|
||||||
|
[[nodiscard]] Wt::WDateTime fromISO8601String(std::string_view dateTime);
|
||||||
|
|
||||||
// to "[minutes:seconds.milliseconds]"
|
// to "[minutes:seconds.milliseconds]"
|
||||||
std::string formatTimestamp(std::chrono::milliseconds timestamp);
|
std::string formatTimestamp(std::chrono::milliseconds timestamp);
|
||||||
} // namespace lms::core::stringUtils
|
} // namespace lms::core::stringUtils
|
||||||
@@ -299,18 +299,25 @@ namespace lms::core::stringUtils::tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Stringutils, date)
|
TEST(Stringutils, DateToString)
|
||||||
{
|
{
|
||||||
const Wt::WDate date{ 2020, 01, 03 };
|
const Wt::WDate date{ 2020, 01, 03 };
|
||||||
EXPECT_EQ(toISO8601String(date), "2020-01-03");
|
EXPECT_EQ(toISO8601String(date), "2020-01-03");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Stringutils, dateTime)
|
TEST(Stringutils, DateTimeToString)
|
||||||
{
|
{
|
||||||
const Wt::WDateTime dateTime{ Wt::WDate{ 2020, 01, 03 }, Wt::WTime{ 9, 8, 11, 75 } };
|
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");
|
EXPECT_EQ(toISO8601String(dateTime), "2020-01-03T09:08:11.075");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Stringutils, DateTimeFromString)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fromISO8601String("2020-01-03T09:08:11.075"), (Wt::WDateTime{ Wt::WDate{ 2020, 01, 03 }, Wt::WTime{ 9, 8, 11, 75 } }));
|
||||||
|
EXPECT_EQ(fromISO8601String("2020-01-03"), Wt::WDateTime{});
|
||||||
|
EXPECT_EQ(fromISO8601String(""), Wt::WDateTime{});
|
||||||
|
}
|
||||||
|
|
||||||
TEST(StringUtils, stringEndsWith)
|
TEST(StringUtils, stringEndsWith)
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(stringEndsWith("FooBar", "Bar"));
|
EXPECT_TRUE(stringEndsWith("FooBar", "Bar"));
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ namespace lms::metadata
|
|||||||
{ TagType::DiscNumber, { "TPOS", "DISC", "DISK", "DISCNUMBER", "WM/PARTOFSET" } },
|
{ TagType::DiscNumber, { "TPOS", "DISC", "DISK", "DISCNUMBER", "WM/PARTOFSET" } },
|
||||||
{ TagType::DiscSubtitle, { "TSST", "DISCSUBTITLE", "SETSUBTITLE" } },
|
{ TagType::DiscSubtitle, { "TSST", "DISCSUBTITLE", "SETSUBTITLE" } },
|
||||||
{ TagType::EncodedBy, { "ENCODEDBY" } },
|
{ TagType::EncodedBy, { "ENCODEDBY" } },
|
||||||
|
{ TagType::EncodingTime, { "ENCODINGTIME", "TDEN" } },
|
||||||
{ TagType::Engineer, { "ENGINEER" } },
|
{ TagType::Engineer, { "ENGINEER" } },
|
||||||
{ TagType::GaplessPlayback, { "GAPLESSPLAYBACK" } },
|
{ TagType::GaplessPlayback, { "GAPLESSPLAYBACK" } },
|
||||||
{ TagType::Genre, { "GENRE" } },
|
{ TagType::Genre, { "GENRE" } },
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ namespace lms::metadata
|
|||||||
DiscSubtitle,
|
DiscSubtitle,
|
||||||
EncodedBy,
|
EncodedBy,
|
||||||
EncoderSettings,
|
EncoderSettings,
|
||||||
|
EncodingTime,
|
||||||
Engineer,
|
Engineer,
|
||||||
GaplessPlayback,
|
GaplessPlayback,
|
||||||
Genre,
|
Genre,
|
||||||
|
|||||||
@@ -390,6 +390,14 @@ namespace lms::metadata
|
|||||||
}
|
}
|
||||||
|
|
||||||
track.advisory = getAdvisory(tagReader);
|
track.advisory = getAdvisory(tagReader);
|
||||||
|
|
||||||
|
if (const auto encodingTime{ getTagValueAs<std::string>(tagReader, TagType::EncodingTime) })
|
||||||
|
{
|
||||||
|
if (auto dateTime{ core::stringUtils::fromISO8601String(*encodingTime) }; dateTime.isValid())
|
||||||
|
track.encodingTime = dateTime;
|
||||||
|
else if (const Wt::WDate date{ utils::parseDate(*encodingTime) }; date.isValid())
|
||||||
|
track.encodingTime = Wt::WDateTime{ date };
|
||||||
|
}
|
||||||
track.lyrics = getLyrics(tagReader); // no custom delimiter on lyrics
|
track.lyrics = getLyrics(tagReader); // no custom delimiter on lyrics
|
||||||
track.comments = getTagValuesAs<std::string>(tagReader, TagType::Comment, {} /* no custom delimiter on comments */);
|
track.comments = getTagValuesAs<std::string>(tagReader, TagType::Comment, {} /* no custom delimiter on comments */);
|
||||||
track.copyright = getTagValueAs<std::string>(tagReader, TagType::Copyright).value_or("");
|
track.copyright = getTagValueAs<std::string>(tagReader, TagType::Copyright).value_or("");
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ namespace lms::metadata
|
|||||||
{ TagType::DiscSubtitle, { "DISCSUBTITLE", "SETSUBTITLE" } },
|
{ TagType::DiscSubtitle, { "DISCSUBTITLE", "SETSUBTITLE" } },
|
||||||
{ TagType::EncodedBy, { "ENCODEDBY" } },
|
{ TagType::EncodedBy, { "ENCODEDBY" } },
|
||||||
{ TagType::Engineer, { "ENGINEER" } },
|
{ TagType::Engineer, { "ENGINEER" } },
|
||||||
|
{ TagType::EncodingTime, { "ENCODINGTIME" } },
|
||||||
{ TagType::GaplessPlayback, { "GAPLESSPLAYBACK" } },
|
{ TagType::GaplessPlayback, { "GAPLESSPLAYBACK" } },
|
||||||
{ TagType::Genre, { "GENRE" } },
|
{ TagType::Genre, { "GENRE" } },
|
||||||
{ TagType::Grouping, { "GROUPING", "ALBUMGROUPING" } },
|
{ TagType::Grouping, { "GROUPING", "ALBUMGROUPING" } },
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <Wt/WDate.h>
|
#include <Wt/WDate.h>
|
||||||
|
#include <Wt/WDateTime.h>
|
||||||
|
|
||||||
#include "core/UUID.hpp"
|
#include "core/UUID.hpp"
|
||||||
|
|
||||||
@@ -124,6 +125,7 @@ namespace lms::metadata
|
|||||||
std::optional<int> originalYear{};
|
std::optional<int> originalYear{};
|
||||||
Wt::WDate originalDate;
|
Wt::WDate originalDate;
|
||||||
std::optional<Advisory> advisory;
|
std::optional<Advisory> advisory;
|
||||||
|
Wt::WDateTime encodingTime;
|
||||||
bool hasCover{};
|
bool hasCover{};
|
||||||
std::optional<core::UUID> acoustID;
|
std::optional<core::UUID> acoustID;
|
||||||
std::string copyright;
|
std::string copyright;
|
||||||
|
|||||||
@@ -17,11 +17,12 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <Wt/WTime.h>
|
||||||
|
|
||||||
#include "Parser.hpp"
|
#include "Parser.hpp"
|
||||||
#include "TestTagReader.hpp"
|
#include "TestTagReader.hpp"
|
||||||
|
|
||||||
@@ -611,4 +612,26 @@ namespace lms::metadata
|
|||||||
doTest("", std::nullopt);
|
doTest("", std::nullopt);
|
||||||
doTest("3", std::nullopt);
|
doTest("3", std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Parser, encodingTime)
|
||||||
|
{
|
||||||
|
auto doTest = [](std::string_view value, Wt::WDateTime expectedValue) {
|
||||||
|
const TestTagReader testTags{
|
||||||
|
{
|
||||||
|
{ TagType::EncodingTime, { value } },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
std::unique_ptr<Track> track{ Parser{}.parse(testTags) };
|
||||||
|
|
||||||
|
ASSERT_EQ(track->encodingTime, expectedValue) << "Value = '" << value << "'";
|
||||||
|
};
|
||||||
|
|
||||||
|
doTest("", Wt::WDateTime{});
|
||||||
|
doTest("foo", Wt::WDateTime{});
|
||||||
|
doTest("2020-01-03T09:08:11.075", Wt::WDateTime{ Wt::WDate{ 2020, 01, 03 }, Wt::WTime{ 9, 8, 11, 75 } });
|
||||||
|
doTest("2020-01-03", Wt::WDateTime{ Wt::WDate{ 2020, 01, 03 } });
|
||||||
|
doTest("2020/01/03", Wt::WDateTime{ Wt::WDate{ 2020, 01, 03 } });
|
||||||
|
}
|
||||||
} // namespace lms::metadata
|
} // namespace lms::metadata
|
||||||
|
|||||||
@@ -479,6 +479,7 @@ namespace lms::scanner
|
|||||||
{
|
{
|
||||||
track = dbSession.create<db::Track>();
|
track = dbSession.create<db::Track>();
|
||||||
track.modify()->setAbsoluteFilePath(_file);
|
track.modify()->setAbsoluteFilePath(_file);
|
||||||
|
track.modify()->setAddedTime(fileInfo->lastWriteTime); // may be erased by encodingTime
|
||||||
added = true;
|
added = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +497,9 @@ namespace lms::scanner
|
|||||||
track.modify()->setFileSize(fileInfo->fileSize);
|
track.modify()->setFileSize(fileInfo->fileSize);
|
||||||
track.modify()->setLastWriteTime(fileInfo->lastWriteTime);
|
track.modify()->setLastWriteTime(fileInfo->lastWriteTime);
|
||||||
|
|
||||||
|
if (_parsedTrack->encodingTime.isValid())
|
||||||
|
track.modify()->setAddedTime(_parsedTrack->encodingTime);
|
||||||
|
|
||||||
db::MediaLibrary::pointer mediaLibrary{ db::MediaLibrary::find(dbSession, _mediaLibrary.id) }; // may be null if settings are updated in // => next scan will correct this
|
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);
|
track.modify()->setMediaLibrary(mediaLibrary);
|
||||||
db::Directory::pointer directory{ utils::getOrCreateDirectory(dbSession, _file.parent_path(), mediaLibrary) };
|
db::Directory::pointer directory{ utils::getOrCreateDirectory(dbSession, _file.parent_path(), mediaLibrary) };
|
||||||
@@ -548,7 +552,6 @@ namespace lms::scanner
|
|||||||
track.modify()->setDiscSubtitle(_parsedTrack->medium ? _parsedTrack->medium->name : "");
|
track.modify()->setDiscSubtitle(_parsedTrack->medium ? _parsedTrack->medium->name : "");
|
||||||
track.modify()->setClusters(getOrCreateClusters(dbSession, *_parsedTrack));
|
track.modify()->setClusters(getOrCreateClusters(dbSession, *_parsedTrack));
|
||||||
track.modify()->setName(title);
|
track.modify()->setName(title);
|
||||||
track.modify()->setAddedTime(Wt::WDateTime::currentDateTime());
|
|
||||||
track.modify()->setTrackNumber(_parsedTrack->position);
|
track.modify()->setTrackNumber(_parsedTrack->position);
|
||||||
track.modify()->setDiscNumber(_parsedTrack->medium ? _parsedTrack->medium->position : std::nullopt);
|
track.modify()->setDiscNumber(_parsedTrack->medium ? _parsedTrack->medium->position : std::nullopt);
|
||||||
track.modify()->setDate(_parsedTrack->date);
|
track.modify()->setDate(_parsedTrack->date);
|
||||||
|
|||||||
@@ -202,9 +202,6 @@ namespace lms::metadata
|
|||||||
for (const Artist& artist : track->remixerArtists)
|
for (const Artist& artist : track->remixerArtists)
|
||||||
std::cout << "Remixer: " << artist << std::endl;
|
std::cout << "Remixer: " << artist << std::endl;
|
||||||
|
|
||||||
if (track->medium)
|
|
||||||
std::cout << "Medium: " << *track->medium;
|
|
||||||
|
|
||||||
std::cout << "Title: " << track->title << std::endl;
|
std::cout << "Title: " << track->title << std::endl;
|
||||||
|
|
||||||
if (track->mbid)
|
if (track->mbid)
|
||||||
@@ -271,6 +268,12 @@ namespace lms::metadata
|
|||||||
if (track->advisory)
|
if (track->advisory)
|
||||||
std::cout << "Advisory: " << *track->advisory << std::endl;
|
std::cout << "Advisory: " << *track->advisory << std::endl;
|
||||||
|
|
||||||
|
if (track->encodingTime.isValid())
|
||||||
|
std::cout << "Encoding time: " << core::stringUtils::toISO8601String(track->encodingTime) << std::endl;
|
||||||
|
|
||||||
|
if (track->medium)
|
||||||
|
std::cout << "Medium: " << *track->medium;
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
} // namespace lms::metadata
|
} // namespace lms::metadata
|
||||||
|
|||||||
Reference in New Issue
Block a user