/* * Copyright (C) 2024 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 . */ #include "metadata/PlayList.hpp" #include #include #include "core/String.hpp" namespace lms::metadata { std::span getSupportedPlayListFileExtensions() { static const std::array fileExtensions{ ".m3u", ".m3u8" }; return fileExtensions; } namespace { struct Comment { std::string_view directive; std::string_view parameter; }; std::optional parseComment(std::string_view line) { if (line.empty() || line.front() != '#') return std::nullopt; Comment comment; const std::string_view::size_type parameterSeparator{ line.find(':') }; if (parameterSeparator == std::string_view::npos) { comment.directive = line; } else { comment.directive = line.substr(0, parameterSeparator + 1); comment.parameter = line.substr(parameterSeparator + 1); }; return comment; } } // namespace 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; // Don't enforce #EXTM3U as first line: be permissive if (const std::optional comment{ parseComment(trimmedLine) }) { if (comment->directive == "#PLAYLIST:") playlist.name = comment->parameter; continue; } // filter out URI = scheme ":" ["//" authority] path ["?" query] ["#" fragment] // Consider an entry with a ':' is actually an url, as filenames are not supposed to have ':' on windows if (trimmedLine.find(':') != std::string_view::npos) continue; const std::filesystem::path path{ std::cbegin(trimmedLine), std::cend(trimmedLine) }; playlist.files.emplace_back(path.lexically_normal()); } return playlist; } } // namespace lms::metadata