diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index c5c9d79d..6d967fdf 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -33,7 +33,6 @@ assignees: '' **Compilation flags** - [ ] dbus_mpris -- [ ] dbus_keyring - [x] alsa_backend - [ ] portaudio_backend - [ ] pulseaudio_backend diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 787cce72..a87b7d6d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -18,9 +18,9 @@ jobs: - artifact_type: 'slim' # Slim version has no features enabled by default. feature: '' - artifact_type: 'default' - feature: 'dbus_keyring,dbus_mpris' # Default version has all extra features enabled + feature: 'dbus_mpris' # Default version has all extra features enabled - artifact_type: 'full' - feature: 'dbus_keyring,dbus_mpris' # Full version has all extra features and audio backends enabled + feature: 'dbus_mpris' # Full version has all extra features and audio backends enabled - build_target: macos os: macos-latest artifact_prefix: macos diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a9ce99a..4b089568 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,13 +55,13 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - rust: [stable, 1.67] + rust: [stable, 1.81] os: [macos-latest, ubuntu-latest] include: - os: macos-latest - features: portaudio_backend,rodio_backend,dbus_keyring + features: portaudio_backend,rodio_backend - os: ubuntu-latest - features: alsa_backend,rodio_backend,dbus_keyring,dbus_mpris + features: alsa_backend,rodio_backend,dbus_mpris steps: - name: Installing Rust toolchain diff --git a/Cargo.lock b/Cargo.lock index c7533d3a..7310f5d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,90 +19,34 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", -] - -[[package]] -name = "aes" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher 0.3.0", + "cipher", "cpufeatures", - "opaque-debug", -] - -[[package]] -name = "aes-ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763" -dependencies = [ - "aes-soft", - "aesni", - "cipher 0.2.5", - "ctr", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher 0.2.5", - "opaque-debug", ] [[package]] name = "aho-corasick" -version = "1.0.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "alsa" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" -dependencies = [ - "alsa-sys", - "bitflags 1.3.2", - "libc", - "nix 0.23.2", -] - -[[package]] -name = "alsa" -version = "0.7.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" dependencies = [ "alsa-sys", - "bitflags 1.3.2", + "bitflags 2.6.0", + "cfg-if", "libc", - "nix 0.24.3", ] [[package]] @@ -131,164 +75,120 @@ dependencies = [ ] [[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "async-broadcast" -version = "0.5.1" +name = "anstream" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ - "event-listener", - "futures-core", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "async-channel" -version = "1.9.0" +name = "anstyle" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] -name = "async-executor" -version = "1.5.1" +name = "anstyle-parse" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ - "async-lock", - "async-task", - "concurrent-queue", - "fastrand 1.9.0", - "futures-lite", - "slab", + "utf8parse", ] [[package]] -name = "async-fs" -version = "1.6.0" +name = "anstyle-query" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "async-lock", - "autocfg", - "blocking", - "futures-lite", + "windows-sys 0.59.0", ] [[package]] -name = "async-io" -version = "1.13.0" +name = "anstyle-wincon" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ - "async-lock", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite", - "log", - "parking", - "polling", - "rustix 0.37.26", - "slab", - "socket2 0.4.9", - "waker-fn", + "anstyle", + "windows-sys 0.59.0", ] [[package]] -name = "async-lock" -version = "2.8.0" +name = "anyhow" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener", -] +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] -name = "async-process" -version = "1.7.0" +name = "arrayvec" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" -dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix 0.37.26", - "signal-hook", - "windows-sys", -] +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] -name = "async-recursion" -version = "1.0.4" +name = "async-trait" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] -name = "async-task" -version = "4.4.0" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] -name = "async-trait" -version = "0.1.73" +name = "autocfg" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] -name = "atomic-waker" -version = "1.1.1" +name = "aws-lc-rs" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" +checksum = "f409eb70b561706bf8abba8ca9c112729c481595893fd06a2dd9af8ed8441148" +dependencies = [ + "aws-lc-sys", + "paste", + "zeroize", +] [[package]] -name = "atty" -version = "0.2.14" +name = "aws-lc-sys" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "8478a5c29ead3f3be14aff8a202ad965cf7da6856860041bfca271becf8ba48b" dependencies = [ - "hermit-abi 0.1.19", + "bindgen 0.69.5", + "cc", + "cmake", + "dunce", + "fs_extra", "libc", - "winapi", + "paste", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -307,50 +207,74 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.3" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bindgen" -version = "0.64.0" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "cexpr", "clang-sys", + "itertools 0.12.1", "lazy_static", "lazycell", - "peeking_take_while", + "log", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 1.0.109", + "syn 2.0.93", + "which", ] [[package]] -name = "bitflags" -version = "1.3.2" +name = "bindgen" +version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.93", +] [[package]] name = "bitflags" -version = "2.4.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "block-buffer" -version = "0.9.0" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -362,62 +286,38 @@ dependencies = [ ] [[package]] -name = "block-modes" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" -dependencies = [ - "block-padding", - "cipher 0.3.0", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "blocking" -version = "1.3.1" +name = "bumpalo" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand 1.9.0", - "futures-lite", - "log", -] +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "bumpalo" -version = "3.13.0" +name = "bytemuck" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" -version = "1.0.83" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -443,69 +343,94 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.28" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "cipher" -version = "0.2.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] -name = "cipher" -version = "0.3.0" +name = "clang-sys" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ - "generic-array", + "glob", + "libc", + "libloading 0.8.6", ] [[package]] -name = "clang-sys" -version = "1.6.1" +name = "clap" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ - "glob", - "libc", - "libloading 0.7.4", + "clap_builder", + "clap_derive", ] [[package]] -name = "clap" -version = "2.34.0" +name = "clap_builder" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "cmake" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +dependencies = [ + "cc", ] [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -518,9 +443,9 @@ dependencies = [ [[package]] name = "color-spantrace" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" dependencies = [ "once_cell", "owo-colors", @@ -529,57 +454,42 @@ dependencies = [ ] [[package]] -name = "combine" -version = "4.6.6" +name = "colorchoice" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] -name = "concurrent-queue" -version = "2.2.0" +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "crossbeam-utils", + "bytes", + "memchr", ] [[package]] -name = "cookie" -version = "0.17.0" +name = "const-oid" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" -dependencies = [ - "percent-encoding", - "time 0.3.36", - "version_check", -] +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "cookie_store" -version = "0.20.0" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "cookie", - "idna 0.3.0", - "indexmap 1.9.3", - "log", - "serde", - "serde_derive", - "serde_json", - "time 0.3.36", - "url", + "core-foundation-sys", + "libc", ] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -587,73 +497,63 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "coreaudio-rs" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" dependencies = [ "bitflags 1.3.2", + "core-foundation-sys", "coreaudio-sys", ] [[package]] name = "coreaudio-sys" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24" +checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b" dependencies = [ - "bindgen", + "bindgen 0.70.1", ] [[package]] name = "cpal" -version = "0.13.5" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" dependencies = [ - "alsa 0.6.0", + "alsa", "core-foundation-sys", "coreaudio-rs", + "dasp_sample", "jack", "jni", "js-sys", - "lazy_static", "libc", - "mach", + "mach2", "ndk", - "ndk-glue", - "nix 0.23.2", + "ndk-context", "oboe", - "parking_lot 0.11.2", - "stdweb", - "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", - "winapi", + "windows 0.54.0", ] [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -664,23 +564,13 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ctr" -version = "0.6.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.2.5", + "cipher", ] [[package]] @@ -694,9 +584,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -704,29 +594,41 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 1.0.109", + "strsim", + "syn 2.0.93", ] [[package]] name = "darling_macro" -version = "0.13.4" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 2.0.93", ] +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "dbus" version = "0.9.7" @@ -760,6 +662,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -770,24 +683,35 @@ dependencies = [ ] [[package]] -name = "derivative" -version = "2.2.0" +name = "derive_builder" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "derive_builder_macro", ] [[package]] -name = "digest" -version = "0.9.0" +name = "derive_builder_core" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "generic-array", -] + "darling", + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.93", +] [[package]] name = "digest" @@ -795,7 +719,8 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -818,53 +743,62 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] -name = "enum_dispatch" -version = "0.3.12" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] -name = "enumflags2" -version = "0.7.7" +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "enumflags2_derive", - "serde", + "cfg-if", ] [[package]] -name = "enumflags2_derive" -version = "0.7.7" +name = "env_filter" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", + "log", + "regex", ] [[package]] name = "env_logger" -version = "0.10.0" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ + "anstream", + "anstyle", + "env_filter", "humantime", - "is-terminal", "log", - "regex", - "termcolor", ] [[package]] @@ -875,23 +809,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ - "cc", "libc", + "windows-sys 0.59.0", ] [[package]] @@ -903,17 +826,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -921,24 +838,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fastrand" -version = "2.0.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fern" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" dependencies = [ "log", "syslog", @@ -958,18 +866,24 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -982,9 +896,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -992,15 +906,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1009,53 +923,44 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-lite" -version = "1.13.0" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1081,60 +986,111 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30" dependencies = [ - "libc", - "windows-targets", + "rustix", + "windows-targets 0.52.6", ] [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", + "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] -name = "hashbrown" -version = "0.12.3" +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "rand", + "smallvec", + "spinning_top", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.2.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "headers" -version = "0.3.8" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64 0.21.7", "bytes", "headers-core", - "http", + "http 1.2.0", "httpdate", "mime", "sha1", @@ -1142,115 +1098,126 @@ dependencies = [ [[package]] name = "headers-core" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.2.0", ] [[package]] name = "heck" -version = "0.3.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "heck" -version = "0.4.1" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "libc", + "digest", ] [[package]] -name = "hermit-abi" -version = "0.3.2" +name = "home" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] [[package]] -name = "hex" -version = "0.4.3" +name = "hostname" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] [[package]] -name = "hkdf" -version = "0.12.3" +name = "hostname" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" dependencies = [ - "hmac 0.12.1", + "cfg-if", + "libc", + "windows 0.52.0", ] [[package]] -name = "hmac" -version = "0.11.0" +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "crypto-mac", - "digest 0.9.0", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "hmac" -version = "0.12.1" +name = "http" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ - "digest 0.10.7", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "hostname" -version = "0.3.1" +name = "http-body" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "libc", - "match_cfg", - "winapi", + "bytes", + "http 0.2.12", + "pin-project-lite", ] [[package]] -name = "http" -version = "0.2.9" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "fnv", - "itoa", + "http 1.2.0", ] [[package]] -name = "http-body" -version = "0.4.5" +name = "http-body-util" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "http", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1266,21 +1233,22 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "http", - "http-body", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -1288,32 +1256,130 @@ dependencies = [ ] [[package]] -name = "hyper-proxy" -version = "0.9.1" +name = "hyper" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", - "futures", + "futures-channel", + "futures-util", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-proxy2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9043b7b23fb0bc4a1c7014c27b50a4fc42cc76206f71d34fc0dfe5b28ddc3faf" +dependencies = [ + "bytes", + "futures-util", "headers", - "http", - "hyper", + "http 1.2.0", + "hyper 1.5.2", + "hyper-rustls 0.26.0", + "hyper-util", + "pin-project-lite", + "rustls-native-certs 0.7.3", + "tokio", + "tokio-rustls 0.25.0", + "tower-service", + "webpki", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", + "log", + "rustls 0.22.4", + "rustls-native-certs 0.7.3", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tower-service", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", + "log", + "rustls 0.23.20", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.1", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "socket2", "tokio", "tower-service", + "tracing", ] [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core 0.52.0", ] [[package]] @@ -1326,39 +1392,158 @@ dependencies = [ ] [[package]] -name = "ident_case" -version = "1.0.1" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "idna" -version = "0.3.0" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "idna" -version = "0.4.0" +name = "icu_locid_transform" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] name = "if-addrs" -version = "0.7.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +checksum = "bb2a33e9c38988ecbda730c85b0fd9ddcdf83c0305ac7fd21c8bb9f57f2f0cc8" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1369,66 +1554,64 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.3" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ - "autocfg", - "hashbrown 0.12.3", + "equivalent", + "hashbrown", ] [[package]] -name = "indexmap" -version = "2.0.0" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "equivalent", - "hashbrown 0.14.0", + "generic-array", ] [[package]] -name = "instant" -version = "0.1.12" +name = "ipnet" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ - "hermit-abi 0.3.2", - "libc", - "windows-sys", + "either", ] [[package]] -name = "is-terminal" -version = "0.4.9" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "hermit-abi 0.3.2", - "rustix 0.38.10", - "windows-sys", + "either", ] [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jack" -version = "0.8.4" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2ac12f11bb369f3c50d24dbb9fdb00dc987434c9dd622a12c13f618106e153" +checksum = "0e5a18a3c2aefb354fb77111ade228b20267bdc779de84e7a4ccf7ea96b9a6cd" dependencies = [ "bitflags 1.3.2", "jack-sys", @@ -1439,28 +1622,32 @@ dependencies = [ [[package]] name = "jack-sys" -version = "0.2.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b91f2d2d10bc2bab38f4dfa4bc77123a988828af39dd3f30dd9db14d44f2cc1" +checksum = "6013b7619b95a22b576dfb43296faa4ecbe40abbdb97dfd22ead520775fc86ab" dependencies = [ + "bitflags 1.3.2", "lazy_static", "libc", - "libloading 0.6.7", + "libloading 0.7.4", + "log", "pkg-config", ] [[package]] name = "jni" -version = "0.19.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -1471,64 +1658,43 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] -name = "keyring" -version = "2.3.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52a1d320b55eacc02d4561fed9714af4e98b7989cf4e696bee192b03fc99720" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "byteorder", - "lazy_static", - "linux-keyutils", - "secret-service", - "security-framework", - "winapi", + "spin", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "lewton" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" -dependencies = [ - "byteorder", - "ogg", - "tinyvec", -] - [[package]] name = "libc" -version = "0.2.153" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libdbus-sys" @@ -1541,9 +1707,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.6.7" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", @@ -1551,50 +1717,48 @@ dependencies = [ [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "winapi", + "windows-targets 0.52.6", ] [[package]] name = "libm" -version = "0.2.7" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libmdns" -version = "0.7.5" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b04ae6b56b3b19ade26f0e7e7c1360a1713514f326c5ed0797cf2c109c9e010" +checksum = "48854699e11b111433431b69cee2365fcab0b29b06993f48c257dfbaf6395862" dependencies = [ "byteorder", "futures-util", - "hostname", + "hostname 0.4.0", "if-addrs", "log", "multimap", - "nix 0.23.2", "rand", - "socket2 0.4.9", - "thiserror", + "socket2", + "thiserror 1.0.69", "tokio", - "winapi", ] [[package]] name = "libpulse-binding" -version = "2.28.1" +version = "2.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3557a2dfc380c8f061189a01c6ae7348354e0c9886038dc6c171219c08eaff" +checksum = "b6b1040a6c4c4d1e9e852000f6202df1a02a4f074320de336ab21e4fd317b538" dependencies = [ "bitflags 1.3.2", "libc", "libpulse-sys", - "num-derive", + "num-derive 0.3.3", "num-traits", "winapi", ] @@ -1627,38 +1791,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc19e110fbf42c17260d30f6d3dc545f58491c7830d38ecb9aaca96e26067a9b" dependencies = [ "libc", - "num-derive", + "num-derive 0.3.3", "num-traits", "pkg-config", "winapi", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "librespot-audio" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c176a31355e1ea8e0b9c4ced19df4947bfe4770661c25c142b6fba2365940d9d" +checksum = "7e07566fe7553042936c61bbdd9bedb524114a904aa7f9738e266c641468bab8" dependencies = [ - "aes-ctr", - "byteorder", + "aes", "bytes", + "ctr", "futures-util", + "http-body-util", + "hyper 1.5.2", + "hyper-util", "librespot-core", "log", + "parking_lot", "tempfile", + "thiserror 1.0.69", "tokio", ] [[package]] name = "librespot-connect" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffafb6a443e9445ccb3d5d591573b5b1da3c89a9b8846c63ba2c3710210d3ec" +checksum = "67ce3a5634669ce051a425cff5437a474831111df045fd5a2bb37fabe316fa60" dependencies = [ "form_urlencoded", "futures-util", "librespot-core", - "librespot-discovery", "librespot-playback", "librespot-protocol", "log", @@ -1666,155 +1844,179 @@ dependencies = [ "rand", "serde", "serde_json", + "thiserror 1.0.69", "tokio", "tokio-stream", ] [[package]] name = "librespot-core" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046349f25888e644bf02d9c5de0164b2a493d29aa4ce18e1ad0b756da9b55d6d" +checksum = "c4c76303efcf949a59f9380220ca420c4d72fa32dbb3641a0079f429cc5e44e7" dependencies = [ - "aes 0.6.0", - "base64 0.13.1", + "aes", + "base64 0.22.1", "byteorder", "bytes", + "data-encoding", "form_urlencoded", "futures-core", "futures-util", - "hmac 0.11.0", - "http", + "governor", + "hmac", + "http 1.2.0", + "http-body-util", "httparse", - "hyper", - "hyper-proxy", + "hyper 1.5.2", + "hyper-proxy2", + "hyper-rustls 0.27.5", + "hyper-util", + "librespot-oauth", "librespot-protocol", "log", + "nonzero_ext", "num-bigint", + "num-derive 0.4.2", "num-integer", "num-traits", "once_cell", + "parking_lot", "pbkdf2", + "pin-project-lite", "priority-queue", "protobuf", + "quick-xml", "rand", + "rsa", "serde", "serde_json", - "sha-1 0.9.8", + "sha1", "shannon", - "thiserror", + "sysinfo", + "thiserror 1.0.69", + "time", "tokio", "tokio-stream", + "tokio-tungstenite", "tokio-util", "url", "uuid", - "vergen", + "vergen-gitcl", ] [[package]] name = "librespot-discovery" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aa877d18f6150364012cb4be5682d62d7c712c88bae2d0d01720fd7c15e2f06" +checksum = "94a952d28c775562b9b6039102c0e3a834cb31157fccb2f2a68a26dc4397fea8" dependencies = [ - "aes-ctr", - "base64 0.13.1", + "aes", + "base64 0.22.1", + "bytes", + "ctr", "form_urlencoded", "futures-core", - "hmac 0.11.0", - "hyper", + "futures-util", + "hmac", + "http-body-util", + "hyper 1.5.2", + "hyper-util", "libmdns", "librespot-core", "log", "rand", "serde_json", - "sha-1 0.9.8", - "thiserror", + "serde_repr", + "sha1", + "thiserror 1.0.69", "tokio", ] [[package]] name = "librespot-metadata" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b80361fcbcb5092056fd47c08c34d5d51b08385d8efb6941c0d3e46d032c21c" +checksum = "7cf6d5c46a401b1dd3e062ebdce959aa694bbae1039841756545d2e9c4f8be5f" dependencies = [ "async-trait", - "byteorder", + "bytes", "librespot-core", "librespot-protocol", "log", "protobuf", + "serde", + "serde_json", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "librespot-oauth" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dcad779aa6c3b442e493e2a40ca83a5c5fcf38a65c05b026c3587bd4f8d14f" +dependencies = [ + "log", + "oauth2", + "thiserror 1.0.69", + "url", ] [[package]] name = "librespot-playback" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5190a0b9bcc7f70ee4196a6b4a1c731d405ca130d4a6fcd4c561cfdde8b7cfb7" +checksum = "4ed1f776a04c8c9275407f8d4df034f2615ea729ec6c6d0fa69f423fc571df64" dependencies = [ - "alsa 0.6.0", - "byteorder", + "alsa", "cpal", - "futures-executor", "futures-util", - "lewton", "libpulse-binding", "libpulse-simple-binding", "librespot-audio", "librespot-core", "librespot-metadata", "log", - "ogg", - "parking_lot 0.12.1", + "parking_lot", "portaudio-rs", "rand", "rand_distr", "rodio", "shell-words", - "thiserror", + "symphonia", + "thiserror 1.0.69", "tokio", "zerocopy", ] [[package]] name = "librespot-protocol" -version = "0.4.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d3ac6196ac0ea67bbe039f56d6730a5d8b31502ef9bce0f504ed729dcb39f" +checksum = "80802f52b5a1b3a2157454e6aca483a627fbf7b942e0a5fea037ebf3ed8b0546" dependencies = [ - "glob", "protobuf", - "protobuf-codegen-pure", -] - -[[package]] -name = "linux-keyutils" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f27bb67f6dd1d0bb5ab582868e4f65052e58da6401188a08f0da09cf512b84b" -dependencies = [ - "bitflags 1.3.2", - "libc", + "protobuf-codegen", ] [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] -name = "linux-raw-sys" -version = "0.4.5" +name = "litemap" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1822,15 +2024,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "mach" -version = "0.3.2" +name = "mach2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" dependencies = [ "libc", ] @@ -1841,40 +2043,11 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "maybe-async" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "memchr" -version = "2.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e" - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -1890,44 +2063,45 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "wasi", + "windows-sys 0.52.0", ] [[package]] name = "multimap" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" dependencies = [ "serde", ] [[package]] name = "ndk" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "jni-sys", + "log", "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1937,121 +2111,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] -name = "ndk-glue" -version = "0.6.2" +name = "ndk-sys" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-macro", - "ndk-sys", + "jni-sys", ] [[package]] -name = "ndk-macro" -version = "0.3.0" +name = "no-std-compat" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" [[package]] -name = "ndk-sys" -version = "0.3.0" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "jni-sys", + "memchr", + "minimal-lexical", ] [[package]] -name = "nix" -version = "0.23.2" +name = "nonzero_ext" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" -dependencies = [ - "bitflags 1.3.2", - "cc", - "cfg-if", - "libc", - "memoffset 0.6.5", -] +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" [[package]] -name = "nix" -version = "0.24.3" +name = "ntapi" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", + "winapi", ] [[package]] -name = "nix" -version = "0.26.4" +name = "num-bigint" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", + "num-integer", + "num-traits", + "rand", ] [[package]] -name = "nom" -version = "7.1.3" +name = "num-bigint-dig" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" -dependencies = [ - "num-bigint", - "num-complex", + "byteorder", + "lazy_static", + "libm", "num-integer", "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", "num-traits", "rand", -] - -[[package]] -name = "num-complex" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" -dependencies = [ - "num-traits", + "smallvec", + "zeroize", ] [[package]] @@ -2072,140 +2196,139 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "num-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "autocfg", - "num-traits", + "proc-macro2", + "quote", + "syn 2.0.93", ] [[package]] -name = "num-iter" -version = "0.1.43" +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", - "num-integer", "num-traits", ] [[package]] -name = "num-rational" -version = "0.4.1" +name = "num-iter" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", - "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.93", ] [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] +[[package]] +name = "oauth2" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" +dependencies = [ + "base64 0.13.1", + "chrono", + "getrandom", + "http 0.2.12", + "rand", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.69", + "url", +] + [[package]] name = "object" -version = "0.32.0" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "oboe" -version = "0.4.6" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" +checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" dependencies = [ "jni", "ndk", "ndk-context", - "num-derive", + "num-derive 0.4.2", "num-traits", "oboe-sys", ] [[package]] name = "oboe-sys" -version = "0.4.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd" +checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" dependencies = [ "cc", ] -[[package]] -name = "ogg" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" -dependencies = [ - "byteorder", -] - [[package]] name = "once_cell" -version = "1.18.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "openssl-probe" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "option-ext" @@ -2213,116 +2336,84 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "owo-colors" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "backtrace", "cfg-if", "libc", "petgraph", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", "thread-id", - "windows-targets", + "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "crypto-mac", - "hmac 0.11.0", + "digest", + "hmac", ] [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "pem-rfc7468" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.0.0", + "indexmap", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2330,11 +2421,32 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "pledge" @@ -2346,20 +2458,10 @@ dependencies = [ ] [[package]] -name = "polling" -version = "2.8.0" +name = "portable-atomic" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys", -] +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "portaudio-rs" @@ -2390,93 +2492,118 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.93", +] [[package]] name = "priority-queue" -version = "1.3.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff39edfcaec0d64e8d0da38564fad195d2d51b680940295fcc307366e101e61" +checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d" dependencies = [ "autocfg", - "indexmap 1.9.3", + "equivalent", + "indexmap", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "once_cell", "toml_edit", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro2" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", + "unicode-ident", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "protobuf" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "once_cell", + "protobuf-support", + "thiserror 1.0.69", ] [[package]] -name = "proc-macro2" -version = "1.0.66" +name = "protobuf-codegen" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "e26b833f144769a30e04b1db0146b2aaa53fd2fd83acf10a6b5f996606c18144" dependencies = [ - "unicode-ident", + "anyhow", + "once_cell", + "protobuf", + "protobuf-parse", + "regex", + "tempfile", + "thiserror 1.0.69", ] [[package]] -name = "protobuf" -version = "2.28.0" +name = "protobuf-parse" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +checksum = "322330e133eab455718444b4e033ebfac7c6528972c784fcde28d2cc783c6257" +dependencies = [ + "anyhow", + "indexmap", + "log", + "protobuf", + "protobuf-support", + "tempfile", + "thiserror 1.0.69", + "which", +] [[package]] -name = "protobuf-codegen" -version = "2.28.0" +name = "protobuf-support" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" +checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" dependencies = [ - "protobuf", + "thiserror 1.0.69", ] [[package]] -name = "protobuf-codegen-pure" -version = "2.28.0" +name = "quick-xml" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a29399fc94bcd3eeaa951c715f7bea69409b2445356b00519740bcd6ddd865" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ - "protobuf", - "protobuf-codegen", + "memchr", + "serde", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2523,47 +2650,29 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", - "redox_syscall 0.2.16", - "thiserror", + "libredox", + "thiserror 1.0.69", ] [[package]] name = "regex" -version = "1.9.4" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2573,9 +2682,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2584,147 +2693,211 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] [[package]] name = "ring" -version = "0.17.3" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rodio" -version = "0.15.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0939e9f626e6c6f1989adb6226a039c855ca483053f0ee7c98b90e41cf731e" +checksum = "6006a627c1a38d37f3d3a85c6575418cfe34a5392d60a686d0071e1c8d427acb" dependencies = [ "cpal", + "thiserror 1.0.69", ] [[package]] -name = "rspotify" -version = "0.12.0" +name = "rsa" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c6f1d86b10201655f0cd4002088bafe4abcc75cc610613c995abd719f40fcb" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ - "base64 0.21.3", - "chrono", - "getrandom", - "log", - "maybe-async", - "rspotify-http", - "rspotify-macros", - "rspotify-model", - "serde", - "serde_json", - "sha2", - "thiserror", - "url", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", ] [[package]] -name = "rspotify-http" -version = "0.12.0" +name = "rustc-demangle" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad45cd393a8685ee36ec6d2accbb2c955e21ac036a2e4eb175985783f30ed78" -dependencies = [ - "log", - "maybe-async", - "serde_json", - "thiserror", - "ureq", -] +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "rspotify-macros" -version = "0.12.0" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4892882851a97ee7210e423725ce116e8239157c649af37e208fe93855638a" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rspotify-model" -version = "0.12.0" +name = "rustix" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae90ab3d6e4cb4ccd7f2887c4363d19b1419800e132d3fb95e2f9b24c05f4d7" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "chrono", - "enum_dispatch", - "serde", - "serde_json", - "strum", - "thiserror", + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", ] [[package]] -name = "rustc-demangle" -version = "0.1.23" +name = "rustls" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "rustls" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ - "semver", + "openssl-probe", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework 2.11.1", ] [[package]] -name = "rustix" -version = "0.37.26" +name = "rustls-native-certs" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f3f8f960ed3b5a59055428714943298bf3fa2d4a1d53135084e0544829d995" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys", + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.1.0", ] [[package]] -name = "rustix" -version = "0.38.10" +name = "rustls-pemfile" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys 0.4.5", - "windows-sys", + "base64 0.21.7", ] [[package]] -name = "rustls" -version = "0.21.11" +name = "rustls-pemfile" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2735,17 +2908,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -2756,6 +2941,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2773,32 +2967,26 @@ dependencies = [ ] [[package]] -name = "secret-service" -version = "3.0.1" +name = "security-framework" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "aes 0.7.5", - "block-modes", - "futures-util", - "generic-array", - "hkdf", - "num", - "once_cell", - "rand", - "serde", - "sha2", - "zbus", + "bitflags 2.6.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] name = "security-framework" -version = "2.9.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc" dependencies = [ - "bitflags 1.3.2", - "core-foundation", + "bitflags 2.6.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -2806,82 +2994,86 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", ] -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - [[package]] name = "serde" -version = "1.0.188" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] -name = "serde_repr" +name = "serde_path_to_error" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] [[package]] -name = "sha-1" -version = "0.9.8" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] @@ -2892,29 +3084,29 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -2928,9 +3120,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -2948,22 +3140,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "signal-hook" -version = "0.3.17" +name = "signal-hook-registry" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", - "signal-hook-registry", ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "signature" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "libc", + "digest", + "rand_core", ] [[package]] @@ -2977,42 +3169,52 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.9" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] -name = "socket2" -version = "0.5.3" +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spinning_top" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" dependencies = [ - "libc", - "windows-sys", + "lock_api", ] [[package]] -name = "spin" -version = "0.9.8" +name = "spki" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] [[package]] name = "spotifyd" version = "0.3.5" dependencies = [ - "alsa 0.7.1", + "alsa", "chrono", + "clap", "color-eyre", "daemonize", "dbus", @@ -3024,20 +3226,22 @@ dependencies = [ "futures", "gethostname", "hex", - "keyring", "libc", "librespot-audio", "librespot-connect", "librespot-core", "librespot-discovery", + "librespot-metadata", + "librespot-oauth", "librespot-playback", + "librespot-protocol", "log", "pledge", - "rspotify", "serde", - "sha-1 0.10.1", - "structopt", + "sha-1", "syslog", + "thiserror 2.0.9", + "time", "tokio", "tokio-stream", "toml", @@ -3046,80 +3250,106 @@ dependencies = [ ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "stdweb" -version = "0.1.3" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "strsim" -version = "0.8.0" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "strsim" -version = "0.10.0" +name = "symphonia" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9" +dependencies = [ + "lazy_static", + "symphonia-bundle-mp3", + "symphonia-codec-vorbis", + "symphonia-core", + "symphonia-format-ogg", + "symphonia-metadata", +] [[package]] -name = "structopt" -version = "0.3.26" +name = "symphonia-bundle-mp3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4" dependencies = [ - "clap", "lazy_static", - "structopt-derive", + "log", + "symphonia-core", + "symphonia-metadata", ] [[package]] -name = "structopt-derive" -version = "0.4.18" +name = "symphonia-codec-vorbis" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +checksum = "5a98765fb46a0a6732b007f7e2870c2129b6f78d87db7987e6533c8f164a9f30" dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", + "log", + "symphonia-core", + "symphonia-utils-xiph", ] [[package]] -name = "strum" -version = "0.25.0" +name = "symphonia-core" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-format-ogg" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "ada3505789516bcf00fc1157c67729eded428b455c27ca370e41f4d785bfa931" dependencies = [ - "strum_macros", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", ] [[package]] -name = "strum_macros" -version = "0.25.2" +name = "symphonia-metadata" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c" dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.29", + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", ] [[package]] -name = "subtle" -version = "2.4.1" +name = "symphonia-utils-xiph" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe" +dependencies = [ + "symphonia-core", + "symphonia-metadata", +] [[package]] name = "syn" @@ -3134,95 +3364,147 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "sysinfo" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "windows 0.57.0", +] + [[package]] name = "syslog" -version = "6.1.0" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434e95bcccce1215d30f4bf84fe8c00e8de1b9be4fb736d747ca53d36e7f96f" +checksum = "dfc7e95b5b795122fafe6519e27629b5ab4232c73ebb2428f568e82b1a457ad3" dependencies = [ "error-chain", - "hostname", + "hostname 0.3.1", "libc", "log", - "time 0.3.36", + "time", +] + +[[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 0.9.4", + "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" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", - "fastrand 2.0.0", - "redox_syscall 0.3.5", - "rustix 0.38.10", - "windows-sys", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "winapi-util", + "thiserror-impl 1.0.69", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "thiserror" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "unicode-width", + "thiserror-impl 2.0.9", ] [[package]] -name = "thiserror" -version = "1.0.47" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.93", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] name = "thread-id" -version = "4.2.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79474f573561cdc4871a0de34a51c92f7f5a56039113fbb5b9c9f96bdb756669" +checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea" dependencies = [ "libc", - "redox_syscall 0.2.16", "winapi", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3230,20 +3512,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -3264,112 +3535,152 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.32.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", - "parking_lot 0.12.1", + "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.3", + "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] -name = "tokio-stream" -version = "0.1.14" +name = "tokio-rustls" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "futures-core", - "pin-project-lite", + "rustls 0.21.12", "tokio", ] [[package]] -name = "tokio-util" -version = "0.7.8" +name = "tokio-rustls" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", + "rustls 0.22.4", + "rustls-pki-types", "tokio", - "tracing", ] [[package]] -name = "toml" -version = "0.7.6" +name = "tokio-rustls" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", + "rustls 0.23.20", + "tokio", ] [[package]] -name = "toml_datetime" -version = "0.6.3" +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.20", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.1", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.0.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -3378,38 +3689,25 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "cfg-if", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3417,9 +3715,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -3427,9 +3725,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "sharded-slab", "thread_local", @@ -3438,102 +3736,92 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "typenum" -version = "1.16.0" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "uds_windows" -version = "1.0.2" +name = "tungstenite" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" dependencies = [ - "tempfile", - "winapi", + "byteorder", + "bytes", + "data-encoding", + "http 1.2.0", + "httparse", + "log", + "rand", + "rustls 0.23.20", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "utf-8", ] [[package]] -name = "unicode-bidi" -version = "0.3.13" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] -name = "unicode-segmentation" -version = "1.10.1" +name = "url" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] [[package]] -name = "unicode-width" -version = "0.1.10" +name = "utf-8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "untrusted" -version = "0.9.0" +name = "utf16_iter" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] -name = "ureq" -version = "2.9.1" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" -dependencies = [ - "base64 0.21.3", - "cookie", - "cookie_store", - "log", - "once_cell", - "rustls", - "rustls-webpki", - "serde", - "serde_json", - "url", - "webpki-roots", -] +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "url" -version = "2.4.1" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" -dependencies = [ - "form_urlencoded", - "idna 0.4.0", - "percent-encoding", -] +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.4.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", + "rand", ] [[package]] @@ -3543,39 +3831,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] -name = "vec_map" -version = "0.8.2" +name = "vergen" +version = "9.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "31f25fc8f8f05df455c7941e87f093ad22522a9ff33d7a027774815acf6f0639" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", + "time", + "vergen-lib", +] [[package]] -name = "vergen" -version = "3.2.0" +name = "vergen-gitcl" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7141e445af09c8919f1d5f8a20dae0b20c3b57a45dee0d5823c6ed5d237f15a" +checksum = "0227006d09f98ab00ea69e9a5e055e676a813cfbed4232986176c86a6080b997" dependencies = [ - "bitflags 1.3.2", - "chrono", - "rustc_version", + "anyhow", + "derive_builder", + "rustversion", + "time", + "vergen", + "vergen-lib", ] [[package]] -name = "version_check" -version = "0.9.4" +name = "vergen-lib" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "c0c767e6751c09fc85cde58722cf2f1007e80e4c8d5a4321fc90d83dc54ca147" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] [[package]] -name = "waker-fn" -version = "1.1.0" +name = "version_check" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -3590,12 +3893,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3610,34 +3907,47 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3645,46 +3955,68 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] [[package]] name = "whoami" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.4.1", + "redox_syscall", "wasite", "web-sys", ] @@ -3707,11 +4039,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -3722,11 +4054,103 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.48.0" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -3735,7 +4159,40 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -3744,147 +4201,223 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" -version = "0.5.15" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] -name = "xdg-home" -version = "1.0.0" +name = "winreg" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "nix 0.26.4", - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] -name = "zbus" -version = "3.14.1" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "byteorder", - "derivative", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix 0.26.4", - "once_cell", - "ordered-stream", - "rand", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "winapi", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] -name = "zbus_macros" -version = "3.14.1" +name = "writeable" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", ] [[package]] -name = "zbus_names" -version = "2.6.0" +name = "yoke-derive" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ - "serde", - "static_assertions", - "zvariant", + "proc-macro2", + "quote", + "syn 2.0.93", + "synstructure", ] [[package]] name = "zerocopy" -version = "0.6.6" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", @@ -3892,49 +4425,60 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.6.6" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.93", ] [[package]] -name = "zvariant" -version = "3.15.0" +name = "zerofrom" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ - "byteorder", - "enumflags2", - "libc", - "serde", - "static_assertions", - "zvariant_derive", + "zerofrom-derive", ] [[package]] -name = "zvariant_derive" -version = "3.15.0" +name = "zerofrom-derive" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ - "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", - "zvariant_utils", + "syn 2.0.93", + "synstructure", ] [[package]] -name = "zvariant_utils" -version = "1.0.1" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.93", ] diff --git a/Cargo.toml b/Cargo.toml index 4ecf0fdf..142d0704 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,36 +6,39 @@ description = "A Spotify daemon" repository = "https://github.com/Spotifyd/spotifyd" license = "GPL-3.0-only" version = "0.3.5" -rust-version = "1.67" +rust-version = "1.81" [dependencies] -alsa = { version = "0.7", optional = true } +alsa = { version = "0.9.1", optional = true } chrono = "0.4" dbus = { version = "0.9", optional = true } dbus-tokio = { version = "0.7.3", optional = true } dbus-crossroads = { version = "0.5.0", optional = true } -fern = { version = "0.6.0", features = ["syslog-6"] } +fern = { version = "0.7.0", features = ["syslog-6"] } futures = "0.3.15" -gethostname = "0.4.0" +gethostname = "0.5.0" hex = "0.4" -keyring = { version = "2.0", optional = true } libc = "0.2.82" log = "0.4.6" -rspotify = { version = "0.12.0", features = ["client-ureq", "ureq-rustls-tls"], default-features = false, optional = true } serde = { version = "1.0.115", features = ["derive"] } sha-1 = "0.10" -structopt = "0.3.17" tokio = {version = "1.26.0", features = ["signal", "rt-multi-thread", "process", "io-std"] } tokio-stream = "0.1.7" url = "2.2.2" -librespot-audio = { version = "0.4", default-features = false } -librespot-playback = { version = "0.4", default-features = false } -librespot-core = { version = "0.4" } -librespot-discovery = { version = "0.4" } -librespot-connect = { version = "0.4" } -toml = "0.7" +librespot-audio = { version = "0.6", default-features = false } +librespot-playback = { version = "0.6", default-features = false } +librespot-core = "0.6" +librespot-discovery = "0.6" +librespot-connect = "0.6" +librespot-metadata = "0.6" +librespot-protocol = "0.6" +librespot-oauth = "0.6" +toml = "0.8.19" color-eyre = "0.6" directories = "5.0.1" +thiserror = "2.0" +time = { version = "0.3.37", default-features = false, features = ["formatting"] } +clap = { version = "4.5.23", features = ["derive"] } [target."cfg(unix)".dependencies] daemonize = "0.5" @@ -48,12 +51,11 @@ whoami = "1" pledge = "0.4.2" [dev-dependencies] -env_logger = "0.10" +env_logger = "0.11" [features] alsa_backend = ["librespot-playback/alsa-backend", "alsa"] -dbus_keyring = ["keyring"] -dbus_mpris = ["dbus", "dbus-tokio", "dbus-crossroads", "rspotify"] +dbus_mpris = ["dbus", "dbus-tokio", "dbus-crossroads"] pipe_backend = [] default = ["alsa_backend", "pipe_backend"] portaudio_backend = ["librespot-playback/portaudio-backend"] @@ -63,7 +65,7 @@ rodiojack_backend = ["librespot-playback/rodiojack-backend"] [package.metadata.deb] depends = "$auto, systemd, pulseaudio" -features = ["pulseaudio_backend", "dbus_keyring", "dbus_mpris"] +features = ["pulseaudio_backend", "dbus_mpris"] assets = [ ["target/release/spotifyd", "usr/bin/", "755"], ["README.md", "usr/share/doc/spotifyd/README", "644"], diff --git a/hooks/pre-commit b/hooks/pre-commit index c07342f9..e43d760e 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -34,16 +34,16 @@ for path in $(git diff --name-only --cached); do done echo "→ Building pre-commit build artifacts..." -cargo check --quiet --no-default-features --features "rodio_backend,dbus_keyring" +cargo check --quiet --no-default-features --features "rodio_backend" if [ $? -ne 0 ]; then exit 1 fi -cargo build --quiet --no-default-features --features "rodio_backend,dbus_keyring" +cargo build --quiet --no-default-features --features "rodio_backend" # Linting is only done with the rodio backend and the keyring feature as those should be # compilable for every supported platform without external library needs. echo "→ Linting Rust code..." -cargo clippy --no-default-features --features "rodio_backend,dbus_keyring" -- -D warnings +cargo clippy --no-default-features --features "rodio_backend" -- -D warnings echo "→ Testing Rust code..." -cargo test --no-default-features --features "rodio_backend,dbus_keyring" +cargo test --no-default-features --features "rodio_backend" diff --git a/src/config.rs b/src/config.rs index c52cef92..83d6550a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,149 +1,51 @@ -use crate::{ - error::{Error as CrateError, ParseError}, - process::run_program, - utils, +use crate::utils; +use clap::{ + builder::{IntoResettable, PossibleValuesParser, TypedValueParser, ValueParser}, + Args, Parser, Subcommand, ValueEnum, }; -use color_eyre::Report; -use gethostname::gethostname; -use librespot_core::{ - cache::Cache, config::DeviceType as LSDeviceType, config::SessionConfig, version, +use color_eyre::{ + eyre::{bail, Context}, + Report, }; +use directories::ProjectDirs; +use gethostname::gethostname; +use librespot_core::{cache::Cache, config::DeviceType as LSDeviceType, config::SessionConfig}; use librespot_playback::{ + audio_backend, config::{AudioFormat as LSAudioFormat, Bitrate as LSBitrate, PlayerConfig}, dither::{mk_ditherer, DithererBuilder, TriangularDitherer}, }; -use log::{error, info, warn}; -use serde::{de::Error, de::Unexpected, Deserialize, Deserializer}; +use log::{debug, error, info, warn}; +use serde::{ + de::{self, Error, Unexpected}, + Deserialize, Deserializer, +}; use sha1::{Digest, Sha1}; -use std::{fmt, fs, path::Path, path::PathBuf, str::FromStr}; -use structopt::{clap::AppSettings, StructOpt}; +use std::{ + borrow::Cow, + convert::TryInto, + fs, + path::{Path, PathBuf}, + str::FromStr, +}; use url::Url; const CONFIG_FILE_NAME: &str = "spotifyd.conf"; -#[cfg(not(any( - feature = "pulseaudio_backend", - feature = "portaudio_backend", - feature = "alsa_backend", - feature = "pipe_backend", - feature = "rodio_backend", - feature = "rodiojack_backend", -)))] -compile_error!("At least one of the backend features is required!"); -static BACKEND_VALUES: &[&str] = &[ - #[cfg(feature = "alsa_backend")] - "alsa", - #[cfg(feature = "pulseaudio_backend")] - "pulseaudio", - #[cfg(feature = "portaudio_backend")] - "portaudio", - #[cfg(feature = "rodio_backend")] - "rodio", - #[cfg(feature = "pipe_backend")] - "pipe", - #[cfg(feature = "rodiojack_backend")] - "rodiojack", -]; - -/// The backend used by librespot -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, StructOpt)] -#[serde(rename_all = "lowercase")] -pub enum Backend { - Alsa, - PortAudio, - PulseAudio, - Rodio, - Pipe, - RodioJack, -} - -fn default_backend() -> Backend { - return Backend::from_str(BACKEND_VALUES.first().unwrap()).unwrap(); -} - -impl FromStr for Backend { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - match s { - "alsa" => Ok(Backend::Alsa), - "portaudio" => Ok(Backend::PortAudio), - "pulseaudio" => Ok(Backend::PulseAudio), - "rodio" => Ok(Backend::Rodio), - "pipe" => Ok(Backend::Pipe), - "rodiojack" => Ok(Backend::RodioJack), - _ => unreachable!(), - } - } -} - -impl fmt::Display for Backend { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Backend::Alsa => write!(f, "alsa"), - Backend::PortAudio => write!(f, "portaudio"), - Backend::PulseAudio => write!(f, "pulseaudio"), - Backend::Rodio => write!(f, "rodio"), - Backend::Pipe => write!(f, "pipe"), - Backend::RodioJack => write!(f, "rodiojack"), - } - } -} - -static VOLUME_CONTROLLER_VALUES: &[&str] = &[ - "softvol", - #[cfg(feature = "alsa_backend")] - "alsa", - #[cfg(feature = "alsa_backend")] - "alsa_linear", - "none", -]; - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, StructOpt)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, ValueEnum)] #[serde(rename_all = "snake_case")] pub enum VolumeController { + #[cfg(feature = "alsa_backend")] Alsa, + #[cfg(feature = "alsa_backend")] AlsaLinear, #[serde(rename = "softvol")] SoftVolume, None, } -impl FromStr for VolumeController { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - match s { - "alsa" => Ok(VolumeController::Alsa), - "alsa_linear" => Ok(VolumeController::AlsaLinear), - "softvol" => Ok(VolumeController::SoftVolume), - "none" => Ok(VolumeController::None), - _ => unreachable!(), - } - } -} - -static DEVICETYPE_VALUES: &[&str] = &[ - "computer", - "tablet", - "smartphone", - "speaker", - "tv", - "avr", - "stb", - "audiodongle", - "gameconsole", - "castaudio", - "castvideo", - "automobile", - "smartwatch", - "chromebook", - "carthing", - "homething", -]; - // Spotify's device type (copied from it's config.rs) -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, StructOpt)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, ValueEnum)] #[serde(rename_all = "snake_case")] pub enum DeviceType { Unknown, @@ -170,34 +72,8 @@ pub enum DeviceType { HomeThing, } -impl From for DeviceType { - fn from(item: LSDeviceType) -> Self { - match item { - LSDeviceType::Unknown => DeviceType::Unknown, - LSDeviceType::Computer => DeviceType::Computer, - LSDeviceType::Tablet => DeviceType::Tablet, - LSDeviceType::Smartphone => DeviceType::Smartphone, - LSDeviceType::Speaker => DeviceType::Speaker, - LSDeviceType::Tv => DeviceType::Tv, - LSDeviceType::Avr => DeviceType::Avr, - LSDeviceType::Stb => DeviceType::Stb, - LSDeviceType::AudioDongle => DeviceType::AudioDongle, - LSDeviceType::GameConsole => DeviceType::GameConsole, - LSDeviceType::CastAudio => DeviceType::CastAudio, - LSDeviceType::CastVideo => DeviceType::CastVideo, - LSDeviceType::Automobile => DeviceType::Automobile, - LSDeviceType::Smartwatch => DeviceType::Smartwatch, - LSDeviceType::Chromebook => DeviceType::Chromebook, - LSDeviceType::UnknownSpotify => DeviceType::UnknownSpotify, - LSDeviceType::CarThing => DeviceType::CarThing, - LSDeviceType::Observer => DeviceType::Observer, - LSDeviceType::HomeThing => DeviceType::HomeThing, - } - } -} - -impl From<&DeviceType> for LSDeviceType { - fn from(item: &DeviceType) -> Self { +impl From for LSDeviceType { + fn from(item: DeviceType) -> Self { match item { DeviceType::Unknown => LSDeviceType::Unknown, DeviceType::Computer => LSDeviceType::Computer, @@ -222,26 +98,18 @@ impl From<&DeviceType> for LSDeviceType { } } -impl FromStr for DeviceType { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - let dt = LSDeviceType::from_str(s).unwrap(); - Ok(dt.into()) - } -} - -impl fmt::Display for DeviceType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let dt: LSDeviceType = self.into(); - write!(f, "{dt}") - } +fn bitrate_parser() -> impl IntoResettable { + let possible_values: PossibleValuesParser = ["96", "160", "320"].into(); + possible_values.map(|val| match val.as_str() { + "96" => Bitrate::Bitrate96, + "160" => Bitrate::Bitrate160, + "320" => Bitrate::Bitrate320, + _ => unreachable!(), + }) } -static BITRATE_VALUES: &[&str] = &["96", "160", "320"]; - /// Spotify's audio bitrate -#[derive(Clone, Copy, Debug, PartialEq, Eq, StructOpt)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)] pub enum Bitrate { Bitrate96, Bitrate160, @@ -263,19 +131,6 @@ impl<'de> Deserialize<'de> for Bitrate { } } -impl FromStr for Bitrate { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - match s { - "96" => Ok(Bitrate::Bitrate96), - "160" => Ok(Bitrate::Bitrate160), - "320" => Ok(Bitrate::Bitrate320), - _ => unreachable!(), - } - } -} - impl From for LSBitrate { fn from(bitrate: Bitrate) -> Self { match bitrate { @@ -286,41 +141,14 @@ impl From for LSBitrate { } } -#[cfg(feature = "dbus_mpris")] -static DBUSTYPE_VALUES: &[&str] = &["session", "system"]; - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, StructOpt)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, ValueEnum)] #[serde(rename_all = "snake_case")] pub enum DBusType { Session, System, } -impl FromStr for DBusType { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - match s { - "session" => Ok(DBusType::Session), - "system" => Ok(DBusType::System), - _ => unreachable!(), - } - } -} - -impl fmt::Display for DBusType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - DBusType::Session => write!(f, "session"), - DBusType::System => write!(f, "system"), - } - } -} - -/// LibreSpot supported audio formats -static AUDIO_FORMAT_VALUES: &[&str] = &["F32", "S32", "S24", "S24_3", "S16"]; - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, StructOpt)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, ValueEnum)] pub enum AudioFormat { F32, S32, @@ -329,33 +157,6 @@ pub enum AudioFormat { S16, } -impl FromStr for AudioFormat { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - match s { - "F32" => Ok(AudioFormat::F32), - "S32" => Ok(AudioFormat::S32), - "S24" => Ok(AudioFormat::S24), - "S24_3" => Ok(AudioFormat::S24_3), - "S16" => Ok(AudioFormat::S16), - _ => unreachable!(), - } - } -} - -impl fmt::Display for AudioFormat { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - AudioFormat::F32 => write!(f, "F32"), - AudioFormat::S32 => write!(f, "S32"), - AudioFormat::S24 => write!(f, "S24"), - AudioFormat::S24_3 => write!(f, "S24_3"), - AudioFormat::S16 => write!(f, "S16"), - } - } -} - impl From for LSAudioFormat { fn from(audio_format: AudioFormat) -> Self { match audio_format { @@ -368,177 +169,218 @@ impl From for LSAudioFormat { } } -#[derive(Debug, Default, StructOpt)] -#[structopt( - about = "A Spotify daemon", - author, - name = "spotifyd", - setting(AppSettings::ColoredHelp) -)] +fn possible_backends() -> Vec<&'static str> { + audio_backend::BACKENDS.iter().map(|b| b.0).collect() +} + +fn number_or_string<'de, D>(de: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let Some(val) = Option::::deserialize(de)? else { + return Ok(None); + }; + + let unexpected = match val { + toml::Value::Integer(num) => { + let num: u8 = num.try_into().map_err(de::Error::custom)?; + return Ok(Some(num)); + } + toml::Value::String(num) => { + return u8::from_str(&num) + .map(Some) + .inspect(|_| warn!("Configuration Warning: `initial_volume` should be a number rather than a string, this will become a hard error in the future")) + .map_err(de::Error::custom) + } + toml::Value::Float(f) => Unexpected::Float(f), + toml::Value::Boolean(b) => Unexpected::Bool(b), + toml::Value::Datetime(_) => Unexpected::Other("datetime"), + toml::Value::Array(_) => Unexpected::Seq, + toml::Value::Table(_) => Unexpected::Map, + }; + Err(de::Error::invalid_type(unexpected, &"number")) +} + +#[derive(Debug, Default, Parser)] +#[command(version, about, long_about = None, args_conflicts_with_subcommands = true)] pub struct CliConfig { /// The path to the config file to use - #[structopt(long, value_name = "string")] + #[arg(long, value_name = "PATH", global = true)] pub config_path: Option, + /// Prints more verbose output + #[arg(short, long, action = clap::ArgAction::Count, global = true)] + pub verbose: u8, + /// If set, starts spotifyd without detaching - #[structopt(long)] + #[arg(long)] pub no_daemon: bool, - /// Prints more verbose output - #[structopt(long)] - pub verbose: bool, - /// Path to PID file. - #[structopt(long)] + #[cfg(unix)] + #[arg(long, value_name = "PATH")] pub pid: Option, - #[structopt(flatten)] + #[command(subcommand)] + pub mode: Option, + + #[command(flatten)] pub shared_config: SharedConfigValues, } +#[derive(Debug, Subcommand)] +pub enum ExecutionMode { + #[command(visible_alias = "auth")] + Authenticate { + /// The port to use for the OAuth redirect + #[arg(long, default_value_t = 8000)] + oauth_port: u16, + }, +} + // A struct that holds all allowed config fields. // The actual config file is made up of two sections, spotifyd and global. -#[derive(Clone, Default, Deserialize, PartialEq, StructOpt)] +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Args)] pub struct SharedConfigValues { - /// The Spotify account user name - #[structopt(conflicts_with = "username_cmd", long, short, value_name = "string")] - username: Option, - - /// A command that can be used to retrieve the Spotify account username - #[structopt( - conflicts_with = "username", - long, - short = "U", - value_name = "string", - visible_alias = "username_cmd" - )] - username_cmd: Option, - - /// The Spotify account password - #[structopt(conflicts_with = "password_cmd", long, short, value_name = "string")] - password: Option, - - /// Enables keyring password access - #[cfg_attr( - feature = "dbus_keyring", - structopt(long), - serde(alias = "use-keyring", default) - )] - #[cfg_attr(not(feature = "dbus_keyring"), structopt(skip), serde(skip))] - use_keyring: bool, - - /// Enables the MPRIS interface - #[cfg_attr( - feature = "dbus_mpris", - structopt(long), - serde(alias = "use-mpris", default) - )] - #[cfg_attr(not(feature = "dbus_mpris"), structopt(skip), serde(skip))] - use_mpris: Option, - - /// The Bus-type to use for the MPRIS interface - #[cfg_attr( - feature = "dbus_mpris", - structopt(long, possible_values = &DBUSTYPE_VALUES, value_name = "string") - )] - #[cfg_attr(not(feature = "dbus_mpris"), structopt(skip), serde(skip))] - dbus_type: Option, - - /// A command that can be used to retrieve the Spotify account password - #[structopt( - conflicts_with = "password", - long, - short = "P", - value_name = "string", - visible_alias = "password_cmd" - )] - password_cmd: Option, - - /// Whether the credentials should be debugged. - #[structopt(long)] - #[serde(skip)] - debug_credentials: bool, - /// A script that gets evaluated in the user's shell when the song changes - #[structopt(visible_alias = "onevent", long, value_name = "string")] + #[arg(visible_alias = "onevent", long, value_name = "CMD")] #[serde(alias = "onevent")] on_song_change_hook: Option, /// The cache path used to store credentials and music file artifacts - #[structopt(long, parse(from_os_str), short, value_name = "string")] + #[arg(long, short, value_name = "PATH", global = true)] cache_path: Option, /// The maximal cache size in bytes - #[structopt(long)] + #[arg(long, value_name = "BYTES")] max_cache_size: Option, /// Disable the use of audio cache - #[structopt(long)] - #[serde(default)] - no_audio_cache: bool, + #[arg( + long, + default_missing_value("true"), + require_equals = true, + num_args(0..=1), + value_name = "BOOL" + )] + no_audio_cache: Option, /// The audio backend to use - #[structopt(long, short, possible_values = &BACKEND_VALUES, value_name = "string")] - backend: Option, + #[arg(long, short, value_parser = possible_backends())] + backend: Option, /// The volume controller to use - #[structopt(long, short, possible_values = &VOLUME_CONTROLLER_VALUES, visible_alias = "volume-control")] + #[arg(value_enum, long, visible_alias = "volume-control")] #[serde(alias = "volume-control")] volume_controller: Option, - /// The audio device (or file handle if using pipe backend) - #[structopt(long, value_name = "string")] + /// The audio device (or pipe file) + #[arg(long)] device: Option, - /// The control device - #[structopt(long, value_name = "string")] - control: Option, - - /// The mixer to use - #[structopt(long, value_name = "string")] - mixer: Option, - /// The device name displayed in Spotify - #[structopt(long, short, value_name = "string")] + #[arg(long, short)] device_name: Option, /// The bitrate of the streamed audio data - #[structopt(long, short = "B", possible_values = &BITRATE_VALUES, value_name = "number")] + #[arg(long, short = 'B', value_parser = bitrate_parser())] bitrate: Option, /// The audio format of the streamed audio data - #[structopt(long, possible_values = &AUDIO_FORMAT_VALUES, value_name = "string")] + #[arg(value_enum, long)] audio_format: Option, /// Initial volume between 0 and 100 - #[structopt(long, value_name = "initial_volume")] - initial_volume: Option, + #[arg(long)] + #[serde(deserialize_with = "number_or_string")] + initial_volume: Option, /// Enable to normalize the volume during playback - #[structopt(long)] - #[serde(default)] - volume_normalisation: bool, + #[arg( + long, + default_missing_value("true"), + require_equals = true, + num_args(0..=1), + value_name = "BOOL" + )] + volume_normalisation: Option, /// A custom pregain applied before sending the audio to the output device - #[structopt(long, value_name = "number")] + #[arg(long)] normalisation_pregain: Option, + #[arg( + long, + default_missing_value("true"), + require_equals = true, + num_args(0..=1), + value_name = "BOOL" + )] + disable_discovery: Option, + /// The port used for the Spotify Connect discovery - #[structopt(long, value_name = "number")] + #[arg(long)] zeroconf_port: Option, /// The proxy used to connect to spotify's servers - #[structopt(long, value_name = "string")] + #[arg(long, value_name = "URL")] proxy: Option, /// The device type shown to clients - #[structopt(long, possible_values = &DEVICETYPE_VALUES, value_name = "string")] + #[arg(value_enum, long)] device_type: Option, /// Start playing similar songs after your music has ended - #[structopt(long)] + #[arg( + long, + default_missing_value("true"), + require_equals = true, + num_args(0..=1), + value_name = "BOOL" + )] #[serde(default)] - autoplay: bool, + autoplay: Option, + + #[cfg(feature = "alsa_backend")] + #[command(flatten)] + #[serde(flatten)] + alsa_config: AlsaConfig, + + #[cfg(feature = "dbus_mpris")] + #[command(flatten)] + #[serde(flatten)] + mpris_config: MprisConfig, +} + +#[cfg(feature = "dbus_mpris")] +#[derive(Debug, Default, Clone, Deserialize, Args, PartialEq, Eq)] +pub struct MprisConfig { + /// Enables the MPRIS interface + #[arg( + long, + default_missing_value("true"), + require_equals = true, + num_args(0..=1), + value_name = "BOOL" + )] + #[serde(alias = "use-mpris")] + pub(crate) use_mpris: Option, + + /// The Bus-type to use for the MPRIS interface + #[arg(value_enum, long)] + pub(crate) dbus_type: Option, +} + +#[cfg(feature = "alsa_backend")] +#[derive(Debug, Default, Clone, Deserialize, Args, PartialEq, Eq)] +pub struct AlsaConfig { + /// The control device + #[arg(long)] + pub(crate) control: Option, + + /// The mixer to use + #[arg(long)] + pub(crate) mixer: Option, } #[derive(Debug, Default, Deserialize)] @@ -549,84 +391,13 @@ pub struct FileConfig { impl FileConfig { pub fn get_merged_sections(self) -> Option { - let global_config_section = self.global; - let spotifyd_config_section = self.spotifyd; - - let merged_config: Option; - // First merge the two sections together. The spotifyd has priority over global - // section. - if let Some(mut spotifyd_section) = spotifyd_config_section { - // spotifyd section exists. Try to merge it with global section. - #[allow(clippy::branches_sharing_code)] - if let Some(global_section) = global_config_section { - spotifyd_section.merge_with(global_section); - merged_config = Some(spotifyd_section); - } else { - // There is no global section. Just use the spotifyd section. - merged_config = Some(spotifyd_section); + match (self.global, self.spotifyd) { + (Some(global), Some(mut spotifyd)) => { + spotifyd.merge_with(global); + Some(spotifyd) } - } else { - // No spotifyd config available. Check for global and use that, if both are - // none, use none. - merged_config = global_config_section; - } - - merged_config - } -} - -impl fmt::Debug for SharedConfigValues { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let placeholder = "taken out for privacy"; - - macro_rules! extract_credential { - ( $e:expr ) => { - match $e { - Some(s) => match self.debug_credentials { - true => Some(s.as_str()), - false => Some(placeholder), - }, - None => None, - } - }; + (global, spotifyd) => global.or(spotifyd), } - - let password_value = extract_credential!(&self.password); - - let password_cmd_value = extract_credential!(&self.password_cmd); - - let username_value = extract_credential!(&self.username); - - let username_cmd_value = extract_credential!(&self.username_cmd); - - f.debug_struct("SharedConfigValues") - .field("username", &username_value) - .field("username_cmd", &username_cmd_value) - .field("password", &password_value) - .field("password_cmd", &password_cmd_value) - .field("use_keyring", &self.use_keyring) - .field("use_mpris", &self.use_mpris) - .field("dbus_type", &self.dbus_type) - .field("on_song_change_hook", &self.on_song_change_hook) - .field("cache_path", &self.cache_path) - .field("no-audio-cache", &self.no_audio_cache) - .field("backend", &self.backend) - .field("volume_controller", &self.volume_controller) - .field("device", &self.device) - .field("control", &self.control) - .field("mixer", &self.mixer) - .field("device_name", &self.device_name) - .field("bitrate", &self.bitrate) - .field("audio_format", &self.audio_format) - .field("initial_volume", &self.initial_volume) - .field("volume_normalisation", &self.volume_normalisation) - .field("normalisation_pregain", &self.normalisation_pregain) - .field("zeroconf_port", &self.zeroconf_port) - .field("proxy", &self.proxy) - .field("device_type", &self.device_type) - .field("autoplay", &self.autoplay) - .field("max_cache_size", &self.max_cache_size) - .finish() } } @@ -661,52 +432,96 @@ impl CliConfig { } impl SharedConfigValues { - pub fn merge_with(&mut self, other: SharedConfigValues) { + pub fn get_cache(&self, for_oauth: bool) -> color_eyre::Result { + let Some(cache_path) = self.cache_path.as_deref().map(Cow::Borrowed).or_else(|| { + ProjectDirs::from("", "", "spotifyd") + .map(|dirs| Cow::Owned(dirs.cache_dir().to_path_buf())) + }) else { + bail!("Failed to determine cache directory, please specify one manually"); + }; + + if for_oauth { + let mut creds_path = cache_path.into_owned(); + creds_path.push("oauth"); + Cache::new(Some(creds_path), None, None, None) + } else { + let audio_cache = !self.no_audio_cache.unwrap_or(false); + + let mut creds_path = cache_path.to_path_buf(); + creds_path.push("zeroconf"); + Cache::new( + Some(creds_path.as_path()), + Some(cache_path.as_ref()), + audio_cache.then_some(cache_path.as_ref()), + self.max_cache_size, + ) + } + .wrap_err("Failed to initialize cache") + } + + pub fn proxy_url(&self) -> Option { + match &self.proxy { + Some(s) => match Url::parse(s) { + Ok(url) => { + if url.scheme() != "http" { + error!("Only HTTP proxies are supported!"); + None + } else { + Some(url) + } + } + Err(err) => { + error!("Invalid proxy URL: {}", err); + None + } + }, + None => { + debug!("No proxy specified"); + None + } + } + } + + pub fn merge_with(&mut self, mut other: SharedConfigValues) { macro_rules! merge { - ($($x:ident),+) => { - $(self.$x = self.$x.clone().or_else(|| other.$x.clone());)+ + ($a:expr; and $b:expr => {$($x:ident),+}) => { + $($a.$x = $a.$x.take().or_else(|| $b.$x.take());)+ } } // Handles Option merging. - merge!( + merge!(self; and other => { backend, - username, - username_cmd, - password, - password_cmd, + volume_normalisation, normalisation_pregain, bitrate, initial_volume, device_name, - mixer, - control, device, volume_controller, cache_path, + no_audio_cache, on_song_change_hook, + disable_discovery, zeroconf_port, proxy, device_type, - use_mpris, max_cache_size, - dbus_type, - audio_format - ); - - // Handles boolean merging. - self.use_keyring |= other.use_keyring; - self.volume_normalisation |= other.volume_normalisation; - self.no_audio_cache |= other.no_audio_cache; - self.autoplay |= other.autoplay; + audio_format, + autoplay + }); + + #[cfg(feature = "dbus_mpris")] + merge!(self.mpris_config; and other.mpris_config => {use_mpris, dbus_type}); + #[cfg(feature = "alsa_backend")] + merge!(self.alsa_config; and other.alsa_config => {mixer, control}); } } pub(crate) fn get_config_file() -> Option { let etc_conf = format!("/etc/{}", CONFIG_FILE_NAME); - let dirs = directories::BaseDirs::new()?; + let dirs = directories::ProjectDirs::from("", "", "spotifyd")?; let mut path = dirs.config_dir().to_path_buf(); - path.push("spotifyd"); path.push(CONFIG_FILE_NAME); if path.exists() { @@ -724,55 +539,43 @@ fn device_id(name: &str) -> String { } pub(crate) struct SpotifydConfig { - pub(crate) username: Option, - pub(crate) password: Option, - #[allow(unused)] - pub(crate) use_keyring: bool, - pub(crate) use_mpris: bool, - pub(crate) dbus_type: DBusType, pub(crate) cache: Option, + pub(crate) oauth_cache: Option, pub(crate) backend: Option, pub(crate) audio_device: Option, pub(crate) audio_format: LSAudioFormat, - #[allow(unused)] - pub(crate) control_device: Option, - #[allow(unused)] - pub(crate) mixer: Option, - #[allow(unused)] pub(crate) volume_controller: VolumeController, pub(crate) initial_volume: Option, pub(crate) device_name: String, pub(crate) player_config: PlayerConfig, pub(crate) session_config: SessionConfig, pub(crate) onevent: Option, - #[allow(unused)] + #[cfg(unix)] pub(crate) pid: Option, pub(crate) shell: String, + pub(crate) discovery: bool, pub(crate) zeroconf_port: Option, - pub(crate) device_type: String, - pub(crate) autoplay: bool, + pub(crate) device_type: LSDeviceType, + #[cfg(feature = "dbus_mpris")] + pub(crate) mpris: MprisConfig, + #[cfg(feature = "alsa_backend")] + pub(crate) alsa_config: AlsaConfig, } pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { - let audio_cache = !config.shared_config.no_audio_cache; - - let size_limit = config.shared_config.max_cache_size; - let cache = config - .shared_config - .cache_path - .map(|path| { - Cache::new( - Some(&path), - Some(&path), - audio_cache.then_some(&path), - size_limit, - ) - }) - .transpose() - .unwrap_or_else(|e| { - warn!("Cache couldn't be initialized: {e}"); - None - }); + let (cache, oauth_cache) = match ( + config.shared_config.get_cache(false), + config.shared_config.get_cache(true), + ) { + (Ok(cache), Ok(oauth_cache)) => (Some(cache), Some(oauth_cache)), + (a, b) => { + // at least one of the results are err + let err = a.or(b).map(|_| ()).unwrap_err(); + warn!("{err}"); + (None, None) + } + }; + let proxy_url = config.shared_config.proxy_url(); let bitrate: LSBitrate = config .shared_config @@ -786,12 +589,6 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { .unwrap_or(AudioFormat::S16) .into(); - let backend = config - .shared_config - .backend - .unwrap_or_else(default_backend) - .to_string(); - let volume_controller = config .shared_config .volume_controller @@ -800,14 +597,15 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { let initial_volume: Option = config .shared_config .initial_volume - .and_then(|input| match input.parse::() { - Ok(v) if (0..=100).contains(&v) => Some(v), - _ => { - warn!("Could not parse initial_volume (must be in the range 0-100)"); - None + .filter(|val| { + if (0..=100).contains(val) { + true + } else { + warn!("initial_volume must be in range 0..100"); + false } }) - .map(|volume| (volume as i32 * 0xFFFF / 100) as u16); + .map(|volume| (volume as i32 * (u16::MAX as i32) / 100) as u16); let device_name = config .shared_config @@ -819,15 +617,13 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { let normalisation_pregain = config.shared_config.normalisation_pregain.unwrap_or(0.0); - let dbus_type = config.shared_config.dbus_type.unwrap_or(DBusType::Session); - let autoplay = config.shared_config.autoplay; - let device_type = config .shared_config .device_type .unwrap_or(DeviceType::Speaker) - .to_string(); + .into(); + #[cfg(unix)] let pid = config.pid.map(|f| { f.into_os_string() .into_string() @@ -839,45 +635,6 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { "sh".to_string() }); - let mut username = config.shared_config.username; - if username.is_none() { - info!("No username specified. Checking username_cmd"); - match config.shared_config.username_cmd { - Some(ref cmd) => match run_program(&shell, cmd) { - Ok(s) => username = Some(s.trim().to_string()), - Err(e) => error!("{}", CrateError::subprocess_with_err(&shell, cmd, e)), - }, - None => info!("No username_cmd specified"), - } - } - - let mut password = config.shared_config.password; - if password.is_none() { - info!("No password specified. Checking password_cmd"); - - match config.shared_config.password_cmd { - Some(ref cmd) => match run_program(&shell, cmd) { - Ok(s) => password = Some(s.trim().to_string()), - Err(e) => error!("{}", CrateError::subprocess_with_err(&shell, cmd, e)), - }, - None => info!("No password_cmd specified"), - } - } - let mut proxy_url = None; - match config.shared_config.proxy { - Some(s) => match Url::parse(&s) { - Ok(url) => { - if url.scheme() != "http" { - error!("Only HTTP proxies are supported!"); - } else { - proxy_url = Some(url); - } - } - Err(err) => error!("Invalid proxy URL: {}", err), - }, - None => info!("No proxy specified"), - } - // choose default ditherer the same way librespot does let ditherer: Option = match audio_format { LSAudioFormat::S16 | LSAudioFormat::S24 | LSAudioFormat::S24_3 => { @@ -891,7 +648,7 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { // should consider adding them to Spotifyd's config system. let pc = PlayerConfig { bitrate, - normalisation: config.shared_config.volume_normalisation, + normalisation: config.shared_config.volume_normalisation.unwrap_or(false), normalisation_pregain_db: normalisation_pregain, gapless: true, ditherer, @@ -899,33 +656,33 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig { }; SpotifydConfig { - username, - password, - use_keyring: config.shared_config.use_keyring, - use_mpris: config.shared_config.use_mpris.unwrap_or(true), - dbus_type, cache, - backend: Some(backend), + oauth_cache, + backend: config.shared_config.backend, audio_device: config.shared_config.device, audio_format, - control_device: config.shared_config.control, - mixer: config.shared_config.mixer, volume_controller, initial_volume, device_name, player_config: pc, session_config: SessionConfig { - user_agent: version::VERSION_STRING.to_string(), + autoplay: config.shared_config.autoplay, device_id, proxy: proxy_url, ap_port: Some(443), + ..Default::default() }, onevent: config.shared_config.on_song_change_hook, - pid, shell, + discovery: !config.shared_config.disable_discovery.unwrap_or(false), zeroconf_port: config.shared_config.zeroconf_port, device_type, - autoplay, + #[cfg(unix)] + pid, + #[cfg(feature = "dbus_mpris")] + mpris: config.shared_config.mpris_config, + #[cfg(feature = "alsa_backend")] + alsa_config: config.shared_config.alsa_config, } } @@ -936,12 +693,12 @@ mod tests { #[test] fn test_section_merging() { let mut spotifyd_section = SharedConfigValues { - password: Some("123456".to_string()), + device_type: Some(DeviceType::Computer), ..Default::default() }; let global_section = SharedConfigValues { - username: Some("testUserName".to_string()), + device_name: Some("spotifyd-test".to_string()), ..Default::default() }; @@ -955,15 +712,7 @@ mod tests { let merged_config = file_config.get_merged_sections().unwrap(); // Add the new field to spotifyd section. - spotifyd_section.username = Some("testUserName".to_string()); + spotifyd_section.device_name = Some("spotifyd-test".to_string()); assert_eq!(merged_config, spotifyd_section); } - #[test] - fn test_default_backend() { - let spotifyd_config = get_internal_config(CliConfig::default()); - assert_eq!( - spotifyd_config.backend.unwrap(), - default_backend().to_string() - ); - } } diff --git a/src/dbus_mpris.rs b/src/dbus_mpris.rs index d5e221db..84ac7c6c 100644 --- a/src/dbus_mpris.rs +++ b/src/dbus_mpris.rs @@ -4,184 +4,435 @@ use dbus::{ arg::{RefArg, Variant}, channel::{MatchingReceiver, Sender}, message::{MatchRule, SignalArgs}, + nonblock::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged, MethodErr, }; use dbus_crossroads::{Crossroads, IfaceToken}; -use dbus_tokio::connection; +use dbus_tokio::connection::{self, IOResourceError}; use futures::{ - self, task::{Context, Poll}, Future, }; -use librespot_connect::spirc::Spirc; -use librespot_core::{ - keymaster::{get_token, Token as LibrespotToken}, - mercury::MercuryError, - session::Session, - spotify_id::SpotifyAudioType, -}; +use librespot_connect::spirc::{Spirc, SpircLoadCommand}; +use librespot_core::{spotify_id::SpotifyItemType, Session, SpotifyId}; +use librespot_metadata::audio::AudioItem; use librespot_playback::player::PlayerEvent; -use log::{error, info, warn}; -use rspotify::{ - model::{ - offset::Offset, parse_uri, AlbumId, ArtistId, EpisodeId, IdError, PlayableItem, PlaylistId, - RepeatState, ShowId, TrackId, Type, +use librespot_protocol::spirc::TrackRef; +use log::{error, warn}; +use std::convert::TryFrom; +use std::{ + collections::HashMap, + pin::Pin, + sync::{Arc, RwLock}, +}; +use thiserror::Error; +use time::format_description::well_known::Iso8601; +use tokio::{ + runtime::Handle, + sync::{ + mpsc::{UnboundedReceiver, UnboundedSender}, + Mutex, }, - prelude::*, - AuthCodeSpotify, Token as RspotifyToken, }; -use std::{collections::HashMap, env, pin::Pin, sync::Arc}; -use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; -pub struct DbusServer { - session: Session, - spirc: Arc, - spotify_client: Arc, - dbus_type: DBusType, - #[allow(clippy::type_complexity)] - token_request: Option>>>>, - dbus_future: Option>>>, - device_name: String, - event_rx: UnboundedReceiver, - event_tx: Option>, +type DbusMap = HashMap>>; + +const MPRIS_PATH: &str = "/org/mpris/MediaPlayer2"; +const CONTROLS_PATH: &str = "/rs/spotifyd/Controls"; + +pub enum ControlMessage { + SetSpirc(Arc), + DropSpirc, + Shutdown, } -const CLIENT_ID: &str = "2c1ea588dfbc4a989e2426f8385297c3"; -const SCOPE: &str = - "user-read-playback-state,user-modify-playback-state,user-read-currently-playing"; +pub(crate) struct DbusServer { + dbus_future: Pin>>>, + control_tx: UnboundedSender, +} impl DbusServer { pub fn new( - session: Session, - spirc: Arc, - device_name: String, event_rx: UnboundedReceiver, dbus_type: DBusType, + session: Session, ) -> DbusServer { + let (control_tx, control_rx) = tokio::sync::mpsc::unbounded_channel(); + let dbus_future = Box::pin(create_dbus_server(event_rx, control_rx, dbus_type, session)); DbusServer { - session, - spirc, - spotify_client: Default::default(), - dbus_type, - token_request: None, - dbus_future: None, - device_name, - event_rx, - event_tx: None, + dbus_future, + control_tx, } } + + pub fn set_spirc(&self, spirc: Arc) -> Result<(), DbusError> { + self.control_tx + .send(ControlMessage::SetSpirc(spirc)) + .map_err(|_| DbusError::ControlChannelBroken) + } + + pub fn drop_spirc(&self) -> Result<(), DbusError> { + self.control_tx + .send(ControlMessage::DropSpirc) + .map_err(|_| DbusError::ControlChannelBroken) + } + + /// Sends a shutdown signal and returns false, if the server was already shut down. + pub fn shutdown(&self) -> bool { + self.control_tx.send(ControlMessage::Shutdown).is_ok() + } +} + +#[derive(Debug, Error)] +pub(crate) enum DbusError { + #[error("Failed to initialize D-Bus: {}", .0)] + InitFailure(#[from] dbus::Error), + #[error("The connection was terminated unexpectedly: {}", .0)] + ConnectionFailure(#[from] IOResourceError), + #[error("Unexpectedly lost control of dbus server")] + ControlChannelBroken, } impl Future for DbusServer { - type Output = (); + type Output = Result<(), DbusError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - if self.event_tx.is_some() { - if let Poll::Ready(Some(msg)) = self.event_rx.poll_recv(cx) { - self.event_tx.as_ref().unwrap().send(msg).unwrap(); - } + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.dbus_future.as_mut().poll(cx) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum PlaybackStatus { + Playing, + Paused, + Stopped, +} + +impl PlaybackStatus { + fn to_mpris(self) -> &'static str { + match self { + PlaybackStatus::Playing => "Playing", + PlaybackStatus::Paused => "Paused", + PlaybackStatus::Stopped => "Stopped", } - let needs_token = match *self.spotify_client.get_token().lock().unwrap() { - Some(ref token) => token.is_expired(), - None => true, - }; - - if needs_token { - if let Some(mut fut) = self.token_request.take() { - if let Poll::Ready(token) = fut.as_mut().poll(cx) { - let token = match token { - Ok(token) => token, - Err(_) => { - error!("failed to request a token for the web API"); - // shutdown DBus-Server - return Poll::Ready(()); - } - }; - - let expires_in = Duration::seconds(token.expires_in as i64); - let api_token = RspotifyToken { - access_token: token.access_token, - expires_in, - expires_at: Some(Utc::now() + expires_in), - ..RspotifyToken::default() - }; - - if self.dbus_future.is_none() { - self.spotify_client = Arc::new(AuthCodeSpotify::from_token(api_token)); - - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - self.event_tx = Some(tx); - self.dbus_future = Some(Box::pin(create_dbus_server( - Arc::clone(&self.spotify_client), - self.spirc.clone(), - self.device_name.clone(), - rx, - self.dbus_type, - ))); - } else { - *self.spotify_client.get_token().lock().unwrap() = Some(api_token); - } - } else { - self.token_request = Some(fut); + } +} + +#[derive(Debug)] +struct Position { + last_position: Duration, + last_update: DateTime, +} + +impl Position { + fn new() -> Self { + Self { + last_position: Duration::zero(), + last_update: Local::now(), + } + } + fn update_position(&mut self, new_position: Duration) { + self.last_update = Local::now(); + self.last_position = new_position; + } + + fn get_position(&self) -> Duration { + (Local::now() - self.last_update) + self.last_position + } +} + +#[derive(Clone, Copy, Debug)] +enum RepeatState { + None, + // Track, + All, +} + +impl RepeatState { + fn to_mpris(self) -> &'static str { + match self { + RepeatState::None => "None", + // RepeatState::Track => "Track", + RepeatState::All => "Playlist", + } + } +} + +impl From for bool { + fn from(repeat: RepeatState) -> Self { + match repeat { + RepeatState::None => false, + RepeatState::All => true, + } + } +} + +impl From for RepeatState { + fn from(repeat: bool) -> Self { + if repeat { + RepeatState::All + } else { + RepeatState::None + } + } +} + +#[derive(Debug)] +struct CurrentStateInner { + status: PlaybackStatus, + position: Option, + audio_item: Option>, + volume: u16, + shuffle: bool, + repeat: RepeatState, +} + +fn insert_attr(map: &mut DbusMap, attr: impl ToString, value: impl RefArg + 'static) { + map.insert(attr.to_string(), Variant(Box::new(value))); +} + +impl CurrentStateInner { + fn mpris_volume(&self) -> f64 { + self.volume as f64 / u16::MAX as f64 + } + + fn get_position(&self) -> Option { + let position = self.position.as_ref()?; + match self.status { + PlaybackStatus::Playing => Some(position.get_position()), + PlaybackStatus::Paused => Some(position.last_position), + PlaybackStatus::Stopped => None, + } + } + + fn update_position(&mut self, position: Duration) { + self.position + .get_or_insert_with(Position::new) + .update_position(position); + } + + fn handle_event(&mut self, event: PlayerEvent) -> (DbusMap, bool) { + let mut changed = DbusMap::new(); + let mut seeked = false; + let mut current_track_id = None; + match event { + PlayerEvent::VolumeChanged { volume } => { + self.volume = volume; + insert_attr(&mut changed, "Volume", self.mpris_volume()); + } + PlayerEvent::Stopped { .. } => { + self.status = PlaybackStatus::Stopped; + insert_attr( + &mut changed, + "PlaybackStatus", + self.status.to_mpris().to_string(), + ); + } + PlayerEvent::Playing { + track_id, + position_ms, + .. + } => { + if self.status != PlaybackStatus::Playing { + self.status = PlaybackStatus::Playing; + insert_attr( + &mut changed, + "PlaybackStatus", + self.status.to_mpris().to_string(), + ); + } + self.update_position(Duration::milliseconds(position_ms as i64)); + seeked = true; + current_track_id = Some(track_id); + } + PlayerEvent::Paused { + track_id, + position_ms, + .. + } => { + if self.status != PlaybackStatus::Paused { + self.status = PlaybackStatus::Paused; + insert_attr( + &mut changed, + "PlaybackStatus", + self.status.to_mpris().to_string(), + ) } - } else { - self.token_request = Some(Box::pin({ - let sess = self.session.clone(); - // This is more meant as a fast hotfix than anything else! - let client_id = - env::var("SPOTIFYD_CLIENT_ID").unwrap_or_else(|_| CLIENT_ID.to_string()); - async move { get_token(&sess, &client_id, SCOPE).await } - })); + current_track_id = Some(track_id); + self.update_position(Duration::milliseconds(position_ms as i64)); + seeked = true; + } + PlayerEvent::TrackChanged { audio_item } => { + self.audio_item = Some(audio_item); + insert_attr(&mut changed, "Metadata", self.to_metadata()); } + PlayerEvent::Loading { track_id, .. } => current_track_id = Some(track_id), + PlayerEvent::PositionCorrection { + track_id, + position_ms, + .. + } + | PlayerEvent::Seeked { + track_id, + position_ms, + .. + } => { + current_track_id = Some(track_id); + self.update_position(Duration::milliseconds(position_ms as i64)); + seeked = true; + } + PlayerEvent::ShuffleChanged { shuffle } => { + self.shuffle = shuffle; + insert_attr(&mut changed, "Shuffle", self.shuffle); + } + PlayerEvent::RepeatChanged { repeat } => { + self.repeat = repeat.into(); + insert_attr( + &mut changed, + "LoopStatus", + self.repeat.to_mpris().to_string(), + ) + } + PlayerEvent::Preloading { .. } + | PlayerEvent::TimeToPreloadNextTrack { .. } + | PlayerEvent::EndOfTrack { .. } + | PlayerEvent::Unavailable { .. } + | PlayerEvent::PlayRequestIdChanged { .. } + | PlayerEvent::AutoPlayChanged { .. } + | PlayerEvent::FilterExplicitContentChanged { .. } + | PlayerEvent::SessionConnected { .. } + | PlayerEvent::SessionDisconnected { .. } + | PlayerEvent::SessionClientChanged { .. } => (), } - // Not polling the future here in some cases is fine, since we will poll it - // immediately after the token request has completed. - // If we would poll the future in any case, we would risk using invalid tokens for API requests. - if self.token_request.is_none() { - if let Some(ref mut fut) = self.dbus_future { - return fut.as_mut().poll(cx); + if current_track_id + .zip(self.audio_item.as_ref()) + .is_some_and(|(track_id, item)| track_id != item.track_id) + { + self.audio_item = None; + insert_attr(&mut changed, "Metadata", self.to_metadata()); + } + + (changed, seeked) + } + + fn to_metadata(&self) -> DbusMap { + let mut m = HashMap::new(); + + insert_attr( + &mut m, + "mpris:trackid", + uri_to_object_path( + self.audio_item + .as_deref() + .and_then(|item| item.track_id.to_uri().ok()) + .as_deref(), + ), + ); + + if let Some(audio_item) = self.audio_item.as_deref() { + if let Some(length) = + Duration::milliseconds(audio_item.duration_ms as i64).num_microseconds() + { + insert_attr(&mut m, "mpris:length", length); + } + + if let Some(cover) = audio_item.covers.iter().max_by_key(|im| im.width) { + insert_attr(&mut m, "mpris:artUrl", cover.url.clone()); + } + + insert_attr(&mut m, "xesam:title", audio_item.name.clone()); + + use librespot_metadata::audio::UniqueFields::*; + match &audio_item.unique_fields { + Track { + artists, + album, + album_artists, + popularity, + number, + disc_number, + } => { + insert_attr( + &mut m, + "xesam:artist", + artists + .iter() + .map(|artist| artist.name.clone()) + .collect::>(), + ); + insert_attr(&mut m, "xesam:album", album.clone()); + insert_attr(&mut m, "xesam:albumArtist", album_artists.clone()); + insert_attr(&mut m, "xesam:autoRating", (*popularity as f64) / 100.0); + insert_attr(&mut m, "xesam:trackNumber", *number); + insert_attr(&mut m, "xesam:discNumber", *disc_number); + } + Episode { + description, + publish_time, + show_name, + } => { + insert_attr(&mut m, "xesam:artist", vec![show_name.clone()]); + insert_attr(&mut m, "xesam:comment", vec![description.clone()]); + if let Ok(formatted_publish) = publish_time.format(&Iso8601::DEFAULT) { + insert_attr(&mut m, "xesam:contentCreated", formatted_publish); + } + } } } - Poll::Pending + m } } -#[derive(Clone, Copy, PartialEq, Eq)] -enum PlaybackStatus { - Playing, - Paused, - Stopped, +#[derive(Debug)] +struct CurrentState(RwLock); + +#[derive(Clone, Copy, Debug, Error)] +#[error("internal state no longer available due to application error")] +struct StatePoisonError; + +impl From for MethodErr { + fn from(value: StatePoisonError) -> Self { + MethodErr::failed(&value) + } +} + +impl CurrentState { + fn new(inner: CurrentStateInner) -> Self { + Self(RwLock::new(inner)) + } + + fn read(&self) -> Result, StatePoisonError> { + self.0.read().map_err(|_| StatePoisonError) + } + + fn write(&self) -> Result, StatePoisonError> { + self.0.write().map_err(|_| StatePoisonError) + } } async fn create_dbus_server( - spotify_api_client: Arc, - spirc: Arc, - device_name: String, mut event_rx: UnboundedReceiver, + mut control_rx: UnboundedReceiver, dbus_type: DBusType, -) { + session: Session, +) -> Result<(), DbusError> { let (resource, conn) = match dbus_type { DBusType::Session => connection::new_session_sync(), DBusType::System => connection::new_system_sync(), - } - .expect("Failed to initialize DBus connection"); - tokio::spawn(async { - let err = resource.await; - panic!("Lost connection to D-Bus: {}", err); - }); + }?; + let mut connection_task = tokio::spawn(async { Err::<(), _>(resource.await) }); - let path = format!( + // this name will be used, once we can provide the mpris interface + let mpris_name = format!( "org.mpris.MediaPlayer2.spotifyd.instance{}", std::process::id() ); + // this name will always be available to allow easy discovery of the controls interface + let spotifyd_name = format!("rs.spotifyd.instance{}", std::process::id()); - // TODO: The first `true` allows us to replace orphaned dbus servers from previous sessions - // later. We should instead properly release the name when the session ends. - conn.request_name(path, true, true, true) - .await - .expect("Failed to register dbus player name"); + conn.request_name(&spotifyd_name, false, true, true).await?; let mut cr = Crossroads::new(); cr.set_async_support(Some(( @@ -191,16 +442,155 @@ async fn create_dbus_server( }), ))); + let current_state = Arc::new(CurrentState::new(CurrentStateInner { + status: PlaybackStatus::Stopped, + position: None, + audio_item: None, + volume: u16::MAX, + shuffle: false, + repeat: RepeatState::None, + })); + + let (quit_tx, mut quit_rx) = tokio::sync::mpsc::unbounded_channel(); + + let cr = Arc::new(Mutex::new(cr)); + + let crossroads = cr.clone(); + conn.start_receive( + MatchRule::new_method_call(), + Box::new(move |msg, conn| { + tokio::task::block_in_place(|| { + let mut cr = cr.blocking_lock(); + cr.handle_message(msg, conn).unwrap(); + }); + true + }), + ); + + let mut spirc: Option> = None; + + struct ConnectionData { + conn_id: String, + seeked_fn: SeekedSignal, + } + let mut cur_conn: Option = None; + + loop { + tokio::select! { + _ = quit_rx.recv() => { + break; + } + result = &mut connection_task => { + result.expect("the dbus connection panicked")?; + break; + } + event = event_rx.recv() => { + let event = event.expect("event channel was unexpectedly closed"); + + if let PlayerEvent::SessionConnected { connection_id, .. } = event { + let mut cr = crossroads.lock().await; + let spirc = spirc.clone().unwrap(); + let seeked_fn = register_player_interface( + &mut cr, + spirc, + session.clone(), + current_state.clone(), + quit_tx.clone(), + ); + if cur_conn.is_none() { + conn.request_name(&mpris_name, true, true, true).await?; + } + cur_conn = Some(ConnectionData { conn_id: connection_id, seeked_fn }); + } else if let PlayerEvent::SessionDisconnected { connection_id, .. } = event { + // if this message isn't outdated yet, we vanish from the bus + if cur_conn.as_ref().is_some_and(|d| d.conn_id == connection_id) { + let mut cr = crossroads.lock().await; + conn.release_name(&mpris_name).await?; + cr.remove::<()>(&MPRIS_PATH.into()); + cur_conn = None; + } + } else { + let (changed, seeked) = current_state + .write() + .expect("state has been poisoned") + .handle_event(event); + + if seeked { + let position = current_state + .read() + .expect("state has been poisoned") + .get_position(); + if let Some((ConnectionData { seeked_fn, .. }, position)) = + Option::zip(cur_conn.as_ref(), position) + { + let msg = seeked_fn( + &MPRIS_PATH.into(), + &(position.num_microseconds().unwrap_or_default(),), + ); + conn.send(msg).unwrap(); + } + } + + if !changed.is_empty() { + let msg = PropertiesPropertiesChanged { + interface_name: "org.mpris.MediaPlayer2.Player".to_owned(), + changed_properties: changed, + invalidated_properties: Vec::new(), + }; + conn.send( + msg.to_emit_message(&dbus::Path::new(MPRIS_PATH).unwrap()), + ) + .unwrap(); + } + } + + } + control = control_rx.recv() => { + let control = control.expect("control channel was unexpectedly closed"); + match control { + ControlMessage::Shutdown => { + break; + }, + ControlMessage::SetSpirc(new_spirc) => { + let mut cr = crossroads.lock().await; + register_controls_interface(&mut cr, new_spirc.clone()); + spirc = Some(new_spirc); + } + ControlMessage::DropSpirc => { + let mut cr = crossroads.lock().await; + conn.release_name(&mpris_name).await?; + cr.remove::<()>(&MPRIS_PATH.into()); + cr.remove::<()>(&CONTROLS_PATH.into()); + spirc = None; + } + } + } + } + } + conn.release_name(&mpris_name).await?; + conn.release_name(&spotifyd_name).await?; + Ok(()) +} + +type SeekedSignal = Box dbus::Message + Send + Sync + 'static>; + +fn register_player_interface( + cr: &mut Crossroads, + spirc: Arc, + session: Session, + current_state: Arc, + quit_tx: tokio::sync::mpsc::UnboundedSender<()>, +) -> SeekedSignal { // The following methods and properties are part of the MediaPlayer2 interface. // https://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html - let media_player2_interface = cr.register("org.mpris.MediaPlayer2", |b| { + let media_player2_interface = cr.register("org.mpris.MediaPlayer2", move |b| { + let mut quit_tx = Some(quit_tx); b.method("Raise", (), (), move |_, _, (): ()| { // noop Ok(()) }); - let local_spirc = spirc.clone(); b.method("Quit", (), (), move |_, _, (): ()| { - local_spirc.shutdown(); + quit_tx.take().unwrap().send(()).ok(); Ok(()) }); b.property("CanQuit") @@ -229,232 +619,202 @@ async fn create_dbus_server( // The following methods and properties are part of the MediaPlayer2.Player interface. // https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html + let mut seeked_signal = None; + let player_interface: IfaceToken<()> = cr.register("org.mpris.MediaPlayer2.Player", |b| { + seeked_signal = Some(b.signal::<(i64,), _>("Seeked", ("Position",)).msg_fn()); let local_spirc = spirc.clone(); b.method("VolumeUp", (), (), move |_, _, (): ()| { - local_spirc.volume_up(); - Ok(()) + local_spirc.volume_up().map_err(|e| MethodErr::failed(&e)) }) .deprecated(); let local_spirc = spirc.clone(); b.method("VolumeDown", (), (), move |_, _, (): ()| { - local_spirc.volume_down(); - Ok(()) + local_spirc.volume_down().map_err(|e| MethodErr::failed(&e)) }) .deprecated(); let local_spirc = spirc.clone(); b.method("Next", (), (), move |_, _, (): ()| { - local_spirc.next(); - Ok(()) + local_spirc.next().map_err(|e| MethodErr::failed(&e)) }); let local_spirc = spirc.clone(); b.method("Previous", (), (), move |_, _, (): ()| { - local_spirc.prev(); - Ok(()) + local_spirc.prev().map_err(|e| MethodErr::failed(&e)) }); let local_spirc = spirc.clone(); b.method("Pause", (), (), move |_, _, (): ()| { - local_spirc.pause(); - Ok(()) + local_spirc.pause().map_err(|e| MethodErr::failed(&e)) }); let local_spirc = spirc.clone(); b.method("PlayPause", (), (), move |_, _, (): ()| { - local_spirc.play_pause(); - Ok(()) + local_spirc.play_pause().map_err(|e| MethodErr::failed(&e)) }); let local_spirc = spirc.clone(); b.method("Play", (), (), move |_, _, (): ()| { - local_spirc.play(); - Ok(()) + local_spirc.play().map_err(|e| MethodErr::failed(&e)) }); let local_spirc = spirc.clone(); b.method("Stop", (), (), move |_, _, (): ()| { - // TODO: add real stop implementation. - local_spirc.pause(); - Ok(()) + local_spirc.disconnect().map_err(|e| MethodErr::failed(&e)) }); - let mv_device_name = device_name.clone(); - let sp_client = Arc::clone(&spotify_api_client); - b.method("Seek", ("offset",), (), move |_, _, (pos,): (i64,)| { - if let Ok(Some(playback)) = sp_client.current_playback(None, None::>) { - if playback.device.name == mv_device_name { - let new_pos = playback - .progress - .and_then(|d| d.checked_add(&Duration::milliseconds(pos / 1000))); - - if let Some(new_pos) = new_pos { - let duration: Duration = match playback.item { - Some(PlayableItem::Track(t)) => t.duration, - Some(PlayableItem::Episode(e)) => e.duration, - None => return Ok(()), - }; - - // MPRIS spec: negative values should be treated as 0 - let new_pos = new_pos.max(Duration::zero()); - if new_pos <= duration { - let _ = sp_client.seek_track(new_pos, playback.device.id.as_deref()); - } else { - // MPRIS spec: values beyond track bounds should act like Next - let _ = sp_client.next_track(playback.device.id.as_deref()); - } - } - } + let local_spirc = spirc.clone(); + let local_state = current_state.clone(); + b.method("Seek", ("offset",), (), move |_, _, (offset,): (i64,)| { + let Some(position) = local_state.read()?.get_position() else { + return Err(dbus::MethodErr::failed( + "cannot seek while playback is stopped", + )); + }; + let new_pos = position + Duration::microseconds(offset); + let new_pos_ms = u32::try_from(new_pos.num_milliseconds()).map_err(|err| { + dbus::MethodErr::invalid_arg(&format!("new position out of bounds: {err}")) + })?; + if let Err(err) = local_spirc.set_position_ms(new_pos_ms) { + warn!("failed to seek by {offset}ms: {err}"); + return Err(dbus::MethodErr::failed(&err)); } Ok(()) }); - let mv_device_name = device_name.clone(); - let sp_client = Arc::clone(&spotify_api_client); + let local_spirc = spirc.clone(); + let local_state = current_state.clone(); b.method( "SetPosition", ("track_id", "position"), (), move |_, _, (track_id, pos): (dbus::Path, i64)| { - if let Ok(Some(playback)) = sp_client.current_playback(None, None::>) { - let (track_matches, duration) = if let Some(item) = playback.item { - let track_matches = item - .id() - .map(|id| uri_to_object_path(id.uri()) == track_id) - .unwrap_or(false); - let duration = match item { - PlayableItem::Track(t) => t.duration.num_microseconds(), - PlayableItem::Episode(e) => e.duration.num_microseconds(), - } - .unwrap_or(i64::MAX); - (track_matches, duration) - } else { - return Ok(()); - }; - - // as per MPRIS spec: pos must be in track bounds and track id must match - if playback.device.name == mv_device_name - && track_matches - && (0..=duration).contains(&pos) - { - // pos is in microseconds, seek_track takes milliseconds - let _ = sp_client.seek_track( - Duration::milliseconds(pos / 1000), - playback.device.id.as_deref(), - ); - } + let Some((current_track_id, duration)) = local_state + .read()? + .audio_item + .as_ref() + .map(|item| (item.track_id, item.duration_ms)) + else { + return Err(dbus::MethodErr::failed( + "can set position while nothing is playing", + )); + }; + let duration = Duration::milliseconds(duration.into()); + + if !track_id.ends_with(¤t_track_id.to_base62().unwrap()) { + // as per mpris spec: ignore as stale + return Ok(()); + } + let new_position = Duration::microseconds(pos); + if new_position < Duration::zero() || new_position > duration { + // ignore as per spec + return Ok(()); + } + if let Err(err) = + local_spirc.set_position_ms(new_position.num_milliseconds() as u32) + { + return Err(dbus::MethodErr::failed(&err)); } + Ok(()) }, ); - let mv_device_name = device_name.clone(); - let sp_client = Arc::clone(&spotify_api_client); + let local_spirc = spirc.clone(); + let local_state = current_state.clone(); b.method("OpenUri", ("uri",), (), move |_, _, (uri,): (String,)| { - enum AnyId<'a> { - Playable(PlayableId<'a>), - Context(PlayContextId<'a>), - } - - fn uri_to_id(uri: &str) -> Result, IdError> { - use AnyId::*; - Ok(match parse_uri(uri)? { - (Type::Track, id) => Playable(TrackId::from_id(id)?.into()), - (Type::Episode, id) => Playable(EpisodeId::from_id(id)?.into()), - (Type::Artist, id) => Context(ArtistId::from_id(id)?.into()), - (Type::Album, id) => Context(AlbumId::from_id(id)?.into()), - (Type::Playlist, id) => Context(PlaylistId::from_id(id)?.into()), - (Type::Show, id) => Context(ShowId::from_id(id)?.into()), - (Type::User | Type::Collection | Type::Collectionyourepisodes, _) => { - Err(IdError::InvalidType)? - } - }) + let id = SpotifyId::from_uri(&uri).map_err(|e| MethodErr::invalid_arg(&e))?; + let CurrentStateInner { + shuffle, repeat, .. + } = *local_state.read()?; + + fn id_to_trackref(id: &SpotifyId) -> TrackRef { + let mut trackref = TrackRef::new(); + if let Ok(uri) = id.to_uri() { + trackref.set_uri(uri); + } else { + trackref.set_gid(id.to_raw().to_vec()); + } + trackref } - let id = uri_to_id(&uri).map_err(|e| MethodErr::invalid_arg(&e))?; + let session = session.clone(); - let device_id = get_device_id(&sp_client, &mv_device_name, true); - - if let Some(device_id) = device_id { - match id { - AnyId::Playable(id) => { - let _ = sp_client.start_uris_playback( - Some(id), - Some(&device_id), - Some(Offset::Position(Duration::zero())), - None, - ); - } - AnyId::Context(id) => { - let _ = sp_client.start_context_playback( - id, - Some(&device_id), - Some(Offset::Position(Duration::zero())), - None, - ); - } - } - } - Ok(()) + let (playing_track_index, context_uri, tracks) = Handle::current() + .block_on(async move { + use librespot_metadata::*; + Ok::<_, librespot_core::Error>(match id.item_type { + SpotifyItemType::Album => { + let album = Album::get(&session, &id).await?; + (0, uri, album.tracks().map(id_to_trackref).collect()) + } + SpotifyItemType::Artist => { + let artist = Artist::get(&session, &id).await?; + ( + 0, + uri, + artist + .top_tracks + .for_country(&session.country()) + .iter() + .map(id_to_trackref) + .collect(), + ) + } + SpotifyItemType::Playlist => { + let playlist = Playlist::get(&session, &id).await?; + (0, uri, playlist.tracks().map(id_to_trackref).collect()) + } + SpotifyItemType::Track => { + let track = Track::get(&session, &id).await?; + ( + track.number as u32, + track.album.id.to_uri()?, + vec![id_to_trackref(&track.id)], + ) + } + SpotifyItemType::Episode => (0, uri, vec![id_to_trackref(&id)]), + SpotifyItemType::Show => { + let show = Show::get(&session, &id).await?; + (0, uri, show.episodes.iter().map(id_to_trackref).collect()) + } + SpotifyItemType::Local | SpotifyItemType::Unknown => { + return Err(librespot_core::Error::unimplemented( + "this type of uri is not supported", + )); + } + }) + }) + .map_err(|e| MethodErr::failed(&e))?; + + local_spirc + .load(SpircLoadCommand { + context_uri, + start_playing: true, + shuffle, + repeat: repeat.into(), + playing_track_index, + tracks, + }) + .map_err(|e| MethodErr::failed(&e)) }); - let mv_device_name = device_name.clone(); - let sp_client = Arc::clone(&spotify_api_client); + let local_state = current_state.clone(); b.property("PlaybackStatus") .emits_changed_false() .get(move |_, _| { - if let Ok(Some(playback)) = sp_client.current_playback(None, None::>) { - if playback.device.name == mv_device_name { - if playback.is_playing { - return Ok("Playing".to_string()); - } else { - return Ok("Paused".to_string()); - } - } - } - Ok("Stopped".to_string()) + let playback_state = local_state.read()?.status; + Ok(playback_state.to_mpris().to_string()) }); - let mv_device_name = device_name.clone(); - let sp_client = Arc::clone(&spotify_api_client); - let sp_client2 = Arc::clone(&spotify_api_client); + let local_spirc = spirc.clone(); + let local_state = current_state.clone(); b.property("Shuffle") .emits_changed_false() - .get(move |_, _| { - let shuffle_status = sp_client - .current_playback(None, None::>) - .ok() - .flatten() - .map_or(false, |p| p.shuffle_state); - Ok(shuffle_status) - }) + .get(move |_, _| Ok(local_state.read()?.shuffle)) .set(move |_, _, value| { - let device_id = get_device_id(&sp_client2, &mv_device_name, true); - if let Some(device_id) = device_id { - match sp_client2.shuffle(value, Some(&device_id)) { - Ok(_) => Ok(None), - Err(err) => { - let e = format!("SetShuffle failed: {}", err); - error!("{}", e); - Err(MethodErr::failed(&e)) - } - } - } else { - let msg = format!("Could not find device with name {}", mv_device_name); - warn!("SetShuffle: {}", msg); - Err(MethodErr::failed(&msg)) - } + local_spirc + .shuffle(value) + .map(|_| None) + .map_err(|err| dbus::MethodErr::failed(&err)) }); b.property("Rate").emits_changed_const().get(|_, _| Ok(1.0)); - - let sp_client = Arc::clone(&spotify_api_client); - b.property("Volume").emits_changed_false().get(move |_, _| { - let vol = sp_client - .current_playback(None, None::>) - .ok() - .flatten() - .and_then(|p| p.device.volume_percent) - .unwrap_or(0) as f64; - - Ok(vol) - }); - b.property("MaximumRate") .emits_changed_const() .get(|_, _| Ok(1.0)); @@ -462,59 +822,57 @@ async fn create_dbus_server( .emits_changed_const() .get(|_, _| Ok(1.0)); - let sp_client = Arc::clone(&spotify_api_client); + let local_spirc = spirc.clone(); + let local_state = current_state.clone(); + b.property("Volume") + .emits_changed_false() + .get(move |_, _| Ok(local_state.read()?.mpris_volume())) + .set(move |_, _, value| { + if let Err(err) = local_spirc.set_volume((value * u16::MAX as f64) as u16) { + return Err(dbus::MethodErr::failed(&err)); + } + Ok(None) + }); + + let local_spirc = spirc.clone(); + let local_state = current_state.clone(); b.property("LoopStatus") .emits_changed_false() .get(move |_, _| { - let status = - if let Ok(Some(player)) = sp_client.current_playback(None, None::>) { - match player.repeat_state { - RepeatState::Off => "None", - RepeatState::Track => "Track", - RepeatState::Context => "Playlist", - } - } else { - "None" + let repeat = local_state.read()?.repeat; + Ok(repeat.to_mpris().to_string()) + }) + .set(move |_, _, value| { + let new_repeat = match value.as_str() { + "None" => false, + "Playlist" => true, + mode => { + return Err(dbus::MethodErr::failed(&format!( + "unsupported repeat mode: {mode}" + ))) } - .to_string(); - Ok(status) + }; + local_spirc + .repeat(new_repeat) + .map_err(|e| MethodErr::failed(&e))?; + Ok(None) }); - let sp_client = Arc::clone(&spotify_api_client); + let local_state = current_state.clone(); b.property("Position") .emits_changed_false() .get(move |_, _| { - let pos = sp_client - .current_playback(None, None::>) - .ok() - .flatten() - .and_then(|p| p.progress?.num_microseconds()) - .unwrap_or(0); - - Ok(pos) + let Some(position) = local_state.read()?.get_position() else { + return Err(dbus::MethodErr::failed("no position available currently")); + }; + + Ok(position.num_microseconds().unwrap_or_default()) }); - let sp_client = Arc::clone(&spotify_api_client); + let local_state = current_state.clone(); b.property("Metadata") .emits_changed_false() - .get(move |_, _| { - let mut m: HashMap>> = HashMap::new(); - let item = match sp_client.current_playing(None, None::>) { - Ok(playing) => playing.and_then(|playing| playing.item), - Err(e) => { - info!("Couldn't fetch metadata from spotify: {:?}", e); - return Ok(m); - } - }; - - if let Some(item) = item { - insert_metadata(&mut m, item); - } else { - info!("Couldn't fetch metadata from spotify: Nothing playing at the moment."); - } - - Ok(m) - }); + .get(move |_, _| Ok(local_state.read()?.to_metadata())); for prop in [ "CanPlay", @@ -528,202 +886,35 @@ async fn create_dbus_server( } }); + cr.insert(MPRIS_PATH, &[media_player2_interface, player_interface], ()); + + seeked_signal.expect("player interface has not been registered") +} + +fn register_controls_interface(cr: &mut Crossroads, spirc: Arc) { let spotifyd_ctrls_interface: IfaceToken<()> = cr.register("rs.spotifyd.Controls", |b| { let local_spirc = spirc.clone(); b.method("VolumeUp", (), (), move |_, _, (): ()| { - local_spirc.volume_up(); - Ok(()) + local_spirc.volume_up().map_err(|e| MethodErr::failed(&e)) }); let local_spirc = spirc.clone(); b.method("VolumeDown", (), (), move |_, _, (): ()| { - local_spirc.volume_down(); - Ok(()) + local_spirc.volume_down().map_err(|e| MethodErr::failed(&e)) }); - let mv_device_name = device_name.clone(); - let sp_client = Arc::clone(&spotify_api_client); + let local_spirc = spirc.clone(); b.method("TransferPlayback", (), (), move |_, _, (): ()| { - let device_id = get_device_id(&sp_client, &mv_device_name, false); - if let Some(device_id) = device_id { - info!("Transferring playback to device {}", device_id); - match sp_client.transfer_playback(&device_id, Some(true)) { - Ok(_) => Ok(()), - Err(err) => { - let e = format!("TransferPlayback failed: {}", err); - error!("{}", e); - Err(MethodErr::failed(&e)) - } - } - } else { - let msg = format!("Could not find device with name {}", mv_device_name); - warn!("TransferPlayback: {}", msg); - Err(MethodErr::failed(&msg)) - } + local_spirc.activate().map_err(|e| MethodErr::failed(&e)) }); }); - cr.insert( - "/org/mpris/MediaPlayer2", - &[media_player2_interface, player_interface], - (), - ); - - cr.insert("/rs/spotifyd/Controls", &[spotifyd_ctrls_interface], ()); - - conn.start_receive( - MatchRule::new_method_call(), - Box::new(move |msg, conn| { - cr.handle_message(msg, conn).unwrap(); - true - }), - ); - - // Store current playback state to be able to detect changes - let mut last_track_id = None; - let mut last_playback_status = None; - let mut last_volume = None; - - loop { - let event = event_rx - .recv() - .await - .expect("Changed track channel was unexpectedly closed"); - let mut seeked_position_ms = None; - - // Update playback state from event - let (track_id, playback_status, player_volume) = match event { - PlayerEvent::VolumeSet { volume } => { - (last_track_id, last_playback_status, Some(volume)) - } - PlayerEvent::Playing { - track_id, - position_ms, - .. - } => { - seeked_position_ms = Some(position_ms); - (Some(track_id), Some(PlaybackStatus::Playing), last_volume) - } - PlayerEvent::Stopped { .. } => { - (last_track_id, Some(PlaybackStatus::Stopped), last_volume) - } - PlayerEvent::Paused { .. } => { - (last_track_id, Some(PlaybackStatus::Paused), last_volume) - } - _ => continue, - }; - - // if playback_status, track_id or volume have changed, emit a PropertiesChanged signal - if last_playback_status != playback_status - || last_track_id != track_id - || last_volume != player_volume - { - let mut changed_properties: HashMap>> = HashMap::new(); - - if last_volume != player_volume { - if let Some(player_volume) = player_volume { - // convert u16 to float - let mut vol_mpris = player_volume as f64; - // max. vol = 1.0 according to mpris spec, round to two decimal places - vol_mpris = (vol_mpris / 65535.0 * 100.0).round() / 100.0; - changed_properties - .insert("Volume".to_owned(), Variant(Box::new(vol_mpris.to_owned()))); - } - } else { - if let Some(track_id) = track_id { - let item = match track_id.audio_type { - SpotifyAudioType::Track => { - let track_id = TrackId::from_id(track_id.to_base62().unwrap()).unwrap(); - let track = spotify_api_client - .track(track_id, None) - .map(PlayableItem::Track); - Some(track) - } - SpotifyAudioType::Podcast => { - let id = EpisodeId::from_id(track_id.to_base62().unwrap()).unwrap(); - let episode = spotify_api_client - .get_an_episode(id, None) - .map(PlayableItem::Episode); - Some(episode) - } - SpotifyAudioType::NonPlayable => None, - }; - - if let Some(item) = item { - match item { - Ok(item) => { - let mut m: HashMap>> = - HashMap::new(); - insert_metadata(&mut m, item); - - changed_properties - .insert("Metadata".to_owned(), Variant(Box::new(m))); - } - Err(e) => info!("Couldn't fetch metadata from spotify: {:?}", e), - } - } - } - if let Some(playback_status) = playback_status { - changed_properties.insert( - "PlaybackStatus".to_owned(), - Variant(Box::new(match playback_status { - PlaybackStatus::Playing => "Playing".to_owned(), - PlaybackStatus::Paused => "Paused".to_owned(), - PlaybackStatus::Stopped => "Stopped".to_owned(), - })), - ); - } - } - - let msg = dbus::nonblock::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged { - interface_name: "org.mpris.MediaPlayer2.Player".to_owned(), - changed_properties, - invalidated_properties: Vec::new(), - }; - conn.send(msg.to_emit_message(&dbus::Path::new("/org/mpris/MediaPlayer2").unwrap())) - .unwrap(); - - last_playback_status = playback_status; - last_track_id = track_id; - last_volume = player_volume; - } - - // if position in track has changed emit a Seeked signal - if let Some(position_ms) = seeked_position_ms { - let msg = dbus::message::Message::signal( - &dbus::Path::new("/org/mpris/MediaPlayer2").unwrap(), - &dbus::strings::Interface::new("org.mpris.MediaPlayer2.Player").unwrap(), - &dbus::strings::Member::new("Seeked").unwrap(), - ) - // position should be in microseconds - .append1(position_ms as i64 * 1000); - conn.send(msg).unwrap(); - } - } + cr.insert(CONTROLS_PATH, &[spotifyd_ctrls_interface], ()); } -fn get_device_id( - sp_client: &AuthCodeSpotify, - device_name: &str, - only_active: bool, -) -> Option { - let device_result = sp_client.device(); - match device_result { - Ok(devices) => devices.into_iter().find_map(|d| { - if d.name == device_name && (d.is_active || !only_active) { - info!("Found device: {}, active: {}", d.name, d.is_active); - d.id - } else { - None - } - }), - Err(err) => { - error!("Get devices error: {}", err); - None - } - } -} - -fn uri_to_object_path(uri: String) -> dbus::Path<'static> { +fn uri_to_object_path(uri: Option<&str>) -> dbus::Path<'static> { + let Some(uri) = uri else { + return dbus::Path::new("/org/mpris/MediaPlayer2/TrackList/NoTrack").unwrap(); + }; let mut path = String::with_capacity(uri.len() + 1); for element in uri.split(':') { path.push('/'); @@ -731,121 +922,3 @@ fn uri_to_object_path(uri: String) -> dbus::Path<'static> { } dbus::Path::new(path).unwrap() } - -fn insert_metadata(m: &mut HashMap>>, item: PlayableItem) { - use rspotify::model::{ - Image, - PlayableItem::{Episode, Track}, - }; - - // some fields that only make sense or only exist for tracks - struct TrackFields { - artists: Vec, - popularity: u32, - track_number: u32, - disc_number: i32, - } - - // a common denominator struct for FullEpisode and FullTrack - struct TrackOrEpisode { - id: Option>, - duration: chrono::Duration, - images: Vec, - name: String, - album_name: String, - album_artists: Vec, - external_urls: HashMap, - track_fields: Option, - } - - let item = match item { - Track(t) => TrackOrEpisode { - id: t.id.map(|t| uri_to_object_path(t.uri())), - duration: t.duration, - images: t.album.images, - name: t.name, - album_name: t.album.name, - album_artists: t.album.artists.into_iter().map(|a| a.name).collect(), - external_urls: t.external_urls, - track_fields: Some(TrackFields { - artists: t.artists.into_iter().map(|a| a.name).collect(), - popularity: t.popularity, - track_number: t.track_number, - disc_number: t.disc_number, - }), - }, - Episode(e) => TrackOrEpisode { - id: Some(uri_to_object_path(e.id.uri())), - duration: e.duration, - images: e.show.images, - name: e.name, - album_name: e.show.name, - album_artists: vec![e.show.publisher], - external_urls: e.external_urls, - track_fields: None, - }, - }; - - m.insert( - "mpris:trackid".to_string(), - Variant(Box::new(item.id.unwrap_or_default())), - ); - - m.insert( - "mpris:length".to_string(), - Variant(Box::new( - item.duration.num_microseconds().unwrap_or_default(), - )), - ); - - m.insert( - "mpris:artUrl".to_string(), - Variant(Box::new( - item.images - .into_iter() - .max_by_key(|i| i.width.unwrap_or(0)) - .map(|i| i.url) - .unwrap_or_default(), - )), - ); - - m.insert("xesam:title".to_string(), Variant(Box::new(item.name))); - - m.insert( - "xesam:album".to_string(), - Variant(Box::new(item.album_name)), - ); - - m.insert( - "xesam:albumArtist".to_string(), - Variant(Box::new(item.album_artists)), - ); - - if let Some(track) = item.track_fields { - m.insert("xesam:artist".to_string(), Variant(Box::new(track.artists))); - - m.insert( - "xesam:autoRating".to_string(), - Variant(Box::new(f64::from(track.popularity) / 100.0)), - ); - - m.insert( - "xesam:trackNumber".to_string(), - Variant(Box::new(track.track_number)), - ); - - m.insert( - "xesam:discNumber".to_string(), - Variant(Box::new(track.disc_number)), - ); - } - - // to avoid cloning here, we take the relevant url directly from the HashMap - let mut external_urls = item.external_urls; - m.insert( - "xesam:url".to_string(), - Variant(Box::new( - external_urls.remove("spotify").unwrap_or_default(), - )), - ); -} diff --git a/src/main.rs b/src/main.rs index deb36d88..f041ab91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,19 @@ use crate::config::CliConfig; +use clap::Parser; #[cfg(unix)] use color_eyre::eyre::eyre; -use color_eyre::{ - eyre::{self, Context}, - Help, SectionExt, -}; +use color_eyre::eyre::{self, Context}; +use config::ExecutionMode; #[cfg(unix)] use daemonize::Daemonize; #[cfg(unix)] use log::error; use log::{info, trace, LevelFilter}; +use oauth::run_oauth; #[cfg(target_os = "openbsd")] use pledge::pledge; #[cfg(windows)] use std::fs; -use structopt::StructOpt; use tokio::runtime::Runtime; #[cfg(feature = "alsa_backend")] @@ -25,6 +24,7 @@ mod dbus_mpris; mod error; mod main_loop; mod no_mixer; +mod oauth; mod process; mod setup; mod utils; @@ -34,18 +34,26 @@ enum LogTarget { Syslog, } -fn setup_logger(log_target: LogTarget, verbose: bool) -> eyre::Result<()> { - let log_level = if verbose { - LevelFilter::Trace - } else { - LevelFilter::Info +fn setup_logger(log_target: LogTarget, verbose: u8) -> eyre::Result<()> { + let log_level = match verbose { + 0 => LevelFilter::Info, + 1 => LevelFilter::Debug, + 2.. => LevelFilter::Trace, }; - let mut logger = fern::Dispatch::new().level(log_level); - if cfg!(feature = "dbus_mpris") && !verbose { - logger = logger.level_for("rspotify_http", LevelFilter::Warn); - } + logger = if verbose > 0 { + logger.format(|out, message, record| { + out.finish(format_args!( + "[{} {}] {}", + record.level(), + record.target(), + message + )) + }) + } else { + logger.level_for("symphonia_format_ogg::demuxer", LevelFilter::Warn) + }; let logger = match log_target { LogTarget::Terminal => logger.chain(std::io::stdout()), @@ -55,7 +63,7 @@ fn setup_logger(log_target: LogTarget, verbose: bool) -> eyre::Result<()> { facility: syslog::Facility::LOG_DAEMON, hostname: None, process: "spotifyd".to_owned(), - pid: 0, + pid: std::process::id(), }; logger.chain( syslog::unix(log_format) @@ -98,8 +106,15 @@ fn main() -> eyre::Result<()> { color_eyre::install().wrap_err("Couldn't initialize error reporting")?; - let mut cli_config: CliConfig = CliConfig::from_args(); + let cli_config = CliConfig::parse(); + match cli_config.mode { + None => run_daemon(cli_config), + Some(ExecutionMode::Authenticate { oauth_port }) => run_oauth(cli_config, oauth_port), + } +} + +fn run_daemon(mut cli_config: CliConfig) -> eyre::Result<()> { let is_daemon = !cli_config.no_daemon; let log_target = if is_daemon { @@ -130,14 +145,7 @@ fn main() -> eyre::Result<()> { cli_config .load_config_file_values() - .wrap_err("could not load the config file") - .with_section(|| { - concat!( - "the config format should be valid TOML\n", - "we recently changed the config format, see https://github.com/Spotifyd/spotifyd/issues/765" - ) - .header("note:") - })?; + .wrap_err("could not load the config file")?; trace!("{:?}", &cli_config); // Returns the old SpotifydConfig struct used within the rest of the daemon. @@ -209,9 +217,8 @@ fn main() -> eyre::Result<()> { let runtime = Runtime::new().unwrap(); runtime.block_on(async { - let mut initial_state = setup::initial_state(internal_config); + let initial_state = setup::initial_state(internal_config)?; initial_state.run().await; - }); - - Ok(()) + Ok(()) + }) } diff --git a/src/main_loop.rs b/src/main_loop.rs index 597edcad..b68c9706 100644 --- a/src/main_loop.rs +++ b/src/main_loop.rs @@ -1,67 +1,63 @@ -use crate::config::DBusType; +#[cfg(feature = "dbus_mpris")] +use crate::config::{DBusType, MprisConfig}; #[cfg(feature = "dbus_mpris")] use crate::dbus_mpris::DbusServer; use crate::process::spawn_program_on_event; +use futures::future::Either; +#[cfg(not(feature = "dbus_mpris"))] +use futures::future::Pending; use futures::{ self, future::{self, Fuse, FusedFuture}, stream::Peekable, Future, FutureExt, StreamExt, }; -use librespot_connect::spirc::Spirc; -use librespot_core::{ - authentication::Credentials, - cache::Cache, - config::{ConnectConfig, DeviceType, SessionConfig}, - session::{Session, SessionError}, -}; +use librespot_connect::{config::ConnectConfig, spirc::Spirc}; +use librespot_core::{authentication::Credentials, config::DeviceType, session::Session, Error}; use librespot_discovery::Discovery; -use librespot_playback::{ - audio_backend::Sink, - config::{AudioFormat, PlayerConfig}, - mixer::Mixer, - player::Player, -}; +use librespot_playback::{mixer::Mixer, player::Player}; use log::error; use std::pin::Pin; use std::sync::Arc; -pub struct AudioSetup { - pub mixer: Box Box>, - pub backend: fn(Option, AudioFormat) -> Box, - pub audio_device: Option, - pub audio_format: AudioFormat, -} +#[cfg(not(feature = "dbus_mpris"))] +type DbusServer = Pending<()>; pub struct SpotifydState { - pub cache: Option, pub device_name: String, pub player_event_program: Option, } pub(crate) enum CredentialsProvider { - Discovery(Peekable), - SpotifyCredentials(Credentials), -} - -impl From for CredentialsProvider { - fn from(stream: Discovery) -> Self { - CredentialsProvider::Discovery(stream.peekable()) - } + Discovery { + stream: Peekable, + last_credentials: Option, + }, + CredentialsOnly(Credentials), } impl CredentialsProvider { async fn get_credentials(&mut self) -> Credentials { match self { - CredentialsProvider::Discovery(stream) => stream.next().await.unwrap(), - CredentialsProvider::SpotifyCredentials(creds) => creds.clone(), + CredentialsProvider::Discovery { + stream, + last_credentials, + } => { + let new_creds = match last_credentials.take() { + Some(creds) => stream.next().now_or_never().flatten().unwrap_or(creds), + None => stream.next().await.unwrap(), + }; + *last_credentials = Some(new_creds.clone()); + new_creds + } + CredentialsProvider::CredentialsOnly(creds) => creds.clone(), } } // wait for an incoming connection if the underlying provider is a discovery stream async fn incoming_connection(&mut self) { match self { - CredentialsProvider::Discovery(stream) => { + CredentialsProvider::Discovery { stream, .. } => { let peeked = Pin::new(stream).peek().await; if peeked.is_none() { future::pending().await @@ -73,47 +69,68 @@ impl CredentialsProvider { } pub(crate) struct MainLoop { - pub(crate) audio_setup: AudioSetup, pub(crate) spotifyd_state: SpotifydState, - pub(crate) player_config: PlayerConfig, - pub(crate) session_config: SessionConfig, - pub(crate) autoplay: bool, + pub(crate) mixer: Arc, + pub(crate) session: Session, + pub(crate) player: Arc, pub(crate) has_volume_ctrl: bool, pub(crate) initial_volume: Option, pub(crate) shell: String, pub(crate) device_type: DeviceType, - #[cfg_attr(not(feature = "dbus_mpris"), allow(unused))] - pub(crate) use_mpris: bool, - #[cfg_attr(not(feature = "dbus_mpris"), allow(unused))] - pub(crate) dbus_type: DBusType, pub(crate) credentials_provider: CredentialsProvider, + #[cfg(feature = "dbus_mpris")] + pub(crate) mpris_config: MprisConfig, } impl MainLoop { - async fn get_session(&mut self) -> Result { + async fn get_connection(&mut self) -> Result<(Spirc, impl Future), Error> { let creds = self.credentials_provider.get_credentials().await; - let session_config = self.session_config.clone(); - let cache = self.spotifyd_state.cache.clone(); - - Session::connect(session_config, creds, cache, true) - .await - .map(|(session, _creds)| session) + // TODO: expose is_group + Spirc::new( + ConnectConfig { + name: self.spotifyd_state.device_name.clone(), + device_type: self.device_type, + is_group: false, + initial_volume: self.initial_volume, + has_volume_ctrl: self.has_volume_ctrl, + }, + self.session.clone(), + creds, + self.player.clone(), + self.mixer.clone(), + ) + .await } - pub(crate) async fn run(&mut self) { + pub(crate) async fn run(mut self) { tokio::pin! { let ctrl_c = tokio::signal::ctrl_c(); + // we don't necessarily have a dbus server + let dbus_server = Either::::Right(future::pending()); } + #[cfg(feature = "dbus_mpris")] + let mpris_event_tx = if self.mpris_config.use_mpris.unwrap_or(true) { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + *dbus_server.as_mut() = Either::Left(DbusServer::new( + rx, + self.mpris_config.dbus_type.unwrap_or(DBusType::Session), + self.session.clone(), + )); + Some(tx) + } else { + None + }; + 'mainloop: loop { - let session = tokio::select!( + let (spirc, spirc_task) = tokio::select!( _ = &mut ctrl_c => { break 'mainloop; } - session = self.get_session() => { - match session { - Ok(session) => session, + spirc = self.get_connection() => { + match spirc { + Ok(spirc) => spirc, Err(err) => { error!("failed to connect to spotify: {}", err); break 'mainloop; @@ -122,64 +139,33 @@ impl MainLoop { } ); - let mixer = (self.audio_setup.mixer)(); - let backend = self.audio_setup.backend; - let audio_device = self.audio_setup.audio_device.clone(); - let audio_format = self.audio_setup.audio_format; - let (player, mut event_channel) = Player::new( - self.player_config.clone(), - session.clone(), - mixer.get_soft_volume(), - move || (backend)(audio_device, audio_format), - ); - - let (spirc, spirc_task) = Spirc::new( - ConnectConfig { - autoplay: self.autoplay, - name: self.spotifyd_state.device_name.clone(), - device_type: self.device_type, - initial_volume: self.initial_volume, - has_volume_ctrl: self.has_volume_ctrl, - }, - session.clone(), - player, - mixer, - ); - tokio::pin!(spirc_task); let shared_spirc = Arc::new(spirc); - // we don't necessarily have a dbus server - let mut dbus_server: Pin>> = Box::pin(future::pending()); - #[cfg(feature = "dbus_mpris")] - let mpris_event_tx = if self.use_mpris { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - dbus_server = Box::pin(DbusServer::new( - session, - shared_spirc.clone(), - self.spotifyd_state.device_name.clone(), - rx, - self.dbus_type, - )); - Some(tx) - } else { - None - }; + if let Either::Left(mut dbus_server) = Either::as_pin_mut(dbus_server.as_mut()) { + if let Err(err) = dbus_server.as_mut().set_spirc(shared_spirc.clone()) { + error!("failed to configure dbus server: {err}"); + let _ = shared_spirc.shutdown(); + break 'mainloop; + } + } let mut running_event_program = Box::pin(Fuse::terminated()); + let mut event_channel = self.player.get_player_event_channel(); + loop { tokio::select!( // a new session has been started via the discovery stream _ = self.credentials_provider.incoming_connection() => { - shared_spirc.shutdown(); + let _ = shared_spirc.shutdown(); break; } // the program should shut down _ = &mut ctrl_c => { - shared_spirc.shutdown(); + let _ = shared_spirc.shutdown(); break 'mainloop; } // spirc was shut down by some external factor @@ -187,9 +173,18 @@ impl MainLoop { break; } // dbus stopped unexpectedly - _ = &mut dbus_server => { - shared_spirc.shutdown(); - break 'mainloop; + result = &mut dbus_server => { + #[cfg(feature = "dbus_mpris")] + { + if let Err(err) = result { + error!("DBus terminated unexpectedly: {err}"); + } + let _ = shared_spirc.shutdown(); + *dbus_server.as_mut() = Either::Right(future::pending()); + break 'mainloop; + } + #[cfg(not(feature = "dbus_mpris"))] + result // unused variable } // a new player event is available and no program is running event = event_channel.recv(), if running_event_program.is_terminated() => { @@ -216,6 +211,24 @@ impl MainLoop { } ) } + #[cfg(feature = "dbus_mpris")] + if let Either::Left(dbus_server) = Either::as_pin_mut(dbus_server.as_mut()) { + if let Err(err) = dbus_server.drop_spirc() { + error!("failed to reconfigure dbus server: {err}"); + break 'mainloop; + } + } + } + if let CredentialsProvider::Discovery { stream, .. } = self.credentials_provider { + let _ = stream.into_inner().shutdown().await; + } + #[cfg(feature = "dbus_mpris")] + if let Either::Left(dbus_server) = Either::as_pin_mut(dbus_server.as_mut()) { + if dbus_server.shutdown() { + if let Err(err) = dbus_server.await { + error!("failed to shutdown the dbus server: {err}"); + } + } } } } diff --git a/src/oauth.rs b/src/oauth.rs new file mode 100644 index 00000000..1effeee7 --- /dev/null +++ b/src/oauth.rs @@ -0,0 +1,75 @@ +use color_eyre::{ + eyre::{self, Context as _}, + Section as _, +}; +use librespot_core::SessionConfig; +use librespot_core::{authentication::Credentials, Session}; +use log::info; +use tokio::runtime::Runtime; + +use crate::{config::CliConfig, setup_logger, LogTarget}; + +pub(crate) fn run_oauth(mut cli_config: CliConfig, oauth_port: u16) -> eyre::Result<()> { + setup_logger(LogTarget::Terminal, cli_config.verbose)?; + + cli_config + .load_config_file_values() + .wrap_err("failed to read config file")?; + + let cache = cli_config + .shared_config + .get_cache(true) + .with_note(|| "The result of the authentication needs to be cached to be usable later.")?; + + const OAUTH_SCOPES: &[&str] = &[ + "app-remote-control", + "playlist-modify", + "playlist-modify-private", + "playlist-modify-public", + "playlist-read", + "playlist-read-collaborative", + "playlist-read-private", + "streaming", + "ugc-image-upload", + "user-follow-modify", + "user-follow-read", + "user-library-modify", + "user-library-read", + "user-modify", + "user-modify-playback-state", + "user-modify-private", + "user-personalized", + "user-read-birthdate", + "user-read-currently-playing", + "user-read-email", + "user-read-play-history", + "user-read-playback-position", + "user-read-playback-state", + "user-read-private", + "user-read-recently-played", + "user-top-read", + ]; + + let session_config = SessionConfig { + proxy: cli_config.shared_config.proxy_url(), + ..Default::default() + }; + + let token = librespot_oauth::get_access_token( + &session_config.client_id, + &format!("http://127.0.0.1:{oauth_port}/login"), + OAUTH_SCOPES.to_vec(), + ) + .wrap_err("token retrieval failed")?; + + let creds = Credentials::with_access_token(token.access_token); + + Runtime::new().unwrap().block_on(async move { + let session = Session::new(session_config, Some(cache)); + session.connect(creds, true).await + })?; + + info!("\nLogin successful! You are now ready to run spotifyd."); + + Ok(()) +} diff --git a/src/process.rs b/src/process.rs index 886187f1..3d781e6c 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,4 +1,5 @@ use crate::error::Error; +use librespot_metadata::audio::AudioItem; use librespot_playback::player::PlayerEvent; use log::info; use std::{collections::HashMap, process::Stdio}; @@ -7,24 +8,6 @@ use tokio::{ process::{self, Command}, }; -/// Blocks while provided command is run in a subprocess using the provided -/// shell. If successful, returns the contents of the subprocess's `stdout` as a -/// `String`. -pub(crate) fn run_program(shell: &str, cmd: &str) -> Result { - info!("Running {:?} using {:?}", cmd, shell); - let output = std::process::Command::new(shell) - .arg("-c") - .arg(cmd) - .output() - .map_err(|e| Error::subprocess_with_err(shell, cmd, e))?; - if !output.status.success() { - let s = std::str::from_utf8(&output.stderr).map_err(|_| Error::subprocess(shell, cmd))?; - return Err(Error::subprocess_with_str(shell, cmd, s)); - } - let s = String::from_utf8(output.stdout).map_err(|_| Error::subprocess(shell, cmd))?; - Ok(s) -} - /// Spawns provided command in a subprocess using the provided shell. fn spawn_program(shell: &str, cmd: &str, env: HashMap<&str, String>) -> Result { info!( @@ -44,6 +27,18 @@ fn spawn_program(shell: &str, cmd: &str, env: HashMap<&str, String>) -> Result, env: &mut HashMap<&str, String>) { + env.insert( + "TRACK_ID", + audio_item.track_id.to_base62().unwrap_or_default(), + ); + env.insert("TRACK_NAME", audio_item.name); + env.insert("TRACK_DURATION", audio_item.duration_ms.to_string()); + if let Some(cover) = audio_item.covers.into_iter().max_by_key(|c| c.width) { + env.insert("TRACK_COVER", cover.url); + } +} + /// Spawns provided command in a subprocess using the provided shell. /// Various environment variables are included in the subprocess's environment /// depending on the `PlayerEvent` that was passed in. @@ -54,15 +49,11 @@ pub(crate) fn spawn_program_on_event( ) -> Result { let mut env = HashMap::new(); match event { - PlayerEvent::Changed { - old_track_id, - new_track_id, - } => { - env.insert("OLD_TRACK_ID", old_track_id.to_base62().unwrap()); + PlayerEvent::TrackChanged { audio_item } => { env.insert("PLAYER_EVENT", "change".to_string()); - env.insert("TRACK_ID", new_track_id.to_base62().unwrap()); + audio_item_to_env(audio_item, &mut env); } - PlayerEvent::Started { + PlayerEvent::Playing { track_id, play_request_id, position_ms, @@ -90,29 +81,15 @@ pub(crate) fn spawn_program_on_event( env.insert("PLAY_REQUEST_ID", play_request_id.to_string()); env.insert("POSITION_MS", position_ms.to_string()); } - PlayerEvent::Playing { - track_id, - play_request_id, - position_ms, - duration_ms, - } => { - env.insert("PLAYER_EVENT", "play".to_string()); - env.insert("TRACK_ID", track_id.to_base62().unwrap()); - env.insert("PLAY_REQUEST_ID", play_request_id.to_string()); - env.insert("POSITION_MS", position_ms.to_string()); - env.insert("DURATION_MS", duration_ms.to_string()); - } PlayerEvent::Paused { track_id, play_request_id, position_ms, - duration_ms, } => { env.insert("PLAYER_EVENT", "pause".to_string()); env.insert("TRACK_ID", track_id.to_base62().unwrap()); env.insert("PLAY_REQUEST_ID", play_request_id.to_string()); env.insert("POSITION_MS", position_ms.to_string()); - env.insert("DURATION_MS", duration_ms.to_string()); } PlayerEvent::TimeToPreloadNextTrack { track_id, @@ -130,7 +107,7 @@ pub(crate) fn spawn_program_on_event( env.insert("TRACK_ID", track_id.to_base62().unwrap()); env.insert("PLAY_REQUEST_ID", play_request_id.to_string()); } - PlayerEvent::VolumeSet { volume } => { + PlayerEvent::VolumeChanged { volume } => { env.insert("PLAYER_EVENT", "volumeset".to_string()); env.insert("VOLUME", volume.to_string()); } @@ -146,6 +123,79 @@ pub(crate) fn spawn_program_on_event( env.insert("PLAYER_EVENT", "preloading".to_string()); env.insert("TRACK_ID", track_id.to_base62().unwrap()); } + PlayerEvent::PositionCorrection { + play_request_id, + track_id, + position_ms, + } => { + env.insert("PLAYER_EVENT", "positioncorrection".to_string()); + env.insert("TRACK_ID", track_id.to_base62().unwrap()); + env.insert("POSITION_MS", position_ms.to_string()); + env.insert("PLAY_REQUEST_ID", play_request_id.to_string()); + } + PlayerEvent::Seeked { + play_request_id, + track_id, + position_ms, + } => { + env.insert("PLAYER_EVENT", "seeked".to_string()); + env.insert("TRACK_ID", track_id.to_base62().unwrap()); + env.insert("POSITION_MS", position_ms.to_string()); + env.insert("PLAY_REQUEST_ID", play_request_id.to_string()); + } + PlayerEvent::PlayRequestIdChanged { play_request_id } => { + env.insert("PLAYER_EVENT", "playrequestid_changed".to_string()); + env.insert("PLAY_REQUEST_ID", play_request_id.to_string()); + } + PlayerEvent::SessionConnected { + connection_id, + user_name, + } => { + env.insert("PLAYER_EVENT", "sessionconnected".to_string()); + env.insert("USERNAME", user_name); + env.insert("CONNECTION_ID", connection_id); + } + PlayerEvent::SessionDisconnected { + connection_id, + user_name, + } => { + env.insert("PLAYER_EVENT", "sessiondisconnected".to_string()); + env.insert("USERNAME", user_name); + env.insert("CONNECTION_ID", connection_id); + } + PlayerEvent::SessionClientChanged { + client_id, + client_name, + client_brand_name, + client_model_name, + } => { + env.insert("PLAYER_EVENT", "clientchanged".to_string()); + env.insert("CLIENT_ID", client_id); + env.insert("CLIENT_NAME", client_name); + env.insert("CLIENT_BRAND", client_brand_name); + env.insert("CLIENT_MODEL", client_model_name); + } + PlayerEvent::ShuffleChanged { shuffle } => { + env.insert("PLAYER_EVENT", "shuffle_changed".to_string()); + env.insert("SHUFFLE", shuffle.to_string()); + } + PlayerEvent::RepeatChanged { repeat } => { + env.insert("PLAYER_EVENT", "repeat_changed".to_string()); + let val = match repeat { + true => "all", + false => "none", + } + .to_string(); + env.insert("REPEAT", val); + } + PlayerEvent::AutoPlayChanged { auto_play } => { + env.insert("PLAYER_EVENT", "autoplay_changed".to_string()); + env.insert("AUTOPLAY", auto_play.to_string()); + } + PlayerEvent::FilterExplicitContentChanged { filter } => { + env.insert("PLAYER_EVENT", "filterexplicit_changed".to_string()); + env.insert("FILTEREXPLICIT", filter.to_string()); + } } spawn_program(shell, cmd, env) } diff --git a/src/setup.rs b/src/setup.rs index 07a713c3..ca0abc30 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -4,183 +4,155 @@ use crate::{ config, main_loop::{self, CredentialsProvider}, }; -#[cfg(feature = "dbus_keyring")] -use keyring::Entry; -use librespot_core::{authentication::Credentials, cache::Cache, config::DeviceType}; -use librespot_playback::mixer::MixerConfig; +use color_eyre::{eyre::eyre, Section}; +use futures::StreamExt as _; +use librespot_core::Session; use librespot_playback::{ - audio_backend::{Sink, BACKENDS}, - config::AudioFormat, + audio_backend::{self}, mixer::{self, Mixer}, }; +use librespot_playback::{mixer::MixerConfig, player::Player}; #[allow(unused_imports)] // cfg use log::{debug, error, info, warn}; -use std::{str::FromStr, thread, time::Duration}; +use std::{sync::Arc, thread, time::Duration}; -pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLoop { - let mixer = { +pub(crate) fn initial_state( + config: config::SpotifydConfig, +) -> color_eyre::Result { + let mixer: Arc = { match config.volume_controller { config::VolumeController::None => { info!("Using no volume controller."); - Box::new(|| Box::new(crate::no_mixer::NoMixer) as Box) - as Box Box> + Arc::new(crate::no_mixer::NoMixer) } #[cfg(feature = "alsa_backend")] config::VolumeController::Alsa | config::VolumeController::AlsaLinear => { let audio_device = config.audio_device.clone(); - let control_device = config.control_device.clone(); - let mixer = config.mixer.clone(); + let control_device = config.alsa_config.control.clone(); + let mixer = config.alsa_config.mixer.clone(); info!("Using alsa volume controller."); let linear = matches!( config.volume_controller, config::VolumeController::AlsaLinear ); - Box::new(move || { - Box::new(alsa_mixer::AlsaMixer { - device: control_device - .clone() - .or_else(|| audio_device.clone()) - .unwrap_or_else(|| "default".to_string()), - mixer: mixer.clone().unwrap_or_else(|| "Master".to_string()), - linear_scaling: linear, - }) as Box - }) as Box Box> + Arc::new(alsa_mixer::AlsaMixer { + device: control_device + .clone() + .or_else(|| audio_device.clone()) + .unwrap_or_else(|| "default".to_string()), + mixer: mixer.clone().unwrap_or_else(|| "Master".to_string()), + linear_scaling: linear, + }) } _ => { info!("Using software volume controller."); - Box::new(move || { - Box::new(mixer::softmixer::SoftMixer::open(MixerConfig::default())) - as Box - }) as Box Box> + Arc::new(mixer::softmixer::SoftMixer::open(MixerConfig::default())) } } }; - let cache = config.cache; let player_config = config.player_config; let session_config = config.session_config; let backend = config.backend.clone(); - let autoplay = config.autoplay; let has_volume_ctrl = !matches!(config.volume_controller, config::VolumeController::None); let zeroconf_port = config.zeroconf_port.unwrap_or(0); - let device_type: DeviceType = DeviceType::from_str(&config.device_type).unwrap_or_default(); - - let username = config.username; - #[allow(unused_mut)] // mut is needed behind the dbus_keyring flag. - let mut password = config.password; + let creds = if let Some(creds) = config.oauth_cache.as_ref().and_then(|c| c.credentials()) { + info!( + "Login via OAuth as user {}.", + creds.username.as_deref().unwrap_or("unknown") + ); + Some(creds) + } else if let Some(creds) = config.cache.as_ref().and_then(|c| c.credentials()) { + info!( + "Restoring previous login as user {}.", + creds.username.as_deref().unwrap_or("unknown") + ); + Some(creds) + } else { + None + }; - #[cfg(feature = "dbus_keyring")] - if config.use_keyring { - match (&username, &password) { - (None, _) => warn!("Can't query the keyring without a username"), - (Some(_), Some(_)) => { - info!("Keyring is ignored, since you already configured a password") - } - (Some(username), None) => { - info!("Checking keyring for password"); - let entry = Entry::new("spotifyd", username); - match entry.and_then(|e| e.get_password()) { - Ok(retrieved_password) => password = Some(retrieved_password), - Err(e) => error!("Keyring did not return any results: {e}"), + let discovery = if config.discovery { + info!("Starting zeroconf server to advertise on local network."); + debug!("Using device id '{}'", session_config.device_id); + const RETRY_MAX: u8 = 4; + let mut retry_counter = 0; + let mut backoff = Duration::from_secs(5); + loop { + match librespot_discovery::Discovery::builder( + session_config.device_id.clone(), + session_config.client_id.clone(), + ) + .name(config.device_name.clone()) + .device_type(config.device_type) + .port(zeroconf_port) + .launch() + { + Ok(discovery_stream) => break Some(discovery_stream), + Err(err) => { + error!("failed to enable discovery: {err}"); + if retry_counter >= RETRY_MAX { + error!("maximum amount of retries exceeded"); + break None; + } + info!("retrying discovery in {} seconds", backoff.as_secs()); + thread::sleep(backoff); + retry_counter += 1; + backoff *= 2; + info!("trying to enable discovery (retry {retry_counter}/{RETRY_MAX})"); } } } - } + } else { + None + }; - let credentials_provider = - if let Some(credentials) = get_credentials(&cache, &username, &password) { - CredentialsProvider::SpotifyCredentials(credentials) - } else { - info!("no usable credentials found, enabling discovery"); - debug!("Using device id '{}'", session_config.device_id); - const RETRY_MAX: u8 = 4; - let mut retry_counter = 0; - let mut backoff = Duration::from_secs(5); - let discovery_stream = loop { - match librespot_discovery::Discovery::builder(session_config.device_id.clone()) - .name(config.device_name.clone()) - .device_type(device_type) - .port(zeroconf_port) - .launch() - { - Ok(discovery_stream) => break discovery_stream, - Err(err) => { - error!("failed to enable discovery: {err}"); - if retry_counter >= RETRY_MAX { - panic!("failed to enable discovery (and no credentials provided)"); - } - info!("retrying discovery in {} seconds", backoff.as_secs()); - thread::sleep(backoff); - retry_counter += 1; - backoff *= 2; - info!("trying to enable discovery (retry {retry_counter}/{RETRY_MAX})"); - } - } - }; - discovery_stream.into() - }; + let credentials_provider = match (discovery, creds) { + (Some(stream), creds) => CredentialsProvider::Discovery { + stream: stream.peekable(), + last_credentials: creds, + }, + (None, Some(creds)) => CredentialsProvider::CredentialsOnly(creds), + (None, None) => { + return Err( + eyre!("Discovery unavailable and no credentials found.").with_suggestion(|| { + "Try enabling discovery or logging in first with `spotifyd authenticate`." + }), + ); + } + }; + + let backend = audio_backend::find(backend).expect("available backends should match ours"); - let backend = find_backend(backend.as_ref().map(String::as_ref)); - main_loop::MainLoop { + let session = Session::new(session_config, config.cache); + let player = { + let audio_device = config.audio_device; + let audio_format = config.audio_format; + Player::new( + player_config, + session.clone(), + mixer.get_soft_volume(), + move || backend(audio_device, audio_format), + ) + }; + + Ok(main_loop::MainLoop { credentials_provider, - audio_setup: main_loop::AudioSetup { - mixer, - backend, - audio_device: config.audio_device, - audio_format: config.audio_format, - }, + mixer, spotifyd_state: main_loop::SpotifydState { - cache, device_name: config.device_name, player_event_program: config.onevent, }, - player_config, - session_config, + session, + player, initial_volume: config.initial_volume, has_volume_ctrl, shell: config.shell, - device_type, - autoplay, - use_mpris: config.use_mpris, - dbus_type: config.dbus_type, - } -} - -fn get_credentials( - cache: &Option, - username: &Option, - password: &Option, -) -> Option { - if let Some(credentials) = cache.as_ref().and_then(Cache::credentials) { - if username.as_ref() == Some(&credentials.username) { - return Some(credentials); - } - } - - Some(Credentials::with_password( - username.as_ref()?, - password.as_ref()?, - )) -} - -fn find_backend(name: Option<&str>) -> fn(Option, AudioFormat) -> Box { - match name { - Some(name) => { - BACKENDS - .iter() - .find(|backend| name == backend.0) - .unwrap_or_else(|| panic!("Unknown backend: {}.", name)) - .1 - } - None => { - let &(name, back) = BACKENDS - .first() - .expect("No backends were enabled at build time"); - info!("No backend specified, defaulting to: {}.", name); - back - } - } + device_type: config.device_type, + #[cfg(feature = "dbus_mpris")] + mpris_config: config.mpris, + }) }