From d1caed200b1b6ce8ca2a6d78e29e0746a3c5c77a Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 25 Jun 2025 13:55:45 +0200 Subject: [PATCH] Fixed bad handling of utf8 bom for playslit files, fixes #694 --- src/libs/metadata/impl/Lyrics.cpp | 15 ++++++++++----- src/libs/metadata/impl/PlayList.cpp | 10 ++++++++++ src/libs/metadata/test/PlayList.cpp | 13 +++++++++++++ .../steps/ScanStepAssociatePlayListTracks.cpp | 2 -- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/libs/metadata/impl/Lyrics.cpp b/src/libs/metadata/impl/Lyrics.cpp index 028d7cbf..45b66cc8 100644 --- a/src/libs/metadata/impl/Lyrics.cpp +++ b/src/libs/metadata/impl/Lyrics.cpp @@ -155,15 +155,20 @@ namespace lms::metadata accumulatedLyrics.clear(); }; + bool firstLine{ true }; std::string line; while (std::getline(is, line)) { - std::string_view trimmedLine{ core::stringUtils::stringTrimEnd(line) }; - // Remove potential UTF8 BOM - constexpr std::string_view utf8BOM{ "\xEF\xBB\xBF" }; - if (trimmedLine.starts_with(utf8BOM)) - trimmedLine = trimmedLine.substr(utf8BOM.size()); + if (firstLine) + { + firstLine = false; + constexpr std::string_view utf8BOM{ "\xEF\xBB\xBF" }; + if (line.starts_with(utf8BOM)) + line.erase(0, utf8BOM.size()); + } + + std::string_view trimmedLine{ core::stringUtils::stringTrimEnd(line) }; // Skip comments if (!trimmedLine.empty() && trimmedLine.front() == '#') diff --git a/src/libs/metadata/impl/PlayList.cpp b/src/libs/metadata/impl/PlayList.cpp index 4d15f9eb..ca0fadb5 100644 --- a/src/libs/metadata/impl/PlayList.cpp +++ b/src/libs/metadata/impl/PlayList.cpp @@ -63,11 +63,21 @@ namespace lms::metadata PlayList parsePlayList(std::istream& is) { + bool firstLine{ true }; PlayList playlist; std::string line; while (std::getline(is, line)) { + // Remove potential UTF8 BOM + if (firstLine) + { + firstLine = false; + constexpr std::string_view utf8BOM{ "\xEF\xBB\xBF" }; + if (line.starts_with(utf8BOM)) + line.erase(0, utf8BOM.size()); + } + const std::string_view trimmedLine{ core::stringUtils::stringTrim(line) }; if (trimmedLine.empty()) continue; diff --git a/src/libs/metadata/test/PlayList.cpp b/src/libs/metadata/test/PlayList.cpp index 165963a5..03be747c 100644 --- a/src/libs/metadata/test/PlayList.cpp +++ b/src/libs/metadata/test/PlayList.cpp @@ -19,6 +19,7 @@ */ #include +#include #include "metadata/PlayList.hpp" @@ -51,4 +52,16 @@ one to be/../one to be/normalized/foo.mp3)" }; EXPECT_EQ(playlist.files[4], "and another one/with relative path/foo.mp3"); EXPECT_EQ(playlist.files[5], "one to be/normalized/foo.mp3"); } + + TEST(PlayList, UTF8_bom) + { + const unsigned char content[] = { 0xEF, 0xBB, 0xBF, '#', 'E', 'X', 'T', 'M', '3', 'U', '\r', '\n', '\r', '\n', '.', '.', '/', 't', 'e', 's', 't', '.', 'm', 'p', '3', '\r', '\n' }; + std::istringstream is{ std::string(reinterpret_cast(content), sizeof(content)) }; + + const PlayList playlist{ parsePlayList(is) }; + EXPECT_EQ(playlist.name, ""); + ASSERT_EQ(playlist.files.size(), 1); + EXPECT_EQ(playlist.files[0], "../test.mp3"); + } + } // namespace lms::metadata::tests \ No newline at end of file diff --git a/src/libs/services/scanner/impl/steps/ScanStepAssociatePlayListTracks.cpp b/src/libs/services/scanner/impl/steps/ScanStepAssociatePlayListTracks.cpp index 7e058cb5..24308574 100644 --- a/src/libs/services/scanner/impl/steps/ScanStepAssociatePlayListTracks.cpp +++ b/src/libs/services/scanner/impl/steps/ScanStepAssociatePlayListTracks.cpp @@ -134,9 +134,7 @@ namespace lms::scanner if (track) playListAssociation.tracks.push_back(TrackInfo{ .trackId = track->getId(), .releaseId = track->getReleaseId() }); else - { pendingErrors.emplace_back(std::make_shared(playListFile->getAbsoluteFilePath(), file)); - } } if (pendingErrors.size() == files.size())