From 0563048949031a28d0c3e06f046dfc8d77b5084e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kleines=20Filmr=C3=B6llchen?= Date: Tue, 7 Mar 2023 16:59:02 +0100 Subject: [PATCH] file: Read more metadata from audio files We can always read the basic format information (sample rate, bit depth, etc.), but we will also print artist, album, and title if available in the metadata. --- Meta/Lagom/CMakeLists.txt | 2 +- Userland/BuggieBox/CMakeLists.txt | 2 +- Userland/Utilities/CMakeLists.txt | 2 +- Userland/Utilities/file.cpp | 53 ++++++++++++++++++++++++++++--- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index a2d69e6030..b8196b67e8 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -492,7 +492,7 @@ if (BUILD_LAGOM) if (NOT EMSCRIPTEN) # LibELF is part of LibC in SerenityOS builds, but not in Lagom. add_executable(file ../../Userland/Utilities/file.cpp) - target_link_libraries(file LibCompress LibCore LibELF LibGfx LibIPC LibMain) + target_link_libraries(file LibAudio LibCompress LibCore LibELF LibGfx LibIPC LibMain) endif() add_executable(gml-format ../../Userland/Utilities/gml-format.cpp) diff --git a/Userland/BuggieBox/CMakeLists.txt b/Userland/BuggieBox/CMakeLists.txt index 200daa5142..fcd294ed8b 100644 --- a/Userland/BuggieBox/CMakeLists.txt +++ b/Userland/BuggieBox/CMakeLists.txt @@ -41,7 +41,7 @@ set(utility_srcs serenity_bin(BuggieBox) target_sources(BuggieBox PRIVATE main.cpp) -target_link_libraries(BuggieBox PRIVATE LibMain LibShell LibCompress LibCore LibCrypto LibGfx LibLine LibRegex) +target_link_libraries(BuggieBox PRIVATE LibMain LibShell LibCompress LibCore LibCrypto LibGfx LibLine LibRegex LibAudio) foreach(file IN LISTS utility_srcs) buggiebox_utility(${file}) diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index 621e9640e5..b13de2e832 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -87,7 +87,7 @@ target_link_libraries(diff PRIVATE LibDiff) target_link_libraries(disasm PRIVATE LibX86) target_link_libraries(expr PRIVATE LibRegex) target_link_libraries(fdtdump PRIVATE LibDeviceTree) -target_link_libraries(file PRIVATE LibGfx LibIPC LibCompress) +target_link_libraries(file PRIVATE LibGfx LibIPC LibCompress LibAudio) target_link_libraries(functrace PRIVATE LibDebug LibX86) target_link_libraries(gml-format PRIVATE LibGUI) target_link_libraries(grep PRIVATE LibRegex) diff --git a/Userland/Utilities/file.cpp b/Userland/Utilities/file.cpp index c574e1f20a..eb0d9b5ebc 100644 --- a/Userland/Utilities/file.cpp +++ b/Userland/Utilities/file.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -51,6 +52,48 @@ static Optional image_details(StringView description, StringVi return builder.to_deprecated_string(); } +static Optional audio_details(StringView description, StringView path) +{ + auto loader_or_error = Audio::Loader::create(path); + if (loader_or_error.is_error()) + return {}; + + auto loader = loader_or_error.release_value(); + StringBuilder builder; + builder.appendff("{}, {} Hz, {}-bit {}, {} samples ({} s)", + description, + loader->sample_rate(), + loader->bits_per_sample(), + loader->num_channels() == 1 ? "Mono" : "Stereo", + loader->total_samples(), + loader->total_samples() / loader->sample_rate()); + auto metadata = loader->metadata(); + Vector metadata_parts; + if (metadata.title.has_value()) { + // FIXME: Use “pretty quotation” once our terminal fonts support these characters. + if (auto title_text = String::formatted("\"{}\"", metadata.title.value_or({})); !title_text.is_error()) { + // We intentionally discard the error, because not printing part of the metadata due to OOM is not a problem. + (void)metadata_parts.try_append(title_text.release_value()); + } + } + if (metadata.album.has_value()) { + if (auto album_text = String::formatted("(Album: {})", metadata.album.value_or({})); !album_text.is_error()) + (void)metadata_parts.try_append(album_text.release_value()); + } + if (auto all_artists = metadata.all_artists(); !all_artists.is_error() && all_artists.value().has_value()) { + if (auto artist_text = String::formatted("by {}", all_artists.release_value().release_value()); !artist_text.is_error()) + (void)metadata_parts.try_append(artist_text.release_value()); + } + + if (!metadata_parts.is_empty()) { + // New line for the metadata. + builder.append_code_point('\n'); + builder.join(" "sv, metadata_parts); + } + + return builder.to_deprecated_string(); +} + static Optional gzip_details(StringView description, StringView path) { auto file_or_error = Core::MappedFile::map(path); @@ -113,11 +156,11 @@ static Optional elf_details(StringView description, StringView __ENUMERATE_MIME_TYPE_DESCRIPTION("application/tar"sv, "tape archive"sv, description_only) \ __ENUMERATE_MIME_TYPE_DESCRIPTION("application/wasm"sv, "WebAssembly bytecode"sv, description_only) \ __ENUMERATE_MIME_TYPE_DESCRIPTION("application/x-7z-compressed"sv, "7-Zip archive"sv, description_only) \ - __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/flac"sv, "FLAC audio"sv, description_only) \ - __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/midi"sv, "MIDI notes"sv, description_only) \ - __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/mpeg"sv, "MP3 audio"sv, description_only) \ - __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/qoa"sv, "Quite OK Audio"sv, description_only) \ - __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/wave"sv, "WAVE audio"sv, description_only) \ + __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/flac"sv, "FLAC audio"sv, audio_details) \ + __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/midi"sv, "MIDI notes"sv, audio_details) \ + __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/mpeg"sv, "MP3 audio"sv, audio_details) \ + __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/qoa"sv, "Quite OK Audio"sv, audio_details) \ + __ENUMERATE_MIME_TYPE_DESCRIPTION("audio/wave"sv, "WAVE audio"sv, audio_details) \ __ENUMERATE_MIME_TYPE_DESCRIPTION("extra/blender"sv, "Blender project file"sv, description_only) \ __ENUMERATE_MIME_TYPE_DESCRIPTION("extra/elf"sv, "ELF"sv, elf_details) \ __ENUMERATE_MIME_TYPE_DESCRIPTION("extra/ext"sv, "ext filesystem"sv, description_only) \