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)
@ -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 {
mkdir_existing(&system_location).unwrap(); fs::create_dir_all(location)?;
Cache {
audio_root: audio_location,
system_root: system_location,
use_audio_cache: use_audio_cache,
}
}
} }
impl Cache { if let Some(location) = &audio_location {
fn credentials_path(&self) -> PathBuf { fs::create_dir_all(location)?;
self.system_root.join("credentials.json") }
let audio_location = audio_location.map(|p| p.as_ref().to_owned());
let volume_location = system_location.as_ref().map(|p| p.as_ref().join("volume"));
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)
}
pub fn save_volume(&self, volume: Volume) { let read = || {
let path = self.volume_path(); let mut file = File::open(location)?;
volume.save_to_file(&path); 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
}
} }
} }
impl Cache { pub fn save_volume(&self, volume: u16) {
fn file_path(&self, file: FileId) -> PathBuf { if let Some(ref location) = self.volume_location {
let result = File::create(location).and_then(|mut file| write!(file, "{}", volume));
if let Err(e) = result {
warn!("Cannot save volume to cache: {}", e);
}
}
}
fn file_path(&self, file: FileId) -> Option<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;
Ok(SpotifyId::as_track(n)) dst += p;
} }
pub fn from_raw(data: &[u8]) -> Result<SpotifyId, SpotifyIdError> { Ok(SpotifyId::as_track(dst))
if data.len() != 16 { }
/// Creates a `SpotifyId` from a copy of `SpotifyId::SIZE` (16) bytes in big-endian order.
///
/// 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 mut id = SpotifyId::from_base62(&src[id_i..])?;
let parts = uri.split(":").collect::<Vec<&str>>();
let gid = parts.last().unwrap(); // Slice offset by 8 as we are skipping the "spotify:" prefix.
if uri.contains(":episode:") { id.audio_type = src[8..id_i - 1].into();
let mut spotify_id = SpotifyId::from_base62(gid).unwrap();
let _ = std::mem::replace(&mut spotify_id.audio_type, SpotifyAudioType::Podcast); Ok(id)
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)
}
} }
/// 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;
} }
std::str::from_utf8(&data).unwrap().to_owned() while carry > 0 {
} dst[i] = (carry % 62) as u8;
carry /= 62;
pub fn to_uri(&self) -> String { i += 1;
match self.audio_type {
SpotifyAudioType::Track => format!("spotify:track:{}", self.to_base62()),
SpotifyAudioType::Podcast => format!("spotify:episode:{}", self.to_base62()),
SpotifyAudioType::NonPlayable => format!("spotify:unknown:{}", self.to_base62()),
} }
} }
pub fn to_raw(&self) -> [u8; 16] { for b in &mut dst {
*b = BASE62_DIGITS[*b as usize];
}
dst.reverse();
unsafe {
// Safety: We are only dealing with ASCII characters.
String::from_utf8_unchecked(dst.to_vec())
}
}
/// 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,23 +656,37 @@ 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 {
Ok(key) => key,
Err(_) => {
error!("Unable to load decryption key");
return None;
}
};
// 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( let encrypted_file = AudioFile::open(
&self.session, &self.session,
file_id, file_id,
@ -683,6 +701,7 @@ impl PlayerTrackLoader {
return None; return None;
} }
}; };
let is_cached = encrypted_file.is_cached();
let mut stream_loader_controller = encrypted_file.get_stream_loader_controller(); let mut stream_loader_controller = encrypted_file.get_stream_loader_controller();
@ -694,17 +713,10 @@ impl PlayerTrackLoader {
stream_loader_controller.set_random_access_mode(); stream_loader_controller.set_random_access_mode();
} }
let key = match key.await {
Ok(key) => key,
Err(_) => {
error!("Unable to load decryption key");
return None;
}
};
let mut decrypted_file = AudioDecrypt::new(key, encrypted_file); let mut decrypted_file = AudioDecrypt::new(key, encrypted_file);
let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) { let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file)
{
Ok(normalisation_data) => { Ok(normalisation_data) => {
NormalisationData::get_factor(&self.config, normalisation_data) NormalisationData::get_factor(&self.config, normalisation_data)
} }
@ -716,25 +728,46 @@ impl PlayerTrackLoader {
let audio_file = Subfile::new(decrypted_file, 0xa7); let audio_file = Subfile::new(decrypted_file, 0xa7);
let mut decoder = VorbisDecoder::new(audio_file).unwrap(); 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 position_ms != 0 {
match decoder.seek(position_ms as i64) { if let Err(err) = decoder.seek(position_ms as i64) {
Ok(_) => (), error!("Vorbis error: {}", err);
Err(err) => error!("Vorbis error: {:?}", err),
} }
stream_loader_controller.set_stream_mode(); stream_loader_controller.set_stream_mode();
} }
let stream_position_pcm = PlayerInternal::position_ms_to_pcm(position_ms); let stream_position_pcm = PlayerInternal::position_ms_to_pcm(position_ms);
info!("<{}> ({} ms) loaded", audio.name, audio.duration); info!("<{}> ({} ms) loaded", audio.name, audio.duration);
Some(PlayerLoadedTrackData {
return Some(PlayerLoadedTrackData {
decoder, decoder,
normalisation_factor, normalisation_factor,
stream_loader_controller, stream_loader_controller,
bytes_per_second, bytes_per_second,
duration_ms, duration_ms,
stream_position_pcm, 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 {