From 7b78375402b90616a1d1835b258a6827e3e77b19 Mon Sep 17 00:00:00 2001 From: Lyssieth Date: Wed, 18 Oct 2023 23:05:09 +0300 Subject: [PATCH] feat: improvements and bug fixes (sarcasm) - `rest/getArtists` and `rest/getArtist` - Improved artist formatting algorithm - Other dispersed improvements --- Cargo.lock | 629 ++++--------------------------- Cargo.toml | 6 +- Justfile | 3 + rave/Cargo.toml | 42 +-- rave/src/main.rs | 24 +- rave/src/rest/get_album.rs | 28 +- rave/src/rest/get_artist.rs | 62 +++ rave/src/rest/get_artists.rs | 36 ++ rave/src/rest/mod.rs | 6 + rave/src/rest/search3.rs | 87 +++-- rave/src/scan/flac.rs | 49 ++- rave/src/scan/mp3.rs | 49 ++- rave/src/subsonic/mod.rs | 27 +- rave/src/subsonic/types/child.rs | 9 +- rave/src/utils.rs | 150 +++++++- 15 files changed, 513 insertions(+), 694 deletions(-) create mode 100644 rave/src/rest/get_artist.rs create mode 100644 rave/src/rest/get_artists.rs diff --git a/Cargo.lock b/Cargo.lock index d60ab7b..78a66f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb42b2197bf15ccb092b62c74515dbd8b86d0effd934795f6687c93b6e679a2c" +checksum = "f658e2baef915ba0f26f1f7c42bfb8e12f532a01f449a090ded75ae7a07e9ba2" dependencies = [ "brotli", "flate2", @@ -212,9 +212,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1da3ae8dabd9c00f453a329dfe1fb28da3c0a72e2478cdcd93171740c20499" +checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" dependencies = [ "async-lock", "async-task", @@ -254,7 +254,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.24", + "rustix 0.37.25", "slab", "socket2 0.4.9", "waker-fn", @@ -320,15 +320,15 @@ dependencies = [ [[package]] name = "async-task" -version = "4.4.1" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", @@ -417,9 +417,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" dependencies = [ "serde", ] @@ -677,16 +677,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -840,16 +830,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "serde", - "uuid", -] - [[package]] name = "der" version = "0.7.8" @@ -863,10 +843,11 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ + "powerfmt", "serde", ] @@ -908,15 +889,6 @@ dependencies = [ "serde", ] -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - [[package]] name = "entities" version = "0.1.0" @@ -1009,18 +981,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "findshlibs" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" -dependencies = [ - "cc", - "lazy_static", - "libc", - "winapi", -] - [[package]] name = "finl_unicode" version = "1.2.0" @@ -1029,9 +989,9 @@ checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -1054,21 +1014,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1383,17 +1328,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "http" version = "0.2.9" @@ -1452,45 +1386,18 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" -dependencies = [ - "futures-util", - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1508,7 +1415,7 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a85caa20f41eef2391fd1dd42b015d0eba17171cdb1a162246a7cc03cd73ab1e" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "byteorder", "flate2", ] @@ -1614,12 +1521,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - [[package]] name = "itertools" version = "0.11.0" @@ -1714,9 +1615,9 @@ checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1731,12 +1632,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -1881,24 +1776,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nom" version = "7.1.3" @@ -2032,50 +1909,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" -dependencies = [ - "bitflags 2.4.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "ordered-float" version = "3.9.2" @@ -2085,17 +1918,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_info" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" -dependencies = [ - "log", - "serde", - "winapi", -] - [[package]] name = "ouroboros" version = "0.17.2" @@ -2134,9 +1956,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "parking" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -2150,13 +1972,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "smallvec", "windows-targets", ] @@ -2345,6 +2167,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2480,8 +2308,6 @@ dependencies = [ "poem", "quick-xml", "sea-orm", - "sentry", - "sentry-tracing", "serde", "serde_json", "time", @@ -2529,15 +2355,24 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.10.0" +name = "redox_syscall" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.1", - "regex-syntax 0.8.1", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -2551,13 +2386,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.1", + "regex-syntax 0.8.2", ] [[package]] @@ -2568,52 +2403,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d84fdd47036b038fc80dd333d10b6aab10d5d31f4a366e20014def75328d33" - -[[package]] -name = "reqwest" -version = "0.11.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.2", - "winreg", -] +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rfc7239" @@ -2667,20 +2459,11 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "rustix" -version = "0.37.24" +version = "0.37.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d" +checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" dependencies = [ "bitflags 1.3.2", "errno", @@ -2692,11 +2475,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.18" +version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a74ee2d7c2581cd139b42447d7d9389b889bdaad3a73f1ebb16f2a3237bb19c" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys 0.4.10", @@ -2709,7 +2492,6 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ - "log", "ring", "rustls-webpki", "sct", @@ -2740,15 +2522,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -2909,171 +2682,20 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - -[[package]] -name = "sentry" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0097a48cd1999d983909f07cb03b15241c5af29e5e679379efac1c06296abecc" -dependencies = [ - "httpdate", - "native-tls", - "reqwest", - "rustls", - "sentry-backtrace", - "sentry-contexts", - "sentry-core", - "sentry-debug-images", - "sentry-panic", - "sentry-tower", - "sentry-tracing", - "tokio", - "ureq", - "webpki-roots 0.25.2", -] - -[[package]] -name = "sentry-backtrace" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a7b80fa1dd6830a348d38a8d3a9761179047757b7dca29aef82db0118b9670" -dependencies = [ - "backtrace", - "once_cell", - "regex", - "sentry-core", -] - -[[package]] -name = "sentry-contexts" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7615dc588930f1fd2e721774f25844ae93add2dbe2d3c2f995ce5049af898147" -dependencies = [ - "hostname", - "libc", - "os_info", - "rustc_version", - "sentry-core", - "uname", -] - -[[package]] -name = "sentry-core" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f51264e4013ed9b16558cce43917b983fa38170de2ca480349ceb57d71d6053" -dependencies = [ - "once_cell", - "rand", - "sentry-types", - "serde", - "serde_json", -] - -[[package]] -name = "sentry-debug-images" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe6180fa564d40bb942c9f0084ffb5de691c7357ead6a2b7a3154fae9e401dd" -dependencies = [ - "findshlibs", - "once_cell", - "sentry-core", -] - -[[package]] -name = "sentry-panic" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323160213bba549f9737317b152af116af35c0410f4468772ee9b606d3d6e0fa" -dependencies = [ - "sentry-backtrace", - "sentry-core", -] - -[[package]] -name = "sentry-tower" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffe3ab7bf7f65c9f8ccd20aa136ce5b2140aa6d6a11339e823cd43a7d694a9e" -dependencies = [ - "sentry-core", - "tower-layer", - "tower-service", -] - -[[package]] -name = "sentry-tracing" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38033822128e73f7b6ca74c1631cef8868890c6cb4008a291cf73530f87b4eac" -dependencies = [ - "sentry-backtrace", - "sentry-core", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "sentry-types" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e663b3eb62ddfc023c9cf5432daf5f1a4f6acb1df4d78dd80b740b32dd1a740" -dependencies = [ - "debugid", - "hex", - "rand", - "serde", - "serde_json", - "thiserror", - "time", - "url", - "uuid", -] - [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", @@ -3284,7 +2906,7 @@ dependencies = [ "tokio-stream", "tracing", "url", - "webpki-roots 0.24.0", + "webpki-roots", ] [[package]] @@ -3334,7 +2956,7 @@ checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" dependencies = [ "atoi", "base64", - "bitflags 2.4.0", + "bitflags 2.4.1", "byteorder", "bytes", "crc", @@ -3377,7 +2999,7 @@ checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" dependencies = [ "atoi", "base64", - "bitflags 2.4.0", + "bitflags 2.4.1", "byteorder", "crc", "dotenvy", @@ -3489,27 +3111,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" version = "3.8.0" @@ -3518,8 +3119,8 @@ checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall", - "rustix 0.38.18", + "redox_syscall 0.3.5", + "rustix 0.38.19", "windows-sys", ] @@ -3566,14 +3167,15 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", "libc", "num_threads", + "powerfmt", "serde", "time-core", "time-macros", @@ -3639,26 +3241,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.14" @@ -3732,11 +3314,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -3756,9 +3337,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", @@ -3767,9 +3348,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -3841,15 +3422,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "uname" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" -dependencies = [ - "libc", -] - [[package]] name = "uncased" version = "0.9.9" @@ -3917,22 +3489,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "ureq" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" -dependencies = [ - "base64", - "log", - "native-tls", - "once_cell", - "rustls", - "rustls-webpki", - "url", - "webpki-roots 0.25.2", -] - [[package]] name = "url" version = "2.4.1" @@ -3960,15 +3516,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" -dependencies = [ - "serde", -] - [[package]] name = "valuable" version = "0.1.0" @@ -4099,12 +3646,6 @@ dependencies = [ "rustls-webpki", ] -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - [[package]] name = "weezl" version = "0.1.7" @@ -4140,10 +3681,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets", ] @@ -4216,23 +3757,13 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907" +checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys", -] - [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 4e9ac9a..5f70f85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ sea-orm = { version = "0.12", default-features = false, features = [ "with-time", "postgres-array", ] } -time = { version = "0.3.29", features = [ +time = { version = "0.3", features = [ "serde-human-readable", "macros", "parsing", ] } -tracing = { version = "0.1.37", features = ["async-await"] } -serde = { version = "1.0.188", features = ["derive"] } +tracing = { version = "0.1", features = ["async-await"] } +serde = { version = "1.0", features = ["derive"] } diff --git a/Justfile b/Justfile index 6853221..7906336 100644 --- a/Justfile +++ b/Justfile @@ -12,6 +12,9 @@ unmount: run: mount RAVE_STORAGE_DIR=/tmp/media-for-rave RAVE_CACHE_DIR=/tmp/cache-for-rave cargo r +run-custom: + cargo r + refresh: sea migrate fresh sea generate entity -o ./entities/src --with-serde both --date-time-crate time --lib diff --git a/rave/Cargo.toml b/rave/Cargo.toml index eecde03..5054b1f 100644 --- a/rave/Cargo.toml +++ b/rave/Cargo.toml @@ -9,9 +9,9 @@ default-run = "rave" [dependencies] cfg-if = "1.0.0" -color-eyre = "0.6.2" -md5 = "0.7.0" -poem = { version = "1.3.58", features = [ +color-eyre = "0.6" +md5 = "0.7" +poem = { version = "1.3", features = [ "compression", "cookie", "session", @@ -19,40 +19,28 @@ poem = { version = "1.3.58", features = [ "xml", "tower-compat", ] } -quick-xml = { version = "0.30.0", features = ["serialize"] } +quick-xml = { version = "0.30", features = ["serialize"] } serde = { workspace = true } -serde_json = "1.0.107" +serde_json = "1.0" time = { workspace = true, features = ["local-offset"] } -tokio = { version = "1.32.0", features = ["full"] } +tokio = { version = "1.33", features = ["full"] } tracing = { workspace = true } -tracing-subscriber = { version = "0.3.17", features = [ +tracing-subscriber = { version = "0.3", features = [ "env-filter", "tracing", "parking_lot", "time", "json", ] } -url = { version = "2.4.1", features = ["serde"] } -url-escape = "0.1.1" +url = { version = "2.4", features = ["serde"] } +url-escape = "0.1" sea-orm = { workspace = true } entities = { workspace = true } migration = { workspace = true } -once_cell = { version = "1.18.0", features = ["parking_lot"] } +once_cell = { version = "1.18", features = ["parking_lot"] } futures = "0.3" -audiotags = "0.4.1" -tracing-appender = "0.2.2" -sentry = { version = "0.31.7", default-features = false, features = [ - "backtrace", - "contexts", - "panic", - "transport", - "debug-images", - "reqwest", - "rustls", - "tower", - "tracing", -] } -sentry-tracing = { version = "0.31.7", features = ["backtrace"] } -blake3 = "1.5.0" -image = "0.24.7" -nate = "0.4.0" +audiotags = "0.4" +tracing-appender = "0.2" +blake3 = "1.5" +image = "0.24" +nate = "0.4" diff --git a/rave/src/main.rs b/rave/src/main.rs index 71121ea..36eb467 100644 --- a/rave/src/main.rs +++ b/rave/src/main.rs @@ -11,13 +11,12 @@ use migration::{Migrator, MigratorTrait}; use poem::{ http::StatusCode, listener::TcpListener, - middleware::{self, TowerLayerCompatExt}, + middleware, web::{CompressionAlgo, CompressionLevel}, - Endpoint, EndpointExt, Request, Route, + Endpoint, EndpointExt, Route, }; use sea_orm::{ConnectOptions, Database, DatabaseConnection}; -use sentry::integrations::tower::NewSentryLayer; -use tracing::{debug, info}; +use tracing::info; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{ fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, @@ -40,21 +39,6 @@ async fn main() -> Result<()> { color_eyre::install()?; let _guards = install_tracing().await?; - let _sentry = if let Ok(dsn) = std::env::var("RAVE_SENTRY_DSN") { - let guard = sentry::init(sentry::ClientOptions { - dsn: Some(dsn.parse()?), - release: sentry::release_name!(), - traces_sample_rate: 1.0, - ..Default::default() - }); - - debug!("Sentry initialized"); - - Some(guard) - } else { - None - }; - let route = create_route(); let dbc = create_pool().await?; @@ -89,7 +73,6 @@ fn create_route() -> Box> { Route::new() .nest("/", ui::build()) .nest("/rest", rest::build()) - .with(NewSentryLayer::::new_from_top().compat()) .with(middleware::CatchPanic::new()) .with( middleware::Compression::new() @@ -135,7 +118,6 @@ async fn install_tracing() -> Result<[WorkerGuard; 1]> { .with_writer(non_blocking) .with_filter(filter), ) - .with(sentry_tracing::layer()) .try_init()?; Ok([guard]) diff --git a/rave/src/rest/get_album.rs b/rave/src/rest/get_album.rs index 0dc0f58..8364f6c 100644 --- a/rave/src/rest/get_album.rs +++ b/rave/src/rest/get_album.rs @@ -1,6 +1,6 @@ use crate::{ authentication::Authentication, - subsonic::{Album as AlbumId3, Child, Error, SubsonicResponse}, + subsonic::{Album as AlbumId3, Artist as ArtistId3, Child, Error, SubsonicResponse}, utils, }; @@ -9,7 +9,7 @@ use entities::prelude::{Album, Artist, Genre, Track}; use poem::web::{Data, Query}; use sea_orm::{EntityTrait, ModelTrait}; use serde::Deserialize; -use tracing::{error, instrument}; +use tracing::{error, instrument, warn}; #[poem::handler] #[instrument(skip(txn, auth))] @@ -107,10 +107,30 @@ pub async fn get_album( } }; + let mut tracks_with_artists = Vec::with_capacity(tracks.len()); + + for track in tracks { + let find = crate::utils::find_track_artist(track, txn.clone()); + + tracks_with_artists.push(find); + } + + let tracks_with_artists = futures::future::join_all(tracks_with_artists).await; + let mut actual = vec![]; + + for track in tracks_with_artists { + match track { + Ok((track, artist)) => actual.push((track, artist)), + Err(e) => { + warn!("Error getting artist: {e}"); + } + } + } + let album = AlbumId3::new(album, artist, genre); - let mut tracks = tracks + let mut tracks = actual .into_iter() - .map(|t| Child::new(&t, &album)) + .map(|(track, artist)| Child::new(&track, &album, &ArtistId3::from(artist))) .collect::>(); tracks.sort_by_cached_key(|tr| tr.track.unwrap_or_default()); diff --git a/rave/src/rest/get_artist.rs b/rave/src/rest/get_artist.rs new file mode 100644 index 0000000..c748500 --- /dev/null +++ b/rave/src/rest/get_artist.rs @@ -0,0 +1,62 @@ +use crate::{ + authentication::Authentication, + subsonic::{Error, SubsonicResponse}, + utils, +}; + +use crate::utils::db::DbTxn; +use entities::prelude::Artist; +use poem::web::{Data, Query}; +use sea_orm::EntityTrait; +use serde::Deserialize; +use tracing::{error, instrument}; + +#[poem::handler] +#[instrument(skip(txn, auth))] +pub async fn get_artist( + Data(txn): Data<&DbTxn>, + auth: Authentication, + Query(params): Query, +) -> SubsonicResponse { + let u = utils::verify_user(txn.clone(), auth).await; + + match u { + Ok(_) => {} + Err(e) => return e, + } + + let id = params.id.split_once('-'); + + if id.is_none() { + return SubsonicResponse::new_error(Error::Generic(None)); + } + + let id = id.expect("none checked").1; + + let Ok(id) = id.parse::() else { + return SubsonicResponse::new_error(Error::Generic(None)); + }; + + let artist = Artist::find_by_id(id).one(&**txn).await.map_err(|e| { + error!( + error = &e as &dyn std::error::Error, + "failed to get artist: {e}" + ); + Error::Generic(None) + }); + + let artist = match artist { + Ok(Some(v)) => v, + Ok(None) => return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(None)), + Err(e) => return SubsonicResponse::new_error(e), + }; + + let artist = artist.into(); + + SubsonicResponse::new_artist(artist) +} + +#[derive(Debug, Deserialize)] +pub struct GetArtistParams { + id: String, +} diff --git a/rave/src/rest/get_artists.rs b/rave/src/rest/get_artists.rs new file mode 100644 index 0000000..b6c7d6d --- /dev/null +++ b/rave/src/rest/get_artists.rs @@ -0,0 +1,36 @@ +use crate::{ + authentication::Authentication, + subsonic::{Artist as ArtistId3, Error, SubsonicResponse}, + utils, +}; + +use crate::utils::db::DbTxn; +use entities::prelude::Artist; +use poem::web::Data; +use sea_orm::EntityTrait; +use tracing::{error, instrument}; + +#[poem::handler] +#[instrument(skip(txn, auth))] +pub async fn get_artists(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { + let u = utils::verify_user(txn.clone(), auth).await; + + match u { + Ok(_) => {} + Err(e) => return e, + } + + let artists = Artist::find().all(&**txn).await; + + let artists = match artists { + Ok(artists) => artists, + Err(e) => { + error!("Failed to get artists: {}", e); + return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(None)); + } + }; + + let artists = artists.into_iter().map(ArtistId3::from).collect(); + + SubsonicResponse::new_artists(artists) +} diff --git a/rave/src/rest/mod.rs b/rave/src/rest/mod.rs index ec11de9..32e486a 100644 --- a/rave/src/rest/mod.rs +++ b/rave/src/rest/mod.rs @@ -20,6 +20,10 @@ mod get_scan_status; mod search3; // rest/getCoverArt mod get_cover_art; +// rest/getArtists +mod get_artists; +// rest/getArtist +mod get_artist; pub fn build() -> Box> { Route::new() @@ -34,5 +38,7 @@ pub fn build() -> Box> { .at("/getScanStatus", get_scan_status::get_scan_status) .at("/search3", search3::search3) .at("/getCoverArt", get_cover_art::get_cover_art) + .at("/getArtists", get_artists::get_artists) + .at("/getArtist", get_artist::get_artist) .boxed() } diff --git a/rave/src/rest/search3.rs b/rave/src/rest/search3.rs index e500d3b..bc64ef2 100644 --- a/rave/src/rest/search3.rs +++ b/rave/src/rest/search3.rs @@ -58,24 +58,22 @@ pub async fn search3( } async fn search_artists(txn: DbTxn, params: &Search3Params) -> Result, Report> { - if params.query.is_empty() { - let artists = Artist::find() + let artists = if params.query.is_empty() { + Artist::find() .limit(params.artist_count) .offset(params.artist_offset) .all(&*txn) - .await?; - - Ok(artists.into_iter().map(Into::into).collect()) + .await? } else { - let artists = Artist::find() + Artist::find() .filter(artist::Column::Name.contains(¶ms.query)) .limit(params.artist_count) .offset(params.artist_offset) .all(&*txn) - .await?; + .await? + }; - Ok(artists.into_iter().map(Into::into).collect()) - } + Ok(artists.into_iter().map(Into::into).collect()) } async fn search_albums(txn: DbTxn, params: &Search3Params) -> Result, Report> { @@ -106,36 +104,73 @@ async fn search_albums(txn: DbTxn, params: &Search3Params) -> Result Result, Report> { - if params.query.is_empty() { - let songs = Track::find() + let tracks_with_albums = if params.query.is_empty() { + Track::find() .limit(params.song_count) .offset(params.song_offset) .find_also_related(Album) .all(&*txn) - .await?; - - Ok(songs - .into_iter() - .filter_map(|(track, album)| { - album.map(|album| ChildId3::new(&track, &AlbumId3::new(album, None, None))) - }) - .collect()) + .await? } else { - let songs = Track::find() + Track::find() .filter(track::Column::Title.contains(¶ms.query)) .limit(params.song_count) .offset(params.song_offset) .find_also_related(Album) .all(&*txn) - .await?; + .await? + }; - Ok(songs + let (tracks, albums): (Vec<_>, Vec<_>) = tracks_with_albums.into_iter().unzip(); + + let albums_with_artists = futures::future::join_all( + albums .into_iter() - .filter_map(|(track, album)| { - album.map(|album| ChildId3::new(&track, &AlbumId3::new(album, None, None))) - }) - .collect()) + .flatten() + .map(|album| utils::find_album_artist(album, txn.clone())), + ) + .await; + + let mut actual_albums = vec![]; + + for album in albums_with_artists { + match album { + Ok((album, artist)) => actual_albums.push((album, artist)), + Err(e) => { + error!("Error getting artist: {e}"); + } + } } + + let tracks_with_artists = futures::future::join_all( + tracks + .into_iter() + .map(|track| utils::find_track_artist(track, txn.clone())), + ) + .await; + + let mut actual_tracks = vec![]; + + for track in tracks_with_artists { + match track { + Ok((track, artist)) => actual_tracks.push((track, artist)), + Err(e) => { + error!("Error getting artist: {e}"); + } + } + } + + Ok(actual_tracks + .into_iter() + .zip(actual_albums) + .map(|((track, artist), (album, album_artist))| { + ChildId3::new( + &track, + &AlbumId3::new(album, Some(album_artist), None), + &ArtistId3::from(artist), + ) + }) + .collect()) } #[derive(Debug, Clone, Deserialize)] diff --git a/rave/src/scan/flac.rs b/rave/src/scan/flac.rs index a315d19..8bc09db 100644 --- a/rave/src/scan/flac.rs +++ b/rave/src/scan/flac.rs @@ -35,9 +35,9 @@ pub async fn handle( FlacTag::from(tag) }; - let artist = find_artist(tx, &tag).await?; + let album_artist = find_album_artist(tx, &tag).await?; - let album = find_album(tx, artist.as_ref().map(|c| c.id), &tag, state.clone()).await?; + let album = find_album(tx, album_artist.as_ref().map(|c| c.id), &tag, state.clone()).await?; if let Some(track) = Track::find() .filter(track::Column::Path.eq(path.to_string_lossy())) @@ -53,9 +53,11 @@ pub async fn handle( let title = tag.title().unwrap_or(&stem).to_string(); let title = title.replace("\0 ", "").replace('\0', ""); // fuck NULLs. + let track_artist = find_track_artist(tx, &tag).await?; + am.title = Set(title); am.album_id = Set(Some(album.id)); - am.artist_id = Set(artist.as_ref().map(|c| c.id)); + am.artist_id = Set(track_artist.as_ref().map(|c| c.id)); am.content_type = Set("audio/flac".to_string()); am.suffix = Set("flac".to_string()); am.path = Set(path.to_string_lossy().to_string()); @@ -208,35 +210,30 @@ async fn find_album( } #[instrument(skip(tx, tag))] -async fn find_artist( +async fn find_album_artist( tx: &DatabaseTransaction, tag: &FlacTag, ) -> Result, Report> { - let artist_to_search = match (tag.album_artist(), tag.artists()) { - (Some(tag_artist), None) => Some(tag_artist.to_string()), - (None, Some(tag_artists)) => { - let tag_artists = tag_artists - .iter() - .flat_map(|artist| artist.split('\0')) - .map(str::trim) - .collect::>(); - Some(tag_artists.join(", ")) - } - (Some(tag_artist), Some(tag_artists)) => { - let mut artists = tag_artists.clone(); - artists.push(tag_artist); + let artist_to_search = crate::utils::format_flac_artist_for_album(tag); - let artists = artists - .iter() - .flat_map(|artist| artist.split('\0')) - .map(str::trim) - .collect::>(); + find_artist(tx, artist_to_search).await +} - Some(artists.join(", ")) - } - _ => None, - }; +#[instrument(skip(tx, tag))] +async fn find_track_artist( + tx: &DatabaseTransaction, + tag: &FlacTag, +) -> Result, Report> { + let artist_to_search = crate::utils::format_flac_artist_for_track(tag); + find_artist(tx, artist_to_search).await +} + +#[instrument(skip(tx))] +async fn find_artist( + tx: &DatabaseTransaction, + artist_to_search: Option, +) -> Result, Report> { match &artist_to_search { Some(artist_to_search) => { let artist_to_search = artist_to_search.trim(); diff --git a/rave/src/scan/mp3.rs b/rave/src/scan/mp3.rs index 5969ccd..dded086 100644 --- a/rave/src/scan/mp3.rs +++ b/rave/src/scan/mp3.rs @@ -35,9 +35,9 @@ pub async fn handle( audiotags::Id3v2Tag::from(tag) }; - let artist = find_artist(tx, &tag).await?; + let album_artist = find_album_artist(tx, &tag).await?; - let album = find_album(tx, artist.as_ref().map(|c| c.id), &tag, state.clone()).await?; + let album = find_album(tx, album_artist.as_ref().map(|c| c.id), &tag, state.clone()).await?; if let Some(track) = Track::find() .filter(track::Column::Path.eq(path.to_string_lossy())) @@ -53,9 +53,11 @@ pub async fn handle( let title = tag.title().unwrap_or(&stem).to_string(); let title = title.replace("\0 ", "").replace('\0', ""); // fuck NULLs. + let track_artist = find_track_artist(tx, &tag).await?; + am.title = Set(title); am.album_id = Set(Some(album.id)); - am.artist_id = Set(artist.as_ref().map(|c| c.id)); + am.artist_id = Set(track_artist.as_ref().map(|c| c.id)); am.content_type = Set("audio/mpeg".to_string()); am.suffix = Set("mp3".to_string()); am.path = Set(path.to_string_lossy().to_string()); @@ -208,35 +210,30 @@ async fn find_album( } #[instrument(skip(tx, tag))] -async fn find_artist( +async fn find_album_artist( tx: &DatabaseTransaction, tag: &Id3v2Tag, ) -> Result, Report> { - let artist_to_search = match (tag.album_artist(), tag.artists()) { - (Some(tag_artist), None) => Some(tag_artist.to_string()), - (None, Some(tag_artists)) => { - let tag_artists = tag_artists - .iter() - .flat_map(|artist| artist.split('\0')) - .map(str::trim) - .collect::>(); - Some(tag_artists.join(", ")) - } - (Some(tag_artist), Some(tag_artists)) => { - let mut artists = tag_artists.clone(); - artists.push(tag_artist); + let artist_to_search = crate::utils::format_id3_artist_for_album(tag); - let artists = artists - .iter() - .flat_map(|artist| artist.split('\0')) - .map(str::trim) - .collect::>(); + find_artist(tx, artist_to_search).await +} - Some(artists.join(", ")) - } - _ => None, - }; +#[instrument(skip(tx, tag))] +async fn find_track_artist( + tx: &DatabaseTransaction, + tag: &Id3v2Tag, +) -> Result, Report> { + let artist_to_search = crate::utils::format_id3_artist_for_track(tag); + find_artist(tx, artist_to_search).await +} + +#[instrument(skip(tx))] +async fn find_artist( + tx: &DatabaseTransaction, + artist_to_search: Option, +) -> Result, Report> { match &artist_to_search { Some(artist_to_search) => { let artist_to_search = artist_to_search.trim(); diff --git a/rave/src/subsonic/mod.rs b/rave/src/subsonic/mod.rs index c76510f..29b978a 100644 --- a/rave/src/subsonic/mod.rs +++ b/rave/src/subsonic/mod.rs @@ -69,6 +69,10 @@ impl SubsonicResponse { }) } + pub fn new_artists(artists: Vec) -> Self { + Self::new(SubResponseType::Artists { artists }) + } + pub fn new_empty() -> Self { Self::new(SubResponseType::Empty) } @@ -83,12 +87,13 @@ impl SubsonicResponse { } pub fn new_scan_status(scanning: bool, count: u64) -> Self { - Self { - xmlns: "http://subsonic.org/restapi".to_string(), - status: ResponseStatus::Ok, - version: VersionTriple(1, 16, 1), - value: Box::new(SubResponseType::ScanStatus { scanning, count }), - } + Self::new(SubResponseType::ScanStatus { scanning, count }) + } + + pub fn new_artist(artist: Artist) -> Self { + Self::new(SubResponseType::Artist { + artist: Box::new(artist), + }) } } @@ -139,6 +144,16 @@ pub enum SubResponseType { #[serde(rename = "song")] songs: Vec, }, + #[serde(rename = "artists")] + Artists { + #[serde(rename = "artist")] + artists: Vec, + }, + #[serde(rename = "artist")] + Artist { + #[serde(flatten)] + artist: Box, + }, Empty, } diff --git a/rave/src/subsonic/types/child.rs b/rave/src/subsonic/types/child.rs index 7ec482c..2f4e509 100644 --- a/rave/src/subsonic/types/child.rs +++ b/rave/src/subsonic/types/child.rs @@ -2,7 +2,7 @@ use entities::track; use serde::Serialize; use time::format_description::well_known::Iso8601; -use crate::subsonic::Album; +use crate::subsonic::{Album, Artist}; #[derive(Debug, Clone, Serialize)] pub struct Child { @@ -44,6 +44,8 @@ pub struct Child { pub album_id: Option, #[serde(rename = "@artistId", skip_serializing_if = "Option::is_none")] pub artist_id: Option, + #[serde(rename = "@artist")] + pub artist: String, #[serde(rename = "@type")] pub child_type: String, #[serde(rename = "@isVideo")] @@ -53,7 +55,7 @@ pub struct Child { impl Child { #[allow(clippy::cast_sign_loss)] #[must_use] - pub fn new(track: &track::Model, album: &Album) -> Self { + pub fn new(track: &track::Model, album: &Album, artist: &Artist) -> Self { Self { id: format!("tr-{}", track.id), is_dir: false, @@ -78,7 +80,8 @@ impl Child { .format(&Iso8601::DEFAULT) .expect("Failed to format date"), album_id: Some(format!("al-{}", album.id)), - artist_id: album.artist_id.clone(), + artist_id: track.artist_id.map(|v| format!("ar-{v}",)), + artist: artist.name.clone(), child_type: "music".to_string(), is_video: false, } diff --git a/rave/src/utils.rs b/rave/src/utils.rs index 5fee22f..1d41863 100644 --- a/rave/src/utils.rs +++ b/rave/src/utils.rs @@ -1,6 +1,11 @@ -use entities::{prelude::User, user}; -use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; -use tracing::error; +use audiotags::{AudioTagEdit, FlacTag, Id3v2Tag}; +use entities::{ + album, artist, + prelude::{Artist, User}, + track, user, +}; +use sea_orm::{ColumnTrait, EntityTrait, ModelTrait, QueryFilter}; +use tracing::{debug, error}; use crate::{ authentication::Authentication, @@ -47,3 +52,142 @@ pub async fn verify_user( } } } + +pub async fn find_track_artist( + track: track::Model, + txn: DbTxn, +) -> Result<(track::Model, artist::Model), Error> { + let artist = track.find_related(Artist).one(&*txn).await; + + match artist { + Ok(Some(artist)) => Ok((track, artist)), + Ok(None) => Err(Error::RequestedDataWasNotFound(None)), + Err(e) => { + error!( + error = &e as &dyn std::error::Error, + "Error getting artist: {e}" + ); + Err(Error::Generic(None)) + } + } +} + +pub async fn find_album_artist( + album: album::Model, + txn: DbTxn, +) -> Result<(album::Model, artist::Model), Error> { + let artist = album.find_related(Artist).one(&*txn).await; + + match artist { + Ok(Some(artist)) => Ok((album, artist)), + Ok(None) => Err(Error::RequestedDataWasNotFound(None)), + Err(e) => { + error!( + error = &e as &dyn std::error::Error, + "Error getting artist: {e}" + ); + Err(Error::Generic(None)) + } + } +} + +pub fn format_id3_artist_for_album(tag: &Id3v2Tag) -> Option { + let album_artist = tag.album_artist(); + let album_artists = tag.album_artists(); + + if (album_artist, &album_artists) == (None, &None) { + format_artist(tag.artist(), tag.artists(), album_artist, album_artists) + } else { + format_artist(None, None, album_artist, album_artists) + } +} + +pub fn format_id3_artist_for_track(tag: &Id3v2Tag) -> Option { + let artist = tag.artist(); + let artists = tag.artists(); + let album_artist = tag.album_artist(); + let album_artists = tag.album_artists(); + + format_artist(artist, artists, album_artist, album_artists) +} + +pub fn format_flac_artist_for_album(tag: &FlacTag) -> Option { + let album_artist = tag.album_artist(); + let album_artists = tag.album_artists(); + + if (album_artist, &album_artists) == (None, &None) { + format_artist(tag.artist(), tag.artists(), album_artist, album_artists) + } else { + format_artist(None, None, album_artist, album_artists) + } +} + +pub fn format_flac_artist_for_track(tag: &FlacTag) -> Option { + let artist = tag.artist(); + let artists = tag.artists(); + let album_artist = tag.album_artist(); + let album_artists = tag.album_artists(); + + format_artist(artist, artists, album_artist, album_artists) +} + +fn format_artist( + artist: Option<&str>, + artists: Option>, + album_artist: Option<&str>, + album_artists: Option>, +) -> Option { + let mut actual_artists = vec![]; + + if let Some(artist) = artist { + actual_artists.push((1, artist)); // Prioritize track artist over album artist + } + + if let Some(artists) = artists { + actual_artists.extend(artists.into_iter().map(|v| (3, v))); // Prioritize track artists over album artists + } + + if let Some(album_artist) = album_artist { + actual_artists.push((2, album_artist)); // Prioritize album artist over album artists + } + + if let Some(album_artists) = album_artists { + actual_artists.extend(album_artists.into_iter().map(|v| (4, v))); // Prioritize album artists over track artists + } + + let mut actual_artists = actual_artists + .into_iter() + .flat_map(|(prio, v)| { + if v.contains(SEPARATORS) { + v.split(SEPARATORS) + .map(|v| (prio, v)) + .collect::>() + .into_iter() + } else { + vec![(prio, v)].into_iter() + } + }) + .map(|(prio, v)| (prio, v.trim())) + .filter(|(_, v)| !v.is_empty()) + .collect::>(); + + actual_artists.sort_unstable_by_key(|v| v.0); // Sort by priority first + actual_artists.sort_by_key(|v| v.1); // Then by name within each priority, hopefully + + let mut actual_artists = actual_artists + .into_iter() + .map(|(_, v)| v) + .collect::>(); + + actual_artists.dedup(); + + debug!("Actual artists: {:?}", actual_artists); + + if actual_artists.is_empty() { + None + } else { + Some(actual_artists.join(", ")) + } +} + +const SEPARATORS: &[char] = &[';', '/', '\\', ',', '\0', '&'];