diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0eb79985..7503b764 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -191,7 +191,7 @@ jobs: - name: Install cross run: cargo install cross || true - name: Build - run: cross build --locked --target ${{ matrix.target }} --no-default-features + run: cross build --target ${{ matrix.target }} --no-default-features clippy: needs: [test-cross-arm, test-windows] diff --git a/.gitignore b/.gitignore index 1fa44327..eebf401d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,4 @@ spotify_appkey.key .project .history *.save - - +*.*~ diff --git a/CHANGELOG.md b/CHANGELOG.md index d93b636c..461133d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,38 +8,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed -- [main] Enforce reasonable ranges for option values (breaking). -- [main] Don't evaluate options that would otherwise have no effect. -- [playback] `alsa`: Improve `--device ?` functionality for the alsa backend. -- [contrib] Hardened security of the systemd service units -- [main] Verbose logging mode (`-v`, `--verbose`) now logs all parsed environment variables and command line arguments (credentials are redacted). -- [playback] `Sink`: `write()` now receives ownership of the packet (breaking). -- [playback] `pipe`: create file if it doesn't already exist -- [playback] More robust dynamic limiter for very wide dynamic range (breaking) +- [playback] `subprocess`: Better error handling +- [playback] `pipe`: Better error handling ### Added -- [cache] Add `disable-credential-cache` flag (breaking). -- [main] Use different option descriptions and error messages based on what backends are enabled at build time. -- [main] Add a `-q`, `--quiet` option that changes the logging level to warn. -- [main] Add a short name for every flag and option. -- [main] Add the ability to parse environment variables. -- [playback] `pulseaudio`: set the PulseAudio name to match librespot's device name via `PULSE_PROP_application.name` environment variable (user set env var value takes precedence). (breaking) -- [playback] `pulseaudio`: set icon to `audio-x-generic` so we get an icon instead of a placeholder via `PULSE_PROP_application.icon_name` environment variable (user set env var value takes precedence). (breaking) -- [playback] `pulseaudio`: set values to: `PULSE_PROP_application.version`, `PULSE_PROP_application.process.binary`, `PULSE_PROP_stream.description`, `PULSE_PROP_media.software` and `PULSE_PROP_media.role` environment variables (user set env var values take precedence). (breaking) +- [playback] `pipe`: Implement stop ### Fixed -- [main] Prevent hang when discovery is disabled and there are no credentials or when bad credentials are given. -- [main] Don't panic when parsing options. Instead list valid values and exit. -- [main] `--alsa-mixer-device` and `--alsa-mixer-index` now fallback to the card and index specified in `--device`. -- [core] Removed unsafe code (breaking) -- [playback] Adhere to ReplayGain spec when calculating gain normalisation factor. -- [playback] `alsa`: Use `--volume-range` overrides for softvol controls -- [connect] Don't panic when activating shuffle without previous interaction. +- [playback] `alsamixer`: make `--volume-ctrl fixed` work as expected when combined with `--mixer alsa` +- [main] fix `--opt=value` line argument logging ### Removed -- [playback] `alsamixer`: previously deprecated option `mixer-card` has been removed. -- [playback] `alsamixer`: previously deprecated option `mixer-name` has been removed. -- [playback] `alsamixer`: previously deprecated option `mixer-index` has been removed. + +## [0.4.1] - 2022-05-23 + +### Changed +- [chore] The MSRV is now 1.56 + +### Fixed +- [playback] Fixed dependency issues when installing from crate + +## [0.4.0] - 2022-05-21 + +### Changed +- [chore] The MSRV is now 1.53 +- [contrib] Hardened security of the `systemd` service units +- [core] `Session`: `connect()` now returns the long-term credentials +- [core] `Session`: `connect()` now accepts a flag if the credentails should be stored via the cache +- [main] Different option descriptions and error messages based on what backends are enabled at build time +- [playback] More robust dynamic limiter for very wide dynamic range (breaking) +- [playback] `alsa`: improve `--device ?` output for the Alsa backend +- [playback] `gstreamer`: create own context, set correct states and use sync handler +- [playback] `pipe`: create file if it doesn't already exist +- [playback] `Sink`: `write()` now receives ownership of the packet (breaking) + +### Added +- [main] Enforce reasonable ranges for option values (breaking) +- [main] Add the ability to parse environment variables +- [main] Log now emits warning when trying to use options that would otherwise have no effect +- [main] Verbose logging now logs all parsed environment variables and command line arguments (credentials are redacted) +- [main] Add a `-q`, `--quiet` option that changes the logging level to WARN +- [main] Add `disable-credential-cache` flag (breaking) +- [main] Add a short name for every flag and option +- [playback] `pulseaudio`: set the PulseAudio name to match librespot's device name via `PULSE_PROP_application.name` environment variable (user set env var value takes precedence) (breaking) +- [playback] `pulseaudio`: set icon to `audio-x-generic` so we get an icon instead of a placeholder via `PULSE_PROP_application.icon_name` environment variable (user set env var value takes precedence) (breaking) +- [playback] `pulseaudio`: set values to: `PULSE_PROP_application.version`, `PULSE_PROP_application.process.binary`, `PULSE_PROP_stream.description`, `PULSE_PROP_media.software` and `PULSE_PROP_media.role` environment variables (user set env var values take precedence) (breaking) + +### Fixed +- [connect] Don't panic when activating shuffle without previous interaction +- [core] Removed unsafe code (breaking) +- [main] Fix crash when built with Avahi support but Avahi is locally unavailable +- [main] Prevent hang when discovery is disabled and there are no credentials or when bad credentials are given +- [main] Don't panic when parsing options, instead list valid values and exit +- [main] `--alsa-mixer-device` and `--alsa-mixer-index` now fallback to the card and index specified in `--device`. +- [playback] Adhere to ReplayGain spec when calculating gain normalisation factor +- [playback] `alsa`: make `--volume-range` overrides apply to Alsa softvol controls + +### Removed +- [playback] `alsamixer`: previously deprecated options `mixer-card`, `mixer-name` and `mixer-index` have been removed ## [0.3.1] - 2021-10-24 @@ -90,7 +116,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - [connect] Fix step size on volume up/down events - [connect] Fix looping back to the first track after the last track of an album or playlist -- [playback] Incorrect `PlayerConfig::default().normalisation_threshold` caused distortion when using dynamic volume normalisation downstream +- [playback] Incorrect `PlayerConfig::default().normalisation_threshold` caused distortion when using dynamic volume normalisation downstream - [playback] Fix `log` and `cubic` volume controls to be mute at zero volume - [playback] Fix `S24_3` format on big-endian systems - [playback] `alsamixer`: make `cubic` consistent between cards that report minimum volume as mute, and cards that report some dB value @@ -114,7 +140,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.0] - 2019-11-06 -[unreleased]: https://github.com/librespot-org/librespot/compare/v0.3.1..HEAD +[unreleased]: https://github.com/librespot-org/librespot/compare/v0.4.1..HEAD +[0.4.1]: https://github.com/librespot-org/librespot/compare/v0.4.0..v0.4.1 +[0.4.0]: https://github.com/librespot-org/librespot/compare/v0.3.1..v0.4.0 [0.3.1]: https://github.com/librespot-org/librespot/compare/v0.3.0..v0.3.1 [0.3.0]: https://github.com/librespot-org/librespot/compare/v0.2.0..v0.3.0 [0.2.0]: https://github.com/librespot-org/librespot/compare/v0.1.6..v0.2.0 diff --git a/COMPILING.md b/COMPILING.md index 8875076e..5176e906 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -7,7 +7,11 @@ In order to compile librespot, you will first need to set up a suitable Rust bui ### Install Rust The easiest, and recommended way to get Rust is to use [rustup](https://rustup.rs). Once that’s installed, Rust's standard tools should be set up and ready to use. +<<<<<<< HEAD *Note: The current minimum required Rust version at the time of writing is 1.56.* +======= +*Note: The current minimum required Rust version at the time of writing is 1.56, you can find the current minimum version specified in the `.github/workflow/test.yml` file.* +>>>>>>> dev #### Additional Rust tools - `rustfmt` To ensure a consistent codebase, we utilise [`rustfmt`](https://github.com/rust-lang/rustfmt) and [`clippy`](https://github.com/rust-lang/rust-clippy), which are installed by default with `rustup` these days, else they can be installed manually with: diff --git a/Cargo.lock b/Cargo.lock index 5985f0b7..a9327ea1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,15 +19,13 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "ctr", - "opaque-debug", ] [[package]] @@ -63,15 +61,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" [[package]] name = "array-init" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6945cc5422176fc5e602e590c2878d2c2acd9a4fe20a4baa7c28022521698ec6" +checksum = "bfb6d71005dc22a708c7496eee5c8dc0300ee47355de6256c3b35b12b5fef596" [[package]] name = "arrayvec" @@ -81,9 +79,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "async-trait" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ "proc-macro2", "quote", @@ -101,15 +99,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -118,9 +107,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", @@ -137,6 +126,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" + [[package]] name = "bindgen" version = "0.59.2" @@ -173,15 +168,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "bytemuck" -version = "1.9.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" [[package]] name = "byteorder" @@ -191,9 +186,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" [[package]] name = "cc" @@ -221,9 +216,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e068cb2806bbc15b439846dc16c5f89f8599f2c3e4d73d4449d38f9b2f0b6c5" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" dependencies = [ "smallvec", ] @@ -234,33 +229,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time 0.1.43", - "winapi", -] - [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] name = "clang-sys" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" dependencies = [ "glob", "libc", @@ -269,9 +252,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.3" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" dependencies = [ "bytes", "memchr", @@ -279,9 +262,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" [[package]] name = "core-foundation" @@ -327,7 +310,7 @@ dependencies = [ "alsa", "core-foundation-sys", "coreaudio-rs", - "jack", + "jack 0.8.3", "jni", "js-sys", "lazy_static", @@ -355,20 +338,19 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.2.11" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" dependencies = [ "generic-array", - "rand_core", "subtle", ] [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -385,9 +367,9 @@ dependencies = [ [[package]] name = "ctr" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "0d14f329cfbaf5d0e06b5e87fff7e265d2673c5ea7d2c27691a2c107db1442a0" dependencies = [ "cipher", ] @@ -429,21 +411,13 @@ dependencies = [ [[package]] name = "der" -version = "0.4.5" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" dependencies = [ "const-oid", "crypto-bigint", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", + "pem-rfc7468", ] [[package]] @@ -478,18 +452,18 @@ dependencies = [ [[package]] name = "enum-iterator" -version = "0.7.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401" dependencies = [ "enum-iterator-derive", ] [[package]] name = "enum-iterator-derive" -version = "0.7.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +checksum = "b13f1e69590421890f90448c3cd5f554746a31adc6dc0dac406ec6901db8dc25" dependencies = [ "proc-macro2", "quote", @@ -511,18 +485,18 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "fixedbitset" -version = "0.2.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "fnv" @@ -650,13 +624,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -673,15 +647,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "git2" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3826a6e0e2215d7a41c2bfc7c9244123969273f3476b939a226aac0ab56e9e3c" +checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" dependencies = [ "bitflags", "libc", @@ -692,9 +666,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.15.10" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a826fad715b57834920839d7a594c3b5e416358c7d790bdaba847a40d7c1d96d" +checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" dependencies = [ "bitflags", "futures-channel", @@ -712,9 +686,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.10" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac4d47c544af67747652ab1865ace0ffa1155709723ac4f32e97587dd4735b2" +checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" dependencies = [ "anyhow", "heck", @@ -754,9 +728,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.18.7" +version = "0.18.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd58af6f8b268fc335122a3ccc66efa0cd56584948f49a37e5feef0b89dfc29b" +checksum = "d66363bacf5e4f6eb281564adc2902e44c52ae5c45082423e7439e9012b75456" dependencies = [ "bitflags", "cfg-if", @@ -891,15 +865,15 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util", "tracing", ] [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "headers" @@ -953,7 +927,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest", ] [[package]] @@ -969,9 +943,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", @@ -980,9 +954,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -991,9 +965,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" [[package]] name = "httpdate" @@ -1009,9 +983,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -1076,10 +1050,10 @@ dependencies = [ "http", "hyper", "log", - "rustls 0.20.4", + "rustls 0.20.6", "rustls-native-certs 0.6.2", "tokio", - "tokio-rustls 0.23.3", + "tokio-rustls 0.23.4", ] [[package]] @@ -1101,33 +1075,31 @@ dependencies = [ [[package]] name = "if-addrs" -version = "0.6.7" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2273e421f7c4f0fc99e1934fe4776f59d8df2972f4199d703fc0da9f2a9f73de" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" dependencies = [ - "if-addrs-sys", "libc", "winapi", ] [[package]] -name = "if-addrs-sys" -version = "0.3.2" +name = "indexmap" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de74b9dd780476e837e5eb5ab7c88b49ed304126e412030a0adba99c8efe79ea" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ - "cc", - "libc", + "autocfg", + "hashbrown", ] [[package]] -name = "indexmap" -version = "1.8.1" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "autocfg 1.1.0", - "hashbrown", + "generic-array", ] [[package]] @@ -1141,18 +1113,31 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "jack" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2ac12f11bb369f3c50d24dbb9fdb00dc987434c9dd622a12c13f618106e153" +checksum = "b3902a02287c3dcad784edd1ecc5f487774b63a5fe77dd9842ea9a993d0a4a23" dependencies = [ "bitflags", - "jack-sys", + "jack-sys 0.2.2", + "lazy_static", + "libc", + "log", +] + +[[package]] +name = "jack" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce722655a29b13bb98ec7e8ba9dc65d670b9b37c7b1c09775c7f7516811c5a36" +dependencies = [ + "bitflags", + "jack-sys 0.4.0", "lazy_static", "libc", "log", @@ -1160,13 +1145,25 @@ dependencies = [ [[package]] name = "jack-sys" -version = "0.2.3" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b91f2d2d10bc2bab38f4dfa4bc77123a988828af39dd3f30dd9db14d44f2cc1" +checksum = "57983f0d72dfecf2b719ed39bc9cacd85194e1a94cb3f9146009eff9856fef41" dependencies = [ "lazy_static", "libc", "libloading 0.6.7", +] + +[[package]] +name = "jack-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d70559ff166d148ccb750ddd77702af760718f3a752c731add168c22c16a9f" +dependencies = [ + "bitflags", + "lazy_static", + "libc", + "libloading 0.7.3", "pkg-config", ] @@ -1201,9 +1198,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" dependencies = [ "wasm-bindgen", ] @@ -1225,15 +1222,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.124" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libgit2-sys" -version = "0.13.2+1.4.2" +version = "0.13.4+1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a42de9a51a5c12e00fc0e4ca6bc2ea43582fc6418488e8f615e905d886f258b" +checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" dependencies = [ "cc", "libc", @@ -1269,9 +1266,9 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" [[package]] name = "libmdns" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac185a4d02e873c6d1ead59d674651f8ae5ec23ffe1637bee8de80665562a6a" +checksum = "d6fb7fd715150e59e9a74049d2b50e862c3959c139b95eea132a66ddae20c3d9" dependencies = [ "byteorder", "futures-util", @@ -1335,7 +1332,7 @@ dependencies = [ [[package]] name = "librespot" -version = "0.3.1" +version = "0.4.1" dependencies = [ "env_logger", "futures-util", @@ -1350,7 +1347,7 @@ dependencies = [ "librespot-protocol", "log", "rpassword", - "sha-1", + "sha1", "thiserror", "tokio", "url", @@ -1358,17 +1355,18 @@ dependencies = [ [[package]] name = "librespot-audio" -version = "0.3.1" +version = "0.4.1" dependencies = [ "aes", "byteorder", "bytes", + "ctr", "futures-core", "futures-util", "hyper", "librespot-core", "log", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "tempfile", "thiserror", "tokio", @@ -1376,7 +1374,7 @@ dependencies = [ [[package]] name = "librespot-connect" -version = "0.3.1" +version = "0.4.1" dependencies = [ "form_urlencoded", "futures-util", @@ -1395,7 +1393,7 @@ dependencies = [ [[package]] name = "librespot-core" -version = "0.3.1" +version = "0.4.1" dependencies = [ "aes", "base64", @@ -1420,7 +1418,7 @@ dependencies = [ "num-integer", "num-traits", "once_cell", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "pbkdf2", "priority-queue", "protobuf", @@ -1429,14 +1427,14 @@ dependencies = [ "rsa", "serde", "serde_json", - "sha-1", + "sha1", "shannon", "thiserror", - "time 0.3.9", + "time", "tokio", "tokio-stream", "tokio-tungstenite", - "tokio-util 0.6.9", + "tokio-util", "url", "uuid", "vergen", @@ -1444,11 +1442,12 @@ dependencies = [ [[package]] name = "librespot-discovery" -version = "0.3.1" +version = "0.4.1" dependencies = [ "aes", "base64", "cfg-if", + "ctr", "dns-sd", "form_urlencoded", "futures", @@ -1462,14 +1461,14 @@ dependencies = [ "log", "rand", "serde_json", - "sha-1", + "sha1", "thiserror", "tokio", ] [[package]] name = "librespot-metadata" -version = "0.3.1" +version = "0.4.1" dependencies = [ "async-trait", "byteorder", @@ -1484,7 +1483,7 @@ dependencies = [ [[package]] name = "librespot-playback" -version = "0.3.1" +version = "0.4.1" dependencies = [ "alsa", "byteorder", @@ -1494,7 +1493,7 @@ dependencies = [ "gstreamer", "gstreamer-app", "gstreamer-audio", - "jack", + "jack 0.10.0", "libpulse-binding", "libpulse-simple-binding", "librespot-audio", @@ -1502,7 +1501,7 @@ dependencies = [ "librespot-metadata", "log", "ogg", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "portaudio-rs", "rand", "rand_distr", @@ -1517,7 +1516,7 @@ dependencies = [ [[package]] name = "librespot-protocol" -version = "0.3.1" +version = "0.4.1" dependencies = [ "glob", "protobuf", @@ -1526,9 +1525,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" dependencies = [ "cc", "libc", @@ -1542,15 +1541,15 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -1578,9 +1577,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" @@ -1588,7 +1587,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -1605,34 +1604,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "miow", - "ntapi", - "wasi 0.11.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "wasi", + "windows-sys", ] [[package]] @@ -1729,15 +1717,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi", -] - [[package]] name = "num" version = "0.4.0" @@ -1758,7 +1737,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", "rand", @@ -1766,11 +1745,10 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" +checksum = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011" dependencies = [ - "autocfg 0.1.8", "byteorder", "lazy_static", "libm", @@ -1784,9 +1762,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" dependencies = [ "num-traits", ] @@ -1804,32 +1782,32 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-rational" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-bigint", "num-integer", "num-traits", @@ -1837,11 +1815,11 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", "libm", ] @@ -1878,31 +1856,31 @@ dependencies = [ [[package]] name = "num_threads" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] [[package]] name = "object" -version = "0.28.3" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "memchr", ] [[package]] name = "oboe" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2463c8f2e19b4e0d0710a21f8e4011501ff28db1c95d7a5482a553b2100502d2" +checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" dependencies = [ "jni", "ndk", - "ndk-glue", + "ndk-context", "num-derive", "num-traits", "oboe-sys", @@ -1919,24 +1897,18 @@ dependencies = [ [[package]] name = "ogg" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +checksum = "960d0efc0531a452c442c777288f704b300a5f743c04a14eba71f9aabc4897ac" dependencies = [ "byteorder", ] [[package]] name = "once_cell" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "openssl-probe" @@ -1946,9 +1918,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "option-operations" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95d6113415f41b268f1195907427519769e40ee6f28cbb053795098a2c16f447" +checksum = "42b01597916c91a493b1e8a2fde64fec1764be3259abc1f06efc99c274f150a2" dependencies = [ "paste", ] @@ -1966,12 +1938,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.2", + "parking_lot_core 0.9.3", ] [[package]] @@ -1980,27 +1952,27 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "backtrace", "cfg-if", "instant", "libc", - "petgraph", "redox_syscall", "smallvec", - "thread-id", "winapi", ] [[package]] name = "parking_lot_core" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ + "backtrace", "cfg-if", "libc", + "petgraph", "redox_syscall", "smallvec", + "thread-id", "windows-sys", ] @@ -2012,11 +1984,11 @@ checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] name = "pbkdf2" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.3", + "digest", "hmac", ] @@ -2026,6 +1998,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem-rfc7468" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -2034,9 +2015,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "petgraph" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" dependencies = [ "fixedbitset", "indexmap", @@ -2044,9 +2025,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -2056,22 +2037,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs1" -version = "0.2.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "116bee8279d783c0cf370efa1a94632f2108e5ef0bb32df31f051647810a4e2c" +checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" dependencies = [ "der", + "pkcs8", "zeroize", ] [[package]] name = "pkcs8" -version = "0.7.6" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" dependencies = [ "der", - "pkcs1", "spki", "zeroize", ] @@ -2111,17 +2092,17 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "pretty-hex" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" [[package]] name = "priority-queue" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ba480ac08d3cfc40dea10fd466fd2c14dee3ea6fc7873bc4079eda2727caf0" +checksum = "815082d99af3acc75a3e67efd2a07f72e67b4e81b4344eb8ca34c6ebf3dfa9c5" dependencies = [ - "autocfg 1.1.0", + "autocfg", "indexmap", ] @@ -2161,11 +2142,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -2195,9 +2176,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" +checksum = "9279fbdacaad3baf559d8cabe0acc3d06e30ea14931af31af79578ac0946decc" dependencies = [ "memchr", "serde", @@ -2205,9 +2186,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -2254,18 +2235,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -2274,9 +2255,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -2313,9 +2294,9 @@ dependencies = [ [[package]] name = "rpassword" -version = "5.0.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +checksum = "26b763cb66df1c928432cc35053f8bd4cec3335d8559fc16010017d16b3c1680" dependencies = [ "libc", "winapi", @@ -2323,20 +2304,20 @@ dependencies = [ [[package]] name = "rsa" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c2603e2823634ab331437001b411b9ed11660fbc4066f3908c84a9439260d" +checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" dependencies = [ "byteorder", - "digest 0.9.0", - "lazy_static", + "digest", "num-bigint-dig", "num-integer", "num-iter", "num-traits", "pkcs1", "pkcs8", - "rand", + "rand_core", + "smallvec", "subtle", "zeroize", ] @@ -2368,9 +2349,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.4" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" dependencies = [ "log", "ring", @@ -2413,15 +2394,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" +checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8" [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "same-file" @@ -2434,12 +2415,12 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "winapi", + "windows-sys", ] [[package]] @@ -2516,18 +2497,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.136" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", @@ -2536,9 +2517,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "itoa", "ryu", @@ -2553,7 +2534,18 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest", +] + +[[package]] +name = "sha1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", ] [[package]] @@ -2588,15 +2580,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "socket2" @@ -2616,10 +2611,11 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spki" -version = "0.4.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" dependencies = [ + "base64ct", "der", ] @@ -2643,9 +2639,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "symphonia" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb30457ee7a904dae1e4ace25156dcabaf71e425db318e7885267f09cd8fb648" +checksum = "17033fe05e4f7f10a6ad602c272bafd2520b2e5cdd9feb61494d9cdce08e002f" dependencies = [ "lazy_static", "symphonia-bundle-mp3", @@ -2657,9 +2653,9 @@ dependencies = [ [[package]] name = "symphonia-bundle-mp3" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9130cae661447f234b58759d74d23500e9c95697b698589b34196cb0fb488a61" +checksum = "db5d3d53535ae2b7d0e39e82f683cac5398a6c8baca25ff1183e107d13959d3e" dependencies = [ "bitflags", "lazy_static", @@ -2670,9 +2666,9 @@ dependencies = [ [[package]] name = "symphonia-codec-vorbis" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "746fc459966b37e277565f9632e5ffd6cbd83d9381152727123f68484cb8f9c4" +checksum = "323b94435a1a807e1001e29490aeaef2660fb72b145d47497e8429a6cb1d67c3" dependencies = [ "log", "symphonia-core", @@ -2681,9 +2677,9 @@ dependencies = [ [[package]] name = "symphonia-core" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edcb254d25e02b688b6f8a290a778153fa5f29674ac50773d03e0a16060391d" +checksum = "199a6417cd4115bac79289b64b859358ea050b7add0ceb364dc991f628c5b347" dependencies = [ "arrayvec", "bitflags", @@ -2694,9 +2690,9 @@ dependencies = [ [[package]] name = "symphonia-format-ogg" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5b92a2a6370873d9dbe3326dad1bf795b3151efcadca6e5f47d732499a518" +checksum = "8d2f741469a0f103607ed1f2605f7f00b13ba044ea9ddc616764558c6d3d9b7d" dependencies = [ "log", "symphonia-core", @@ -2706,9 +2702,9 @@ dependencies = [ [[package]] name = "symphonia-metadata" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04ee665c99fd2b919b87261c86a5312e996b720ca142646a163d9583e72bd0e" +checksum = "6ed71acf6b5e6e8bee1509597b86365a06b78c1d73218df47357620a6fe5997b" dependencies = [ "encoding_rs", "lazy_static", @@ -2718,9 +2714,9 @@ dependencies = [ [[package]] name = "symphonia-utils-xiph" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abadfa53359fa437836f2554a0019dd06bfdf742fbb735d0645db3b6c5a763e0" +checksum = "73cbb0766ce77a8aef535f9438db645e7b6f1b2c4cf3be9bf246b4e11a7d5531" dependencies = [ "symphonia-core", "symphonia-metadata", @@ -2728,13 +2724,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.91" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -2787,18 +2783,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -2818,29 +2814,20 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" dependencies = [ + "itoa", "libc", "num_threads", ] [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -2853,17 +2840,18 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.17.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" dependencies = [ + "autocfg", "bytes", "libc", "memchr", "mio", "num_cpus", "once_cell", - "parking_lot 0.12.0", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2873,9 +2861,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ "proc-macro2", "quote", @@ -2895,20 +2883,20 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.4", + "rustls 0.20.6", "tokio", "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" dependencies = [ "futures-core", "pin-project-lite", @@ -2917,39 +2905,25 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.1" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" dependencies = [ "futures-util", "log", - "rustls 0.20.4", + "rustls 0.20.6", "rustls-native-certs 0.6.2", "tokio", - "tokio-rustls 0.23.3", + "tokio-rustls 0.23.4", "tungstenite", "webpki 0.22.0", ] [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" dependencies = [ "bytes", "futures-core", @@ -2970,40 +2944,28 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] @@ -3014,9 +2976,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "tungstenite" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ "base64", "byteorder", @@ -3025,7 +2987,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.20.4", + "rustls 0.20.6", "sha-1", "thiserror", "url", @@ -3041,15 +3003,21 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" dependencies = [ "tinyvec", ] @@ -3062,9 +3030,9 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "untrusted" @@ -3092,9 +3060,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "0.8.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" dependencies = [ "getrandom", ] @@ -3107,18 +3075,18 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "6.0.2" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3893329bee75c101278e0234b646fa72221547d63f97fb66ac112a0569acd110" +checksum = "f10de320f0fe3f21913dabbfcbced6867bbe47a6b1a5db830e37df3a50279bd0" dependencies = [ "anyhow", "cfg-if", - "chrono", "enum-iterator", "getset", "git2", "rustversion", "thiserror", + "time", ] [[package]] @@ -3154,12 +3122,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3168,9 +3130,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3178,13 +3140,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -3193,9 +3155,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3203,9 +3165,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ "proc-macro2", "quote", @@ -3216,15 +3178,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -3283,9 +3245,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -3296,33 +3258,33 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_i686_gnu" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_x86_64_gnu" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "zerocopy" @@ -3347,21 +3309,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.4.3" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/Cargo.toml b/Cargo.toml index 7fe13f43..0c46816c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot" -version = "0.3.1" +version = "0.4.1" authors = ["Librespot Org"] license = "MIT" description = "An open source client library for Spotify, with support for Spotify Connect" @@ -22,31 +22,31 @@ doc = false [dependencies.librespot-audio] path = "audio" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-connect] path = "connect" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-core] path = "core" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-discovery] path = "discovery" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-metadata] path = "metadata" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-playback] path = "playback" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-protocol] path = "protocol" -version = "0.3.1" +version = "0.4.1" [dependencies] env_logger = { version = "0.9", default-features = false, features = ["termcolor", "humantime", "atty"] } @@ -54,8 +54,8 @@ futures-util = { version = "0.3", default_features = false } getopts = "0.2" hex = "0.4" log = "0.4" -rpassword = "5.0" -sha-1 = "0.10" +rpassword = "7.0" +sha1 = "0.10" thiserror = "1.0" tokio = { version = "1", features = ["rt", "macros", "signal", "sync", "parking_lot", "process"] } url = "2.2" diff --git a/PUBLISHING.md b/PUBLISHING.md index ceab506c..e38cd6d8 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -2,7 +2,17 @@ ## How To -The bash script in the root of the project, named `publish.sh` can be used to publish a new version of librespot and it's corresponding crates. the command should be used as follows: `./publish 0.1.0` from the project root, substituting the new version number that you wish to publish. *Note the lack of a v prefix on the version number. This is important, do not add one.* The v prefix is added where appropriate by the script. +Read through this paragraph in its entirety before running anything. + +The Bash script in the root of the project, named `publish.sh` can be used to publish a new version of librespot and it's corresponding crates. the command should be used as follows from the project root: `./publish 0.1.0` from the project root, substituting the new version number that you wish to publish. *Note the lack of a v prefix on the version number. This is important, do not add one.* The v prefix is added where appropriate by the script. + +Make sure that you are are starting from a clean working directory for both `dev` and `master`, completely up to date with remote and all local changes either committed and pushed or stashed. + +You will want to perform a dry run first: `./publish --dry-run 0.1.0`. Please make note of any errors or warnings. In particular, you may need to explicitly inform Git which remote you want to track for the `master` branch like so: `git --track origin/master` (or whatever you have called the `librespot-org` remote `master` branch). + +Depending on your system the script may fail to publish the main `librespot` crate after having published all the `librespot-xyz` sub-crates. If so then make sure the working directory is committed and pushed (watch `Cargo.toml`) and then run `cargo publish` manually after `publish.sh` finished. + +To publish the crates your GitHub account needs to be authorized on `crates.io` by `librespot-org`. ## What the script does diff --git a/audio/Cargo.toml b/audio/Cargo.toml index bd5eb455..4ae74091 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -1,24 +1,26 @@ [package] name = "librespot-audio" -version = "0.3.1" +version = "0.4.1" authors = ["Paul Lietar "] -description="The audio fetching and processing logic for librespot" +description = "The audio fetching logic for librespot" license = "MIT" +repository = "https://github.com/librespot-org/librespot" edition = "2018" [dependencies.librespot-core] path = "../core" -version = "0.3.1" +version = "0.4.1" [dependencies] -aes = { version = "0.7", features = ["ctr"] } +aes = "0.8" byteorder = "1.4" bytes = "1" +ctr = "0.9" futures-core = "0.3" futures-util = "0.3" hyper = { version = "0.14", features = ["client"] } log = "0.4" -parking_lot = { version = "0.11", features = ["deadlock_detection"] } +parking_lot = { version = "0.12", features = ["deadlock_detection"] } tempfile = "3" thiserror = "1.0" tokio = { version = "1", features = ["macros", "parking_lot", "sync"] } diff --git a/audio/src/decrypt.rs b/audio/src/decrypt.rs index 912d1793..365ec46e 100644 --- a/audio/src/decrypt.rs +++ b/audio/src/decrypt.rs @@ -1,9 +1,8 @@ use std::io; -use aes::{ - cipher::{generic_array::GenericArray, NewCipher, StreamCipher, StreamCipherSeek}, - Aes128Ctr, -}; +use aes::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; + +type Aes128Ctr = ctr::Ctr128BE; use librespot_core::audio_key::AudioKey; @@ -19,12 +18,12 @@ pub struct AudioDecrypt { impl AudioDecrypt { pub fn new(key: Option, reader: T) -> AudioDecrypt { - let cipher = key.map(|key| { - Aes128Ctr::new( - GenericArray::from_slice(&key.0), - GenericArray::from_slice(&AUDIO_AESIV), - ) - }); + let cipher = if let Some(key) = key { + Aes128Ctr::new_from_slices(&key.0, &AUDIO_AESIV).ok() + } else { + // some files are unencrypted + None + }; AudioDecrypt { cipher, reader } } diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 3408043b..1a0cb6ba 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-connect" -version = "0.3.1" +version = "0.4.1" authors = ["Paul Lietar "] description = "The discovery and Spotify Connect logic for librespot" license = "MIT" @@ -21,12 +21,12 @@ tokio-stream = "0.1" [dependencies.librespot-core] path = "../core" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-playback] path = "../playback" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-protocol] path = "../protocol" -version = "0.3.1" +version = "0.4.1" diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index db5ff0c9..1b50c068 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -331,7 +331,7 @@ impl Spirc { ); // Connect *after* all message listeners are registered - session.connect(credentials).await?; + session.connect(credentials, true).await?; let canonical_username = &session.username(); debug!("canonical_username: {}", canonical_username); @@ -612,6 +612,7 @@ impl SpircTask { } SpircCommand::Shutdown => { CommandSender::new(self, MessageType::kMessageTypeGoodbye).send()?; + self.player.stop(); self.shutdown = true; if let Some(rx) = self.commands.as_mut() { rx.close() diff --git a/core/Cargo.toml b/core/Cargo.toml index b65736ef..f4049d3f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-core" -version = "0.3.1" +version = "0.4.1" authors = ["Paul Lietar "] build = "build.rs" description = "The core functionality provided by librespot" @@ -10,10 +10,10 @@ edition = "2018" [dependencies.librespot-protocol] path = "../protocol" -version = "0.3.1" +version = "0.4.1" [dependencies] -aes = "0.7" +aes = "0.8" base64 = "0.13" byteorder = "1.4" bytes = "1" @@ -22,7 +22,7 @@ form_urlencoded = "1.0" futures-core = "0.3" futures-util = { version = "0.3", features = ["alloc", "bilock", "sink", "unstable"] } hmac = "0.12" -httparse = "1.5" +httparse = "1.7" http = "0.2" hyper = { version = "0.14", features = ["client", "http1", "http2", "tcp"] } hyper-proxy = { version = "0.9", default-features = false, features = ["rustls"] } @@ -33,30 +33,30 @@ num-bigint = { version = "0.4", features = ["rand"] } num-derive = "0.3" num-integer = "0.1" num-traits = "0.2" -once_cell = "1.9" -parking_lot = { version = "0.11", features = ["deadlock_detection"] } -pbkdf2 = { version = "0.10", default-features = false, features = ["hmac"] } -priority-queue = "1.2.1" +once_cell = "1" +parking_lot = { version = "0.12", features = ["deadlock_detection"] } +pbkdf2 = { version = "0.11", default-features = false, features = ["hmac"] } +priority-queue = "1.2" protobuf = "2" -quick-xml = { version = "0.22", features = ["serialize"] } +quick-xml = { version = "0.23", features = ["serialize"] } rand = "0.8" -rsa = { version = "0.5", default-features = false, features = ["alloc"] } +rsa = "0.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -sha-1 = "0.10" +sha1 = "0.10" shannon = "0.2" thiserror = "1.0" time = "0.3" tokio = { version = "1", features = ["io-util", "macros", "net", "parking_lot", "rt", "sync", "time"] } tokio-stream = "0.1" tokio-tungstenite = { version = "*", default-features = false, features = ["rustls-tls-native-roots"] } -tokio-util = { version = "0.6", features = ["codec"] } +tokio-util = { version = "0.7", features = ["codec"] } url = "2" -uuid = { version = "0.8", default-features = false, features = ["v4"] } +uuid = { version = "1", default-features = false, features = ["v4"] } [build-dependencies] rand = "0.8" -vergen = { version = "6", default-features = false, features = ["build", "git"] } +vergen = { version = "7", default-features = false, features = ["build", "git"] } [dev-dependencies] env_logger = "0.9" diff --git a/core/build.rs b/core/build.rs index 85a626e9..784f7c2f 100644 --- a/core/build.rs +++ b/core/build.rs @@ -13,11 +13,14 @@ fn main() { vergen(config).expect("Unable to generate the cargo keys!"); - let build_id: String = rand::thread_rng() - .sample_iter(Alphanumeric) - .take(8) - .map(char::from) - .collect(); + let build_id = match std::env::var("SOURCE_DATE_EPOCH") { + Ok(val) => val, + Err(_) => rand::thread_rng() + .sample_iter(Alphanumeric) + .take(8) + .map(char::from) + .collect(), + }; println!("cargo:rustc-env=LIBRESPOT_BUILD_ID={}", build_id); } diff --git a/core/src/authentication.rs b/core/src/authentication.rs index 1e5cf436..dad514b0 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -1,6 +1,6 @@ use std::io::{self, Read}; -use aes::{Aes192, BlockDecrypt}; +use aes::Aes192; use byteorder::{BigEndian, ByteOrder}; use hmac::Hmac; use pbkdf2::pbkdf2; @@ -106,13 +106,12 @@ impl Credentials { // decrypt data using ECB mode without padding let blob = { - use aes::cipher::generic_array::typenum::Unsigned; use aes::cipher::generic_array::GenericArray; - use aes::cipher::{BlockCipher, NewBlockCipher}; + use aes::cipher::{BlockDecrypt, BlockSizeUser, KeyInit}; let mut data = base64::decode(encrypted_blob)?; let cipher = Aes192::new(GenericArray::from_slice(&key)); - let block_size = ::BlockSize::to_usize(); + let block_size = Aes192::block_size(); for chunk in data.chunks_exact_mut(block_size) { cipher.decrypt_block(GenericArray::from_mut_slice(chunk)); diff --git a/core/src/cache.rs b/core/src/cache.rs index f4fadc67..11bfdf47 100644 --- a/core/src/cache.rs +++ b/core/src/cache.rs @@ -375,7 +375,7 @@ impl Cache { path }), Err(e) => { - warn!("{}", e); + warn!("Invalid FileId: {}", e); None } } diff --git a/core/src/config.rs b/core/src/config.rs index bc4e3037..46f11fe8 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -15,7 +15,7 @@ pub struct SessionConfig { impl Default for SessionConfig { fn default() -> SessionConfig { - let device_id = uuid::Uuid::new_v4().to_hyphenated().to_string(); + let device_id = uuid::Uuid::new_v4().as_hyphenated().to_string(); SessionConfig { client_id: KEYMASTER_CLIENT_ID.to_owned(), device_id, diff --git a/core/src/session.rs b/core/src/session.rs index 76571a94..11f2cf8a 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -126,7 +126,11 @@ impl Session { })) } - pub async fn connect(&self, credentials: Credentials) -> Result<(), Error> { + pub async fn connect( + &self, + credentials: Credentials, + store_credentials: bool, + ) -> Result<(), Error> { let ap = self.apresolver().resolve("accesspoint").await?; info!("Connecting to AP \"{}:{}\"", ap.0, ap.1); let mut transport = connection::connect(&ap.0, ap.1, self.config().proxy.as_ref()).await?; @@ -136,7 +140,9 @@ impl Session { info!("Authenticated as \"{}\" !", reusable_credentials.username); self.set_username(&reusable_credentials.username); if let Some(cache) = self.cache() { - cache.save_credentials(&reusable_credentials); + if store_credentials { + cache.save_credentials(&reusable_credentials); + } } let (tx_connection, rx_connection) = mpsc::unbounded_channel(); diff --git a/core/src/spclient.rs b/core/src/spclient.rs index 820b2182..c4f13656 100644 --- a/core/src/spclient.rs +++ b/core/src/spclient.rs @@ -1,5 +1,6 @@ use std::{ convert::TryInto, + fmt::Write, time::{Duration, Instant}, }; @@ -273,7 +274,7 @@ impl SpClient { Some(_) => "&", None => "?", }; - url.push_str(&format!("{}product=0", separator)); + let _ = write!(url, "{}product=0", separator); let mut request = Request::builder() .method(method) @@ -464,7 +465,7 @@ impl SpClient { Some(_) => "&", None => "?", }; - url.push_str(&format!("{}cid={}", separator, self.session().client_id())); + let _ = write!(url, "{}cid={}", separator, self.session().client_id()); self.request_url(url).await } diff --git a/core/src/spotify_id.rs b/core/src/spotify_id.rs index 7591d427..3db010e9 100644 --- a/core/src/spotify_id.rs +++ b/core/src/spotify_id.rs @@ -200,7 +200,6 @@ impl SpotifyId { /// character long `String`. /// /// [canonically]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids - #[allow(clippy::wrong_self_convention)] pub fn to_base62(&self) -> Result { let mut dst = [0u8; 22]; @@ -258,7 +257,6 @@ impl SpotifyId { /// be encoded as `unknown`. /// /// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids - #[allow(clippy::wrong_self_convention)] pub fn to_uri(&self) -> Result { // 8 chars for the "spotify:" prefix + 1 colon + 22 chars base62 encoded ID = 31 diff --git a/core/tests/connect.rs b/core/tests/connect.rs index c76ba7ce..9411bc87 100644 --- a/core/tests/connect.rs +++ b/core/tests/connect.rs @@ -7,7 +7,7 @@ use librespot_core::{authentication::Credentials, config::SessionConfig, session #[tokio::test] async fn test_connection() { timeout(Duration::from_secs(30), async { - let result = Session::new(SessionConfig::default(), None) + let result = Session::new(SessionConfig::default(), None, false) .connect(Credentials::with_password("test", "test")) .await; diff --git a/discovery/Cargo.toml b/discovery/Cargo.toml index ccf055d0..3b3dcd59 100644 --- a/discovery/Cargo.toml +++ b/discovery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-discovery" -version = "0.3.1" +version = "0.4.1" authors = ["Paul Lietar "] description = "The discovery logic for librespot" license = "MIT" @@ -8,26 +8,27 @@ repository = "https://github.com/librespot-org/librespot" edition = "2018" [dependencies] -aes = { version = "0.7", features = ["ctr"] } +aes = "0.8" base64 = "0.13" cfg-if = "1.0" +ctr = "0.9" dns-sd = { version = "0.1.3", optional = true } form_urlencoded = "1.0" futures-core = "0.3" futures-util = "0.3" hmac = "0.12" hyper = { version = "0.14", features = ["http1", "server", "tcp"] } -libmdns = "0.6" +libmdns = "0.7" log = "0.4" rand = "0.8" serde_json = "1.0" -sha-1 = "0.10" +sha1 = "0.10" thiserror = "1.0" tokio = { version = "1", features = ["parking_lot", "sync", "rt"] } [dependencies.librespot-core] path = "../core" -version = "0.3.1" +version = "0.4.1" [dev-dependencies] futures = "0.3" diff --git a/discovery/src/lib.rs b/discovery/src/lib.rs index 02686e3e..718d9dec 100644 --- a/discovery/src/lib.rs +++ b/discovery/src/lib.rs @@ -52,13 +52,13 @@ pub struct Builder { /// Errors that can occur while setting up a [`Discovery`] instance. #[derive(Debug, Error)] pub enum DiscoveryError { - /// Setting up service discovery via DNS-SD failed. + #[error("Creating SHA1 block cipher failed")] + AesError(#[from] aes::cipher::InvalidLength), #[error("Setting up dns-sd failed: {0}")] DnsSdError(#[from] io::Error), - /// Setting up the http server failed. #[error("Creating SHA1 HMAC failed for base key {0:?}")] HmacError(Vec), - #[error("Setting up the http server failed: {0}")] + #[error("Setting up the HTTP server failed: {0}")] HttpServerError(#[from] hyper::Error), #[error("Missing params for key {0}")] ParamsError(&'static str), @@ -67,6 +67,7 @@ pub enum DiscoveryError { impl From for Error { fn from(err: DiscoveryError) -> Self { match err { + DiscoveryError::AesError(_) => Error::unavailable(err), DiscoveryError::DnsSdError(_) => Error::unavailable(err), DiscoveryError::HmacError(_) => Error::invalid_argument(err), DiscoveryError::HttpServerError(_) => Error::unavailable(err), diff --git a/discovery/src/server.rs b/discovery/src/server.rs index 6c63e683..3edc2fb6 100644 --- a/discovery/src/server.rs +++ b/discovery/src/server.rs @@ -8,11 +8,7 @@ use std::{ task::{Context, Poll}, }; -use aes::{ - cipher::generic_array::GenericArray, - cipher::{NewCipher, StreamCipher}, - Aes128Ctr, -}; +use aes::cipher::{KeyIvInit, StreamCipher}; use futures_core::Stream; use futures_util::{FutureExt, TryFutureExt}; use hmac::{Hmac, Mac}; @@ -32,6 +28,8 @@ use crate::{ core::{authentication::Credentials, diffie_hellman::DhLocalKeys, Error}, }; +type Aes128Ctr = ctr::Ctr128BE; + type Params<'a> = BTreeMap, Cow<'a, str>>; pub struct Config { @@ -151,10 +149,8 @@ impl RequestHandler { let decrypted = { let mut data = encrypted.to_vec(); - let mut cipher = Aes128Ctr::new( - GenericArray::from_slice(&encryption_key[0..16]), - GenericArray::from_slice(iv), - ); + let mut cipher = Aes128Ctr::new_from_slices(&encryption_key[0..16], iv) + .map_err(DiscoveryError::AesError)?; cipher.apply_keystream(&mut data); data }; diff --git a/examples/get_token.rs b/examples/get_token.rs index a568ae07..0473e122 100644 --- a/examples/get_token.rs +++ b/examples/get_token.rs @@ -19,7 +19,7 @@ async fn main() { let credentials = Credentials::with_password(&args[1], &args[2]); let session = Session::new(session_config, None); - match session.connect(credentials).await { + match session.connect(credentials, false).await { Ok(()) => println!( "Token: {:#?}", session.token_provider().get_token(SCOPES).await.unwrap() diff --git a/examples/play.rs b/examples/play.rs index 3cbbc43b..dc22103b 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -7,6 +7,7 @@ use librespot::{ playback::{ audio_backend, config::{AudioFormat, PlayerConfig}, + mixer::NoOpVolume, player::Player, }, }; @@ -30,12 +31,12 @@ async fn main() { println!("Connecting..."); let session = Session::new(session_config, None); - if let Err(e) = session.connect(credentials).await { + if let Err(e) = session.connect(credentials, false).await { println!("Error connecting: {}", e); exit(1); } - let (mut player, _) = Player::new(player_config, session, None, move || { + let (mut player, _) = Player::new(player_config, session, Box::new(NoOpVolume), move || { backend(None, audio_format) }); diff --git a/examples/playlist_tracks.rs b/examples/playlist_tracks.rs index fdadc61d..7404e755 100644 --- a/examples/playlist_tracks.rs +++ b/examples/playlist_tracks.rs @@ -28,7 +28,7 @@ async fn main() { }); let session = Session::new(session_config, None); - if let Err(e) = session.connect(credentials).await { + if let Err(e) = session.connect(credentials, false).await { println!("Error connecting: {}", e); exit(1); } diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 76635219..2b75a077 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-metadata" -version = "0.3.1" +version = "0.4.1" authors = ["Paul Lietar "] description = "The metadata logic for librespot" license = "MIT" @@ -14,12 +14,12 @@ bytes = "1" log = "0.4" protobuf = "2" thiserror = "1" -uuid = { version = "0.8", default-features = false } +uuid = { version = "1", default-features = false } [dependencies.librespot-core] path = "../core" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-protocol] path = "../protocol" -version = "0.3.1" +version = "0.4.1" diff --git a/metadata/src/request.rs b/metadata/src/request.rs index df276722..503dcf88 100644 --- a/metadata/src/request.rs +++ b/metadata/src/request.rs @@ -1,3 +1,5 @@ +use std::fmt::Write; + use crate::MetadataError; use librespot_core::{Error, Session}; @@ -13,10 +15,10 @@ pub trait MercuryRequest { Some(_) => "&", None => "?", }; - metrics_uri.push_str(&format!("{}country={}", separator, session.country())); + let _ = write!(metrics_uri, "{}country={}", separator, session.country()); if let Some(product) = session.get_user_attribute("type") { - metrics_uri.push_str(&format!("&product={}", product)); + let _ = write!(metrics_uri, "&product={}", product); } trace!("Requesting {}", metrics_uri); diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 1eee5924..43b5872e 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-playback" -version = "0.3.1" +version = "0.4.1" authors = ["Sasha Hilton "] description = "The audio playback logic for librespot" license = "MIT" @@ -9,21 +9,21 @@ edition = "2018" [dependencies.librespot-audio] path = "../audio" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-core] path = "../core" -version = "0.3.1" +version = "0.4.1" [dependencies.librespot-metadata] path = "../metadata" -version = "0.3.1" +version = "0.4.1" [dependencies] byteorder = "1" futures-util = "0.3" log = "0.4" -parking_lot = { version = "0.11", features = ["deadlock_detection"] } -shell-words = "1.0.0" -thiserror = "1.0" +parking_lot = { version = "0.12", features = ["deadlock_detection"] } +shell-words = "1.1" +thiserror = "1" tokio = { version = "1", features = ["parking_lot", "rt", "rt-multi-thread", "sync"] } zerocopy = "0.6" @@ -32,11 +32,11 @@ alsa = { version = "0.6", optional = true } portaudio-rs = { version = "0.3", optional = true } libpulse-binding = { version = "2", optional = true, default-features = false } libpulse-simple-binding = { version = "2", optional = true, default-features = false } -jack = { version = "0.8", optional = true } +jack = { version = "0.10", optional = true } sdl2 = { version = "0.35", optional = true } -gst = { package = "gstreamer", version = "0.18", optional = true } -gst-app = { package = "gstreamer-app", version = "0.18", optional = true } -gst-audio = { package = "gstreamer-audio", version = "0.18", optional = true } +gstreamer = { version = "0.18", optional = true } +gstreamer-app = { version = "0.18", optional = true } +gstreamer-audio = { version = "0.18", optional = true } glib = { version = "0.15", optional = true } # Rodio dependencies @@ -61,6 +61,6 @@ jackaudio-backend = ["jack"] rodio-backend = ["rodio", "cpal"] rodiojack-backend = ["rodio", "cpal/jack"] sdl-backend = ["sdl2"] -gstreamer-backend = ["gst", "gst-app", "gst-audio", "glib"] +gstreamer-backend = ["gstreamer", "gstreamer-app", "gstreamer-audio", "glib"] passthrough-decoder = ["ogg"] diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index ebeeef13..e8d9ee05 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -6,7 +6,6 @@ use crate::{NUM_CHANNELS, SAMPLE_RATE}; use alsa::device_name::HintIter; use alsa::pcm::{Access, Format, Frames, HwParams, PCM}; use alsa::{Direction, ValueOr}; -use std::cmp::min; use std::process::exit; use thiserror::Error; @@ -467,7 +466,7 @@ impl SinkAsBytes for AlsaSink { loop { let data_left = data_len - start_index; let space_left = capacity - self.period_buffer.len(); - let data_to_buffer = min(data_left, space_left); + let data_to_buffer = data_left.min(space_left); let end_index = start_index + data_to_buffer; self.period_buffer diff --git a/playback/src/audio_backend/gstreamer.rs b/playback/src/audio_backend/gstreamer.rs index 721c0db8..ad2a7591 100644 --- a/playback/src/audio_backend/gstreamer.rs +++ b/playback/src/audio_backend/gstreamer.rs @@ -1,9 +1,13 @@ -use gst::{ +use gstreamer::{ event::{FlushStart, FlushStop}, prelude::*, State, }; +use gstreamer as gst; +use gstreamer_app as gst_app; +use gstreamer_audio as gst_audio; + use parking_lot::Mutex; use std::sync::Arc; @@ -41,7 +45,7 @@ impl Open for GstreamerSink { let gst_caps = gst_info.to_caps().expect("Failed to create GStreamer caps"); let sample_size = format.size(); - let gst_bytes = NUM_CHANNELS as usize * 1024 * sample_size; + let gst_bytes = NUM_CHANNELS as usize * 2048 * sample_size; let pipeline = gst::Pipeline::new(None); let appsrc = gst::ElementFactory::make("appsrc", None) diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 682f8124..e0e8a77c 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -2,9 +2,38 @@ use super::{Open, Sink, SinkAsBytes, SinkError, SinkResult}; use crate::config::AudioFormat; use crate::convert::Converter; use crate::decoder::AudioPacket; + use std::fs::OpenOptions; use std::io::{self, Write}; use std::process::exit; +use thiserror::Error; + +#[derive(Debug, Error)] +enum StdoutError { + #[error(" {0}")] + OnWrite(std::io::Error), + + #[error(" File Path {file} Can Not be Opened and/or Created, {e}")] + OpenFailure { file: String, e: std::io::Error }, + + #[error(" Failed to Flush the Output Stream, {0}")] + FlushFailure(std::io::Error), + + #[error(" The Output Stream is None")] + NoOutput, +} + +impl From for SinkError { + fn from(e: StdoutError) -> SinkError { + use StdoutError::*; + let es = e.to_string(); + match e { + FlushFailure(_) | OnWrite(_) => SinkError::OnWrite(es), + OpenFailure { .. } => SinkError::ConnectionRefused(es), + NoOutput => SinkError::NotConnected(es), + } + } +} pub struct StdoutSink { output: Option>, @@ -15,13 +44,12 @@ pub struct StdoutSink { impl Open for StdoutSink { fn open(file: Option, format: AudioFormat) -> Self { if let Some("?") = file.as_deref() { - info!("Usage:"); - println!(" Output to stdout: --backend pipe"); - println!(" Output to file: --backend pipe --device {{filename}}"); + println!("\nUsage:\n\nOutput to stdout:\n\n\t--backend pipe\n\nOutput to file:\n\n\t--backend pipe --device {{filename}}\n"); exit(0); } - info!("Using pipe sink with format: {:?}", format); + info!("Using StdoutSink (pipe) with format: {:?}", format); + Self { output: None, file, @@ -32,21 +60,31 @@ impl Open for StdoutSink { impl Sink for StdoutSink { fn start(&mut self) -> SinkResult<()> { - if self.output.is_none() { - let output: Box = match self.file.as_deref() { - Some(file) => { - let open_op = OpenOptions::new() + self.output.get_or_insert({ + match self.file.as_deref() { + Some(file) => Box::new( + OpenOptions::new() .write(true) .create(true) .open(file) - .map_err(|e| SinkError::ConnectionRefused(e.to_string()))?; - Box::new(open_op) - } + .map_err(|e| StdoutError::OpenFailure { + file: file.to_string(), + e, + })?, + ), None => Box::new(io::stdout()), - }; + } + }); - self.output = Some(output); - } + Ok(()) + } + + fn stop(&mut self) -> SinkResult<()> { + self.output + .take() + .ok_or(StdoutError::NoOutput)? + .flush() + .map_err(StdoutError::FlushFailure)?; Ok(()) } @@ -56,19 +94,11 @@ impl Sink for StdoutSink { impl SinkAsBytes for StdoutSink { fn write_bytes(&mut self, data: &[u8]) -> SinkResult<()> { - match self.output.as_deref_mut() { - Some(output) => { - output - .write_all(data) - .map_err(|e| SinkError::OnWrite(e.to_string()))?; - output - .flush() - .map_err(|e| SinkError::OnWrite(e.to_string()))?; - } - None => { - return Err(SinkError::NotConnected("Output is None".to_string())); - } - } + self.output + .as_deref_mut() + .ok_or(StdoutError::NoOutput)? + .write_all(data) + .map_err(StdoutError::OnWrite)?; Ok(()) } diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs index 63fc5d88..6ce545da 100644 --- a/playback/src/audio_backend/subprocess.rs +++ b/playback/src/audio_backend/subprocess.rs @@ -4,30 +4,75 @@ use crate::convert::Converter; use crate::decoder::AudioPacket; use shell_words::split; -use std::io::Write; +use std::io::{ErrorKind, Write}; use std::process::{exit, Child, Command, Stdio}; +use thiserror::Error; + +#[derive(Debug, Error)] +enum SubprocessError { + #[error(" {0}")] + OnWrite(std::io::Error), + + #[error(" Command {command} Can Not be Executed, {e}")] + SpawnFailure { command: String, e: std::io::Error }, + + #[error(" Failed to Parse Command args for {command}, {e}")] + InvalidArgs { + command: String, + e: shell_words::ParseError, + }, + + #[error(" Failed to Flush the Subprocess, {0}")] + FlushFailure(std::io::Error), + + #[error(" Failed to Kill the Subprocess, {0}")] + KillFailure(std::io::Error), + + #[error(" Failed to Wait for the Subprocess to Exit, {0}")] + WaitFailure(std::io::Error), + + #[error(" The Subprocess is no longer able to accept Bytes")] + WriteZero, + + #[error(" Missing Required Shell Command")] + MissingCommand, + + #[error(" The Subprocess is None")] + NoChild, + + #[error(" The Subprocess's stdin is None")] + NoStdin, +} + +impl From for SinkError { + fn from(e: SubprocessError) -> SinkError { + use SubprocessError::*; + let es = e.to_string(); + match e { + FlushFailure(_) | KillFailure(_) | WaitFailure(_) | OnWrite(_) | WriteZero => { + SinkError::OnWrite(es) + } + SpawnFailure { .. } => SinkError::ConnectionRefused(es), + MissingCommand | InvalidArgs { .. } => SinkError::InvalidParams(es), + NoChild | NoStdin => SinkError::NotConnected(es), + } + } +} pub struct SubprocessSink { - shell_command: String, + shell_command: Option, child: Option, format: AudioFormat, } impl Open for SubprocessSink { fn open(shell_command: Option, format: AudioFormat) -> Self { - let shell_command = match shell_command.as_deref() { - Some("?") => { - info!("Usage: --backend subprocess --device {{shell_command}}"); - exit(0); - } - Some(cmd) => cmd.to_owned(), - None => { - error!("subprocess sink requires specifying a shell command"); - exit(1); - } - }; + if let Some("?") = shell_command.as_deref() { + println!("\nUsage:\n\nOutput to a Subprocess:\n\n\t--backend subprocess --device {{shell_command}}\n"); + exit(0); + } - info!("Using subprocess sink with format: {:?}", format); + info!("Using SubprocessSink with format: {:?}", format); Self { shell_command, @@ -39,26 +84,53 @@ impl Open for SubprocessSink { impl Sink for SubprocessSink { fn start(&mut self) -> SinkResult<()> { - let args = split(&self.shell_command).unwrap(); - let child = Command::new(&args[0]) - .args(&args[1..]) - .stdin(Stdio::piped()) - .spawn() - .map_err(|e| SinkError::ConnectionRefused(e.to_string()))?; - self.child = Some(child); + self.child.get_or_insert({ + match self.shell_command.as_deref() { + Some(command) => { + let args = split(command).map_err(|e| SubprocessError::InvalidArgs { + command: command.to_string(), + e, + })?; + + Command::new(&args[0]) + .args(&args[1..]) + .stdin(Stdio::piped()) + .spawn() + .map_err(|e| SubprocessError::SpawnFailure { + command: command.to_string(), + e, + })? + } + None => return Err(SubprocessError::MissingCommand.into()), + } + }); + Ok(()) } fn stop(&mut self) -> SinkResult<()> { - if let Some(child) = &mut self.child.take() { - child - .kill() - .map_err(|e| SinkError::OnWrite(e.to_string()))?; - child - .wait() - .map_err(|e| SinkError::OnWrite(e.to_string()))?; + let child = &mut self.child.take().ok_or(SubprocessError::NoChild)?; + + match child.try_wait() { + // The process has already exited + // nothing to do. + Ok(Some(_)) => Ok(()), + Ok(_) => { + // The process Must DIE!!! + child + .stdin + .take() + .ok_or(SubprocessError::NoStdin)? + .flush() + .map_err(SubprocessError::FlushFailure)?; + + child.kill().map_err(SubprocessError::KillFailure)?; + child.wait().map_err(SubprocessError::WaitFailure)?; + + Ok(()) + } + Err(e) => Err(SubprocessError::WaitFailure(e).into()), } - Ok(()) } sink_as_bytes!(); @@ -66,22 +138,67 @@ impl Sink for SubprocessSink { impl SinkAsBytes for SubprocessSink { fn write_bytes(&mut self, data: &[u8]) -> SinkResult<()> { - if let Some(child) = &mut self.child { - let child_stdin = child + // We get one attempted restart per write. + // We don't want to get stuck in a restart loop. + let mut restarted = false; + let mut start_index = 0; + let data_len = data.len(); + let mut end_index = data_len; + + loop { + match self + .child + .as_ref() + .ok_or(SubprocessError::NoChild)? .stdin - .as_mut() - .ok_or_else(|| SinkError::NotConnected("Child is None".to_string()))?; - child_stdin - .write_all(data) - .map_err(|e| SinkError::OnWrite(e.to_string()))?; - child_stdin - .flush() - .map_err(|e| SinkError::OnWrite(e.to_string()))?; + .as_ref() + .ok_or(SubprocessError::NoStdin)? + .write(&data[start_index..end_index]) + { + Ok(0) => { + // Potentially fatal. + // As per the docs a return value of 0 + // means we shouldn't try to write to the + // process anymore so let's try a restart + // if we haven't already. + self.try_restart(SubprocessError::WriteZero, &mut restarted)?; + + continue; + } + Ok(bytes_written) => { + // What we want, a successful write. + start_index = data_len.min(start_index + bytes_written); + end_index = data_len.min(start_index + bytes_written); + + if end_index == data_len { + break Ok(()); + } + } + // Non-fatal, retry the write. + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => { + // Very possibly fatal, + // but let's try a restart anyway if we haven't already. + self.try_restart(SubprocessError::OnWrite(e), &mut restarted)?; + + continue; + } + } } - Ok(()) } } impl SubprocessSink { pub const NAME: &'static str = "subprocess"; + + fn try_restart(&mut self, e: SubprocessError, restarted: &mut bool) -> SinkResult<()> { + // If the restart fails throw the original error back. + if !*restarted && self.stop().is_ok() && self.start().is_ok() { + *restarted = true; + + Ok(()) + } else { + Err(e.into()) + } + } } diff --git a/playback/src/mixer/alsamixer.rs b/playback/src/mixer/alsamixer.rs index aff441d1..52be1085 100644 --- a/playback/src/mixer/alsamixer.rs +++ b/playback/src/mixer/alsamixer.rs @@ -104,23 +104,33 @@ impl Mixer for AlsaMixer { let min_db = min_millibel.to_db() as f64; let max_db = max_millibel.to_db() as f64; - let mut db_range = f64::abs(max_db - min_db); + let reported_db_range = f64::abs(max_db - min_db); // Synchronize the volume control dB range with the mixer control, // unless it was already set with a command line option. - if !config.volume_ctrl.range_ok() { - if db_range > 100.0 { - debug!("Alsa mixer reported dB range > 100, which is suspect"); - warn!("Please manually set `--volume-range` if this is incorrect"); - } - config.volume_ctrl.set_db_range(db_range); - } else { + let db_range = if config.volume_ctrl.range_ok() { let db_range_override = config.volume_ctrl.db_range(); + if db_range_override.is_normal() { + db_range_override + } else { + reported_db_range + } + } else { + config.volume_ctrl.set_db_range(reported_db_range); + reported_db_range + }; + + if reported_db_range == db_range { + debug!("Alsa dB volume range was reported as {}", reported_db_range); + if reported_db_range > 100.0 { + debug!("Alsa mixer reported dB range > 100, which is suspect"); + debug!("Please manually set `--volume-range` if this is incorrect"); + } + } else { debug!( - "Alsa dB volume range was detected as {} but overridden as {}", - db_range, db_range_override + "Alsa dB volume range was reported as {} but overridden to {}", + reported_db_range, db_range ); - db_range = db_range_override; } // For hardware controls with a small range (24 dB or less), diff --git a/playback/src/mixer/mod.rs b/playback/src/mixer/mod.rs index a3c7a5a1..0a8b8d6c 100644 --- a/playback/src/mixer/mod.rs +++ b/playback/src/mixer/mod.rs @@ -3,6 +3,8 @@ use crate::config::VolumeCtrl; pub mod mappings; use self::mappings::MappedCtrl; +pub struct NoOpVolume; + pub trait Mixer: Send { fn open(config: MixerConfig) -> Self where @@ -11,13 +13,19 @@ pub trait Mixer: Send { fn set_volume(&self, volume: u16); fn volume(&self) -> u16; - fn get_audio_filter(&self) -> Option> { - None + fn get_soft_volume(&self) -> Box { + Box::new(NoOpVolume) } } -pub trait AudioFilter { - fn modify_stream(&self, data: &mut [f64]); +pub trait VolumeGetter { + fn attenuation_factor(&self) -> f64; +} + +impl VolumeGetter for NoOpVolume { + fn attenuation_factor(&self) -> f64 { + 1.0 + } } pub mod softmixer; diff --git a/playback/src/mixer/softmixer.rs b/playback/src/mixer/softmixer.rs index b0d94a6e..061f39b9 100644 --- a/playback/src/mixer/softmixer.rs +++ b/playback/src/mixer/softmixer.rs @@ -1,7 +1,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; -use super::AudioFilter; +use super::VolumeGetter; use super::{MappedCtrl, VolumeCtrl}; use super::{Mixer, MixerConfig}; @@ -35,10 +35,8 @@ impl Mixer for SoftMixer { .store(mapped_volume.to_bits(), Ordering::Relaxed) } - fn get_audio_filter(&self) -> Option> { - Some(Box::new(SoftVolumeApplier { - volume: self.volume.clone(), - })) + fn get_soft_volume(&self) -> Box { + Box::new(SoftVolume(self.volume.clone())) } } @@ -46,17 +44,10 @@ impl SoftMixer { pub const NAME: &'static str = "softvol"; } -struct SoftVolumeApplier { - volume: Arc, -} +struct SoftVolume(Arc); -impl AudioFilter for SoftVolumeApplier { - fn modify_stream(&self, data: &mut [f64]) { - let volume = f64::from_bits(self.volume.load(Ordering::Relaxed)); - if volume < 1.0 { - for x in data.iter_mut() { - *x *= volume; - } - } +impl VolumeGetter for SoftVolume { + fn attenuation_factor(&self) -> f64 { + f64::from_bits(self.0.load(Ordering::Relaxed)) } } diff --git a/playback/src/player.rs b/playback/src/player.rs index e4002878..1ebf42d7 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -37,7 +37,7 @@ use crate::{ core::{util::SeqGenerator, Error, Session, SpotifyId}, decoder::{AudioDecoder, AudioPacket, AudioPacketPosition, SymphoniaDecoder}, metadata::audio::{AudioFileFormat, AudioFiles, AudioItem}, - mixer::AudioFilter, + mixer::VolumeGetter, }; #[cfg(feature = "passthrough-decoder")] @@ -81,7 +81,7 @@ struct PlayerInternal { sink: Box, sink_status: SinkStatus, sink_event_callback: Option, - audio_filter: Option>, + volume_getter: Box, event_senders: Vec>, converter: Converter, @@ -368,7 +368,7 @@ impl Player { pub fn new( config: PlayerConfig, session: Session, - audio_filter: Option>, + volume_getter: Box, sink_builder: F, ) -> (Player, PlayerEventChannel) where @@ -420,7 +420,7 @@ impl Player { sink: sink_builder(), sink_status: SinkStatus::Closed, sink_event_callback: None, - audio_filter, + volume_getter, event_senders: [event_sender].to_vec(), converter, @@ -1445,109 +1445,111 @@ impl PlayerInternal { Some((_, mut packet)) => { if !packet.is_empty() { if let AudioPacket::Samples(ref mut data) = packet { + // Get the volume for the packet. + // In the case of hardware volume control this will + // always be 1.0 (no change). + let volume = self.volume_getter.attenuation_factor(); + // For the basic normalisation method, a normalisation factor of 1.0 indicates that // there is nothing to normalise (all samples should pass unaltered). For the // dynamic method, there may still be peaks that we want to shave off. - if self.config.normalisation { - if self.config.normalisation_method == NormalisationMethod::Basic - && normalisation_factor < 1.0 - { - for sample in data.iter_mut() { - *sample *= normalisation_factor; - } - } else if self.config.normalisation_method - == NormalisationMethod::Dynamic - { - // zero-cost shorthands - let threshold_db = self.config.normalisation_threshold_dbfs; - let knee_db = self.config.normalisation_knee_db; - let attack_cf = self.config.normalisation_attack_cf; - let release_cf = self.config.normalisation_release_cf; - for sample in data.iter_mut() { - *sample *= normalisation_factor; - - // Feedforward limiter in the log domain - // After: Giannoulis, D., Massberg, M., & Reiss, J.D. (2012). Digital Dynamic - // Range Compressor Design—A Tutorial and Analysis. Journal of The Audio - // Engineering Society, 60, 399-408. - - // Some tracks have samples that are precisely 0.0. That's silence - // and we know we don't need to limit that, in which we can spare - // the CPU cycles. - // - // Also, calling `ratio_to_db(0.0)` returns `inf` and would get the - // peak detector stuck. Also catch the unlikely case where a sample - // is decoded as `NaN` or some other non-normal value. - let limiter_db = if sample.is_normal() { - // step 1-4: half-wave rectification and conversion into dB - // and gain computer with soft knee and subtractor - let bias_db = ratio_to_db(sample.abs()) - threshold_db; - let knee_boundary_db = bias_db * 2.0; - - if knee_boundary_db < -knee_db { - 0.0 - } else if knee_boundary_db.abs() <= knee_db { - // The textbook equation: - // ratio_to_db(sample.abs()) - (ratio_to_db(sample.abs()) - (bias_db + knee_db / 2.0).powi(2) / (2.0 * knee_db)) - // Simplifies to: - // ((2.0 * bias_db) + knee_db).powi(2) / (8.0 * knee_db) - // Which in our case further simplifies to: - // (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db) - // because knee_boundary_db is 2.0 * bias_db. - (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db) - } else { - // Textbook: - // ratio_to_db(sample.abs()) - threshold_db, which is already our bias_db. - bias_db - } - } else { - 0.0 - }; - - // Spare the CPU unless (1) the limiter is engaged, (2) we - // were in attack or (3) we were in release, and that attack/ - // release wasn't finished yet. - if limiter_db > 0.0 - || self.normalisation_integrator > 0.0 - || self.normalisation_peak > 0.0 - { - // step 5: smooth, decoupled peak detector - // Textbook: - // release_cf * self.normalisation_integrator + (1.0 - release_cf) * limiter_db - // Simplifies to: - // release_cf * self.normalisation_integrator - release_cf * limiter_db + limiter_db - self.normalisation_integrator = f64::max( - limiter_db, - release_cf * self.normalisation_integrator - - release_cf * limiter_db - + limiter_db, - ); - // Textbook: - // attack_cf * self.normalisation_peak + (1.0 - attack_cf) * self.normalisation_integrator - // Simplifies to: - // attack_cf * self.normalisation_peak - attack_cf * self.normalisation_integrator + self.normalisation_integrator - self.normalisation_peak = attack_cf - * self.normalisation_peak - - attack_cf * self.normalisation_integrator - + self.normalisation_integrator; - - // step 6: make-up gain applied later (volume attenuation) - // Applying the standard normalisation factor here won't work, - // because there are tracks with peaks as high as 6 dB above - // the default threshold, so that would clip. - - // steps 7-8: conversion into level and multiplication into gain stage - *sample *= db_to_ratio(-self.normalisation_peak); - } - } + // No matter the case we apply volume attenuation last if there is any. + if !self.config.normalisation && volume < 1.0 { + for sample in data.iter_mut() { + *sample *= volume; } - } + } else if self.config.normalisation_method == NormalisationMethod::Basic + && (normalisation_factor < 1.0 || volume < 1.0) + { + for sample in data.iter_mut() { + *sample *= normalisation_factor * volume; + } + } else if self.config.normalisation_method == NormalisationMethod::Dynamic { + // zero-cost shorthands + let threshold_db = self.config.normalisation_threshold_dbfs; + let knee_db = self.config.normalisation_knee_db; + let attack_cf = self.config.normalisation_attack_cf; + let release_cf = self.config.normalisation_release_cf; - // Apply volume attenuation last. TODO: make this so we can chain - // the normaliser and mixer as a processing pipeline. - if let Some(ref editor) = self.audio_filter { - editor.modify_stream(data) + for sample in data.iter_mut() { + *sample *= normalisation_factor; + + // Feedforward limiter in the log domain + // After: Giannoulis, D., Massberg, M., & Reiss, J.D. (2012). Digital Dynamic + // Range Compressor Design—A Tutorial and Analysis. Journal of The Audio + // Engineering Society, 60, 399-408. + + // Some tracks have samples that are precisely 0.0. That's silence + // and we know we don't need to limit that, in which we can spare + // the CPU cycles. + // + // Also, calling `ratio_to_db(0.0)` returns `inf` and would get the + // peak detector stuck. Also catch the unlikely case where a sample + // is decoded as `NaN` or some other non-normal value. + let limiter_db = if sample.is_normal() { + // step 1-4: half-wave rectification and conversion into dB + // and gain computer with soft knee and subtractor + let bias_db = ratio_to_db(sample.abs()) - threshold_db; + let knee_boundary_db = bias_db * 2.0; + + if knee_boundary_db < -knee_db { + 0.0 + } else if knee_boundary_db.abs() <= knee_db { + // The textbook equation: + // ratio_to_db(sample.abs()) - (ratio_to_db(sample.abs()) - (bias_db + knee_db / 2.0).powi(2) / (2.0 * knee_db)) + // Simplifies to: + // ((2.0 * bias_db) + knee_db).powi(2) / (8.0 * knee_db) + // Which in our case further simplifies to: + // (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db) + // because knee_boundary_db is 2.0 * bias_db. + (knee_boundary_db + knee_db).powi(2) / (8.0 * knee_db) + } else { + // Textbook: + // ratio_to_db(sample.abs()) - threshold_db, which is already our bias_db. + bias_db + } + } else { + 0.0 + }; + + // Spare the CPU unless (1) the limiter is engaged, (2) we + // were in attack or (3) we were in release, and that attack/ + // release wasn't finished yet. + if limiter_db > 0.0 + || self.normalisation_integrator > 0.0 + || self.normalisation_peak > 0.0 + { + // step 5: smooth, decoupled peak detector + // Textbook: + // release_cf * self.normalisation_integrator + (1.0 - release_cf) * limiter_db + // Simplifies to: + // release_cf * self.normalisation_integrator - release_cf * limiter_db + limiter_db + self.normalisation_integrator = f64::max( + limiter_db, + release_cf * self.normalisation_integrator + - release_cf * limiter_db + + limiter_db, + ); + // Textbook: + // attack_cf * self.normalisation_peak + (1.0 - attack_cf) * self.normalisation_integrator + // Simplifies to: + // attack_cf * self.normalisation_peak - attack_cf * self.normalisation_integrator + self.normalisation_integrator + self.normalisation_peak = attack_cf * self.normalisation_peak + - attack_cf * self.normalisation_integrator + + self.normalisation_integrator; + + // step 6: make-up gain applied later (volume attenuation) + // Applying the standard normalisation factor here won't work, + // because there are tracks with peaks as high as 6 dB above + // the default threshold, so that would clip. + + // steps 7-8: conversion into level and multiplication into gain stage + *sample *= db_to_ratio(-self.normalisation_peak); + } + + *sample *= volume; + } } } @@ -1979,7 +1981,7 @@ impl PlayerInternal { fn handle_command(&mut self, cmd: PlayerCommand) -> PlayerResult { debug!("command={:?}", cmd); - let result = match cmd { + match cmd { PlayerCommand::Load { track_id, play_request_id, @@ -2034,7 +2036,7 @@ impl PlayerInternal { } }; - Ok(result) + Ok(()) } fn send_event(&mut self, event: PlayerEvent) { diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index c90c82d2..1d2ed143 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-protocol" -version = "0.3.1" +version = "0.4.1" authors = ["Paul Liétar "] build = "build.rs" description = "The protobuf logic for communicating with Spotify servers" diff --git a/publish.sh b/publish.sh index fb4a475a..cf51212f 100755 --- a/publish.sh +++ b/publish.sh @@ -27,14 +27,17 @@ function updateVersion { do if [ "$CRATE" = "librespot" ] then - CRATE='' + CRATE_DIR='' + else + CRATE_DIR=$CRATE fi - crate_path="$WORKINGDIR/$CRATE/Cargo.toml" + crate_path="$WORKINGDIR/$CRATE_DIR/Cargo.toml" crate_path=${crate_path//\/\///} sed -i '' "s/^version.*/version = \"$1\"/g" "$crate_path" echo "Path is $crate_path" if [ "$CRATE" = "librespot" ] then + echo "Updating lockfile" if [ "$DRY_RUN" = 'true' ] ; then cargo update --dry-run git add . && git commit --dry-run -a -m "Update Cargo.lock" diff --git a/src/main.rs b/src/main.rs index a194ec0a..0aaa613b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -668,7 +668,15 @@ fn get_setup() -> Setup { trace!("Command line argument(s):"); for (index, key) in args.iter().enumerate() { - let opt = key.trim_start_matches('-'); + let opt = { + let key = key.trim_start_matches('-'); + + if let Some((s, _)) = key.split_once('=') { + s + } else { + key + } + }; if index > 0 && key.starts_with('-') @@ -678,13 +686,13 @@ fn get_setup() -> Setup { { if matches!(opt, PASSWORD | PASSWORD_SHORT | USERNAME | USERNAME_SHORT) { // Don't log creds. - trace!("\t\t{} \"XXXXXXXX\"", key); + trace!("\t\t{} \"XXXXXXXX\"", opt); } else { let value = matches.opt_str(opt).unwrap_or_else(|| "".to_string()); if value.is_empty() { - trace!("\t\t{}", key); + trace!("\t\t{}", opt); } else { - trace!("\t\t{} \"{}\"", key, value); + trace!("\t\t{} \"{}\"", opt, value); } } } @@ -1072,7 +1080,7 @@ fn get_setup() -> Setup { Some(creds) if username == creds.username => Some(creds), _ => { let prompt = &format!("Password for {}: ", username); - match rpassword::prompt_password_stderr(prompt) { + match rpassword::prompt_password(prompt) { Ok(password) => { if !password.is_empty() { Some(Credentials::with_password(username, password)) @@ -1599,24 +1607,25 @@ async fn main() { if setup.enable_discovery { let device_id = setup.session_config.device_id.clone(); - - discovery = match librespot::discovery::Discovery::builder(device_id) + match librespot::discovery::Discovery::builder(device_id) .name(setup.connect_config.name.clone()) .device_type(setup.connect_config.device_type) .port(setup.zeroconf_port) .launch() { - Ok(d) => Some(d), - Err(e) => { - error!("Discovery Error: {}", e); - exit(1); - } - } + Ok(d) => discovery = Some(d), + Err(err) => warn!("Could not initialise discovery: {}.", err), + }; } if let Some(credentials) = setup.credentials { last_credentials = Some(credentials); connecting = true; + } else if discovery.is_none() { + error!( + "Discovery is unavailable and no credentials provided. Authentication is not possible." + ); + exit(1); } loop { @@ -1656,12 +1665,12 @@ async fn main() { let player_config = setup.player_config.clone(); let connect_config = setup.connect_config.clone(); - let audio_filter = mixer.get_audio_filter(); + let soft_volume = mixer.get_soft_volume(); let format = setup.format; let backend = setup.backend; let device = setup.device.clone(); let (player, event_channel) = - Player::new(player_config, session.clone(), audio_filter, move || { + Player::new(player_config, session.clone(), soft_volume, move || { (backend)(device, format) }); diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index ef6d195c..99b1645d 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -17,17 +17,17 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option match old_track_id.to_base62() { - Err(_) => { + Err(e) => { return Some(Err(Error::new( ErrorKind::InvalidData, - "PlayerEvent::Changed: Invalid old track id", + format!("PlayerEvent::Changed: Invalid old track id: {}", e), ))) } Ok(old_id) => match new_track_id.to_base62() { - Err(_) => { + Err(e) => { return Some(Err(Error::new( ErrorKind::InvalidData, - "PlayerEvent::Changed: Invalid new track id", + format!("PlayerEvent::Changed: Invalid old track id: {}", e), ))) } Ok(new_id) => { @@ -38,10 +38,10 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option match track_id.to_base62() { - Err(_) => { + Err(e) => { return Some(Err(Error::new( ErrorKind::InvalidData, - "PlayerEvent::Started: Invalid track id", + format!("PlayerEvent::Started: Invalid track id: {}", e), ))) } Ok(id) => { @@ -50,10 +50,10 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option match track_id.to_base62() { - Err(_) => { + Err(e) => { return Some(Err(Error::new( ErrorKind::InvalidData, - "PlayerEvent::Stopped: Invalid track id", + format!("PlayerEvent::Stopped: Invalid track id: {}", e), ))) } Ok(id) => { @@ -67,10 +67,10 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option match track_id.to_base62() { - Err(_) => { + Err(e) => { return Some(Err(Error::new( ErrorKind::InvalidData, - "PlayerEvent::Playing: Invalid track id", + format!("PlayerEvent::Playing: Invalid track id: {}", e), ))) } Ok(id) => { @@ -86,10 +86,10 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option match track_id.to_base62() { - Err(_) => { + Err(e) => { return Some(Err(Error::new( ErrorKind::InvalidData, - "PlayerEvent::Paused: Invalid track id", + format!("PlayerEvent::Paused: Invalid track id: {}", e), ))) } Ok(id) => { @@ -100,10 +100,10 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option match track_id.to_base62() { - Err(_) => { + Err(e) => { return Some(Err(Error::new( ErrorKind::InvalidData, - "PlayerEvent::Preloading: Invalid track id", + format!("PlayerEvent::Preloading: Invalid track id: {}", e), ))) } Ok(id) => {