Merge branch 'dev' into tokio_migration

This commit is contained in:
johannesd3 2021-02-10 21:51:33 +01:00
commit 872fab62d8
23 changed files with 884 additions and 562 deletions

View file

@ -41,7 +41,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
toolchain: toolchain:
- 1.40.0 # MSRV (Minimum supported rust version) - 1.42.0 # MSRV (Minimum supported rust version)
- stable - stable
- beta - beta
experimental: [false] experimental: [false]
@ -59,6 +59,15 @@ jobs:
profile: minimal profile: minimal
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
override: true override: true
- name: Cache Rust dependencies
uses: actions/cache@v2
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git
target
key: ${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }}
- name: Install developer package dependencies - name: Install developer package dependencies
run: sudo apt-get update && sudo apt-get install libpulse-dev portaudio19-dev libasound2-dev libsdl2-dev gstreamer1.0-dev libgstreamer-plugins-base1.0-dev run: sudo apt-get update && sudo apt-get install libpulse-dev portaudio19-dev libasound2-dev libsdl2-dev gstreamer1.0-dev libgstreamer-plugins-base1.0-dev
- run: cargo build --locked --no-default-features - run: cargo build --locked --no-default-features
@ -94,6 +103,15 @@ jobs:
target: ${{ matrix.target }} target: ${{ matrix.target }}
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
override: true override: true
- name: Cache Rust dependencies
uses: actions/cache@v2
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git
target
key: ${{ runner.os }}-build-${{ hashFiles('**/Cargo.lock') }}
- name: Install cross - name: Install cross
run: cargo install cross || true run: cargo install cross || true
- name: Build - name: Build

View file

@ -1,73 +0,0 @@
language: rust
rust:
- 1.40.0
- stable
- beta
- nightly
# Need to cache the whole `.cargo` directory to keep .crates.toml for
# cargo-update to work
cache:
directories:
- /home/travis/.cargo
# But don't cache the cargo registry
before_cache:
- rm -rf /home/travis/.cargo/registry
matrix:
# Performance tweak
fast_finish: true
# Ignore failures in nightly, not ideal, but necessary
allow_failures:
- rust: nightly
# Only run the formatting check for stable
include:
- name: 'Rust: format check'
rust: stable
install:
- rustup component add rustfmt
script:
- cargo fmt --verbose --all -- --check
addons:
apt:
packages:
- gcc-arm-linux-gnueabihf
- libc6-dev-armhf-cross
- libpulse-dev
- portaudio19-dev
- libasound2-dev
- libsdl2-dev
- gstreamer1.0-dev
- libgstreamer-plugins-base1.0-dev
before_script:
- mkdir -p ~/.cargo
- echo '[target.armv7-unknown-linux-gnueabihf]' > ~/.cargo/config
- echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config
- rustup target add armv7-unknown-linux-gnueabihf
script:
- cargo build --locked --no-default-features
- cargo build --locked --examples
- cargo build --locked --no-default-features --features "with-tremor"
- cargo build --locked --no-default-features --features "with-vorbis"
- cargo build --locked --no-default-features --features "alsa-backend"
- cargo build --locked --no-default-features --features "portaudio-backend"
- cargo build --locked --no-default-features --features "pulseaudio-backend"
- cargo build --locked --no-default-features --features "jackaudio-backend"
- cargo build --locked --no-default-features --features "rodio-backend"
- cargo build --locked --no-default-features --features "sdl-backend"
- cargo build --locked --no-default-features --features "gstreamer-backend"
- cargo build --locked --no-default-features --target armv7-unknown-linux-gnueabihf
notifications:
email: false
webhooks:
urls:
- https://webhooks.gitter.im/e/780b178b15811059752e
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: never # options: [always|never|change] default: always

347
Cargo.lock generated
View file

@ -73,38 +73,16 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "alsa"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4a0d4ebc8b23041c5de9bc9aee13b4bad844a589479701f31a5934cfe4aeb32"
dependencies = [
"alsa-sys 0.1.2",
"bitflags 0.9.1",
"libc",
"nix 0.9.0",
]
[[package]] [[package]]
name = "alsa" name = "alsa"
version = "0.4.3" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934" checksum = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934"
dependencies = [ dependencies = [
"alsa-sys 0.3.1", "alsa-sys",
"bitflags 1.2.1", "bitflags",
"libc", "libc",
"nix 0.15.0", "nix",
]
[[package]]
name = "alsa-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0edcbbf9ef68f15ae1b620f722180b82a98b6f0628d30baa6b8d2a5abc87d58"
dependencies = [
"libc",
"pkg-config",
] ]
[[package]] [[package]]
@ -129,27 +107,6 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"
[[package]]
name = "async-stream"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3670df70cbc01729f901f94c887814b3c68db038aad1329a418bae178bc5295c"
dependencies = [
"async-stream-impl",
"futures-core",
]
[[package]]
name = "async-stream-impl"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3548b8efc9f8e8a5a0a2808c5bd8451a9031b9e5b879a79590304ae928b0a70"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.42" version = "0.1.42"
@ -220,7 +177,7 @@ version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"cexpr", "cexpr",
"clang-sys", "clang-sys",
"lazy_static", "lazy_static",
@ -248,12 +205,6 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.2.1"
@ -283,9 +234,9 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.4.0" version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
[[package]] [[package]]
name = "byte-tools" name = "byte-tools"
@ -353,9 +304,9 @@ dependencies = [
[[package]] [[package]]
name = "chunked_transfer" name = "chunked_transfer"
version = "1.3.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
[[package]] [[package]]
name = "cipher" name = "cipher"
@ -368,9 +319,9 @@ dependencies = [
[[package]] [[package]]
name = "clang-sys" name = "clang-sys"
version = "1.0.3" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c" checksum = "5cb92721cb37482245ed88428f72253ce422b3b4ee169c70a0642521bb5db4cc"
dependencies = [ dependencies = [
"glob", "glob",
"libc", "libc",
@ -383,7 +334,7 @@ version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
] ]
[[package]] [[package]]
@ -422,7 +373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f"
dependencies = [ dependencies = [
"percent-encoding 2.1.0", "percent-encoding 2.1.0",
"time 0.2.24", "time 0.2.25",
"version_check", "version_check",
] ]
@ -433,12 +384,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3"
dependencies = [ dependencies = [
"cookie", "cookie",
"idna 0.2.0", "idna 0.2.1",
"log", "log",
"publicsuffix", "publicsuffix",
"serde", "serde",
"serde_json", "serde_json",
"time 0.2.24", "time 0.2.25",
"url 2.2.0", "url 2.2.0",
] ]
@ -454,7 +405,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491" checksum = "f229761965dad3e9b11081668a6ea00f1def7aa46062321b5ec245b834f6e491"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"coreaudio-sys", "coreaudio-sys",
] ]
@ -473,7 +424,7 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05631e2089dfa5d3b6ea1cfbbfd092e2ee5deeb69698911bc976b28b746d3657" checksum = "05631e2089dfa5d3b6ea1cfbbfd092e2ee5deeb69698911bc976b28b746d3657"
dependencies = [ dependencies = [
"alsa 0.4.3", "alsa",
"core-foundation-sys", "core-foundation-sys",
"coreaudio-rs", "coreaudio-rs",
"jni 0.17.0", "jni 0.17.0",
@ -483,7 +434,7 @@ dependencies = [
"mach", "mach",
"ndk", "ndk",
"ndk-glue", "ndk-glue",
"nix 0.15.0", "nix",
"oboe", "oboe",
"parking_lot", "parking_lot",
"stdweb 0.1.3", "stdweb 0.1.3",
@ -557,9 +508,9 @@ dependencies = [
[[package]] [[package]]
name = "derivative" name = "derivative"
version = "2.1.3" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaed5874effa6cde088c644ddcdcb4ffd1511391c5be4fdd7a5ccd02c7e4a183" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -800,7 +751,7 @@ checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"wasi 0.10.1+wasi-snapshot-preview1", "wasi 0.10.2+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@ -815,7 +766,7 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor", "futures-executor",
@ -877,7 +828,7 @@ version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf" checksum = "5d50f822055923f1cbede233aa5dfd4ee957cf328fb3076e330886094e11d6cf"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -901,7 +852,7 @@ version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc80888271338c3ede875d8cafc452eb207476ff5539dcbe0018a8f5b827af0e" checksum = "cc80888271338c3ede875d8cafc452eb207476ff5539dcbe0018a8f5b827af0e"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"glib", "glib",
@ -934,7 +885,7 @@ version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bafd01c56f59cb10f4b5a10f97bb4bdf8c2b2784ae5b04da7e2d400cf6e6afcf" checksum = "bafd01c56f59cb10f4b5a10f97bb4bdf8c2b2784ae5b04da7e2d400cf6e6afcf"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"glib", "glib",
"glib-sys", "glib-sys",
"gobject-sys", "gobject-sys",
@ -1046,9 +997,9 @@ dependencies = [
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.3.4" version = "1.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691"
[[package]] [[package]]
name = "httpdate" name = "httpdate"
@ -1064,9 +1015,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.2" version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12219dc884514cb4a6a03737f4413c0e01c23a1b059b0156004b23f1e19dccbe" checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -1078,7 +1029,7 @@ dependencies = [
"httparse", "httparse",
"httpdate", "httpdate",
"itoa", "itoa",
"pin-project 1.0.4", "pin-project 1.0.5",
"socket2", "socket2",
"tokio", "tokio",
"tower-service", "tower-service",
@ -1105,9 +1056,9 @@ dependencies = [
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.2.0" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094"
dependencies = [ dependencies = [
"matches", "matches",
"unicode-bidi", "unicode-bidi",
@ -1154,7 +1105,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c1871c91fa65aa328f3bedbaa54a6e5d1de009264684c153eb708ba933aa6f5" checksum = "7c1871c91fa65aa328f3bedbaa54a6e5d1de009264684c153eb708ba933aa6f5"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"jack-sys", "jack-sys",
"lazy_static", "lazy_static",
"libc", "libc",
@ -1207,9 +1158,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.46" version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@ -1239,9 +1190,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.82" version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
[[package]] [[package]]
name = "libflate" name = "libflate"
@ -1273,11 +1224,11 @@ dependencies = [
[[package]] [[package]]
name = "libpulse-binding" name = "libpulse-binding"
version = "2.22.0" version = "2.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce89ab17bd343b08bd4321c674ef1477d34f83be18b1ab2ee47a5e5fbee64a91" checksum = "b2405f806801527dfb3d2b6d48a282cdebe9a1b41b0652e0d7b5bad81dbc700e"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"libc", "libc",
"libpulse-sys", "libpulse-sys",
"num-derive", "num-derive",
@ -1287,9 +1238,9 @@ dependencies = [
[[package]] [[package]]
name = "libpulse-simple-binding" name = "libpulse-simple-binding"
version = "2.20.1" version = "2.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e47f6cda2748fb86f15e5e65cc33be306577140f4b500622b5d98df2ca17240" checksum = "a574975292db859087c3957b9182f7d53278553f06bddaa2099c90e4ac3a0ee0"
dependencies = [ dependencies = [
"libpulse-binding", "libpulse-binding",
"libpulse-simple-sys", "libpulse-simple-sys",
@ -1308,9 +1259,9 @@ dependencies = [
[[package]] [[package]]
name = "libpulse-sys" name = "libpulse-sys"
version = "1.17.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcfb56118765adba111da47e36278b77d00aebf822e4f014a832fbfa183a13b" checksum = "cf17e9832643c4f320c42b7d78b2c0510f45aa5e823af094413b94e45076ba82"
dependencies = [ dependencies = [
"libc", "libc",
"num-derive", "num-derive",
@ -1359,6 +1310,7 @@ dependencies = [
"byteorder", "byteorder",
"bytes", "bytes",
"env_logger", "env_logger",
"error-chain",
"futures", "futures",
"hmac", "hmac",
"httparse", "httparse",
@ -1404,7 +1356,7 @@ dependencies = [
name = "librespot-playback" name = "librespot-playback"
version = "0.1.3" version = "0.1.3"
dependencies = [ dependencies = [
"alsa 0.2.2", "alsa",
"byteorder", "byteorder",
"cpal", "cpal",
"futures", "futures",
@ -1438,9 +1390,9 @@ dependencies = [
[[package]] [[package]]
name = "librespot-tremor" name = "librespot-tremor"
version = "0.1.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b155a7dc4e4d272e01c37a1b85c1ee1bee7f04980ad4a7784c1a6e0f2de5929b" checksum = "97f525bff915d478a76940a7b988e5ea34911ba7280c97bd3a7673f54d68b4fe"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -1465,11 +1417,11 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.13" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [ dependencies = [
"cfg-if 0.1.10", "cfg-if 1.0.0",
] ]
[[package]] [[package]]
@ -1577,25 +1529,13 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d"
[[package]]
name = "nix"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32"
dependencies = [
"bitflags 0.9.1",
"cfg-if 0.1.10",
"libc",
"void",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.15.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"cc", "cc",
"cfg-if 0.1.10", "cfg-if 0.1.10",
"libc", "libc",
@ -1836,6 +1776,15 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "0.4.27" version = "0.4.27"
@ -1847,11 +1796,11 @@ dependencies = [
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.0.4" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95b70b68509f17aa2857863b6fa00bf21fc93674c7a8893de2f469f6aa7ca2f2" checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63"
dependencies = [ dependencies = [
"pin-project-internal 1.0.4", "pin-project-internal 1.0.5",
] ]
[[package]] [[package]]
@ -1867,9 +1816,9 @@ dependencies = [
[[package]] [[package]]
name = "pin-project-internal" name = "pin-project-internal"
version = "1.0.4" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa25a6393f22ce819b0f50e0be89287292fda8d425be38ee0ca14c4931d9e71" checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1900,7 +1849,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb6b5eff96ccc9bf44d34c379ab03ae944426d83d1694345bdf8159d561d562" checksum = "cdb6b5eff96ccc9bf44d34c379ab03ae944426d83d1694345bdf8159d561d562"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"libc", "libc",
"portaudio-sys", "portaudio-sys",
] ]
@ -2013,7 +1962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b"
dependencies = [ dependencies = [
"error-chain", "error-chain",
"idna 0.2.0", "idna 0.2.1",
"lazy_static", "lazy_static",
"regex", "regex",
"url 2.2.0", "url 2.2.0",
@ -2065,9 +2014,9 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.2" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha 0.3.0", "rand_chacha 0.3.0",
@ -2158,7 +2107,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
] ]
[[package]] [[package]]
@ -2190,9 +2139,9 @@ dependencies = [
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.16.19" version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -2236,7 +2185,16 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [ dependencies = [
"semver", "semver 0.9.0",
]
[[package]]
name = "rustc_version"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
dependencies = [
"semver 0.11.0",
] ]
[[package]] [[package]]
@ -2295,7 +2253,7 @@ version = "0.34.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c" checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"lazy_static", "lazy_static",
"libc", "libc",
"sdl2-sys", "sdl2-sys",
@ -2318,7 +2276,16 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [ dependencies = [
"semver-parser", "semver-parser 0.7.0",
]
[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser 0.10.2",
] ]
[[package]] [[package]]
@ -2328,19 +2295,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "semver-parser"
version = "1.0.120" version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab" checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
[[package]]
name = "serde"
version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.120" version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775" checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2349,9 +2325,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.61" version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -2440,9 +2416,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "standback" name = "standback"
version = "0.2.14" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66a8cff4fa24853fdf6b51f75c6d7f8206d7c75cab4e467bcd7f25c2b1febe0" checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8"
dependencies = [ dependencies = [
"version_check", "version_check",
] ]
@ -2460,7 +2436,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
dependencies = [ dependencies = [
"discard", "discard",
"rustc_version", "rustc_version 0.2.3",
"stdweb-derive", "stdweb-derive",
"stdweb-internal-macros", "stdweb-internal-macros",
"stdweb-internal-runtime", "stdweb-internal-runtime",
@ -2534,9 +2510,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.58" version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2572,13 +2548,12 @@ dependencies = [
[[package]] [[package]]
name = "tar" name = "tar"
version = "0.4.30" version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290" checksum = "0313546c01d59e29be4f09687bcb4fb6690cec931cc3607b6aec7a0e417f4cc6"
dependencies = [ dependencies = [
"filetime", "filetime",
"libc", "libc",
"redox_syscall 0.1.57",
"xattr", "xattr",
] ]
@ -2590,7 +2565,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"rand 0.8.2", "rand 0.8.3",
"redox_syscall 0.2.4", "redox_syscall 0.2.4",
"remove_dir_all", "remove_dir_all",
"winapi", "winapi",
@ -2646,9 +2621,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.2.24" version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "273d3ed44dca264b0d6b3665e8d48fb515042d42466fad93d2a45b90ec4058f7" checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7"
dependencies = [ dependencies = [
"const_fn", "const_fn",
"libc", "libc",
@ -2684,9 +2659,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.1.0" version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
dependencies = [ dependencies = [
"tinyvec_macros", "tinyvec_macros",
] ]
@ -2699,9 +2674,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8efab2086f17abcddb8f756117665c958feee6b2e39974c2f1600592ab3a4195" checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bytes", "bytes",
@ -2715,40 +2690,27 @@ dependencies = [
[[package]] [[package]]
name = "tokio-macros" name = "tokio-macros"
version = "1.0.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42517d2975ca3114b22a16192634e8241dc5cc1f130be194645970cc1c371494" checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "tokio-stream"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76066865172052eb8796c686f0b441a93df8b08d40a950b062ffb9a426f00edd"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.6.2" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb971a26599ffd28066d387f109746df178eff14d5ea1e235015c5601967a4b" checksum = "ebb7cb2f00c5ae8df755b252306272cd1790d39728363936e01827e11f0b017b"
dependencies = [ dependencies = [
"async-stream",
"bytes", "bytes",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"log", "log",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
"tokio-stream",
] ]
[[package]] [[package]]
@ -2762,15 +2724,15 @@ dependencies = [
[[package]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.22" version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"pin-project-lite", "pin-project-lite",
@ -2808,6 +2770,12 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.4" version = "0.3.4"
@ -2819,9 +2787,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
version = "0.1.16" version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
dependencies = [ dependencies = [
"tinyvec", "tinyvec",
] ]
@ -2890,7 +2858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna 0.2.0", "idna 0.2.1",
"matches", "matches",
"percent-encoding 2.1.0", "percent-encoding 2.1.0",
] ]
@ -2906,12 +2874,13 @@ dependencies = [
[[package]] [[package]]
name = "vergen" name = "vergen"
version = "3.1.0" version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" checksum = "e7141e445af09c8919f1d5f8a20dae0b20c3b57a45dee0d5823c6ed5d237f15a"
dependencies = [ dependencies = [
"bitflags 1.2.1", "bitflags",
"chrono", "chrono",
"rustc_version 0.3.3",
] ]
[[package]] [[package]]
@ -2998,15 +2967,15 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.1+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.69" version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@ -3014,9 +2983,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.69" version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@ -3029,9 +2998,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.69" version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -3039,9 +3008,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.69" version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3052,15 +3021,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.69" version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.46" version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",

View file

@ -1,5 +1,4 @@
[![Build Status](https://img.shields.io/github/workflow/status/librespot-org/librespot/test/dev)](https://github.com/librespot-org/librespot/actions) [![Build Status](https://github.com/librespot-org/librespot/workflows/test/badge.svg)](https://github.com/librespot-org/librespot/actions)
[![Build Status](https://travis-ci.org/librespot-org/librespot.svg?branch=dev)](https://travis-ci.org/librespot-org/librespot)
[![Gitter chat](https://badges.gitter.im/librespot-org/librespot.png)](https://gitter.im/librespot-org/spotify-connect-resources) [![Gitter chat](https://badges.gitter.im/librespot-org/librespot.png)](https://gitter.im/librespot-org/spotify-connect-resources)
[![Crates.io](https://img.shields.io/crates/v/librespot.svg)](https://crates.io/crates/librespot) [![Crates.io](https://img.shields.io/crates/v/librespot.svg)](https://crates.io/crates/librespot)
@ -21,7 +20,7 @@ As the origin by [plietar](https://github.com/plietar/) is no longer actively ma
# Documentation # Documentation
Documentation is currently a work in progress, contributions are welcome! Documentation is currently a work in progress, contributions are welcome!
There is some brief documentation on how the protocol works in the [docs](https://github.com/librespot-org/librespot/tree/master/docs) folder, There is some brief documentation on how the protocol works in the [docs](https://github.com/librespot-org/librespot/tree/master/docs) folder,
[COMPILING.md](https://github.com/librespot-org/librespot/blob/master/COMPILING.md) contains detailed instructions on setting up a development environment, and compiling librespot. More general usage and compilation information is available on the [wiki](https://github.com/librespot-org/librespot/wiki). [COMPILING.md](https://github.com/librespot-org/librespot/blob/master/COMPILING.md) contains detailed instructions on setting up a development environment, and compiling librespot. More general usage and compilation information is available on the [wiki](https://github.com/librespot-org/librespot/wiki).
[CONTRIBUTING.md](https://github.com/librespot-org/librespot/blob/master/CONTRIBUTING.md) also contains our contributing guidelines. [CONTRIBUTING.md](https://github.com/librespot-org/librespot/blob/master/CONTRIBUTING.md) also contains our contributing guidelines.
@ -32,7 +31,7 @@ If you wish to learn more about how librespot works overall, the best way is to
If you run into a bug when using librespot, please search the existing issues before opening a new one. Chances are, we've encountered it before, and have provided a resolution. If not, please open a new one, and where possible, include the backtrace librespot generates on crashing, along with anything we can use to reproduce the issue, eg. the Spotify URI of the song that caused the crash. If you run into a bug when using librespot, please search the existing issues before opening a new one. Chances are, we've encountered it before, and have provided a resolution. If not, please open a new one, and where possible, include the backtrace librespot generates on crashing, along with anything we can use to reproduce the issue, eg. the Spotify URI of the song that caused the crash.
# Building # Building
A quick walk through of the build process is outlined here, while a detailed compilation guide can be found [here](https://github.com/librespot-org/librespot/blob/master/COMPILING.md). A quick walk through of the build process is outlined here, while a detailed compilation guide can be found [here](https://github.com/librespot-org/librespot/blob/master/COMPILING.md).
## Additional Dependencies ## Additional Dependencies
We recently switched to using [Rodio](https://github.com/tomaka/rodio) for audio playback by default, hence for macOS and Windows, you should just be able to clone and build librespot (with the command below). We recently switched to using [Rodio](https://github.com/tomaka/rodio) for audio playback by default, hence for macOS and Windows, you should just be able to clone and build librespot (with the command below).
@ -86,6 +85,7 @@ The above command will create a receiver named ```Librespot```, with bitrate set
A full list of runtime options are available [here](https://github.com/librespot-org/librespot/wiki/Options) A full list of runtime options are available [here](https://github.com/librespot-org/librespot/wiki/Options)
_Please Note: When using the cache feature, an authentication blob is stored for your account in the cache directory. For security purposes, we recommend that you set directory permissions on the cache directory to `700`._
## Contact ## Contact
Come and hang out on gitter if you need help or want to offer some. Come and hang out on gitter if you need help or want to offer some.
https://gitter.im/librespot-org/spotify-connect-resources https://gitter.im/librespot-org/spotify-connect-resources
@ -110,4 +110,3 @@ functionality.
- [librespot-java](https://github.com/devgianlu/librespot-java) - A Java port of librespot. - [librespot-java](https://github.com/devgianlu/librespot-java) - A Java port of librespot.
- [ncspot](https://github.com/hrkfdn/ncspot) - Cross-platform ncurses Spotify client. - [ncspot](https://github.com/hrkfdn/ncspot) - Cross-platform ncurses Spotify client.
- [ansible-role-librespot](https://github.com/xMordax/ansible-role-librespot/tree/master) - Ansible role that will build, install and configure Librespot. - [ansible-role-librespot](https://github.com/xMordax/ansible-role-librespot/tree/master) - Ansible role that will build, install and configure Librespot.

View file

@ -23,7 +23,7 @@ num-traits = "0.2"
pin-project-lite = "0.2.4" pin-project-lite = "0.2.4"
tempfile = "3.1" tempfile = "3.1"
librespot-tremor = { version = "0.1.0", optional = true } librespot-tremor = { version = "0.2.0", optional = true }
vorbis = { version ="0.0.14", optional = true } vorbis = { version ="0.0.14", optional = true }
[features] [features]

View file

@ -312,8 +312,8 @@ impl AudioFile {
let session_ = session.clone(); let session_ = session.clone();
session.spawn(complete_rx.map_ok(move |mut file| { session.spawn(complete_rx.map_ok(move |mut file| {
if let Some(cache) = session_.cache() { if let Some(cache) = session_.cache() {
cache.save_file(file_id, &mut file);
debug!("File {} complete, saving to cache", file_id); debug!("File {} complete, saving to cache", file_id);
cache.save_file(file_id, &mut file);
} else { } else {
debug!("File {} complete", file_id); debug!("File {} complete", file_id);
} }
@ -336,6 +336,13 @@ impl AudioFile {
}, },
} }
} }
pub fn is_cached(&self) -> bool {
match self {
AudioFile::Cached { .. } => true,
_ => false,
}
}
} }
impl AudioFileStreaming { impl AudioFileStreaming {

View file

@ -73,10 +73,6 @@ impl fmt::Display for VorbisError {
} }
impl error::Error for VorbisError { impl error::Error for VorbisError {
fn description(&self) -> &str {
error::Error::description(&self.0)
}
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
error::Error::source(&self.0) error::Error::source(&self.0)
} }

View file

@ -77,7 +77,7 @@ impl Discovery {
"status": 101, "status": 101,
"statusString": "ERROR-OK", "statusString": "ERROR-OK",
"spotifyError": 0, "spotifyError": 0,
"version": "2.1.0", "version": "2.7.1",
"deviceID": (self.0.device_id), "deviceID": (self.0.device_id),
"remoteName": (self.0.config.name), "remoteName": (self.0.config.name),
"activeUser": "", "activeUser": "",
@ -87,6 +87,9 @@ impl Discovery {
"accountReq": "PREMIUM", "accountReq": "PREMIUM",
"brandDisplayName": "librespot", "brandDisplayName": "librespot",
"modelDisplayName": "librespot", "modelDisplayName": "librespot",
"resolverVersion": "0",
"groupStatus": "NONE",
"voiceSupport": "NO",
}); });
let body = result.to_string(); let body = result.to_string();

View file

@ -21,7 +21,6 @@ use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError};
use librespot_core::util::url_encode; use librespot_core::util::url_encode;
use librespot_core::util::SeqGenerator; use librespot_core::util::SeqGenerator;
use librespot_core::version; use librespot_core::version;
use librespot_core::volume::Volume;
enum SpircPlayStatus { enum SpircPlayStatus {
Stopped, Stopped,
@ -1297,7 +1296,7 @@ impl SpircTask {
self.mixer self.mixer
.set_volume(volume_to_mixer(volume, &self.config.volume_ctrl)); .set_volume(volume_to_mixer(volume, &self.config.volume_ctrl));
if let Some(cache) = self.session.cache() { if let Some(cache) = self.session.cache() {
cache.save_volume(Volume { volume }) cache.save_volume(volume)
} }
self.player.emit_volume_set_event(volume); self.player.emit_volume_set_event(volume);
} }

View file

@ -17,6 +17,7 @@ aes = "0.6"
base64 = "0.13" base64 = "0.13"
byteorder = "1.4" byteorder = "1.4"
bytes = "1.0" bytes = "1.0"
error-chain = { version = "0.12", default-features = false }
futures = { version = "0.3", features = ["bilock", "unstable"] } futures = { version = "0.3", features = ["bilock", "unstable"] }
hmac = "0.7" hmac = "0.7"
httparse = "1.3" httparse = "1.3"

View file

@ -5,12 +5,10 @@ use hmac::Hmac;
use pbkdf2::pbkdf2; use pbkdf2::pbkdf2;
use protobuf::ProtobufEnum; use protobuf::ProtobufEnum;
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
use std::fs::File; use std::io::{self, Read};
use std::io::{self, Read, Write};
use std::ops::FnOnce;
use std::path::Path;
use crate::protocol::authentication::AuthenticationType; use crate::protocol::authentication::AuthenticationType;
use crate::protocol::keyexchange::{APLoginFailed, ErrorCode};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Credentials { pub struct Credentials {
@ -110,27 +108,6 @@ impl Credentials {
auth_data: auth_data, auth_data: auth_data,
} }
} }
fn from_reader<R: Read>(mut reader: R) -> Credentials {
let mut contents = String::new();
reader.read_to_string(&mut contents).unwrap();
serde_json::from_str(&contents).unwrap()
}
pub(crate) fn from_file<P: AsRef<Path>>(path: P) -> Option<Credentials> {
File::open(path).ok().map(Credentials::from_reader)
}
fn save_to_writer<W: Write>(&self, writer: &mut W) {
let contents = serde_json::to_string(&self.clone()).unwrap();
writer.write_all(contents.as_bytes()).unwrap();
}
pub(crate) fn save_to_file<P: AsRef<Path>>(&self, path: P) {
let mut file = File::create(path).unwrap();
self.save_to_writer(&mut file)
}
} }
fn serialize_protobuf_enum<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error> fn serialize_protobuf_enum<T, S>(v: &T, ser: S) -> Result<S::Ok, S::Error>
@ -189,3 +166,37 @@ pub fn get_credentials<F: FnOnce(&String) -> String>(
(None, _, None) => None, (None, _, None) => None,
} }
} }
error_chain! {
types {
AuthenticationError, AuthenticationErrorKind, AuthenticationResultExt, AuthenticationResult;
}
foreign_links {
Io(::std::io::Error);
}
errors {
BadCredentials {
description("Bad credentials")
display("Authentication failed with error: Bad credentials")
}
PremiumAccountRequired {
description("Premium account required")
display("Authentication failed with error: Premium account required")
}
}
}
impl From<APLoginFailed> for AuthenticationError {
fn from(login_failure: APLoginFailed) -> Self {
let error_code = login_failure.get_error_code();
match error_code {
ErrorCode::BadCredentials => Self::from_kind(AuthenticationErrorKind::BadCredentials),
ErrorCode::PremiumAccountRequired => {
Self::from_kind(AuthenticationErrorKind::PremiumAccountRequired)
}
_ => format!("Authentication failed with error: {:?}", error_code).into(),
}
}
}

View file

@ -1,115 +1,178 @@
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
use std::io; use std::io::{self, Error, ErrorKind, Read, Write};
use std::io::Read; use std::path::{Path, PathBuf};
use std::path::Path;
use std::path::PathBuf;
use crate::authentication::Credentials; use crate::authentication::Credentials;
use crate::spotify_id::FileId; use crate::spotify_id::FileId;
use crate::volume::Volume;
/// A cache for volume, credentials and audio files.
#[derive(Clone)] #[derive(Clone)]
pub struct Cache { pub struct Cache {
audio_root: PathBuf, credentials_location: Option<PathBuf>,
system_root: PathBuf, volume_location: Option<PathBuf>,
use_audio_cache: bool, audio_location: Option<PathBuf>,
}
fn mkdir_existing(path: &Path) -> io::Result<()> {
fs::create_dir(path).or_else(|err| {
if err.kind() == io::ErrorKind::AlreadyExists {
Ok(())
} else {
Err(err)
}
})
} }
impl Cache { impl Cache {
pub fn new(audio_location: PathBuf, system_location: PathBuf, use_audio_cache: bool) -> Cache { pub fn new<P: AsRef<Path>>(
if use_audio_cache == true { system_location: Option<P>,
mkdir_existing(&audio_location).unwrap(); audio_location: Option<P>,
mkdir_existing(&audio_location.join("files")).unwrap(); ) -> io::Result<Self> {
if let Some(location) = &system_location {
fs::create_dir_all(location)?;
} }
mkdir_existing(&system_location).unwrap();
Cache { if let Some(location) = &audio_location {
audio_root: audio_location, fs::create_dir_all(location)?;
system_root: system_location,
use_audio_cache: use_audio_cache,
} }
}
}
impl Cache { let audio_location = audio_location.map(|p| p.as_ref().to_owned());
fn credentials_path(&self) -> PathBuf { let volume_location = system_location.as_ref().map(|p| p.as_ref().join("volume"));
self.system_root.join("credentials.json") let credentials_location = system_location
.as_ref()
.map(|p| p.as_ref().join("credentials.json"));
let cache = Cache {
credentials_location,
volume_location,
audio_location,
};
Ok(cache)
} }
pub fn credentials(&self) -> Option<Credentials> { pub fn credentials(&self) -> Option<Credentials> {
let path = self.credentials_path(); let location = self.credentials_location.as_ref()?;
Credentials::from_file(path)
// This closure is just convencience to enable the question mark operator
let read = || {
let mut file = File::open(location)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
serde_json::from_str(&contents).map_err(|e| Error::new(ErrorKind::InvalidData, e))
};
match read() {
Ok(c) => Some(c),
Err(e) => {
// If the file did not exist, the file was probably not written
// before. Otherwise, log the error.
if e.kind() != ErrorKind::NotFound {
warn!("Error reading credentials from cache: {}", e);
}
None
}
}
} }
pub fn save_credentials(&self, cred: &Credentials) { pub fn save_credentials(&self, cred: &Credentials) {
let path = self.credentials_path(); if let Some(location) = &self.credentials_location {
cred.save_to_file(&path); let result = File::create(location).and_then(|mut file| {
} let data = serde_json::to_string(cred)?;
} write!(file, "{}", data)
});
// cache volume to system_root/volume if let Err(e) = result {
impl Cache { warn!("Cannot save credentials to cache: {}", e)
fn volume_path(&self) -> PathBuf { }
self.system_root.join("volume") }
} }
pub fn volume(&self) -> Option<u16> { pub fn volume(&self) -> Option<u16> {
let path = self.volume_path(); let location = self.volume_location.as_ref()?;
Volume::from_file(path)
let read = || {
let mut file = File::open(location)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
contents
.parse()
.map_err(|e| Error::new(ErrorKind::InvalidData, e))
};
match read() {
Ok(v) => Some(v),
Err(e) => {
if e.kind() != ErrorKind::NotFound {
warn!("Error reading volume from cache: {}", e);
}
None
}
}
} }
pub fn save_volume(&self, volume: Volume) { pub fn save_volume(&self, volume: u16) {
let path = self.volume_path(); if let Some(ref location) = self.volume_location {
volume.save_to_file(&path); let result = File::create(location).and_then(|mut file| write!(file, "{}", volume));
if let Err(e) = result {
warn!("Cannot save volume to cache: {}", e);
}
}
} }
}
impl Cache { fn file_path(&self, file: FileId) -> Option<PathBuf> {
fn file_path(&self, file: FileId) -> PathBuf { self.audio_location.as_ref().map(|location| {
let name = file.to_base16(); let name = file.to_base16();
self.audio_root let mut path = location.join(&name[0..2]);
.join("files") path.push(&name[2..]);
.join(&name[0..2]) path
.join(&name[2..]) })
} }
pub fn file(&self, file: FileId) -> Option<File> { pub fn file(&self, file: FileId) -> Option<File> {
File::open(self.file_path(file)).ok() File::open(self.file_path(file)?)
.map_err(|e| {
if e.kind() != ErrorKind::NotFound {
warn!("Error reading file from cache: {}", e)
}
})
.ok()
} }
pub fn save_file(&self, file: FileId, contents: &mut dyn Read) { pub fn save_file<F: Read>(&self, file: FileId, contents: &mut F) {
if self.use_audio_cache { let path = if let Some(path) = self.file_path(file) {
let path = self.file_path(file); path
mkdir_existing(path.parent().unwrap()).unwrap(); } else {
return;
};
let parent = path.parent().unwrap();
let mut cache_file = File::create(path).unwrap_or_else(|_e| { let result = fs::create_dir_all(parent)
::std::fs::remove_dir_all(&self.audio_root.join("files")).unwrap(); .and_then(|_| File::create(&path))
mkdir_existing(&self.audio_root.join("files")).unwrap(); .and_then(|mut file| io::copy(contents, &mut file));
let path = self.file_path(file); if let Err(e) = result {
mkdir_existing(path.parent().unwrap()).unwrap(); if e.kind() == ErrorKind::Other {
File::create(path).unwrap() // Perhaps there's no space left in the cache
}); // TODO: try to narrow down the error (platform-dependently)
::std::io::copy(contents, &mut cache_file).unwrap_or_else(|_e| { info!("An error occured while writing to cache, trying to flush the cache");
::std::fs::remove_dir_all(&self.audio_root.join("files")).unwrap();
mkdir_existing(&self.audio_root.join("files")).unwrap();
let path = self.file_path(file); if fs::remove_dir_all(self.audio_location.as_ref().unwrap())
mkdir_existing(path.parent().unwrap()).unwrap(); .and_then(|_| fs::create_dir_all(parent))
let mut file = File::create(path).unwrap(); .and_then(|_| File::create(&path))
::std::io::copy(contents, &mut file).unwrap() .and_then(|mut file| io::copy(contents, &mut file))
}); .is_ok()
{
// It worked, there's no need to print a warning
return;
}
}
warn!("Cannot save file to cache: {}", e)
}
}
pub fn remove_file(&self, file: FileId) -> bool {
if let Some(path) = self.file_path(file) {
if let Err(err) = fs::remove_file(path) {
warn!("Unable to remove file from cache: {}", err);
false
} else {
true
}
} else {
false
} }
} }
} }

View file

@ -36,6 +36,16 @@ pub enum DeviceType {
AVR = 6, AVR = 6,
STB = 7, STB = 7,
AudioDongle = 8, AudioDongle = 8,
GameConsole = 9,
CastAudio = 10,
CastVideo = 11,
Automobile = 12,
Smartwatch = 13,
Chromebook = 14,
UnknownSpotify = 100,
CarThing = 101,
Observer = 102,
HomeThing = 103,
} }
impl FromStr for DeviceType { impl FromStr for DeviceType {
@ -51,6 +61,14 @@ impl FromStr for DeviceType {
"avr" => Ok(AVR), "avr" => Ok(AVR),
"stb" => Ok(STB), "stb" => Ok(STB),
"audiodongle" => Ok(AudioDongle), "audiodongle" => Ok(AudioDongle),
"gameconsole" => Ok(GameConsole),
"castaudio" => Ok(CastAudio),
"castvideo" => Ok(CastVideo),
"automobile" => Ok(Automobile),
"smartwatch" => Ok(Smartwatch),
"chromebook" => Ok(Chromebook),
"carthing" => Ok(CarThing),
"homething" => Ok(HomeThing),
_ => Err(()), _ => Err(()),
} }
} }
@ -69,6 +87,16 @@ impl fmt::Display for DeviceType {
AVR => f.write_str("AVR"), AVR => f.write_str("AVR"),
STB => f.write_str("STB"), STB => f.write_str("STB"),
AudioDongle => f.write_str("AudioDongle"), AudioDongle => f.write_str("AudioDongle"),
GameConsole => f.write_str("GameConsole"),
CastAudio => f.write_str("CastAudio"),
CastVideo => f.write_str("CastVideo"),
Automobile => f.write_str("Automobile"),
Smartwatch => f.write_str("Smartwatch"),
Chromebook => f.write_str("Chromebook"),
UnknownSpotify => f.write_str("UnknownSpotify"),
CarThing => f.write_str("CarThing"),
Observer => f.write_str("Observer"),
HomeThing => f.write_str("HomeThing"),
} }
} }
} }

View file

@ -12,7 +12,7 @@ use tokio::net::TcpStream;
use tokio_util::codec::Framed; use tokio_util::codec::Framed;
use url::Url; use url::Url;
use crate::authentication::Credentials; use crate::authentication::{AuthenticationError, Credentials};
use crate::version; use crate::version;
use crate::proxytunnel; use crate::proxytunnel;
@ -64,7 +64,7 @@ pub async fn authenticate(
transport: &mut Transport, transport: &mut Transport,
credentials: Credentials, credentials: Credentials,
device_id: &str, device_id: &str,
) -> io::Result<Credentials> { ) -> Result<Credentials, AuthenticationError> {
use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os};
use crate::protocol::keyexchange::APLoginFailed; use crate::protocol::keyexchange::APLoginFailed;
@ -114,10 +114,7 @@ pub async fn authenticate(
0xad => { 0xad => {
let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap(); let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap();
panic!( Err(error_data.into())
"Authentication failed with reason: {:?}",
error_data.get_error_code()
)
} }
_ => panic!("Unexpected packet {:?}", cmd), _ => panic!("Unexpected packet {:?}", cmd),

View file

@ -6,6 +6,8 @@ extern crate log;
extern crate serde_derive; extern crate serde_derive;
#[macro_use] #[macro_use]
extern crate pin_project_lite; extern crate pin_project_lite;
#[macro_use]
extern crate error_chain;
extern crate aes; extern crate aes;
extern crate base64; extern crate base64;
extern crate byteorder; extern crate byteorder;
@ -51,4 +53,3 @@ pub mod session;
pub mod spotify_id; pub mod spotify_id;
pub mod util; pub mod util;
pub mod version; pub mod version;
pub mod volume;

View file

@ -19,6 +19,8 @@ use crate::config::SessionConfig;
use crate::connection; use crate::connection;
use crate::mercury::MercuryManager; use crate::mercury::MercuryManager;
pub use crate::authentication::{AuthenticationError, AuthenticationErrorKind};
struct SessionData { struct SessionData {
country: String, country: String,
time_delta: i64, time_delta: i64,
@ -50,7 +52,7 @@ impl Session {
config: SessionConfig, config: SessionConfig,
credentials: Credentials, credentials: Credentials,
cache: Option<Cache>, cache: Option<Cache>,
) -> io::Result<Session> { ) -> Result<Session, AuthenticationError> {
let ap = apresolve_or_fallback(&config.proxy, &config.ap_port).await; let ap = apresolve_or_fallback(&config.proxy, &config.ap_port).await;
info!("Connecting to AP \"{}\"", ap); info!("Connecting to AP \"{}\"", ap);

View file

@ -1,4 +1,4 @@
use std; use std::convert::TryInto;
use std::fmt; use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -8,6 +8,26 @@ pub enum SpotifyAudioType {
NonPlayable, NonPlayable,
} }
impl From<&str> for SpotifyAudioType {
fn from(v: &str) -> Self {
match v {
"track" => SpotifyAudioType::Track,
"episode" => SpotifyAudioType::Podcast,
_ => SpotifyAudioType::NonPlayable,
}
}
}
impl Into<&str> for SpotifyAudioType {
fn into(self) -> &'static str {
match self {
SpotifyAudioType::Track => "track",
SpotifyAudioType::Podcast => "episode",
SpotifyAudioType::NonPlayable => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpotifyId { pub struct SpotifyId {
pub id: u128, pub id: u128,
@ -17,104 +37,178 @@ pub struct SpotifyId {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SpotifyIdError; pub struct SpotifyIdError;
const BASE62_DIGITS: &'static [u8] = const BASE62_DIGITS: &[u8; 62] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const BASE16_DIGITS: &[u8; 16] = b"0123456789abcdef";
const BASE16_DIGITS: &'static [u8] = b"0123456789abcdef";
impl SpotifyId { impl SpotifyId {
const SIZE: usize = 16;
const SIZE_BASE16: usize = 32;
const SIZE_BASE62: usize = 22;
fn as_track(n: u128) -> SpotifyId { fn as_track(n: u128) -> SpotifyId {
SpotifyId { SpotifyId {
id: n.to_owned(), id: n,
audio_type: SpotifyAudioType::Track, audio_type: SpotifyAudioType::Track,
} }
} }
pub fn from_base16(id: &str) -> Result<SpotifyId, SpotifyIdError> { /// Parses a base16 (hex) encoded [Spotify ID] into a `SpotifyId`.
let data = id.as_bytes(); ///
/// `src` is expected to be 32 bytes long and encoded using valid characters.
///
/// [Spotify ID]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
pub fn from_base16(src: &str) -> Result<SpotifyId, SpotifyIdError> {
let mut dst: u128 = 0;
let mut n = 0u128; for c in src.as_bytes() {
for c in data { let p = match c {
let d = match BASE16_DIGITS.iter().position(|e| e == c) { b'0'..=b'9' => c - b'0',
None => return Err(SpotifyIdError), b'a'..=b'f' => c - b'a' + 10,
Some(x) => x as u128, _ => return Err(SpotifyIdError),
}; } as u128;
n = n * 16;
n = n + d; dst <<= 4;
dst += p;
} }
Ok(SpotifyId::as_track(n)) Ok(SpotifyId::as_track(dst))
} }
pub fn from_base62(id: &str) -> Result<SpotifyId, SpotifyIdError> { /// Parses a base62 encoded [Spotify ID] into a `SpotifyId`.
let data = id.as_bytes(); ///
/// `src` is expected to be 22 bytes long and encoded using valid characters.
///
/// [Spotify ID]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
pub fn from_base62(src: &str) -> Result<SpotifyId, SpotifyIdError> {
let mut dst: u128 = 0;
let mut n = 0u128; for c in src.as_bytes() {
for c in data { let p = match c {
let d = match BASE62_DIGITS.iter().position(|e| e == c) { b'0'..=b'9' => c - b'0',
None => return Err(SpotifyIdError), b'a'..=b'z' => c - b'a' + 10,
Some(x) => x as u128, b'A'..=b'Z' => c - b'A' + 36,
}; _ => return Err(SpotifyIdError),
n = n * 62; } as u128;
n = n + d;
dst *= 62;
dst += p;
} }
Ok(SpotifyId::as_track(n))
Ok(SpotifyId::as_track(dst))
} }
pub fn from_raw(data: &[u8]) -> Result<SpotifyId, SpotifyIdError> { /// Creates a `SpotifyId` from a copy of `SpotifyId::SIZE` (16) bytes in big-endian order.
if data.len() != 16 { ///
/// The resulting `SpotifyId` will default to a `SpotifyAudioType::TRACK`.
pub fn from_raw(src: &[u8]) -> Result<SpotifyId, SpotifyIdError> {
match src.try_into() {
Ok(dst) => Ok(SpotifyId::as_track(u128::from_be_bytes(dst))),
Err(_) => Err(SpotifyIdError),
}
}
/// Parses a [Spotify URI] into a `SpotifyId`.
///
/// `uri` is expected to be in the canonical form `spotify:{type}:{id}`, where `{type}`
/// can be arbitrary while `{id}` is a 22-character long, base62 encoded Spotify ID.
///
/// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
pub fn from_uri(src: &str) -> Result<SpotifyId, SpotifyIdError> {
// We expect the ID to be the last colon-delimited item in the URI.
let b = src.as_bytes();
let id_i = b.len() - SpotifyId::SIZE_BASE62;
if b[id_i - 1] != b':' {
return Err(SpotifyIdError); return Err(SpotifyIdError);
};
let mut arr: [u8; 16] = Default::default();
arr.copy_from_slice(&data[0..16]);
Ok(SpotifyId::as_track(u128::from_be_bytes(arr)))
}
pub fn from_uri(uri: &str) -> Result<SpotifyId, SpotifyIdError> {
let parts = uri.split(":").collect::<Vec<&str>>();
let gid = parts.last().unwrap();
if uri.contains(":episode:") {
let mut spotify_id = SpotifyId::from_base62(gid).unwrap();
let _ = std::mem::replace(&mut spotify_id.audio_type, SpotifyAudioType::Podcast);
Ok(spotify_id)
} else if uri.contains(":track:") {
SpotifyId::from_base62(gid)
} else {
// show/playlist/artist/album/??
let mut spotify_id = SpotifyId::from_base62(gid).unwrap();
let _ = std::mem::replace(&mut spotify_id.audio_type, SpotifyAudioType::NonPlayable);
Ok(spotify_id)
} }
let mut id = SpotifyId::from_base62(&src[id_i..])?;
// Slice offset by 8 as we are skipping the "spotify:" prefix.
id.audio_type = src[8..id_i - 1].into();
Ok(id)
} }
/// Returns the `SpotifyId` as a base16 (hex) encoded, `SpotifyId::SIZE_BASE62` (22)
/// character long `String`.
pub fn to_base16(&self) -> String { pub fn to_base16(&self) -> String {
format!("{:032x}", self.id) to_base16(&self.to_raw(), &mut [0u8; SpotifyId::SIZE_BASE16])
} }
/// Returns the `SpotifyId` as a [canonically] base62 encoded, `SpotifyId::SIZE_BASE62` (22)
/// character long `String`.
///
/// [canonically]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
pub fn to_base62(&self) -> String { pub fn to_base62(&self) -> String {
let &SpotifyId { id: mut n, .. } = self; let mut dst = [0u8; 22];
let mut i = 0;
let n = self.id;
let mut data = [0u8; 22]; // The algorithm is based on:
for i in 0..22 { // https://github.com/trezor/trezor-crypto/blob/c316e775a2152db255ace96b6b65ac0f20525ec0/base58.c
data[21 - i] = BASE62_DIGITS[(n % 62) as usize]; //
n /= 62; // We are not using naive division of self.id as it is an u128 and div + mod are software
// emulated at runtime (and unoptimized into mul + shift) on non-128bit platforms,
// making them very expensive.
//
// Trezor's algorithm allows us to stick to arithmetic on native registers making this
// an order of magnitude faster. Additionally, as our sizes are known, instead of
// dealing with the ID on a byte by byte basis, we decompose it into four u32s and
// use 64-bit arithmetic on them for an additional speedup.
for shift in &[96, 64, 32, 0] {
let mut carry = (n >> shift) as u32 as u64;
for b in &mut dst[..i] {
carry += (*b as u64) << 32;
*b = (carry % 62) as u8;
carry /= 62;
}
while carry > 0 {
dst[i] = (carry % 62) as u8;
carry /= 62;
i += 1;
}
} }
std::str::from_utf8(&data).unwrap().to_owned() for b in &mut dst {
} *b = BASE62_DIGITS[*b as usize];
}
pub fn to_uri(&self) -> String { dst.reverse();
match self.audio_type {
SpotifyAudioType::Track => format!("spotify:track:{}", self.to_base62()), unsafe {
SpotifyAudioType::Podcast => format!("spotify:episode:{}", self.to_base62()), // Safety: We are only dealing with ASCII characters.
SpotifyAudioType::NonPlayable => format!("spotify:unknown:{}", self.to_base62()), String::from_utf8_unchecked(dst.to_vec())
} }
} }
pub fn to_raw(&self) -> [u8; 16] { /// Returns a copy of the `SpotifyId` as an array of `SpotifyId::SIZE` (16) bytes in
/// big-endian order.
pub fn to_raw(&self) -> [u8; SpotifyId::SIZE] {
self.id.to_be_bytes() self.id.to_be_bytes()
} }
/// Returns the `SpotifyId` as a [Spotify URI] in the canonical form `spotify:{type}:{id}`,
/// where `{type}` is an arbitrary string and `{id}` is a 22-character long, base62 encoded
/// Spotify ID.
///
/// If the `SpotifyId` has an associated type unrecognized by the library, `{type}` will
/// be encoded as `unknown`.
///
/// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
pub fn to_uri(&self) -> String {
// 8 chars for the "spotify:" prefix + 1 colon + 22 chars base62 encoded ID = 31
// + unknown size audio_type.
let audio_type: &str = self.audio_type.into();
let mut dst = String::with_capacity(31 + audio_type.len());
dst.push_str("spotify:");
dst.push_str(audio_type);
dst.push(':');
dst.push_str(&self.to_base62());
dst
}
} }
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -122,11 +216,7 @@ pub struct FileId(pub [u8; 20]);
impl FileId { impl FileId {
pub fn to_base16(&self) -> String { pub fn to_base16(&self) -> String {
self.0 to_base16(&self.0, &mut [0u8; 40])
.iter()
.map(|b| format!("{:02x}", b))
.collect::<Vec<String>>()
.concat()
} }
} }
@ -141,3 +231,185 @@ impl fmt::Display for FileId {
f.write_str(&self.to_base16()) f.write_str(&self.to_base16())
} }
} }
#[inline]
fn to_base16(src: &[u8], buf: &mut [u8]) -> String {
let mut i = 0;
for v in src {
buf[i] = BASE16_DIGITS[(v >> 4) as usize];
buf[i + 1] = BASE16_DIGITS[(v & 0x0f) as usize];
i += 2;
}
unsafe {
// Safety: We are only dealing with ASCII characters.
String::from_utf8_unchecked(buf.to_vec())
}
}
#[cfg(test)]
mod tests {
use super::*;
struct ConversionCase {
id: u128,
kind: SpotifyAudioType,
uri: &'static str,
base16: &'static str,
base62: &'static str,
raw: &'static [u8],
}
static CONV_VALID: [ConversionCase; 4] = [
ConversionCase {
id: 238762092608182713602505436543891614649,
kind: SpotifyAudioType::Track,
uri: "spotify:track:5sWHDYs0csV6RS48xBl0tH",
base16: "b39fe8081e1f4c54be38e8d6f9f12bb9",
base62: "5sWHDYs0csV6RS48xBl0tH",
raw: &[
179, 159, 232, 8, 30, 31, 76, 84, 190, 56, 232, 214, 249, 241, 43, 185,
],
},
ConversionCase {
id: 204841891221366092811751085145916697048,
kind: SpotifyAudioType::Track,
uri: "spotify:track:4GNcXTGWmnZ3ySrqvol3o4",
base16: "9a1b1cfbc6f244569ae0356c77bbe9d8",
base62: "4GNcXTGWmnZ3ySrqvol3o4",
raw: &[
154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216,
],
},
ConversionCase {
id: 204841891221366092811751085145916697048,
kind: SpotifyAudioType::Podcast,
uri: "spotify:episode:4GNcXTGWmnZ3ySrqvol3o4",
base16: "9a1b1cfbc6f244569ae0356c77bbe9d8",
base62: "4GNcXTGWmnZ3ySrqvol3o4",
raw: &[
154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216,
],
},
ConversionCase {
id: 204841891221366092811751085145916697048,
kind: SpotifyAudioType::NonPlayable,
uri: "spotify:unknown:4GNcXTGWmnZ3ySrqvol3o4",
base16: "9a1b1cfbc6f244569ae0356c77bbe9d8",
base62: "4GNcXTGWmnZ3ySrqvol3o4",
raw: &[
154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216,
],
},
];
static CONV_INVALID: [ConversionCase; 2] = [
ConversionCase {
id: 0,
kind: SpotifyAudioType::NonPlayable,
// Invalid ID in the URI.
uri: "spotify:arbitrarywhatever:5sWHDYs0Bl0tH",
base16: "ZZZZZ8081e1f4c54be38e8d6f9f12bb9",
base62: "!!!!!Ys0csV6RS48xBl0tH",
raw: &[
// Invalid length.
154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 5, 3, 108, 119, 187, 233, 216, 255,
],
},
ConversionCase {
id: 0,
kind: SpotifyAudioType::NonPlayable,
// Missing colon between ID and type.
uri: "spotify:arbitrarywhatever5sWHDYs0csV6RS48xBl0tH",
base16: "--------------------",
base62: "....................",
raw: &[
// Invalid length.
154, 27, 28, 251,
],
},
];
#[test]
fn from_base62() {
for c in &CONV_VALID {
assert_eq!(SpotifyId::from_base62(c.base62).unwrap().id, c.id);
}
for c in &CONV_INVALID {
assert_eq!(SpotifyId::from_base62(c.base62), Err(SpotifyIdError));
}
}
#[test]
fn to_base62() {
for c in &CONV_VALID {
let id = SpotifyId {
id: c.id,
audio_type: c.kind,
};
assert_eq!(id.to_base62(), c.base62);
}
}
#[test]
fn from_base16() {
for c in &CONV_VALID {
assert_eq!(SpotifyId::from_base16(c.base16).unwrap().id, c.id);
}
for c in &CONV_INVALID {
assert_eq!(SpotifyId::from_base16(c.base16), Err(SpotifyIdError));
}
}
#[test]
fn to_base16() {
for c in &CONV_VALID {
let id = SpotifyId {
id: c.id,
audio_type: c.kind,
};
assert_eq!(id.to_base16(), c.base16);
}
}
#[test]
fn from_uri() {
for c in &CONV_VALID {
let actual = SpotifyId::from_uri(c.uri).unwrap();
assert_eq!(actual.id, c.id);
assert_eq!(actual.audio_type, c.kind);
}
for c in &CONV_INVALID {
assert_eq!(SpotifyId::from_uri(c.uri), Err(SpotifyIdError));
}
}
#[test]
fn to_uri() {
for c in &CONV_VALID {
let id = SpotifyId {
id: c.id,
audio_type: c.kind,
};
assert_eq!(id.to_uri(), c.uri);
}
}
#[test]
fn from_raw() {
for c in &CONV_VALID {
assert_eq!(SpotifyId::from_raw(c.raw).unwrap().id, c.id);
}
for c in &CONV_INVALID {
assert_eq!(SpotifyId::from_raw(c.raw), Err(SpotifyIdError));
}
}
}

View file

@ -1,33 +0,0 @@
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
#[derive(Clone, Copy, Debug)]
pub struct Volume {
pub volume: u16,
}
impl Volume {
// read volume from file
fn from_reader<R: Read>(mut reader: R) -> u16 {
let mut contents = String::new();
reader.read_to_string(&mut contents).unwrap();
contents.trim().parse::<u16>().unwrap()
}
pub(crate) fn from_file<P: AsRef<Path>>(path: P) -> Option<u16> {
File::open(path).ok().map(Volume::from_reader)
}
// write volume to file
fn save_to_writer<W: Write>(&self, writer: &mut W) {
writer
.write_all(self.volume.to_string().as_bytes())
.unwrap();
}
pub(crate) fn save_to_file<P: AsRef<Path>>(&self, path: P) {
let mut file = File::create(path).unwrap();
self.save_to_writer(&mut file)
}
}

View file

@ -23,7 +23,7 @@ log = "0.4"
byteorder = "1.4" byteorder = "1.4"
shell-words = "1.0.0" shell-words = "1.0.0"
alsa = { version = "0.2", optional = true } alsa = { version = "0.4", optional = true }
portaudio-rs = { version = "0.3", optional = true } portaudio-rs = { version = "0.3", optional = true }
libpulse-binding = { version = "2.13", optional = true, default-features = false } libpulse-binding = { version = "2.13", optional = true, default-features = false }
libpulse-simple-binding = { version = "2.13", optional = true, default-features = false } libpulse-simple-binding = { version = "2.13", optional = true, default-features = false }

View file

@ -67,7 +67,11 @@ impl Sink for PulseAudioSink {
fn write(&mut self, data: &[i16]) -> io::Result<()> { fn write(&mut self, data: &[i16]) -> io::Result<()> {
if let Some(s) = &self.s { if let Some(s) = &self.s {
let d: &[u8] = unsafe { std::mem::transmute(data) }; // SAFETY: An i16 consists of two bytes, so that the given slice can be interpreted
// as a byte array of double length. Each byte pointer is validly aligned, and so
// is the newly created slice.
let d: &[u8] =
unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) };
match s.write(d) { match s.write(d) {
Ok(_) => Ok(()), Ok(_) => Ok(()),

View file

@ -25,10 +25,34 @@ impl Default for Bitrate {
} }
} }
#[derive(Clone, Debug)]
pub enum NormalisationType {
Album,
Track,
}
impl FromStr for NormalisationType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"album" => Ok(NormalisationType::Album),
"track" => Ok(NormalisationType::Track),
_ => Err(()),
}
}
}
impl Default for NormalisationType {
fn default() -> NormalisationType {
NormalisationType::Album
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PlayerConfig { pub struct PlayerConfig {
pub bitrate: Bitrate, pub bitrate: Bitrate,
pub normalisation: bool, pub normalisation: bool,
pub normalisation_type: NormalisationType,
pub normalisation_pregain: f32, pub normalisation_pregain: f32,
pub gapless: bool, pub gapless: bool,
} }
@ -38,6 +62,7 @@ impl Default for PlayerConfig {
PlayerConfig { PlayerConfig {
bitrate: Bitrate::default(), bitrate: Bitrate::default(),
normalisation: false, normalisation: false,
normalisation_type: NormalisationType::default(),
normalisation_pregain: 0.0, normalisation_pregain: 0.0,
gapless: true, gapless: true,
} }

View file

@ -5,6 +5,7 @@ use crate::audio::{
READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS, READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS,
}; };
use crate::audio_backend::Sink; use crate::audio_backend::Sink;
use crate::config::NormalisationType;
use crate::config::{Bitrate, PlayerConfig}; use crate::config::{Bitrate, PlayerConfig};
use crate::librespot_core::tokio; use crate::librespot_core::tokio;
use crate::metadata::{AudioItem, FileFormat}; use crate::metadata::{AudioItem, FileFormat};
@ -217,17 +218,20 @@ impl NormalisationData {
} }
fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 { fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 {
let mut normalisation_factor = f32::powf( let [gain_db, gain_peak] = match config.normalisation_type {
10.0, NormalisationType::Album => [data.album_gain_db, data.album_peak],
(data.track_gain_db + config.normalisation_pregain) / 20.0, NormalisationType::Track => [data.track_gain_db, data.track_peak],
); };
let mut normalisation_factor =
f32::powf(10.0, (gain_db + config.normalisation_pregain) / 20.0);
if normalisation_factor * data.track_peak > 1.0 { if normalisation_factor * gain_peak > 1.0 {
warn!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid."); warn!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid.");
normalisation_factor = 1.0 / data.track_peak; normalisation_factor = 1.0 / gain_peak;
} }
debug!("Normalisation Data: {:?}", data); debug!("Normalisation Data: {:?}", data);
debug!("Normalisation Type: {:?}", config.normalisation_type);
debug!("Applied normalisation factor: {}", normalisation_factor); debug!("Applied normalisation factor: {}", normalisation_factor);
normalisation_factor normalisation_factor
@ -652,49 +656,27 @@ impl PlayerTrackLoader {
FileFormat::OGG_VORBIS_96, FileFormat::OGG_VORBIS_96,
], ],
}; };
let format = formats
.iter()
.find(|format| audio.files.contains_key(format))
.unwrap();
let file_id = match audio.files.get(&format) { let entry = formats.iter().find_map(|format| {
Some(&file_id) => file_id, if let Some(&file_id) = audio.files.get(format) {
Some((*format, file_id))
} else {
None
}
});
let (format, file_id) = match entry {
Some(t) => t,
None => { None => {
warn!("<{}> in not available in format {:?}", audio.name, format); warn!("<{}> is not available in any supported format", audio.name);
return None; return None;
} }
}; };
let bytes_per_second = self.stream_data_rate(*format); let bytes_per_second = self.stream_data_rate(format);
let play_from_beginning = position_ms == 0; let play_from_beginning = position_ms == 0;
let key = self.session.audio_key().request(spotify_id, file_id); let key = match self.session.audio_key().request(spotify_id, file_id).await {
let encrypted_file = AudioFile::open(
&self.session,
file_id,
bytes_per_second,
play_from_beginning,
);
let encrypted_file = match encrypted_file.await {
Ok(encrypted_file) => encrypted_file,
Err(_) => {
error!("Unable to load encrypted file.");
return None;
}
};
let mut stream_loader_controller = encrypted_file.get_stream_loader_controller();
if play_from_beginning {
// No need to seek -> we stream from the beginning
stream_loader_controller.set_stream_mode();
} else {
// we need to seek -> we set stream mode after the initial seek.
stream_loader_controller.set_random_access_mode();
}
let key = match key.await {
Ok(key) => key, Ok(key) => key,
Err(_) => { Err(_) => {
error!("Unable to load decryption key"); error!("Unable to load decryption key");
@ -702,39 +684,90 @@ impl PlayerTrackLoader {
} }
}; };
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file); // This is only a loop to be able to reload the file if an error occured
// while opening a cached file.
loop {
let encrypted_file = AudioFile::open(
&self.session,
file_id,
bytes_per_second,
play_from_beginning,
);
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) { let encrypted_file = match encrypted_file.await {
Ok(normalisation_data) => { Ok(encrypted_file) => encrypted_file,
NormalisationData::get_factor(&self.config, normalisation_data) Err(_) => {
error!("Unable to load encrypted file.");
return None;
}
};
let is_cached = encrypted_file.is_cached();
let mut stream_loader_controller = encrypted_file.get_stream_loader_controller();
if play_from_beginning {
// No need to seek -> we stream from the beginning
stream_loader_controller.set_stream_mode();
} else {
// we need to seek -> we set stream mode after the initial seek.
stream_loader_controller.set_random_access_mode();
} }
Err(_) => {
warn!("Unable to extract normalisation data, using default value."); let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
1.0_f32
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file)
{
Ok(normalisation_data) => {
NormalisationData::get_factor(&self.config, normalisation_data)
}
Err(_) => {
warn!("Unable to extract normalisation data, using default value.");
1.0_f32
}
};
let audio_file = Subfile::new(decrypted_file, 0xa7);
let mut decoder = match VorbisDecoder::new(audio_file) {
Ok(decoder) => decoder,
Err(e) if is_cached => {
warn!(
"Unable to read cached audio file: {}. Trying to download it.",
e
);
// unwrap safety: The file is cached, so session must have a cache
if !self.session.cache().unwrap().remove_file(file_id) {
return None;
}
// Just try it again
continue;
}
Err(e) => {
error!("Unable to read audio file: {}", e);
return None;
}
};
if position_ms != 0 {
if let Err(err) = decoder.seek(position_ms as i64) {
error!("Vorbis error: {}", err);
}
stream_loader_controller.set_stream_mode();
} }
}; let stream_position_pcm = PlayerInternal::position_ms_to_pcm(position_ms);
info!("<{}> ({} ms) loaded", audio.name, audio.duration);
let audio_file = Subfile::new(decrypted_file, 0xa7); return Some(PlayerLoadedTrackData {
decoder,
let mut decoder = VorbisDecoder::new(audio_file).unwrap(); normalisation_factor,
stream_loader_controller,
if position_ms != 0 { bytes_per_second,
match decoder.seek(position_ms as i64) { duration_ms,
Ok(_) => (), stream_position_pcm,
Err(err) => error!("Vorbis error: {:?}", err), });
}
stream_loader_controller.set_stream_mode();
} }
let stream_position_pcm = PlayerInternal::position_ms_to_pcm(position_ms);
info!("<{}> ({} ms) loaded", audio.name, audio.duration);
Some(PlayerLoadedTrackData {
decoder,
normalisation_factor,
stream_loader_controller,
bytes_per_second,
duration_ms,
stream_position_pcm,
})
} }
} }

View file

@ -24,16 +24,16 @@ pub fn run_program_on_events(event: PlayerEvent, onevent: &str) -> Option<io::Re
old_track_id, old_track_id,
new_track_id, new_track_id,
} => { } => {
env_vars.insert("PLAYER_EVENT", "change".to_string()); env_vars.insert("PLAYER_EVENT", "changed".to_string());
env_vars.insert("OLD_TRACK_ID", old_track_id.to_base62()); env_vars.insert("OLD_TRACK_ID", old_track_id.to_base62());
env_vars.insert("TRACK_ID", new_track_id.to_base62()); env_vars.insert("TRACK_ID", new_track_id.to_base62());
} }
PlayerEvent::Started { track_id, .. } => { PlayerEvent::Started { track_id, .. } => {
env_vars.insert("PLAYER_EVENT", "start".to_string()); env_vars.insert("PLAYER_EVENT", "started".to_string());
env_vars.insert("TRACK_ID", track_id.to_base62()); env_vars.insert("TRACK_ID", track_id.to_base62());
} }
PlayerEvent::Stopped { track_id, .. } => { PlayerEvent::Stopped { track_id, .. } => {
env_vars.insert("PLAYER_EVENT", "stop".to_string()); env_vars.insert("PLAYER_EVENT", "stopped".to_string());
env_vars.insert("TRACK_ID", track_id.to_base62()); env_vars.insert("TRACK_ID", track_id.to_base62());
} }
PlayerEvent::Playing { PlayerEvent::Playing {