Serenity Operating System
at master 94 lines 3.7 kB view raw
1/* 2 * Copyright (c) 2021, Cesar Torres <shortanemoia@protonmail.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "M3UParser.h" 8#include <AK/OwnPtr.h> 9#include <AK/RefPtr.h> 10#include <AK/ScopeGuard.h> 11#include <AK/Utf8View.h> 12#include <LibCore/File.h> 13 14M3UParser::M3UParser() 15{ 16} 17 18NonnullOwnPtr<M3UParser> M3UParser::from_file(StringView path) 19{ 20 auto file_result = Core::File::open(path, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); 21 auto contents = file_result->read_until_eof().release_value_but_fixme_should_propagate_errors(); 22 auto use_utf8 = path.ends_with(".m3u8"sv, CaseSensitivity::CaseInsensitive); 23 return from_memory(DeprecatedString { contents, NoChomp }, use_utf8); 24} 25 26NonnullOwnPtr<M3UParser> M3UParser::from_memory(DeprecatedString const& m3u_contents, bool utf8) 27{ 28 auto parser = make<M3UParser>(); 29 VERIFY(!m3u_contents.is_null() && !m3u_contents.is_empty() && !m3u_contents.is_whitespace()); 30 parser->m_m3u_raw_data = m3u_contents; 31 parser->m_use_utf8 = utf8; 32 return parser; 33} 34 35NonnullOwnPtr<Vector<M3UEntry>> M3UParser::parse(bool include_extended_info) 36{ 37 auto vec = make<Vector<M3UEntry>>(); 38 39 if (m_use_utf8) { 40 // TODO: Implement M3U8 parsing 41 TODO(); 42 return vec; 43 } 44 45 auto lines = m_m3u_raw_data.split_view('\n'); 46 47 bool has_extended_info_tag = include_extended_info && (lines[0] == "#EXTM3U"); 48 49 M3UExtendedInfo metadata_for_next_file {}; 50 for (auto& line : lines) { 51 line = line.trim_whitespace(); 52 53 if (!has_extended_info_tag || !line.starts_with('#')) { 54 vec->append({ line, metadata_for_next_file }); 55 metadata_for_next_file = {}; 56 continue; 57 } 58 59 auto tag = [&line](StringView tag_name) -> Optional<StringView> { 60 if (line.starts_with(tag_name)) { 61 auto value = line.substring_view(tag_name.length()); 62 VERIFY(!value.is_empty()); 63 return value; 64 } 65 return {}; 66 }; 67 68 if (auto ext_inf = tag("#EXTINF:"sv); ext_inf.has_value()) { 69 auto separator = ext_inf.value().find(','); 70 VERIFY(separator.has_value()); 71 auto seconds = ext_inf.value().substring_view(0, separator.value()); 72 VERIFY(!seconds.is_whitespace() && !seconds.is_null() && !seconds.is_empty()); 73 metadata_for_next_file.track_length_in_seconds = seconds.to_uint(); 74 auto display_name = ext_inf.value().substring_view(seconds.length() + 1); 75 VERIFY(!display_name.is_empty() && !display_name.is_null() && !display_name.is_empty()); 76 metadata_for_next_file.track_display_title = display_name; 77 // TODO: support the alternative, non-standard #EXTINF value of a key=value dictionary 78 continue; 79 } 80 if (auto playlist = tag("#PLAYLIST:"sv); playlist.has_value()) 81 m_parsed_playlist_title = move(playlist.value()); 82 else if (auto ext_grp = tag("#EXTGRP:"sv); ext_grp.has_value()) 83 metadata_for_next_file.group_name = move(ext_grp.value()); 84 else if (auto ext_alb = tag("#EXTALB:"sv); ext_alb.has_value()) 85 metadata_for_next_file.album_title = move(ext_alb.value()); 86 else if (auto ext_art = tag("#EXTART:"sv); ext_art.has_value()) 87 metadata_for_next_file.album_artist = move(ext_art.value()); 88 else if (auto ext_genre = tag("#EXTGENRE:"sv); ext_genre.has_value()) 89 metadata_for_next_file.album_genre = move(ext_genre.value()); 90 // TODO: Support M3A files (M3U files with embedded mp3 files) 91 } 92 93 return vec; 94}