From 0812965f505e2d7100a054ddef721c62f37444a7 Mon Sep 17 00:00:00 2001 From: Leandro Pereira Date: Fri, 1 Oct 2021 21:57:00 -0700 Subject: [PATCH] SoundPlayer: Make M3UParser more idiomatic Let's use the nice APIs we have, and make the M3U parser a bit more readable, shorter, and resilient. --- .../Applications/SoundPlayer/M3UParser.cpp | 128 ++++++++---------- 1 file changed, 57 insertions(+), 71 deletions(-) diff --git a/Userland/Applications/SoundPlayer/M3UParser.cpp b/Userland/Applications/SoundPlayer/M3UParser.cpp index 19e3dd5cbc6..5b32bff491d 100644 --- a/Userland/Applications/SoundPlayer/M3UParser.cpp +++ b/Userland/Applications/SoundPlayer/M3UParser.cpp @@ -9,6 +9,7 @@ #include #include #include +#include M3UParser::M3UParser() { @@ -16,22 +17,11 @@ M3UParser::M3UParser() NonnullOwnPtr M3UParser::from_file(const String path) { - auto parser = make(); - VERIFY(!path.is_null() && !path.is_empty() && !path.is_whitespace()); - parser->m_use_utf8 = path.ends_with(".m3u8", AK::CaseSensitivity::CaseInsensitive); - FILE* file = fopen(path.characters(), "r"); - ScopeGuard file_guard = [&] { fclose(file); }; - VERIFY(file != nullptr); - fseek(file, 0, SEEK_END); - size_t file_size = ftell(file); - fseek(file, 0, SEEK_SET); - VERIFY(file_size > 0); - Vector temp_buffer; - temp_buffer.resize(file_size); - auto bytes_read = fread(temp_buffer.data(), 1, file_size, file); - VERIFY(bytes_read == file_size); - parser->m_m3u_raw_data = *new String(temp_buffer.span(), NoChomp); - return parser; + auto file_result = Core::File::open(path, Core::OpenMode::ReadOnly); + VERIFY(!file_result.is_error()); + auto contents = file_result.value()->read_all(); + auto use_utf8 = path.ends_with(".m3u8", CaseSensitivity::CaseInsensitive); + return from_memory(String { contents, NoChomp }, use_utf8); } NonnullOwnPtr M3UParser::from_memory(const String& m3u_contents, bool utf8) @@ -47,63 +37,59 @@ NonnullOwnPtr> M3UParser::parse(bool include_extended_info) { auto vec = make>(); - bool has_extended_info_tag = false; - if (!m_use_utf8) { - auto lines = m_m3u_raw_data.split_view('\n'); - - if (include_extended_info) { - if (lines[0] == "#EXTM3U") - has_extended_info_tag = true; - } - - M3UExtendedInfo metadata_for_next_file {}; - for (auto& line : lines) { - line = line.trim_whitespace(); - M3UEntry entry {}; - if (line.starts_with('#') && has_extended_info_tag) { - if (line.starts_with("#EXTINF:")) { - auto data = line.substring_view(8); - auto separator = data.find(','); - VERIFY(separator.has_value()); - auto seconds = data.substring_view(0, separator.value()); - VERIFY(!seconds.is_whitespace() && !seconds.is_null() && !seconds.is_empty()); - metadata_for_next_file.track_length_in_seconds = seconds.to_uint(); - auto display_name = data.substring_view(seconds.length() + 1); - VERIFY(!display_name.is_empty() && !display_name.is_null() && !display_name.is_empty()); - metadata_for_next_file.track_display_title = display_name; - //TODO: support the alternative, non-standard #EXTINF value of a key=value dictionary - } else if (line.starts_with("#PLAYLIST:")) { - auto name = line.substring_view(10); - VERIFY(!name.is_empty()); - m_parsed_playlist_title = name; - } else if (line.starts_with("#EXTGRP:")) { - auto name = line.substring_view(8); - VERIFY(!name.is_empty()); - metadata_for_next_file.group_name = name; - } else if (line.starts_with("#EXTALB:")) { - auto name = line.substring_view(8); - VERIFY(!name.is_empty()); - metadata_for_next_file.album_title = name; - } else if (line.starts_with("#EXTART:")) { - auto name = line.substring_view(8); - VERIFY(!name.is_empty()); - metadata_for_next_file.album_artist = name; - } else if (line.starts_with("#EXTGENRE:")) { - auto name = line.substring_view(10); - VERIFY(!name.is_empty()); - metadata_for_next_file.album_genre = name; - } - //TODO: Support M3A files (M3U files with embedded mp3 files) - } else { - entry.path = line; - entry.extended_info = metadata_for_next_file; - vec->append(entry); - metadata_for_next_file = {}; - } - } - } else { + if (m_use_utf8) { //TODO: Implement M3U8 parsing TODO(); + return vec; } + + auto lines = m_m3u_raw_data.split_view('\n'); + + bool has_extended_info_tag = include_extended_info && (lines[0] == "#EXTM3U"); + + M3UExtendedInfo metadata_for_next_file {}; + for (auto& line : lines) { + line = line.trim_whitespace(); + + if (!has_extended_info_tag || !line.starts_with('#')) { + vec->append({ line, metadata_for_next_file }); + metadata_for_next_file = {}; + continue; + } + + auto tag = [&line](StringView tag_name) -> Optional { + if (line.starts_with(tag_name)) { + auto value = line.substring_view(tag_name.length()); + VERIFY(!value.is_empty()); + return value; + } + return {}; + }; + + if (auto ext_inf = tag("#EXTINF:"); ext_inf.has_value()) { + auto separator = ext_inf.value().find(','); + VERIFY(separator.has_value()); + auto seconds = ext_inf.value().substring_view(0, separator.value()); + VERIFY(!seconds.is_whitespace() && !seconds.is_null() && !seconds.is_empty()); + metadata_for_next_file.track_length_in_seconds = seconds.to_uint(); + auto display_name = ext_inf.value().substring_view(seconds.length() + 1); + VERIFY(!display_name.is_empty() && !display_name.is_null() && !display_name.is_empty()); + metadata_for_next_file.track_display_title = display_name; + //TODO: support the alternative, non-standard #EXTINF value of a key=value dictionary + continue; + } + if (auto playlist = tag("#PLAYLIST:"); playlist.has_value()) + m_parsed_playlist_title = move(playlist.value()); + else if (auto ext_grp = tag("#EXTGRP:"); ext_grp.has_value()) + metadata_for_next_file.group_name = move(ext_grp.value()); + else if (auto ext_alb = tag("#EXTALB:"); ext_alb.has_value()) + metadata_for_next_file.album_title = move(ext_alb.value()); + else if (auto ext_art = tag("#EXTART:"); ext_art.has_value()) + metadata_for_next_file.album_artist = move(ext_art.value()); + else if (auto ext_genre = tag("#EXTGENRE:"); ext_genre.has_value()) + metadata_for_next_file.album_genre = move(ext_genre.value()); + //TODO: Support M3A files (M3U files with embedded mp3 files) + } + return vec; }