Sort by release date, not just release year, fixes #128

This commit is contained in:
emeric
2021-09-23 13:18:43 +02:00
parent 7c649c82ce
commit 2ebc4280a0
21 changed files with 701 additions and 349 deletions
+3
View File
@@ -1,7 +1,10 @@
add_subdirectory(test)
add_library(lmsmetadata SHARED
impl/AvFormatParser.cpp
impl/TagLibParser.cpp
impl/Utils.cpp
)
target_include_directories(lmsmetadata INTERFACE
+3 -3
View File
@@ -25,7 +25,7 @@
#include "av/IAudioFile.hpp"
#include "utils/Logger.hpp"
#include "utils/String.hpp"
#include "Utils.hpp"
namespace MetaData
{
@@ -186,12 +186,12 @@ AvFormatParser::parse(const std::filesystem::path& p, bool debug)
|| tag == "YEAR"
|| tag == "WM/Year")
{
track.year = StringUtils::readAs<int>(value);
track.date = Utils::parseDate(value);
}
else if (tag == "TDOR" // Original release time (ID3v2 2.4)
|| tag == "TORY") // Original release year
{
track.originalYear = StringUtils::readAs<int>(value);
track.originalDate = Utils::parseDate(value);
}
else if (tag == "ACOUSTID ID")
{
+19 -11
View File
@@ -35,7 +35,7 @@
#include "utils/Logger.hpp"
#include "utils/String.hpp"
#include "Utils.hpp"
namespace MetaData
{
@@ -214,18 +214,26 @@ TagLibParser::processTag(Track& track, const std::string& tag, const TagLib::Str
}
}
else if (tag == "DATE")
track.year = StringUtils::readAs<int>(value);
else if (tag == "ORIGINALDATE" && !track.originalYear)
{
// Lower priority than ORIGINALYEAR
track.originalYear = StringUtils::readAs<int>(value);
// Higher priority than YEAR
if (const Wt::WDate date {Utils::parseDate(value)}; date.isValid())
track.date = date;
}
else if (tag == "ORIGINALYEAR")
else if (tag == "YEAR" && !track.date.isValid())
{
// Higher priority than ORIGINALDATE
auto originalYear = StringUtils::readAs<int>(value);
if (originalYear)
track.originalYear = originalYear;
// lower priority than DATE
track.date = Utils::parseDate(value);
}
else if (tag == "ORIGINALDATE")
{
// Higher priority than ORIGINALYEAR
if (const Wt::WDate date {Utils::parseDate(value)}; date.isValid())
track.originalDate = date;
}
else if (tag == "ORIGINALYEAR" && !track.originalDate.isValid())
{
// Lower priority than ORIGINALDATE
track.originalDate = Utils::parseDate(value);
}
else if (tag == "METADATA_BLOCK_PICTURE")
track.hasCover = true;
@@ -260,7 +268,7 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
{
TagLib::FileRef f {p.string().c_str(),
true, // read audio properties
TagLib::AudioProperties::Fast};
TagLib::AudioProperties::Fast}; // TODO parametrize this
if (f.isNull())
{
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2021 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 "Utils.hpp"
#include <ctime>
#include <string_view>
#include <iomanip>
#include <sstream>
namespace MetaData::Utils
{
Wt::WDate
parseDate(const std::string& dateStr)
{
static constexpr const char* formats[]
{
"%Y-%m-%d",
"%Y/%m/%d",
};
for (const char* format : formats)
{
std::tm tm = {};
std::stringstream ss {dateStr};
ss >> std::get_time(&tm, format);
if (ss.fail())
continue;
const Wt::WDate res
{
tm.tm_year + 1900, // years since 1900
tm.tm_mon + 1, // months since January [0, 11]
tm.tm_mday ? tm.tm_mday : 1 // day of the month [1, 31]
};
if (!res.isValid())
continue;
return res;
}
return {};
}
}
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016 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 <Wt/WDate.h>
namespace MetaData::Utils
{
Wt::WDate parseDate(const std::string& dateStr);
}
+11 -10
View File
@@ -27,6 +27,7 @@
#include <string_view>
#include <vector>
#include <Wt/WDate.h>
#include "utils/UUID.hpp"
namespace MetaData
@@ -56,21 +57,21 @@ namespace MetaData
struct Track
{
std::vector<Artist> artists;
std::vector<Artist> albumArtists;
std::string title;
std::optional<UUID> trackMBID;
std::optional<UUID> recordingMBID;
std::optional<Album> album;
Clusters clusters;
std::vector<Artist> artists;
std::vector<Artist> albumArtists;
std::string title;
std::optional<UUID> trackMBID;
std::optional<UUID> recordingMBID;
std::optional<Album> album;
Clusters clusters;
std::chrono::milliseconds duration;
std::optional<std::size_t> trackNumber;
std::optional<std::size_t> totalTrack;
std::optional<std::size_t> discNumber;
std::optional<std::size_t> totalDisc;
std::optional<int> year;
std::optional<int> originalYear;
bool hasCover {};
Wt::WDate date;
Wt::WDate originalDate;
bool hasCover {};
std::vector<AudioStream> audioStreams;
std::optional<UUID> acoustID;
std::string copyright;
+18
View File
@@ -0,0 +1,18 @@
include(GoogleTest)
add_executable(test-metadata
Metadata.cpp
Utils.cpp
)
target_include_directories(test-metadata PRIVATE
../impl
)
target_link_libraries(test-metadata PRIVATE
lmsmetadata
GTest::GTest
)
gtest_discover_tests(test-metadata)
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2021 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 <gtest/gtest.h>
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+74
View File
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2019 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 <gtest/gtest.h>
#include "Utils.hpp"
TEST(MetaData, parseDate)
{
using namespace MetaData::Utils;
struct TestCase
{
std::string str;
Wt::WDate result;
} testCases []
{
{ "1995-05-09", Wt::WDate {1995, 5, 9} },
{ "1995-01-01", Wt::WDate {1995, 1, 1} },
{ "1900-01-01", Wt::WDate {1900, 1, 1} },
{ "1899-01-01", Wt::WDate {1899, 1, 1} },
{ "1899-12-31", Wt::WDate {1899, 12, 31} },
{ "1899-11-30", Wt::WDate {1899, 11, 30} },
{ "1500-11-30", Wt::WDate {1500, 11, 30} },
{ "1000-11-30", Wt::WDate {1000, 11, 30} },
{ "1899-11-31", Wt::WDate {} }, // invalid day
{ "1899-13-01", Wt::WDate {} }, // invalid month
{ "1899-11", Wt::WDate {1899, 11, 1} }, // missing day
{ "1899", Wt::WDate {1899, 1, 1} }, // missing month and days
{ "1600", Wt::WDate {1600, 1, 1} }, // missing month and days
{ "1995/05/09", Wt::WDate {1995, 5, 9} },
{ "1995/01/01", Wt::WDate {1995, 1, 1} },
{ "1900/01/01", Wt::WDate {1900, 1, 1} },
{ "1899/01/01", Wt::WDate {1899, 1, 1} },
{ "1899/12/31", Wt::WDate {1899, 12, 31} },
{ "1899/11/30", Wt::WDate {1899, 11, 30} },
{ "1500/11/30", Wt::WDate {1500, 11, 30} },
{ "1000/11/30", Wt::WDate {1000, 11, 30} },
{ "1899/11/31", Wt::WDate {} }, // invalid day
{ "1899/13/01", Wt::WDate {} }, // invalid month
{ "1899/11", Wt::WDate {1899, 11, 1} }, // missing day
{ "1899", Wt::WDate {1899, 1, 1} }, // missing month and days
{ "1600", Wt::WDate {1600, 1, 1} }, // missing month and days
{ "1995/05-09", Wt::WDate {} }, // invalid mixup separators
{ "1995-05/09", Wt::WDate {} }, // invalid mixup separators
};
for (const TestCase& testCase : testCases)
{
const Wt::WDate parsed {parseDate(testCase.str)};
EXPECT_EQ(parsed.year(), testCase.result.year()) << " str was '" << testCase.str << "'";
EXPECT_EQ(parsed.month(), testCase.result.month()) << " str was '" << testCase.str << "'";
EXPECT_EQ(parsed.day(), testCase.result.day()) << " str was '" << testCase.str << "'";
}
}