├── .github ├── CODEOWNERS └── workflows │ └── pr-checks.yml ├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── README.md ├── application ├── auth-server │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── ffplay │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── cli.rs │ │ ├── cli_test.rs │ │ └── main.rs └── soundcloud-tui │ ├── Cargo.toml │ ├── README.md │ └── src │ └── main.rs ├── docs └── assets │ └── solution-diagram.png └── library ├── player ├── Cargo.toml ├── README.md ├── src │ ├── device.rs │ ├── device_test.rs │ ├── internal │ │ ├── client.rs │ │ ├── error.rs │ │ ├── hls │ │ │ ├── mod.rs │ │ │ ├── playlist.rs │ │ │ ├── playlist_test.rs │ │ │ ├── track.rs │ │ │ └── track_test.rs │ │ ├── mod.rs │ │ ├── track_lookup.rs │ │ └── track_lookup_test.rs │ ├── lib.rs │ ├── player.rs │ └── player_test.rs └── testdata │ └── segment_12.ts ├── render ├── Cargo.toml ├── README.md └── src │ ├── app.rs │ ├── components │ ├── component.rs │ ├── mod.rs │ ├── style │ │ ├── border.rs │ │ ├── box_style.rs │ │ ├── layout.rs │ │ ├── mod.rs │ │ └── text.rs │ └── widgets │ │ ├── button.rs │ │ └── mod.rs │ ├── context.rs │ ├── event │ ├── crossterm │ │ ├── event_source.rs │ │ └── mod.rs │ ├── event_server.rs │ ├── key.rs │ ├── mod.rs │ ├── tests │ │ ├── event_server.rs │ │ ├── mod.rs │ │ └── utils.rs │ └── utils.rs │ ├── lib.rs │ ├── pages │ ├── login.rs │ ├── mod.rs │ └── splash.rs │ └── router.rs └── soundcloud-rs ├── Cargo.toml ├── README.md └── src ├── lib.rs └── me.rs /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @cherbie -------------------------------------------------------------------------------- /.github/workflows/pr-checks.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | container: ubuntu:latest 10 | steps: 11 | - name: install default tooling 12 | run: apt update -y && apt-get install -y curl build-essential pkg-config libssl-dev libasound2-dev 13 | - uses: actions/checkout@v4 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | profile: minimal 17 | toolchain: stable 18 | override: true 19 | - uses: actions-rs/cargo@v1 20 | with: 21 | command: check 22 | 23 | test: 24 | name: Test Suite 25 | runs-on: ubuntu-latest 26 | container: ubuntu:latest 27 | steps: 28 | - name: install default tooling 29 | run: apt update -y && apt-get install -y curl build-essential pkg-config libssl-dev libasound2-dev 30 | - uses: actions/checkout@v4 31 | - uses: actions-rs/toolchain@v1 32 | with: 33 | profile: minimal 34 | toolchain: stable 35 | override: true 36 | - uses: actions-rs/cargo@v1 37 | with: 38 | command: test 39 | 40 | fmt: 41 | name: Rustfmt 42 | runs-on: ubuntu-latest 43 | container: ubuntu:latest 44 | steps: 45 | - name: install default tooling 46 | run: apt update -y && apt-get install -y curl build-essential pkg-config libssl-dev libasound2-dev 47 | - uses: actions/checkout@v4 48 | - uses: actions-rs/toolchain@v1 49 | with: 50 | profile: minimal 51 | toolchain: stable 52 | override: true 53 | components: rustfmt 54 | - uses: actions-rs/cargo@v1 55 | with: 56 | command: fmt 57 | args: --all -- --check 58 | 59 | clippy: 60 | name: Clippy 61 | runs-on: ubuntu-latest 62 | container: ubuntu:latest 63 | steps: 64 | - name: install default tooling 65 | run: apt update -y && apt-get install -y curl build-essential pkg-config libssl-dev libasound2-dev 66 | - uses: actions/checkout@v4 67 | - uses: actions-rs/toolchain@v1 68 | with: 69 | profile: minimal 70 | toolchain: stable 71 | override: true 72 | components: clippy 73 | - uses: actions-rs/cargo@v1 74 | with: 75 | command: clippy 76 | args: -- -D warnings 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | # macOS 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./Cargo.toml" 4 | ] 5 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "actix-codec" 7 | version = "0.5.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" 10 | dependencies = [ 11 | "bitflags 2.5.0", 12 | "bytes", 13 | "futures-core", 14 | "futures-sink", 15 | "memchr", 16 | "pin-project-lite", 17 | "tokio", 18 | "tokio-util", 19 | "tracing", 20 | ] 21 | 22 | [[package]] 23 | name = "actix-http" 24 | version = "3.8.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "3ae682f693a9cd7b058f2b0b5d9a6d7728a8555779bedbbc35dd88528611d020" 27 | dependencies = [ 28 | "actix-codec", 29 | "actix-rt", 30 | "actix-service", 31 | "actix-utils", 32 | "ahash", 33 | "base64", 34 | "bitflags 2.5.0", 35 | "brotli", 36 | "bytes", 37 | "bytestring", 38 | "derive_more", 39 | "encoding_rs", 40 | "flate2", 41 | "futures-core", 42 | "h2 0.3.26", 43 | "http 0.2.12", 44 | "httparse", 45 | "httpdate", 46 | "itoa", 47 | "language-tags", 48 | "local-channel", 49 | "mime", 50 | "percent-encoding", 51 | "pin-project-lite", 52 | "rand", 53 | "sha1", 54 | "smallvec", 55 | "tokio", 56 | "tokio-util", 57 | "tracing", 58 | "zstd", 59 | ] 60 | 61 | [[package]] 62 | name = "actix-macros" 63 | version = "0.2.4" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" 66 | dependencies = [ 67 | "quote", 68 | "syn", 69 | ] 70 | 71 | [[package]] 72 | name = "actix-router" 73 | version = "0.5.3" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" 76 | dependencies = [ 77 | "bytestring", 78 | "cfg-if", 79 | "http 0.2.12", 80 | "regex", 81 | "regex-lite", 82 | "serde", 83 | "tracing", 84 | ] 85 | 86 | [[package]] 87 | name = "actix-rt" 88 | version = "2.10.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" 91 | dependencies = [ 92 | "futures-core", 93 | "tokio", 94 | ] 95 | 96 | [[package]] 97 | name = "actix-server" 98 | version = "2.5.0" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" 101 | dependencies = [ 102 | "actix-rt", 103 | "actix-service", 104 | "actix-utils", 105 | "futures-core", 106 | "futures-util", 107 | "mio 1.0.1", 108 | "socket2", 109 | "tokio", 110 | "tracing", 111 | ] 112 | 113 | [[package]] 114 | name = "actix-service" 115 | version = "2.0.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" 118 | dependencies = [ 119 | "futures-core", 120 | "paste", 121 | "pin-project-lite", 122 | ] 123 | 124 | [[package]] 125 | name = "actix-utils" 126 | version = "3.0.1" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" 129 | dependencies = [ 130 | "local-waker", 131 | "pin-project-lite", 132 | ] 133 | 134 | [[package]] 135 | name = "actix-web" 136 | version = "4.8.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "1988c02af8d2b718c05bc4aeb6a66395b7cdf32858c2c71131e5637a8c05a9ff" 139 | dependencies = [ 140 | "actix-codec", 141 | "actix-http", 142 | "actix-macros", 143 | "actix-router", 144 | "actix-rt", 145 | "actix-server", 146 | "actix-service", 147 | "actix-utils", 148 | "actix-web-codegen", 149 | "ahash", 150 | "bytes", 151 | "bytestring", 152 | "cfg-if", 153 | "cookie", 154 | "derive_more", 155 | "encoding_rs", 156 | "futures-core", 157 | "futures-util", 158 | "itoa", 159 | "language-tags", 160 | "log", 161 | "mime", 162 | "once_cell", 163 | "pin-project-lite", 164 | "regex", 165 | "regex-lite", 166 | "serde", 167 | "serde_json", 168 | "serde_urlencoded", 169 | "smallvec", 170 | "socket2", 171 | "time", 172 | "url", 173 | ] 174 | 175 | [[package]] 176 | name = "actix-web-codegen" 177 | version = "4.3.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" 180 | dependencies = [ 181 | "actix-router", 182 | "proc-macro2", 183 | "quote", 184 | "syn", 185 | ] 186 | 187 | [[package]] 188 | name = "addr2line" 189 | version = "0.21.0" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 192 | dependencies = [ 193 | "gimli", 194 | ] 195 | 196 | [[package]] 197 | name = "adler" 198 | version = "1.0.2" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 201 | 202 | [[package]] 203 | name = "ahash" 204 | version = "0.8.11" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 207 | dependencies = [ 208 | "cfg-if", 209 | "getrandom", 210 | "once_cell", 211 | "version_check", 212 | "zerocopy", 213 | ] 214 | 215 | [[package]] 216 | name = "aho-corasick" 217 | version = "1.1.3" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 220 | dependencies = [ 221 | "memchr", 222 | ] 223 | 224 | [[package]] 225 | name = "alloc-no-stdlib" 226 | version = "2.0.4" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 229 | 230 | [[package]] 231 | name = "alloc-stdlib" 232 | version = "0.2.2" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 235 | dependencies = [ 236 | "alloc-no-stdlib", 237 | ] 238 | 239 | [[package]] 240 | name = "alsa" 241 | version = "0.9.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce" 244 | dependencies = [ 245 | "alsa-sys", 246 | "bitflags 2.5.0", 247 | "libc", 248 | ] 249 | 250 | [[package]] 251 | name = "alsa-sys" 252 | version = "0.3.1" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" 255 | dependencies = [ 256 | "libc", 257 | "pkg-config", 258 | ] 259 | 260 | [[package]] 261 | name = "anstream" 262 | version = "0.6.14" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" 265 | dependencies = [ 266 | "anstyle", 267 | "anstyle-parse", 268 | "anstyle-query", 269 | "anstyle-wincon", 270 | "colorchoice", 271 | "is_terminal_polyfill", 272 | "utf8parse", 273 | ] 274 | 275 | [[package]] 276 | name = "anstyle" 277 | version = "1.0.7" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" 280 | 281 | [[package]] 282 | name = "anstyle-parse" 283 | version = "0.2.4" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" 286 | dependencies = [ 287 | "utf8parse", 288 | ] 289 | 290 | [[package]] 291 | name = "anstyle-query" 292 | version = "1.0.3" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" 295 | dependencies = [ 296 | "windows-sys 0.52.0", 297 | ] 298 | 299 | [[package]] 300 | name = "anstyle-wincon" 301 | version = "3.0.3" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" 304 | dependencies = [ 305 | "anstyle", 306 | "windows-sys 0.52.0", 307 | ] 308 | 309 | [[package]] 310 | name = "anyhow" 311 | version = "1.0.83" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" 314 | 315 | [[package]] 316 | name = "arrayvec" 317 | version = "0.7.4" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" 320 | 321 | [[package]] 322 | name = "assert-json-diff" 323 | version = "2.0.2" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" 326 | dependencies = [ 327 | "serde", 328 | "serde_json", 329 | ] 330 | 331 | [[package]] 332 | name = "async-stream" 333 | version = "0.3.5" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" 336 | dependencies = [ 337 | "async-stream-impl", 338 | "futures-core", 339 | "pin-project-lite", 340 | ] 341 | 342 | [[package]] 343 | name = "async-stream-impl" 344 | version = "0.3.5" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" 347 | dependencies = [ 348 | "proc-macro2", 349 | "quote", 350 | "syn", 351 | ] 352 | 353 | [[package]] 354 | name = "autocfg" 355 | version = "1.2.0" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" 358 | 359 | [[package]] 360 | name = "backtrace" 361 | version = "0.3.71" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 364 | dependencies = [ 365 | "addr2line", 366 | "cc", 367 | "cfg-if", 368 | "libc", 369 | "miniz_oxide", 370 | "object", 371 | "rustc-demangle", 372 | ] 373 | 374 | [[package]] 375 | name = "base64" 376 | version = "0.22.1" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 379 | 380 | [[package]] 381 | name = "bindgen" 382 | version = "0.69.4" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" 385 | dependencies = [ 386 | "bitflags 2.5.0", 387 | "cexpr", 388 | "clang-sys", 389 | "itertools", 390 | "lazy_static", 391 | "lazycell", 392 | "proc-macro2", 393 | "quote", 394 | "regex", 395 | "rustc-hash", 396 | "shlex", 397 | "syn", 398 | ] 399 | 400 | [[package]] 401 | name = "bitflags" 402 | version = "1.3.2" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 405 | 406 | [[package]] 407 | name = "bitflags" 408 | version = "2.5.0" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 411 | 412 | [[package]] 413 | name = "block-buffer" 414 | version = "0.10.4" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 417 | dependencies = [ 418 | "generic-array", 419 | ] 420 | 421 | [[package]] 422 | name = "brotli" 423 | version = "6.0.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" 426 | dependencies = [ 427 | "alloc-no-stdlib", 428 | "alloc-stdlib", 429 | "brotli-decompressor", 430 | ] 431 | 432 | [[package]] 433 | name = "brotli-decompressor" 434 | version = "4.0.1" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" 437 | dependencies = [ 438 | "alloc-no-stdlib", 439 | "alloc-stdlib", 440 | ] 441 | 442 | [[package]] 443 | name = "bumpalo" 444 | version = "3.16.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 447 | 448 | [[package]] 449 | name = "bytemuck" 450 | version = "1.15.0" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" 453 | 454 | [[package]] 455 | name = "byteorder" 456 | version = "1.5.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 459 | 460 | [[package]] 461 | name = "bytes" 462 | version = "1.6.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 465 | 466 | [[package]] 467 | name = "bytestring" 468 | version = "1.3.1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" 471 | dependencies = [ 472 | "bytes", 473 | ] 474 | 475 | [[package]] 476 | name = "cassowary" 477 | version = "0.3.0" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 480 | 481 | [[package]] 482 | name = "cc" 483 | version = "1.0.96" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" 486 | dependencies = [ 487 | "jobserver", 488 | "libc", 489 | "once_cell", 490 | ] 491 | 492 | [[package]] 493 | name = "cesu8" 494 | version = "1.1.0" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" 497 | 498 | [[package]] 499 | name = "cexpr" 500 | version = "0.6.0" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 503 | dependencies = [ 504 | "nom", 505 | ] 506 | 507 | [[package]] 508 | name = "cfg-if" 509 | version = "1.0.0" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 512 | 513 | [[package]] 514 | name = "chrono" 515 | version = "0.4.38" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 518 | dependencies = [ 519 | "num-traits", 520 | ] 521 | 522 | [[package]] 523 | name = "clang-sys" 524 | version = "1.7.0" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" 527 | dependencies = [ 528 | "glob", 529 | "libc", 530 | "libloading", 531 | ] 532 | 533 | [[package]] 534 | name = "clap" 535 | version = "4.5.4" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" 538 | dependencies = [ 539 | "clap_builder", 540 | ] 541 | 542 | [[package]] 543 | name = "clap_builder" 544 | version = "4.5.2" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" 547 | dependencies = [ 548 | "anstream", 549 | "anstyle", 550 | "clap_lex", 551 | "strsim", 552 | ] 553 | 554 | [[package]] 555 | name = "clap_lex" 556 | version = "0.7.0" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 559 | 560 | [[package]] 561 | name = "claxon" 562 | version = "0.4.3" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" 565 | 566 | [[package]] 567 | name = "colorchoice" 568 | version = "1.0.1" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" 571 | 572 | [[package]] 573 | name = "colored" 574 | version = "2.1.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" 577 | dependencies = [ 578 | "lazy_static", 579 | "windows-sys 0.48.0", 580 | ] 581 | 582 | [[package]] 583 | name = "combine" 584 | version = "4.6.7" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" 587 | dependencies = [ 588 | "bytes", 589 | "memchr", 590 | ] 591 | 592 | [[package]] 593 | name = "convert_case" 594 | version = "0.4.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 597 | 598 | [[package]] 599 | name = "cookie" 600 | version = "0.16.2" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" 603 | dependencies = [ 604 | "percent-encoding", 605 | "time", 606 | "version_check", 607 | ] 608 | 609 | [[package]] 610 | name = "core-foundation" 611 | version = "0.9.4" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 614 | dependencies = [ 615 | "core-foundation-sys", 616 | "libc", 617 | ] 618 | 619 | [[package]] 620 | name = "core-foundation-sys" 621 | version = "0.8.6" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 624 | 625 | [[package]] 626 | name = "coreaudio-rs" 627 | version = "0.11.3" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" 630 | dependencies = [ 631 | "bitflags 1.3.2", 632 | "core-foundation-sys", 633 | "coreaudio-sys", 634 | ] 635 | 636 | [[package]] 637 | name = "coreaudio-sys" 638 | version = "0.2.15" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" 641 | dependencies = [ 642 | "bindgen", 643 | ] 644 | 645 | [[package]] 646 | name = "cpal" 647 | version = "0.15.3" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" 650 | dependencies = [ 651 | "alsa", 652 | "core-foundation-sys", 653 | "coreaudio-rs", 654 | "dasp_sample", 655 | "jni", 656 | "js-sys", 657 | "libc", 658 | "mach2", 659 | "ndk", 660 | "ndk-context", 661 | "oboe", 662 | "wasm-bindgen", 663 | "wasm-bindgen-futures", 664 | "web-sys", 665 | "windows", 666 | ] 667 | 668 | [[package]] 669 | name = "cpufeatures" 670 | version = "0.2.12" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 673 | dependencies = [ 674 | "libc", 675 | ] 676 | 677 | [[package]] 678 | name = "crc32fast" 679 | version = "1.4.2" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 682 | dependencies = [ 683 | "cfg-if", 684 | ] 685 | 686 | [[package]] 687 | name = "crossterm" 688 | version = "0.25.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" 691 | dependencies = [ 692 | "bitflags 1.3.2", 693 | "crossterm_winapi", 694 | "libc", 695 | "mio 0.8.11", 696 | "parking_lot", 697 | "signal-hook", 698 | "signal-hook-mio", 699 | "winapi", 700 | ] 701 | 702 | [[package]] 703 | name = "crossterm" 704 | version = "0.27.0" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" 707 | dependencies = [ 708 | "bitflags 2.5.0", 709 | "crossterm_winapi", 710 | "libc", 711 | "mio 0.8.11", 712 | "parking_lot", 713 | "signal-hook", 714 | "signal-hook-mio", 715 | "winapi", 716 | ] 717 | 718 | [[package]] 719 | name = "crossterm_winapi" 720 | version = "0.9.1" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 723 | dependencies = [ 724 | "winapi", 725 | ] 726 | 727 | [[package]] 728 | name = "crypto-common" 729 | version = "0.1.6" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 732 | dependencies = [ 733 | "generic-array", 734 | "typenum", 735 | ] 736 | 737 | [[package]] 738 | name = "dasp_sample" 739 | version = "0.11.0" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" 742 | 743 | [[package]] 744 | name = "deranged" 745 | version = "0.3.11" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 748 | dependencies = [ 749 | "powerfmt", 750 | ] 751 | 752 | [[package]] 753 | name = "derive_more" 754 | version = "0.99.18" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" 757 | dependencies = [ 758 | "convert_case", 759 | "proc-macro2", 760 | "quote", 761 | "rustc_version", 762 | "syn", 763 | ] 764 | 765 | [[package]] 766 | name = "diff" 767 | version = "0.1.13" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 770 | 771 | [[package]] 772 | name = "digest" 773 | version = "0.10.7" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 776 | dependencies = [ 777 | "block-buffer", 778 | "crypto-common", 779 | ] 780 | 781 | [[package]] 782 | name = "downcast" 783 | version = "0.11.0" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" 786 | 787 | [[package]] 788 | name = "either" 789 | version = "1.11.0" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" 792 | 793 | [[package]] 794 | name = "encoding_rs" 795 | version = "0.8.34" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 798 | dependencies = [ 799 | "cfg-if", 800 | ] 801 | 802 | [[package]] 803 | name = "equivalent" 804 | version = "1.0.1" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 807 | 808 | [[package]] 809 | name = "errno" 810 | version = "0.3.8" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 813 | dependencies = [ 814 | "libc", 815 | "windows-sys 0.52.0", 816 | ] 817 | 818 | [[package]] 819 | name = "fastrand" 820 | version = "2.1.0" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" 823 | 824 | [[package]] 825 | name = "ffplay" 826 | version = "0.1.0" 827 | dependencies = [ 828 | "anyhow", 829 | "clap", 830 | "player", 831 | "rodio 0.18.0", 832 | "tokio", 833 | ] 834 | 835 | [[package]] 836 | name = "flate2" 837 | version = "1.0.31" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" 840 | dependencies = [ 841 | "crc32fast", 842 | "miniz_oxide", 843 | ] 844 | 845 | [[package]] 846 | name = "fnv" 847 | version = "1.0.7" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 850 | 851 | [[package]] 852 | name = "foreign-types" 853 | version = "0.3.2" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 856 | dependencies = [ 857 | "foreign-types-shared", 858 | ] 859 | 860 | [[package]] 861 | name = "foreign-types-shared" 862 | version = "0.1.1" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 865 | 866 | [[package]] 867 | name = "form_urlencoded" 868 | version = "1.2.1" 869 | source = "registry+https://github.com/rust-lang/crates.io-index" 870 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 871 | dependencies = [ 872 | "percent-encoding", 873 | ] 874 | 875 | [[package]] 876 | name = "fragile" 877 | version = "2.0.0" 878 | source = "registry+https://github.com/rust-lang/crates.io-index" 879 | checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" 880 | 881 | [[package]] 882 | name = "futures" 883 | version = "0.3.30" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 886 | dependencies = [ 887 | "futures-channel", 888 | "futures-core", 889 | "futures-executor", 890 | "futures-io", 891 | "futures-sink", 892 | "futures-task", 893 | "futures-util", 894 | ] 895 | 896 | [[package]] 897 | name = "futures-channel" 898 | version = "0.3.30" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 901 | dependencies = [ 902 | "futures-core", 903 | "futures-sink", 904 | ] 905 | 906 | [[package]] 907 | name = "futures-core" 908 | version = "0.3.30" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 911 | 912 | [[package]] 913 | name = "futures-executor" 914 | version = "0.3.30" 915 | source = "registry+https://github.com/rust-lang/crates.io-index" 916 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 917 | dependencies = [ 918 | "futures-core", 919 | "futures-task", 920 | "futures-util", 921 | ] 922 | 923 | [[package]] 924 | name = "futures-io" 925 | version = "0.3.30" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 928 | 929 | [[package]] 930 | name = "futures-macro" 931 | version = "0.3.30" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 934 | dependencies = [ 935 | "proc-macro2", 936 | "quote", 937 | "syn", 938 | ] 939 | 940 | [[package]] 941 | name = "futures-sink" 942 | version = "0.3.30" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 945 | 946 | [[package]] 947 | name = "futures-task" 948 | version = "0.3.30" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 951 | 952 | [[package]] 953 | name = "futures-util" 954 | version = "0.3.30" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 957 | dependencies = [ 958 | "futures-channel", 959 | "futures-core", 960 | "futures-io", 961 | "futures-macro", 962 | "futures-sink", 963 | "futures-task", 964 | "memchr", 965 | "pin-project-lite", 966 | "pin-utils", 967 | "slab", 968 | ] 969 | 970 | [[package]] 971 | name = "generic-array" 972 | version = "0.14.7" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 975 | dependencies = [ 976 | "typenum", 977 | "version_check", 978 | ] 979 | 980 | [[package]] 981 | name = "getrandom" 982 | version = "0.2.14" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" 985 | dependencies = [ 986 | "cfg-if", 987 | "libc", 988 | "wasi", 989 | ] 990 | 991 | [[package]] 992 | name = "gimli" 993 | version = "0.28.1" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 996 | 997 | [[package]] 998 | name = "glob" 999 | version = "0.3.1" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 1002 | 1003 | [[package]] 1004 | name = "h2" 1005 | version = "0.3.26" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" 1008 | dependencies = [ 1009 | "bytes", 1010 | "fnv", 1011 | "futures-core", 1012 | "futures-sink", 1013 | "futures-util", 1014 | "http 0.2.12", 1015 | "indexmap", 1016 | "slab", 1017 | "tokio", 1018 | "tokio-util", 1019 | "tracing", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "h2" 1024 | version = "0.4.4" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" 1027 | dependencies = [ 1028 | "bytes", 1029 | "fnv", 1030 | "futures-core", 1031 | "futures-sink", 1032 | "futures-util", 1033 | "http 1.1.0", 1034 | "indexmap", 1035 | "slab", 1036 | "tokio", 1037 | "tokio-util", 1038 | "tracing", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "hashbrown" 1043 | version = "0.14.5" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 1046 | 1047 | [[package]] 1048 | name = "hermit-abi" 1049 | version = "0.3.9" 1050 | source = "registry+https://github.com/rust-lang/crates.io-index" 1051 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 1052 | 1053 | [[package]] 1054 | name = "higher-kinded-types" 1055 | version = "0.1.1" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "561985554c8b8d4808605c90a5f1979cc6c31a5d20b78465cd59501233c6678e" 1058 | dependencies = [ 1059 | "never-say-never", 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "hound" 1064 | version = "3.5.1" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" 1067 | 1068 | [[package]] 1069 | name = "http" 1070 | version = "0.2.12" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 1073 | dependencies = [ 1074 | "bytes", 1075 | "fnv", 1076 | "itoa", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "http" 1081 | version = "1.1.0" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 1084 | dependencies = [ 1085 | "bytes", 1086 | "fnv", 1087 | "itoa", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "http-body" 1092 | version = "0.4.6" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 1095 | dependencies = [ 1096 | "bytes", 1097 | "http 0.2.12", 1098 | "pin-project-lite", 1099 | ] 1100 | 1101 | [[package]] 1102 | name = "http-body" 1103 | version = "1.0.0" 1104 | source = "registry+https://github.com/rust-lang/crates.io-index" 1105 | checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" 1106 | dependencies = [ 1107 | "bytes", 1108 | "http 1.1.0", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "http-body-util" 1113 | version = "0.1.1" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" 1116 | dependencies = [ 1117 | "bytes", 1118 | "futures-core", 1119 | "http 1.1.0", 1120 | "http-body 1.0.0", 1121 | "pin-project-lite", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "httparse" 1126 | version = "1.8.0" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 1129 | 1130 | [[package]] 1131 | name = "httpdate" 1132 | version = "1.0.3" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 1135 | 1136 | [[package]] 1137 | name = "hyper" 1138 | version = "0.14.28" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" 1141 | dependencies = [ 1142 | "bytes", 1143 | "futures-channel", 1144 | "futures-core", 1145 | "futures-util", 1146 | "h2 0.3.26", 1147 | "http 0.2.12", 1148 | "http-body 0.4.6", 1149 | "httparse", 1150 | "httpdate", 1151 | "itoa", 1152 | "pin-project-lite", 1153 | "tokio", 1154 | "tower-service", 1155 | "tracing", 1156 | "want", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "hyper" 1161 | version = "1.3.1" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" 1164 | dependencies = [ 1165 | "bytes", 1166 | "futures-channel", 1167 | "futures-util", 1168 | "h2 0.4.4", 1169 | "http 1.1.0", 1170 | "http-body 1.0.0", 1171 | "httparse", 1172 | "itoa", 1173 | "pin-project-lite", 1174 | "smallvec", 1175 | "tokio", 1176 | "want", 1177 | ] 1178 | 1179 | [[package]] 1180 | name = "hyper-tls" 1181 | version = "0.6.0" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 1184 | dependencies = [ 1185 | "bytes", 1186 | "http-body-util", 1187 | "hyper 1.3.1", 1188 | "hyper-util", 1189 | "native-tls", 1190 | "tokio", 1191 | "tokio-native-tls", 1192 | "tower-service", 1193 | ] 1194 | 1195 | [[package]] 1196 | name = "hyper-util" 1197 | version = "0.1.3" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" 1200 | dependencies = [ 1201 | "bytes", 1202 | "futures-channel", 1203 | "futures-util", 1204 | "http 1.1.0", 1205 | "http-body 1.0.0", 1206 | "hyper 1.3.1", 1207 | "pin-project-lite", 1208 | "socket2", 1209 | "tokio", 1210 | "tower", 1211 | "tower-service", 1212 | "tracing", 1213 | ] 1214 | 1215 | [[package]] 1216 | name = "idna" 1217 | version = "0.5.0" 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" 1219 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1220 | dependencies = [ 1221 | "unicode-bidi", 1222 | "unicode-normalization", 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "indexmap" 1227 | version = "2.2.6" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 1230 | dependencies = [ 1231 | "equivalent", 1232 | "hashbrown", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "ipnet" 1237 | version = "2.9.0" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 1240 | 1241 | [[package]] 1242 | name = "is_terminal_polyfill" 1243 | version = "1.70.0" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" 1246 | 1247 | [[package]] 1248 | name = "itertools" 1249 | version = "0.12.1" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 1252 | dependencies = [ 1253 | "either", 1254 | ] 1255 | 1256 | [[package]] 1257 | name = "itoa" 1258 | version = "1.0.11" 1259 | source = "registry+https://github.com/rust-lang/crates.io-index" 1260 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 1261 | 1262 | [[package]] 1263 | name = "jni" 1264 | version = "0.21.1" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" 1267 | dependencies = [ 1268 | "cesu8", 1269 | "cfg-if", 1270 | "combine", 1271 | "jni-sys", 1272 | "log", 1273 | "thiserror", 1274 | "walkdir", 1275 | "windows-sys 0.45.0", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "jni-sys" 1280 | version = "0.3.0" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 1283 | 1284 | [[package]] 1285 | name = "jobserver" 1286 | version = "0.1.31" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 1289 | dependencies = [ 1290 | "libc", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "js-sys" 1295 | version = "0.3.69" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 1298 | dependencies = [ 1299 | "wasm-bindgen", 1300 | ] 1301 | 1302 | [[package]] 1303 | name = "language-tags" 1304 | version = "0.3.2" 1305 | source = "registry+https://github.com/rust-lang/crates.io-index" 1306 | checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" 1307 | 1308 | [[package]] 1309 | name = "lazy_static" 1310 | version = "1.4.0" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1313 | 1314 | [[package]] 1315 | name = "lazycell" 1316 | version = "1.3.0" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 1319 | 1320 | [[package]] 1321 | name = "lewton" 1322 | version = "0.10.2" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" 1325 | dependencies = [ 1326 | "byteorder", 1327 | "ogg", 1328 | "tinyvec", 1329 | ] 1330 | 1331 | [[package]] 1332 | name = "libc" 1333 | version = "0.2.154" 1334 | source = "registry+https://github.com/rust-lang/crates.io-index" 1335 | checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" 1336 | 1337 | [[package]] 1338 | name = "libloading" 1339 | version = "0.8.3" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" 1342 | dependencies = [ 1343 | "cfg-if", 1344 | "windows-targets 0.52.5", 1345 | ] 1346 | 1347 | [[package]] 1348 | name = "linux-raw-sys" 1349 | version = "0.4.13" 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" 1351 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 1352 | 1353 | [[package]] 1354 | name = "local-channel" 1355 | version = "0.1.5" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" 1358 | dependencies = [ 1359 | "futures-core", 1360 | "futures-sink", 1361 | "local-waker", 1362 | ] 1363 | 1364 | [[package]] 1365 | name = "local-waker" 1366 | version = "0.1.4" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" 1369 | 1370 | [[package]] 1371 | name = "lock_api" 1372 | version = "0.4.12" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1375 | dependencies = [ 1376 | "autocfg", 1377 | "scopeguard", 1378 | ] 1379 | 1380 | [[package]] 1381 | name = "log" 1382 | version = "0.4.21" 1383 | source = "registry+https://github.com/rust-lang/crates.io-index" 1384 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 1385 | 1386 | [[package]] 1387 | name = "m3u8-rs" 1388 | version = "6.0.0" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "f03cd3335fb5f2447755d45cda9c70f76013626a9db44374973791b0926a86c3" 1391 | dependencies = [ 1392 | "chrono", 1393 | "nom", 1394 | ] 1395 | 1396 | [[package]] 1397 | name = "mach2" 1398 | version = "0.4.2" 1399 | source = "registry+https://github.com/rust-lang/crates.io-index" 1400 | checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" 1401 | dependencies = [ 1402 | "libc", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "memchr" 1407 | version = "2.7.2" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 1410 | 1411 | [[package]] 1412 | name = "mime" 1413 | version = "0.3.17" 1414 | source = "registry+https://github.com/rust-lang/crates.io-index" 1415 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1416 | 1417 | [[package]] 1418 | name = "minimal-lexical" 1419 | version = "0.2.1" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1422 | 1423 | [[package]] 1424 | name = "miniz_oxide" 1425 | version = "0.7.2" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 1428 | dependencies = [ 1429 | "adler", 1430 | ] 1431 | 1432 | [[package]] 1433 | name = "mio" 1434 | version = "0.8.11" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 1437 | dependencies = [ 1438 | "libc", 1439 | "log", 1440 | "wasi", 1441 | "windows-sys 0.48.0", 1442 | ] 1443 | 1444 | [[package]] 1445 | name = "mio" 1446 | version = "1.0.1" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" 1449 | dependencies = [ 1450 | "hermit-abi", 1451 | "libc", 1452 | "log", 1453 | "wasi", 1454 | "windows-sys 0.52.0", 1455 | ] 1456 | 1457 | [[package]] 1458 | name = "mockall" 1459 | version = "0.12.1" 1460 | source = "registry+https://github.com/rust-lang/crates.io-index" 1461 | checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" 1462 | dependencies = [ 1463 | "cfg-if", 1464 | "downcast", 1465 | "fragile", 1466 | "lazy_static", 1467 | "mockall_derive", 1468 | "predicates", 1469 | "predicates-tree", 1470 | ] 1471 | 1472 | [[package]] 1473 | name = "mockall_derive" 1474 | version = "0.12.1" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" 1477 | dependencies = [ 1478 | "cfg-if", 1479 | "proc-macro2", 1480 | "quote", 1481 | "syn", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "mockito" 1486 | version = "1.4.0" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "d2f6e023aa5bdf392aa06c78e4a4e6d498baab5138d0c993503350ebbc37bf1e" 1489 | dependencies = [ 1490 | "assert-json-diff", 1491 | "colored", 1492 | "futures-core", 1493 | "hyper 0.14.28", 1494 | "log", 1495 | "rand", 1496 | "regex", 1497 | "serde_json", 1498 | "serde_urlencoded", 1499 | "similar", 1500 | "tokio", 1501 | ] 1502 | 1503 | [[package]] 1504 | name = "native-tls" 1505 | version = "0.2.11" 1506 | source = "registry+https://github.com/rust-lang/crates.io-index" 1507 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 1508 | dependencies = [ 1509 | "lazy_static", 1510 | "libc", 1511 | "log", 1512 | "openssl", 1513 | "openssl-probe", 1514 | "openssl-sys", 1515 | "schannel", 1516 | "security-framework", 1517 | "security-framework-sys", 1518 | "tempfile", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "ndk" 1523 | version = "0.8.0" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" 1526 | dependencies = [ 1527 | "bitflags 2.5.0", 1528 | "jni-sys", 1529 | "log", 1530 | "ndk-sys", 1531 | "num_enum", 1532 | "thiserror", 1533 | ] 1534 | 1535 | [[package]] 1536 | name = "ndk-context" 1537 | version = "0.1.1" 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" 1539 | checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 1540 | 1541 | [[package]] 1542 | name = "ndk-sys" 1543 | version = "0.5.0+25.2.9519653" 1544 | source = "registry+https://github.com/rust-lang/crates.io-index" 1545 | checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" 1546 | dependencies = [ 1547 | "jni-sys", 1548 | ] 1549 | 1550 | [[package]] 1551 | name = "never-say-never" 1552 | version = "6.6.666" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "cf5a574dadd7941adeaa71823ecba5e28331b8313fb2e1c6a5c7e5981ea53ad6" 1555 | 1556 | [[package]] 1557 | name = "nom" 1558 | version = "7.1.3" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1561 | dependencies = [ 1562 | "memchr", 1563 | "minimal-lexical", 1564 | ] 1565 | 1566 | [[package]] 1567 | name = "num-conv" 1568 | version = "0.1.0" 1569 | source = "registry+https://github.com/rust-lang/crates.io-index" 1570 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1571 | 1572 | [[package]] 1573 | name = "num-derive" 1574 | version = "0.4.2" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 1577 | dependencies = [ 1578 | "proc-macro2", 1579 | "quote", 1580 | "syn", 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "num-traits" 1585 | version = "0.2.18" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 1588 | dependencies = [ 1589 | "autocfg", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "num_enum" 1594 | version = "0.7.2" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" 1597 | dependencies = [ 1598 | "num_enum_derive", 1599 | ] 1600 | 1601 | [[package]] 1602 | name = "num_enum_derive" 1603 | version = "0.7.2" 1604 | source = "registry+https://github.com/rust-lang/crates.io-index" 1605 | checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" 1606 | dependencies = [ 1607 | "proc-macro-crate", 1608 | "proc-macro2", 1609 | "quote", 1610 | "syn", 1611 | ] 1612 | 1613 | [[package]] 1614 | name = "object" 1615 | version = "0.32.2" 1616 | source = "registry+https://github.com/rust-lang/crates.io-index" 1617 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 1618 | dependencies = [ 1619 | "memchr", 1620 | ] 1621 | 1622 | [[package]] 1623 | name = "oboe" 1624 | version = "0.6.1" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" 1627 | dependencies = [ 1628 | "jni", 1629 | "ndk", 1630 | "ndk-context", 1631 | "num-derive", 1632 | "num-traits", 1633 | "oboe-sys", 1634 | ] 1635 | 1636 | [[package]] 1637 | name = "oboe-sys" 1638 | version = "0.6.1" 1639 | source = "registry+https://github.com/rust-lang/crates.io-index" 1640 | checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" 1641 | dependencies = [ 1642 | "cc", 1643 | ] 1644 | 1645 | [[package]] 1646 | name = "ogg" 1647 | version = "0.8.0" 1648 | source = "registry+https://github.com/rust-lang/crates.io-index" 1649 | checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" 1650 | dependencies = [ 1651 | "byteorder", 1652 | ] 1653 | 1654 | [[package]] 1655 | name = "once_cell" 1656 | version = "1.19.0" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1659 | 1660 | [[package]] 1661 | name = "openssl" 1662 | version = "0.10.64" 1663 | source = "registry+https://github.com/rust-lang/crates.io-index" 1664 | checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" 1665 | dependencies = [ 1666 | "bitflags 2.5.0", 1667 | "cfg-if", 1668 | "foreign-types", 1669 | "libc", 1670 | "once_cell", 1671 | "openssl-macros", 1672 | "openssl-sys", 1673 | ] 1674 | 1675 | [[package]] 1676 | name = "openssl-macros" 1677 | version = "0.1.1" 1678 | source = "registry+https://github.com/rust-lang/crates.io-index" 1679 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1680 | dependencies = [ 1681 | "proc-macro2", 1682 | "quote", 1683 | "syn", 1684 | ] 1685 | 1686 | [[package]] 1687 | name = "openssl-probe" 1688 | version = "0.1.5" 1689 | source = "registry+https://github.com/rust-lang/crates.io-index" 1690 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1691 | 1692 | [[package]] 1693 | name = "openssl-sys" 1694 | version = "0.9.102" 1695 | source = "registry+https://github.com/rust-lang/crates.io-index" 1696 | checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" 1697 | dependencies = [ 1698 | "cc", 1699 | "libc", 1700 | "pkg-config", 1701 | "vcpkg", 1702 | ] 1703 | 1704 | [[package]] 1705 | name = "parking_lot" 1706 | version = "0.12.2" 1707 | source = "registry+https://github.com/rust-lang/crates.io-index" 1708 | checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" 1709 | dependencies = [ 1710 | "lock_api", 1711 | "parking_lot_core", 1712 | ] 1713 | 1714 | [[package]] 1715 | name = "parking_lot_core" 1716 | version = "0.9.10" 1717 | source = "registry+https://github.com/rust-lang/crates.io-index" 1718 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1719 | dependencies = [ 1720 | "cfg-if", 1721 | "libc", 1722 | "redox_syscall", 1723 | "smallvec", 1724 | "windows-targets 0.52.5", 1725 | ] 1726 | 1727 | [[package]] 1728 | name = "paste" 1729 | version = "1.0.15" 1730 | source = "registry+https://github.com/rust-lang/crates.io-index" 1731 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1732 | 1733 | [[package]] 1734 | name = "percent-encoding" 1735 | version = "2.3.1" 1736 | source = "registry+https://github.com/rust-lang/crates.io-index" 1737 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1738 | 1739 | [[package]] 1740 | name = "pin-project" 1741 | version = "1.1.5" 1742 | source = "registry+https://github.com/rust-lang/crates.io-index" 1743 | checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 1744 | dependencies = [ 1745 | "pin-project-internal", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "pin-project-internal" 1750 | version = "1.1.5" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 1753 | dependencies = [ 1754 | "proc-macro2", 1755 | "quote", 1756 | "syn", 1757 | ] 1758 | 1759 | [[package]] 1760 | name = "pin-project-lite" 1761 | version = "0.2.14" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1764 | 1765 | [[package]] 1766 | name = "pin-utils" 1767 | version = "0.1.0" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1770 | 1771 | [[package]] 1772 | name = "pkg-config" 1773 | version = "0.3.30" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1776 | 1777 | [[package]] 1778 | name = "player" 1779 | version = "0.1.0" 1780 | dependencies = [ 1781 | "anyhow", 1782 | "bytes", 1783 | "futures", 1784 | "m3u8-rs", 1785 | "mockall", 1786 | "reqwest", 1787 | "rodio 0.17.3", 1788 | "tokio", 1789 | "tokio-test", 1790 | ] 1791 | 1792 | [[package]] 1793 | name = "polonius-the-crab" 1794 | version = "0.4.1" 1795 | source = "registry+https://github.com/rust-lang/crates.io-index" 1796 | checksum = "96d9b0ba45e2c294fcd0795bc51e45c12746758e9954bb1190f97603ec9a3e91" 1797 | dependencies = [ 1798 | "higher-kinded-types", 1799 | "never-say-never", 1800 | ] 1801 | 1802 | [[package]] 1803 | name = "powerfmt" 1804 | version = "0.2.0" 1805 | source = "registry+https://github.com/rust-lang/crates.io-index" 1806 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1807 | 1808 | [[package]] 1809 | name = "ppv-lite86" 1810 | version = "0.2.17" 1811 | source = "registry+https://github.com/rust-lang/crates.io-index" 1812 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1813 | 1814 | [[package]] 1815 | name = "predicates" 1816 | version = "3.1.0" 1817 | source = "registry+https://github.com/rust-lang/crates.io-index" 1818 | checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" 1819 | dependencies = [ 1820 | "anstyle", 1821 | "predicates-core", 1822 | ] 1823 | 1824 | [[package]] 1825 | name = "predicates-core" 1826 | version = "1.0.6" 1827 | source = "registry+https://github.com/rust-lang/crates.io-index" 1828 | checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" 1829 | 1830 | [[package]] 1831 | name = "predicates-tree" 1832 | version = "1.0.9" 1833 | source = "registry+https://github.com/rust-lang/crates.io-index" 1834 | checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" 1835 | dependencies = [ 1836 | "predicates-core", 1837 | "termtree", 1838 | ] 1839 | 1840 | [[package]] 1841 | name = "pretty_assertions" 1842 | version = "1.4.0" 1843 | source = "registry+https://github.com/rust-lang/crates.io-index" 1844 | checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" 1845 | dependencies = [ 1846 | "diff", 1847 | "yansi", 1848 | ] 1849 | 1850 | [[package]] 1851 | name = "proc-macro-crate" 1852 | version = "3.1.0" 1853 | source = "registry+https://github.com/rust-lang/crates.io-index" 1854 | checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" 1855 | dependencies = [ 1856 | "toml_edit", 1857 | ] 1858 | 1859 | [[package]] 1860 | name = "proc-macro2" 1861 | version = "1.0.81" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 1864 | dependencies = [ 1865 | "unicode-ident", 1866 | ] 1867 | 1868 | [[package]] 1869 | name = "quote" 1870 | version = "1.0.36" 1871 | source = "registry+https://github.com/rust-lang/crates.io-index" 1872 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 1873 | dependencies = [ 1874 | "proc-macro2", 1875 | ] 1876 | 1877 | [[package]] 1878 | name = "rand" 1879 | version = "0.8.5" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1882 | dependencies = [ 1883 | "libc", 1884 | "rand_chacha", 1885 | "rand_core", 1886 | ] 1887 | 1888 | [[package]] 1889 | name = "rand_chacha" 1890 | version = "0.3.1" 1891 | source = "registry+https://github.com/rust-lang/crates.io-index" 1892 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1893 | dependencies = [ 1894 | "ppv-lite86", 1895 | "rand_core", 1896 | ] 1897 | 1898 | [[package]] 1899 | name = "rand_core" 1900 | version = "0.6.4" 1901 | source = "registry+https://github.com/rust-lang/crates.io-index" 1902 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1903 | dependencies = [ 1904 | "getrandom", 1905 | ] 1906 | 1907 | [[package]] 1908 | name = "redox_syscall" 1909 | version = "0.5.1" 1910 | source = "registry+https://github.com/rust-lang/crates.io-index" 1911 | checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" 1912 | dependencies = [ 1913 | "bitflags 2.5.0", 1914 | ] 1915 | 1916 | [[package]] 1917 | name = "regex" 1918 | version = "1.10.4" 1919 | source = "registry+https://github.com/rust-lang/crates.io-index" 1920 | checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" 1921 | dependencies = [ 1922 | "aho-corasick", 1923 | "memchr", 1924 | "regex-automata", 1925 | "regex-syntax", 1926 | ] 1927 | 1928 | [[package]] 1929 | name = "regex-automata" 1930 | version = "0.4.6" 1931 | source = "registry+https://github.com/rust-lang/crates.io-index" 1932 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 1933 | dependencies = [ 1934 | "aho-corasick", 1935 | "memchr", 1936 | "regex-syntax", 1937 | ] 1938 | 1939 | [[package]] 1940 | name = "regex-lite" 1941 | version = "0.1.6" 1942 | source = "registry+https://github.com/rust-lang/crates.io-index" 1943 | checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" 1944 | 1945 | [[package]] 1946 | name = "regex-syntax" 1947 | version = "0.8.3" 1948 | source = "registry+https://github.com/rust-lang/crates.io-index" 1949 | checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" 1950 | 1951 | [[package]] 1952 | name = "render" 1953 | version = "0.1.0" 1954 | dependencies = [ 1955 | "anyhow", 1956 | "crossterm 0.27.0", 1957 | "tokio", 1958 | "tokio-test", 1959 | "tui", 1960 | "unimock", 1961 | ] 1962 | 1963 | [[package]] 1964 | name = "reqwest" 1965 | version = "0.12.4" 1966 | source = "registry+https://github.com/rust-lang/crates.io-index" 1967 | checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" 1968 | dependencies = [ 1969 | "base64", 1970 | "bytes", 1971 | "encoding_rs", 1972 | "futures-core", 1973 | "futures-util", 1974 | "h2 0.4.4", 1975 | "http 1.1.0", 1976 | "http-body 1.0.0", 1977 | "http-body-util", 1978 | "hyper 1.3.1", 1979 | "hyper-tls", 1980 | "hyper-util", 1981 | "ipnet", 1982 | "js-sys", 1983 | "log", 1984 | "mime", 1985 | "native-tls", 1986 | "once_cell", 1987 | "percent-encoding", 1988 | "pin-project-lite", 1989 | "rustls-pemfile", 1990 | "serde", 1991 | "serde_json", 1992 | "serde_urlencoded", 1993 | "sync_wrapper", 1994 | "system-configuration", 1995 | "tokio", 1996 | "tokio-native-tls", 1997 | "tower-service", 1998 | "url", 1999 | "wasm-bindgen", 2000 | "wasm-bindgen-futures", 2001 | "web-sys", 2002 | "winreg", 2003 | ] 2004 | 2005 | [[package]] 2006 | name = "rodio" 2007 | version = "0.17.3" 2008 | source = "registry+https://github.com/rust-lang/crates.io-index" 2009 | checksum = "3b1bb7b48ee48471f55da122c0044fcc7600cfcc85db88240b89cb832935e611" 2010 | dependencies = [ 2011 | "claxon", 2012 | "cpal", 2013 | "hound", 2014 | "lewton", 2015 | "symphonia", 2016 | ] 2017 | 2018 | [[package]] 2019 | name = "rodio" 2020 | version = "0.18.0" 2021 | source = "registry+https://github.com/rust-lang/crates.io-index" 2022 | checksum = "16e3ea3fb0b6a1837445e29e36971b101c154920791113ff860768a68a0b1cad" 2023 | dependencies = [ 2024 | "claxon", 2025 | "cpal", 2026 | "hound", 2027 | "lewton", 2028 | "symphonia", 2029 | "thiserror", 2030 | ] 2031 | 2032 | [[package]] 2033 | name = "rustc-demangle" 2034 | version = "0.1.23" 2035 | source = "registry+https://github.com/rust-lang/crates.io-index" 2036 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 2037 | 2038 | [[package]] 2039 | name = "rustc-hash" 2040 | version = "1.1.0" 2041 | source = "registry+https://github.com/rust-lang/crates.io-index" 2042 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 2043 | 2044 | [[package]] 2045 | name = "rustc_version" 2046 | version = "0.4.0" 2047 | source = "registry+https://github.com/rust-lang/crates.io-index" 2048 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 2049 | dependencies = [ 2050 | "semver", 2051 | ] 2052 | 2053 | [[package]] 2054 | name = "rustix" 2055 | version = "0.38.34" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 2058 | dependencies = [ 2059 | "bitflags 2.5.0", 2060 | "errno", 2061 | "libc", 2062 | "linux-raw-sys", 2063 | "windows-sys 0.52.0", 2064 | ] 2065 | 2066 | [[package]] 2067 | name = "rustls-pemfile" 2068 | version = "2.1.2" 2069 | source = "registry+https://github.com/rust-lang/crates.io-index" 2070 | checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" 2071 | dependencies = [ 2072 | "base64", 2073 | "rustls-pki-types", 2074 | ] 2075 | 2076 | [[package]] 2077 | name = "rustls-pki-types" 2078 | version = "1.5.0" 2079 | source = "registry+https://github.com/rust-lang/crates.io-index" 2080 | checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" 2081 | 2082 | [[package]] 2083 | name = "ryu" 2084 | version = "1.0.17" 2085 | source = "registry+https://github.com/rust-lang/crates.io-index" 2086 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 2087 | 2088 | [[package]] 2089 | name = "same-file" 2090 | version = "1.0.6" 2091 | source = "registry+https://github.com/rust-lang/crates.io-index" 2092 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 2093 | dependencies = [ 2094 | "winapi-util", 2095 | ] 2096 | 2097 | [[package]] 2098 | name = "schannel" 2099 | version = "0.1.23" 2100 | source = "registry+https://github.com/rust-lang/crates.io-index" 2101 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 2102 | dependencies = [ 2103 | "windows-sys 0.52.0", 2104 | ] 2105 | 2106 | [[package]] 2107 | name = "scopeguard" 2108 | version = "1.2.0" 2109 | source = "registry+https://github.com/rust-lang/crates.io-index" 2110 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 2111 | 2112 | [[package]] 2113 | name = "security-framework" 2114 | version = "2.10.0" 2115 | source = "registry+https://github.com/rust-lang/crates.io-index" 2116 | checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" 2117 | dependencies = [ 2118 | "bitflags 1.3.2", 2119 | "core-foundation", 2120 | "core-foundation-sys", 2121 | "libc", 2122 | "security-framework-sys", 2123 | ] 2124 | 2125 | [[package]] 2126 | name = "security-framework-sys" 2127 | version = "2.10.0" 2128 | source = "registry+https://github.com/rust-lang/crates.io-index" 2129 | checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" 2130 | dependencies = [ 2131 | "core-foundation-sys", 2132 | "libc", 2133 | ] 2134 | 2135 | [[package]] 2136 | name = "semver" 2137 | version = "1.0.23" 2138 | source = "registry+https://github.com/rust-lang/crates.io-index" 2139 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 2140 | 2141 | [[package]] 2142 | name = "serde" 2143 | version = "1.0.200" 2144 | source = "registry+https://github.com/rust-lang/crates.io-index" 2145 | checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" 2146 | dependencies = [ 2147 | "serde_derive", 2148 | ] 2149 | 2150 | [[package]] 2151 | name = "serde_derive" 2152 | version = "1.0.200" 2153 | source = "registry+https://github.com/rust-lang/crates.io-index" 2154 | checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" 2155 | dependencies = [ 2156 | "proc-macro2", 2157 | "quote", 2158 | "syn", 2159 | ] 2160 | 2161 | [[package]] 2162 | name = "serde_json" 2163 | version = "1.0.116" 2164 | source = "registry+https://github.com/rust-lang/crates.io-index" 2165 | checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" 2166 | dependencies = [ 2167 | "itoa", 2168 | "ryu", 2169 | "serde", 2170 | ] 2171 | 2172 | [[package]] 2173 | name = "serde_urlencoded" 2174 | version = "0.7.1" 2175 | source = "registry+https://github.com/rust-lang/crates.io-index" 2176 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 2177 | dependencies = [ 2178 | "form_urlencoded", 2179 | "itoa", 2180 | "ryu", 2181 | "serde", 2182 | ] 2183 | 2184 | [[package]] 2185 | name = "sha1" 2186 | version = "0.10.6" 2187 | source = "registry+https://github.com/rust-lang/crates.io-index" 2188 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 2189 | dependencies = [ 2190 | "cfg-if", 2191 | "cpufeatures", 2192 | "digest", 2193 | ] 2194 | 2195 | [[package]] 2196 | name = "shlex" 2197 | version = "1.3.0" 2198 | source = "registry+https://github.com/rust-lang/crates.io-index" 2199 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2200 | 2201 | [[package]] 2202 | name = "signal-hook" 2203 | version = "0.3.17" 2204 | source = "registry+https://github.com/rust-lang/crates.io-index" 2205 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 2206 | dependencies = [ 2207 | "libc", 2208 | "signal-hook-registry", 2209 | ] 2210 | 2211 | [[package]] 2212 | name = "signal-hook-mio" 2213 | version = "0.2.3" 2214 | source = "registry+https://github.com/rust-lang/crates.io-index" 2215 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 2216 | dependencies = [ 2217 | "libc", 2218 | "mio 0.8.11", 2219 | "signal-hook", 2220 | ] 2221 | 2222 | [[package]] 2223 | name = "signal-hook-registry" 2224 | version = "1.4.2" 2225 | source = "registry+https://github.com/rust-lang/crates.io-index" 2226 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 2227 | dependencies = [ 2228 | "libc", 2229 | ] 2230 | 2231 | [[package]] 2232 | name = "similar" 2233 | version = "2.5.0" 2234 | source = "registry+https://github.com/rust-lang/crates.io-index" 2235 | checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" 2236 | 2237 | [[package]] 2238 | name = "slab" 2239 | version = "0.4.9" 2240 | source = "registry+https://github.com/rust-lang/crates.io-index" 2241 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 2242 | dependencies = [ 2243 | "autocfg", 2244 | ] 2245 | 2246 | [[package]] 2247 | name = "smallvec" 2248 | version = "1.13.2" 2249 | source = "registry+https://github.com/rust-lang/crates.io-index" 2250 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 2251 | 2252 | [[package]] 2253 | name = "socket2" 2254 | version = "0.5.7" 2255 | source = "registry+https://github.com/rust-lang/crates.io-index" 2256 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 2257 | dependencies = [ 2258 | "libc", 2259 | "windows-sys 0.52.0", 2260 | ] 2261 | 2262 | [[package]] 2263 | name = "soundcloud-auth" 2264 | version = "0.1.0" 2265 | dependencies = [ 2266 | "actix-web", 2267 | "rand", 2268 | "tokio", 2269 | ] 2270 | 2271 | [[package]] 2272 | name = "soundcloud-rs" 2273 | version = "0.1.0" 2274 | dependencies = [ 2275 | "mockito", 2276 | "reqwest", 2277 | "serde_json", 2278 | "tokio", 2279 | "tokio-test", 2280 | "unimock", 2281 | ] 2282 | 2283 | [[package]] 2284 | name = "soundcloud-tui" 2285 | version = "0.1.0" 2286 | dependencies = [ 2287 | "anyhow", 2288 | "render", 2289 | "tokio", 2290 | ] 2291 | 2292 | [[package]] 2293 | name = "strsim" 2294 | version = "0.11.1" 2295 | source = "registry+https://github.com/rust-lang/crates.io-index" 2296 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 2297 | 2298 | [[package]] 2299 | name = "symphonia" 2300 | version = "0.5.4" 2301 | source = "registry+https://github.com/rust-lang/crates.io-index" 2302 | checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9" 2303 | dependencies = [ 2304 | "lazy_static", 2305 | "symphonia-bundle-mp3", 2306 | "symphonia-core", 2307 | "symphonia-metadata", 2308 | ] 2309 | 2310 | [[package]] 2311 | name = "symphonia-bundle-mp3" 2312 | version = "0.5.4" 2313 | source = "registry+https://github.com/rust-lang/crates.io-index" 2314 | checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4" 2315 | dependencies = [ 2316 | "lazy_static", 2317 | "log", 2318 | "symphonia-core", 2319 | "symphonia-metadata", 2320 | ] 2321 | 2322 | [[package]] 2323 | name = "symphonia-core" 2324 | version = "0.5.4" 2325 | source = "registry+https://github.com/rust-lang/crates.io-index" 2326 | checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3" 2327 | dependencies = [ 2328 | "arrayvec", 2329 | "bitflags 1.3.2", 2330 | "bytemuck", 2331 | "lazy_static", 2332 | "log", 2333 | ] 2334 | 2335 | [[package]] 2336 | name = "symphonia-metadata" 2337 | version = "0.5.4" 2338 | source = "registry+https://github.com/rust-lang/crates.io-index" 2339 | checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c" 2340 | dependencies = [ 2341 | "encoding_rs", 2342 | "lazy_static", 2343 | "log", 2344 | "symphonia-core", 2345 | ] 2346 | 2347 | [[package]] 2348 | name = "syn" 2349 | version = "2.0.60" 2350 | source = "registry+https://github.com/rust-lang/crates.io-index" 2351 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 2352 | dependencies = [ 2353 | "proc-macro2", 2354 | "quote", 2355 | "unicode-ident", 2356 | ] 2357 | 2358 | [[package]] 2359 | name = "sync_wrapper" 2360 | version = "0.1.2" 2361 | source = "registry+https://github.com/rust-lang/crates.io-index" 2362 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 2363 | 2364 | [[package]] 2365 | name = "system-configuration" 2366 | version = "0.5.1" 2367 | source = "registry+https://github.com/rust-lang/crates.io-index" 2368 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 2369 | dependencies = [ 2370 | "bitflags 1.3.2", 2371 | "core-foundation", 2372 | "system-configuration-sys", 2373 | ] 2374 | 2375 | [[package]] 2376 | name = "system-configuration-sys" 2377 | version = "0.5.0" 2378 | source = "registry+https://github.com/rust-lang/crates.io-index" 2379 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 2380 | dependencies = [ 2381 | "core-foundation-sys", 2382 | "libc", 2383 | ] 2384 | 2385 | [[package]] 2386 | name = "tempfile" 2387 | version = "3.10.1" 2388 | source = "registry+https://github.com/rust-lang/crates.io-index" 2389 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 2390 | dependencies = [ 2391 | "cfg-if", 2392 | "fastrand", 2393 | "rustix", 2394 | "windows-sys 0.52.0", 2395 | ] 2396 | 2397 | [[package]] 2398 | name = "termtree" 2399 | version = "0.4.1" 2400 | source = "registry+https://github.com/rust-lang/crates.io-index" 2401 | checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" 2402 | 2403 | [[package]] 2404 | name = "thiserror" 2405 | version = "1.0.59" 2406 | source = "registry+https://github.com/rust-lang/crates.io-index" 2407 | checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" 2408 | dependencies = [ 2409 | "thiserror-impl", 2410 | ] 2411 | 2412 | [[package]] 2413 | name = "thiserror-impl" 2414 | version = "1.0.59" 2415 | source = "registry+https://github.com/rust-lang/crates.io-index" 2416 | checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" 2417 | dependencies = [ 2418 | "proc-macro2", 2419 | "quote", 2420 | "syn", 2421 | ] 2422 | 2423 | [[package]] 2424 | name = "time" 2425 | version = "0.3.36" 2426 | source = "registry+https://github.com/rust-lang/crates.io-index" 2427 | checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 2428 | dependencies = [ 2429 | "deranged", 2430 | "itoa", 2431 | "num-conv", 2432 | "powerfmt", 2433 | "serde", 2434 | "time-core", 2435 | "time-macros", 2436 | ] 2437 | 2438 | [[package]] 2439 | name = "time-core" 2440 | version = "0.1.2" 2441 | source = "registry+https://github.com/rust-lang/crates.io-index" 2442 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 2443 | 2444 | [[package]] 2445 | name = "time-macros" 2446 | version = "0.2.18" 2447 | source = "registry+https://github.com/rust-lang/crates.io-index" 2448 | checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 2449 | dependencies = [ 2450 | "num-conv", 2451 | "time-core", 2452 | ] 2453 | 2454 | [[package]] 2455 | name = "tinyvec" 2456 | version = "1.6.0" 2457 | source = "registry+https://github.com/rust-lang/crates.io-index" 2458 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 2459 | dependencies = [ 2460 | "tinyvec_macros", 2461 | ] 2462 | 2463 | [[package]] 2464 | name = "tinyvec_macros" 2465 | version = "0.1.1" 2466 | source = "registry+https://github.com/rust-lang/crates.io-index" 2467 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2468 | 2469 | [[package]] 2470 | name = "tokio" 2471 | version = "1.39.2" 2472 | source = "registry+https://github.com/rust-lang/crates.io-index" 2473 | checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" 2474 | dependencies = [ 2475 | "backtrace", 2476 | "bytes", 2477 | "libc", 2478 | "mio 1.0.1", 2479 | "parking_lot", 2480 | "pin-project-lite", 2481 | "signal-hook-registry", 2482 | "socket2", 2483 | "tokio-macros", 2484 | "windows-sys 0.52.0", 2485 | ] 2486 | 2487 | [[package]] 2488 | name = "tokio-macros" 2489 | version = "2.4.0" 2490 | source = "registry+https://github.com/rust-lang/crates.io-index" 2491 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 2492 | dependencies = [ 2493 | "proc-macro2", 2494 | "quote", 2495 | "syn", 2496 | ] 2497 | 2498 | [[package]] 2499 | name = "tokio-native-tls" 2500 | version = "0.3.1" 2501 | source = "registry+https://github.com/rust-lang/crates.io-index" 2502 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 2503 | dependencies = [ 2504 | "native-tls", 2505 | "tokio", 2506 | ] 2507 | 2508 | [[package]] 2509 | name = "tokio-stream" 2510 | version = "0.1.15" 2511 | source = "registry+https://github.com/rust-lang/crates.io-index" 2512 | checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" 2513 | dependencies = [ 2514 | "futures-core", 2515 | "pin-project-lite", 2516 | "tokio", 2517 | ] 2518 | 2519 | [[package]] 2520 | name = "tokio-test" 2521 | version = "0.4.4" 2522 | source = "registry+https://github.com/rust-lang/crates.io-index" 2523 | checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" 2524 | dependencies = [ 2525 | "async-stream", 2526 | "bytes", 2527 | "futures-core", 2528 | "tokio", 2529 | "tokio-stream", 2530 | ] 2531 | 2532 | [[package]] 2533 | name = "tokio-util" 2534 | version = "0.7.10" 2535 | source = "registry+https://github.com/rust-lang/crates.io-index" 2536 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" 2537 | dependencies = [ 2538 | "bytes", 2539 | "futures-core", 2540 | "futures-sink", 2541 | "pin-project-lite", 2542 | "tokio", 2543 | "tracing", 2544 | ] 2545 | 2546 | [[package]] 2547 | name = "toml_datetime" 2548 | version = "0.6.5" 2549 | source = "registry+https://github.com/rust-lang/crates.io-index" 2550 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 2551 | 2552 | [[package]] 2553 | name = "toml_edit" 2554 | version = "0.21.1" 2555 | source = "registry+https://github.com/rust-lang/crates.io-index" 2556 | checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" 2557 | dependencies = [ 2558 | "indexmap", 2559 | "toml_datetime", 2560 | "winnow", 2561 | ] 2562 | 2563 | [[package]] 2564 | name = "tower" 2565 | version = "0.4.13" 2566 | source = "registry+https://github.com/rust-lang/crates.io-index" 2567 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 2568 | dependencies = [ 2569 | "futures-core", 2570 | "futures-util", 2571 | "pin-project", 2572 | "pin-project-lite", 2573 | "tokio", 2574 | "tower-layer", 2575 | "tower-service", 2576 | "tracing", 2577 | ] 2578 | 2579 | [[package]] 2580 | name = "tower-layer" 2581 | version = "0.3.2" 2582 | source = "registry+https://github.com/rust-lang/crates.io-index" 2583 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 2584 | 2585 | [[package]] 2586 | name = "tower-service" 2587 | version = "0.3.2" 2588 | source = "registry+https://github.com/rust-lang/crates.io-index" 2589 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 2590 | 2591 | [[package]] 2592 | name = "tracing" 2593 | version = "0.1.40" 2594 | source = "registry+https://github.com/rust-lang/crates.io-index" 2595 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2596 | dependencies = [ 2597 | "log", 2598 | "pin-project-lite", 2599 | "tracing-core", 2600 | ] 2601 | 2602 | [[package]] 2603 | name = "tracing-core" 2604 | version = "0.1.32" 2605 | source = "registry+https://github.com/rust-lang/crates.io-index" 2606 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2607 | dependencies = [ 2608 | "once_cell", 2609 | ] 2610 | 2611 | [[package]] 2612 | name = "try-lock" 2613 | version = "0.2.5" 2614 | source = "registry+https://github.com/rust-lang/crates.io-index" 2615 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2616 | 2617 | [[package]] 2618 | name = "tui" 2619 | version = "0.19.0" 2620 | source = "registry+https://github.com/rust-lang/crates.io-index" 2621 | checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" 2622 | dependencies = [ 2623 | "bitflags 1.3.2", 2624 | "cassowary", 2625 | "crossterm 0.25.0", 2626 | "unicode-segmentation", 2627 | "unicode-width", 2628 | ] 2629 | 2630 | [[package]] 2631 | name = "typenum" 2632 | version = "1.17.0" 2633 | source = "registry+https://github.com/rust-lang/crates.io-index" 2634 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2635 | 2636 | [[package]] 2637 | name = "unicode-bidi" 2638 | version = "0.3.15" 2639 | source = "registry+https://github.com/rust-lang/crates.io-index" 2640 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 2641 | 2642 | [[package]] 2643 | name = "unicode-ident" 2644 | version = "1.0.12" 2645 | source = "registry+https://github.com/rust-lang/crates.io-index" 2646 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 2647 | 2648 | [[package]] 2649 | name = "unicode-normalization" 2650 | version = "0.1.23" 2651 | source = "registry+https://github.com/rust-lang/crates.io-index" 2652 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 2653 | dependencies = [ 2654 | "tinyvec", 2655 | ] 2656 | 2657 | [[package]] 2658 | name = "unicode-segmentation" 2659 | version = "1.11.0" 2660 | source = "registry+https://github.com/rust-lang/crates.io-index" 2661 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 2662 | 2663 | [[package]] 2664 | name = "unicode-width" 2665 | version = "0.1.12" 2666 | source = "registry+https://github.com/rust-lang/crates.io-index" 2667 | checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" 2668 | 2669 | [[package]] 2670 | name = "unimock" 2671 | version = "0.6.5" 2672 | source = "registry+https://github.com/rust-lang/crates.io-index" 2673 | checksum = "a7e9c7c18e058dcbce1e9cf51cf35c44664695d9c3d5cdf419399d05af23f8cd" 2674 | dependencies = [ 2675 | "once_cell", 2676 | "polonius-the-crab", 2677 | "pretty_assertions", 2678 | "unimock_macros", 2679 | ] 2680 | 2681 | [[package]] 2682 | name = "unimock_macros" 2683 | version = "0.6.5" 2684 | source = "registry+https://github.com/rust-lang/crates.io-index" 2685 | checksum = "d225937848bc8c3cf3b18353b7af75151846eb011eab02f259df164327f76863" 2686 | dependencies = [ 2687 | "proc-macro2", 2688 | "quote", 2689 | "syn", 2690 | ] 2691 | 2692 | [[package]] 2693 | name = "url" 2694 | version = "2.5.0" 2695 | source = "registry+https://github.com/rust-lang/crates.io-index" 2696 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" 2697 | dependencies = [ 2698 | "form_urlencoded", 2699 | "idna", 2700 | "percent-encoding", 2701 | ] 2702 | 2703 | [[package]] 2704 | name = "utf8parse" 2705 | version = "0.2.1" 2706 | source = "registry+https://github.com/rust-lang/crates.io-index" 2707 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 2708 | 2709 | [[package]] 2710 | name = "vcpkg" 2711 | version = "0.2.15" 2712 | source = "registry+https://github.com/rust-lang/crates.io-index" 2713 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2714 | 2715 | [[package]] 2716 | name = "version_check" 2717 | version = "0.9.5" 2718 | source = "registry+https://github.com/rust-lang/crates.io-index" 2719 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2720 | 2721 | [[package]] 2722 | name = "walkdir" 2723 | version = "2.5.0" 2724 | source = "registry+https://github.com/rust-lang/crates.io-index" 2725 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 2726 | dependencies = [ 2727 | "same-file", 2728 | "winapi-util", 2729 | ] 2730 | 2731 | [[package]] 2732 | name = "want" 2733 | version = "0.3.1" 2734 | source = "registry+https://github.com/rust-lang/crates.io-index" 2735 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2736 | dependencies = [ 2737 | "try-lock", 2738 | ] 2739 | 2740 | [[package]] 2741 | name = "wasi" 2742 | version = "0.11.0+wasi-snapshot-preview1" 2743 | source = "registry+https://github.com/rust-lang/crates.io-index" 2744 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2745 | 2746 | [[package]] 2747 | name = "wasm-bindgen" 2748 | version = "0.2.92" 2749 | source = "registry+https://github.com/rust-lang/crates.io-index" 2750 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 2751 | dependencies = [ 2752 | "cfg-if", 2753 | "wasm-bindgen-macro", 2754 | ] 2755 | 2756 | [[package]] 2757 | name = "wasm-bindgen-backend" 2758 | version = "0.2.92" 2759 | source = "registry+https://github.com/rust-lang/crates.io-index" 2760 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 2761 | dependencies = [ 2762 | "bumpalo", 2763 | "log", 2764 | "once_cell", 2765 | "proc-macro2", 2766 | "quote", 2767 | "syn", 2768 | "wasm-bindgen-shared", 2769 | ] 2770 | 2771 | [[package]] 2772 | name = "wasm-bindgen-futures" 2773 | version = "0.4.42" 2774 | source = "registry+https://github.com/rust-lang/crates.io-index" 2775 | checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" 2776 | dependencies = [ 2777 | "cfg-if", 2778 | "js-sys", 2779 | "wasm-bindgen", 2780 | "web-sys", 2781 | ] 2782 | 2783 | [[package]] 2784 | name = "wasm-bindgen-macro" 2785 | version = "0.2.92" 2786 | source = "registry+https://github.com/rust-lang/crates.io-index" 2787 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 2788 | dependencies = [ 2789 | "quote", 2790 | "wasm-bindgen-macro-support", 2791 | ] 2792 | 2793 | [[package]] 2794 | name = "wasm-bindgen-macro-support" 2795 | version = "0.2.92" 2796 | source = "registry+https://github.com/rust-lang/crates.io-index" 2797 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 2798 | dependencies = [ 2799 | "proc-macro2", 2800 | "quote", 2801 | "syn", 2802 | "wasm-bindgen-backend", 2803 | "wasm-bindgen-shared", 2804 | ] 2805 | 2806 | [[package]] 2807 | name = "wasm-bindgen-shared" 2808 | version = "0.2.92" 2809 | source = "registry+https://github.com/rust-lang/crates.io-index" 2810 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 2811 | 2812 | [[package]] 2813 | name = "web-sys" 2814 | version = "0.3.69" 2815 | source = "registry+https://github.com/rust-lang/crates.io-index" 2816 | checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 2817 | dependencies = [ 2818 | "js-sys", 2819 | "wasm-bindgen", 2820 | ] 2821 | 2822 | [[package]] 2823 | name = "winapi" 2824 | version = "0.3.9" 2825 | source = "registry+https://github.com/rust-lang/crates.io-index" 2826 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2827 | dependencies = [ 2828 | "winapi-i686-pc-windows-gnu", 2829 | "winapi-x86_64-pc-windows-gnu", 2830 | ] 2831 | 2832 | [[package]] 2833 | name = "winapi-i686-pc-windows-gnu" 2834 | version = "0.4.0" 2835 | source = "registry+https://github.com/rust-lang/crates.io-index" 2836 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2837 | 2838 | [[package]] 2839 | name = "winapi-util" 2840 | version = "0.1.8" 2841 | source = "registry+https://github.com/rust-lang/crates.io-index" 2842 | checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" 2843 | dependencies = [ 2844 | "windows-sys 0.52.0", 2845 | ] 2846 | 2847 | [[package]] 2848 | name = "winapi-x86_64-pc-windows-gnu" 2849 | version = "0.4.0" 2850 | source = "registry+https://github.com/rust-lang/crates.io-index" 2851 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2852 | 2853 | [[package]] 2854 | name = "windows" 2855 | version = "0.54.0" 2856 | source = "registry+https://github.com/rust-lang/crates.io-index" 2857 | checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" 2858 | dependencies = [ 2859 | "windows-core", 2860 | "windows-targets 0.52.5", 2861 | ] 2862 | 2863 | [[package]] 2864 | name = "windows-core" 2865 | version = "0.54.0" 2866 | source = "registry+https://github.com/rust-lang/crates.io-index" 2867 | checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" 2868 | dependencies = [ 2869 | "windows-result", 2870 | "windows-targets 0.52.5", 2871 | ] 2872 | 2873 | [[package]] 2874 | name = "windows-result" 2875 | version = "0.1.1" 2876 | source = "registry+https://github.com/rust-lang/crates.io-index" 2877 | checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" 2878 | dependencies = [ 2879 | "windows-targets 0.52.5", 2880 | ] 2881 | 2882 | [[package]] 2883 | name = "windows-sys" 2884 | version = "0.45.0" 2885 | source = "registry+https://github.com/rust-lang/crates.io-index" 2886 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 2887 | dependencies = [ 2888 | "windows-targets 0.42.2", 2889 | ] 2890 | 2891 | [[package]] 2892 | name = "windows-sys" 2893 | version = "0.48.0" 2894 | source = "registry+https://github.com/rust-lang/crates.io-index" 2895 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2896 | dependencies = [ 2897 | "windows-targets 0.48.5", 2898 | ] 2899 | 2900 | [[package]] 2901 | name = "windows-sys" 2902 | version = "0.52.0" 2903 | source = "registry+https://github.com/rust-lang/crates.io-index" 2904 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2905 | dependencies = [ 2906 | "windows-targets 0.52.5", 2907 | ] 2908 | 2909 | [[package]] 2910 | name = "windows-targets" 2911 | version = "0.42.2" 2912 | source = "registry+https://github.com/rust-lang/crates.io-index" 2913 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 2914 | dependencies = [ 2915 | "windows_aarch64_gnullvm 0.42.2", 2916 | "windows_aarch64_msvc 0.42.2", 2917 | "windows_i686_gnu 0.42.2", 2918 | "windows_i686_msvc 0.42.2", 2919 | "windows_x86_64_gnu 0.42.2", 2920 | "windows_x86_64_gnullvm 0.42.2", 2921 | "windows_x86_64_msvc 0.42.2", 2922 | ] 2923 | 2924 | [[package]] 2925 | name = "windows-targets" 2926 | version = "0.48.5" 2927 | source = "registry+https://github.com/rust-lang/crates.io-index" 2928 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2929 | dependencies = [ 2930 | "windows_aarch64_gnullvm 0.48.5", 2931 | "windows_aarch64_msvc 0.48.5", 2932 | "windows_i686_gnu 0.48.5", 2933 | "windows_i686_msvc 0.48.5", 2934 | "windows_x86_64_gnu 0.48.5", 2935 | "windows_x86_64_gnullvm 0.48.5", 2936 | "windows_x86_64_msvc 0.48.5", 2937 | ] 2938 | 2939 | [[package]] 2940 | name = "windows-targets" 2941 | version = "0.52.5" 2942 | source = "registry+https://github.com/rust-lang/crates.io-index" 2943 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 2944 | dependencies = [ 2945 | "windows_aarch64_gnullvm 0.52.5", 2946 | "windows_aarch64_msvc 0.52.5", 2947 | "windows_i686_gnu 0.52.5", 2948 | "windows_i686_gnullvm", 2949 | "windows_i686_msvc 0.52.5", 2950 | "windows_x86_64_gnu 0.52.5", 2951 | "windows_x86_64_gnullvm 0.52.5", 2952 | "windows_x86_64_msvc 0.52.5", 2953 | ] 2954 | 2955 | [[package]] 2956 | name = "windows_aarch64_gnullvm" 2957 | version = "0.42.2" 2958 | source = "registry+https://github.com/rust-lang/crates.io-index" 2959 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 2960 | 2961 | [[package]] 2962 | name = "windows_aarch64_gnullvm" 2963 | version = "0.48.5" 2964 | source = "registry+https://github.com/rust-lang/crates.io-index" 2965 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2966 | 2967 | [[package]] 2968 | name = "windows_aarch64_gnullvm" 2969 | version = "0.52.5" 2970 | source = "registry+https://github.com/rust-lang/crates.io-index" 2971 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 2972 | 2973 | [[package]] 2974 | name = "windows_aarch64_msvc" 2975 | version = "0.42.2" 2976 | source = "registry+https://github.com/rust-lang/crates.io-index" 2977 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 2978 | 2979 | [[package]] 2980 | name = "windows_aarch64_msvc" 2981 | version = "0.48.5" 2982 | source = "registry+https://github.com/rust-lang/crates.io-index" 2983 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2984 | 2985 | [[package]] 2986 | name = "windows_aarch64_msvc" 2987 | version = "0.52.5" 2988 | source = "registry+https://github.com/rust-lang/crates.io-index" 2989 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 2990 | 2991 | [[package]] 2992 | name = "windows_i686_gnu" 2993 | version = "0.42.2" 2994 | source = "registry+https://github.com/rust-lang/crates.io-index" 2995 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 2996 | 2997 | [[package]] 2998 | name = "windows_i686_gnu" 2999 | version = "0.48.5" 3000 | source = "registry+https://github.com/rust-lang/crates.io-index" 3001 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 3002 | 3003 | [[package]] 3004 | name = "windows_i686_gnu" 3005 | version = "0.52.5" 3006 | source = "registry+https://github.com/rust-lang/crates.io-index" 3007 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 3008 | 3009 | [[package]] 3010 | name = "windows_i686_gnullvm" 3011 | version = "0.52.5" 3012 | source = "registry+https://github.com/rust-lang/crates.io-index" 3013 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 3014 | 3015 | [[package]] 3016 | name = "windows_i686_msvc" 3017 | version = "0.42.2" 3018 | source = "registry+https://github.com/rust-lang/crates.io-index" 3019 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 3020 | 3021 | [[package]] 3022 | name = "windows_i686_msvc" 3023 | version = "0.48.5" 3024 | source = "registry+https://github.com/rust-lang/crates.io-index" 3025 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 3026 | 3027 | [[package]] 3028 | name = "windows_i686_msvc" 3029 | version = "0.52.5" 3030 | source = "registry+https://github.com/rust-lang/crates.io-index" 3031 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 3032 | 3033 | [[package]] 3034 | name = "windows_x86_64_gnu" 3035 | version = "0.42.2" 3036 | source = "registry+https://github.com/rust-lang/crates.io-index" 3037 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 3038 | 3039 | [[package]] 3040 | name = "windows_x86_64_gnu" 3041 | version = "0.48.5" 3042 | source = "registry+https://github.com/rust-lang/crates.io-index" 3043 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 3044 | 3045 | [[package]] 3046 | name = "windows_x86_64_gnu" 3047 | version = "0.52.5" 3048 | source = "registry+https://github.com/rust-lang/crates.io-index" 3049 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 3050 | 3051 | [[package]] 3052 | name = "windows_x86_64_gnullvm" 3053 | version = "0.42.2" 3054 | source = "registry+https://github.com/rust-lang/crates.io-index" 3055 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 3056 | 3057 | [[package]] 3058 | name = "windows_x86_64_gnullvm" 3059 | version = "0.48.5" 3060 | source = "registry+https://github.com/rust-lang/crates.io-index" 3061 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 3062 | 3063 | [[package]] 3064 | name = "windows_x86_64_gnullvm" 3065 | version = "0.52.5" 3066 | source = "registry+https://github.com/rust-lang/crates.io-index" 3067 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 3068 | 3069 | [[package]] 3070 | name = "windows_x86_64_msvc" 3071 | version = "0.42.2" 3072 | source = "registry+https://github.com/rust-lang/crates.io-index" 3073 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 3074 | 3075 | [[package]] 3076 | name = "windows_x86_64_msvc" 3077 | version = "0.48.5" 3078 | source = "registry+https://github.com/rust-lang/crates.io-index" 3079 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 3080 | 3081 | [[package]] 3082 | name = "windows_x86_64_msvc" 3083 | version = "0.52.5" 3084 | source = "registry+https://github.com/rust-lang/crates.io-index" 3085 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 3086 | 3087 | [[package]] 3088 | name = "winnow" 3089 | version = "0.5.40" 3090 | source = "registry+https://github.com/rust-lang/crates.io-index" 3091 | checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" 3092 | dependencies = [ 3093 | "memchr", 3094 | ] 3095 | 3096 | [[package]] 3097 | name = "winreg" 3098 | version = "0.52.0" 3099 | source = "registry+https://github.com/rust-lang/crates.io-index" 3100 | checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" 3101 | dependencies = [ 3102 | "cfg-if", 3103 | "windows-sys 0.48.0", 3104 | ] 3105 | 3106 | [[package]] 3107 | name = "yansi" 3108 | version = "0.5.1" 3109 | source = "registry+https://github.com/rust-lang/crates.io-index" 3110 | checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" 3111 | 3112 | [[package]] 3113 | name = "zerocopy" 3114 | version = "0.7.35" 3115 | source = "registry+https://github.com/rust-lang/crates.io-index" 3116 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 3117 | dependencies = [ 3118 | "zerocopy-derive", 3119 | ] 3120 | 3121 | [[package]] 3122 | name = "zerocopy-derive" 3123 | version = "0.7.35" 3124 | source = "registry+https://github.com/rust-lang/crates.io-index" 3125 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 3126 | dependencies = [ 3127 | "proc-macro2", 3128 | "quote", 3129 | "syn", 3130 | ] 3131 | 3132 | [[package]] 3133 | name = "zstd" 3134 | version = "0.13.2" 3135 | source = "registry+https://github.com/rust-lang/crates.io-index" 3136 | checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" 3137 | dependencies = [ 3138 | "zstd-safe", 3139 | ] 3140 | 3141 | [[package]] 3142 | name = "zstd-safe" 3143 | version = "7.2.1" 3144 | source = "registry+https://github.com/rust-lang/crates.io-index" 3145 | checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" 3146 | dependencies = [ 3147 | "zstd-sys", 3148 | ] 3149 | 3150 | [[package]] 3151 | name = "zstd-sys" 3152 | version = "2.0.13+zstd.1.5.6" 3153 | source = "registry+https://github.com/rust-lang/crates.io-index" 3154 | checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" 3155 | dependencies = [ 3156 | "cc", 3157 | "pkg-config", 3158 | ] 3159 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ "application/auth-server", 3 | "application/ffplay", 4 | "application/soundcloud-tui", 5 | "library/player", 6 | "library/render", 7 | "library/soundcloud-rs", 8 | ] 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Soundcloud TUI 2 | 3 | Terminal UI (_tui_) client that allows you to interact with your soundcloud playlists directly from within your terminal. 4 | 5 | ## Solution Diagram 6 | 7 | ![Solution Diagram](./docs/assets/solution-diagram.png) 8 | 9 | ## Roadmap 10 | 11 | 1. HLS audio stream processing 12 | 2. Soundcloud authentication flow 13 | 3. Soundcloud api-client expansion 14 | 4. MVP UI 15 | -------------------------------------------------------------------------------- /application/auth-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "soundcloud-auth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | actix-web = "4.8.0" 10 | tokio = { version = "1.39.2", features = ["full"] } 11 | 12 | [dev-dependencies] 13 | rand = "0.8.5" 14 | -------------------------------------------------------------------------------- /application/auth-server/README.md: -------------------------------------------------------------------------------- 1 | # Soundcloud OAuth2 Flow Server 2 | 3 | -------------------------------------------------------------------------------- /application/auth-server/src/main.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{App, HttpRequest, HttpResponse, HttpServer}; 2 | use tokio; 3 | 4 | #[tokio::main] 5 | async fn main() -> std::io::Result<()> { 6 | HttpServer::new(|| App::new().service(get_health).service(get_oauth2_redirect)) 7 | .bind("127.0.0.1:8080")? 8 | .run() 9 | .await 10 | } 11 | 12 | #[actix_web::get("/health")] 13 | async fn get_health(_req: HttpRequest) -> &'static str { 14 | "ok!" 15 | } 16 | 17 | #[actix_web::get("/oauth2/redirect")] 18 | async fn get_oauth2_redirect(req: HttpRequest) -> HttpResponse { 19 | let uri_query_string = req.query_string(); 20 | 21 | let params_map = match parse_query_string(uri_query_string) { 22 | Ok(params_map) => params_map, 23 | Err(err) => { 24 | eprintln!("Error parsing query string: {}", err); 25 | return HttpResponse::BadRequest().finish(); 26 | } 27 | }; 28 | if let Some(code) = params_map.get("code") { 29 | println!("code: {}", code); 30 | } else { 31 | return HttpResponse::BadRequest().finish(); 32 | } 33 | 34 | HttpResponse::Ok().finish() 35 | } 36 | 37 | fn parse_query_string( 38 | query_string: &str, 39 | ) -> Result, String> { 40 | query_string 41 | .split('&') 42 | .map(|pair| { 43 | let split: Vec<&str> = pair.split('=').filter(|s| s.len() > 0).collect(); 44 | if split.len() == 0 { 45 | return Ok((None, None)); 46 | } else if split.len() != 2 { 47 | return Err(format!("Invalid query string param ({})", pair)); 48 | } 49 | 50 | Ok(( 51 | split.get(0).and_then(|v| Some(v.to_string())), 52 | split.get(1).and_then(|v| Some(v.to_string())), 53 | )) 54 | }) 55 | .filter_map(|pair| match pair { 56 | Ok((Some(key), Some(value))) => Some(Ok((key, value))), 57 | Ok((None, _)) => None, 58 | Err(err) => Some(Err(err)), 59 | _ => Some(Err(format!("No key-value pair found"))), 60 | }) 61 | .collect() 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use super::*; 67 | use rand; 68 | 69 | #[test] 70 | fn test_query_string_singular() { 71 | let mock_value: String = (1..11).map(|_| rand::random::()).collect(); 72 | let query_string = format!("mockKey={}", mock_value); 73 | let params_map = parse_query_string(&query_string); 74 | assert!(params_map.is_ok()); 75 | assert_eq!(params_map.unwrap().get("mockKey"), Some(&mock_value)); 76 | } 77 | 78 | #[test] 79 | fn test_query_string_multiple() { 80 | let mock_value: String = (1..11).map(|_| rand::random::()).collect(); 81 | let mock_key: String = (1..11).map(|_| rand::random::()).collect(); 82 | 83 | let query_string = format!( 84 | "code_verifier=deadbeef&{}={}&id=test_id", 85 | mock_key, mock_value 86 | ); 87 | let params_map_result = parse_query_string(&query_string); 88 | assert!(params_map_result.is_ok()); 89 | let params_map = params_map_result.unwrap(); 90 | assert_eq!(params_map.get(&mock_key), Some(&mock_value)); 91 | assert_eq!(params_map.len(), 3); 92 | } 93 | 94 | #[test] 95 | fn test_query_string_empty() { 96 | let params_map = parse_query_string(&""); 97 | assert!( 98 | params_map.is_ok(), 99 | "params map error {}", 100 | params_map.err().unwrap() 101 | ); 102 | assert_eq!(params_map.unwrap().len(), 0); 103 | } 104 | 105 | #[actix_web::test] 106 | async fn test_health_service() { 107 | let app = actix_web::test::init_service(App::new().service(get_health)).await; 108 | let req = actix_web::test::TestRequest::get() 109 | .uri("/health") 110 | .to_request(); 111 | let resp = actix_web::test::call_service(&app, req).await; 112 | assert!(resp.status().is_success()); 113 | let resp_body = actix_web::body::to_bytes(resp.into_body()).await; 114 | assert!(resp_body.is_ok()); 115 | assert_eq!(resp_body.unwrap(), actix_web::web::Bytes::from("ok!")); 116 | } 117 | 118 | #[actix_web::test] 119 | async fn test_oauth2_redirect_service_ok() { 120 | let app = actix_web::test::init_service(App::new().service(get_oauth2_redirect)).await; 121 | let req = actix_web::test::TestRequest::get() 122 | .uri("/oauth2/redirect?code=oauth2token&code_verifier=pkce_verifier") 123 | .to_request(); 124 | let resp = actix_web::test::call_service(&app, req).await; 125 | assert!(resp.status().is_success()); 126 | } 127 | 128 | #[actix_web::test] 129 | async fn test_oauth2_redirect_service_no_code() { 130 | let app = actix_web::test::init_service(App::new().service(get_oauth2_redirect)).await; 131 | let req = actix_web::test::TestRequest::get() 132 | .uri("/oauth2/redirect?code_verifier=pkce_verifier") 133 | .to_request(); 134 | let resp = actix_web::test::call_service(&app, req).await; 135 | assert!(resp.status().is_client_error()); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /application/ffplay/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ffplay" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.82" 10 | clap = "4.5.4" 11 | player = { version = "0.1.0", path = "../../library/player" } 12 | rodio = "0.18.0" 13 | tokio = { version = "1.37.0", features = ["full"] } 14 | -------------------------------------------------------------------------------- /application/ffplay/README.md: -------------------------------------------------------------------------------- 1 | # FFPlay 2 | 3 | An ffplay alternative used for testing the underlying music player. -------------------------------------------------------------------------------- /application/ffplay/src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::{arg, value_parser, Command}; 2 | 3 | pub fn args_parser() -> Command { 4 | Command::new("ffplay") 5 | .version("0.1") 6 | .author("Clayton Herbst ") 7 | .about("A simple audio player using the underlying HLS client library.") 8 | .arg( 9 | arg!(input: -i --input "Sets the HLS source URI") 10 | .value_parser(clap::builder::NonEmptyStringValueParser::new()) 11 | .required(true), 12 | ) 13 | .arg( 14 | arg!(verbose: -v [LEVEL] "Sets the level of verbosity") 15 | .value_parser(value_parser!(u8).range(0..=3)) 16 | .default_value("0"), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /application/ffplay/src/cli_test.rs: -------------------------------------------------------------------------------- 1 | use crate::cli::*; 2 | 3 | #[test] 4 | fn test_cli_uri() { 5 | let exp_uri = "http://example.com"; 6 | let cli_matches = args_parser().get_matches_from(vec!["ffplay", "-i", exp_uri]); 7 | let uri = cli_matches.get_one::("input").unwrap(); 8 | assert_eq!(uri, exp_uri); 9 | } 10 | 11 | #[test] 12 | fn test_cli_uri_empty_failure() { 13 | let parse_err = args_parser() 14 | .try_get_matches_from(vec!["ffplay", "-i", ""]) 15 | .unwrap_err(); 16 | assert_eq!(parse_err.kind(), clap::error::ErrorKind::InvalidValue); 17 | assert!(parse_err 18 | .to_string() 19 | .contains("value is required for '--input ' but none was supplied")); 20 | } 21 | 22 | #[test] 23 | fn test_cli_verbosity_outofrange() { 24 | let parse_err = args_parser() 25 | .try_get_matches_from(vec!["ffplay", "-i", "https://example.com", "-v", "4"]) 26 | .unwrap_err(); 27 | assert_eq!(parse_err.kind(), clap::error::ErrorKind::ValueValidation); 28 | assert!( 29 | parse_err 30 | .to_string() 31 | .contains("invalid value '4' for '-v []'"), 32 | "received error message: {}", 33 | parse_err.to_string(), 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /application/ffplay/src/main.rs: -------------------------------------------------------------------------------- 1 | mod cli; 2 | #[cfg(test)] 3 | mod cli_test; 4 | 5 | use anyhow::Result; 6 | use rodio::Sink; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<()> { 10 | let cli_matches = cli::args_parser().get_matches(); 11 | 12 | // Gets a value for config if supplied by user, or defaults to "default.conf" 13 | let uri = cli_matches.get_one::("input").unwrap(); 14 | 15 | app(uri).await 16 | } 17 | 18 | async fn app(uri: &str) -> Result<()> { 19 | print!("App started with uri: {}", uri); 20 | let (_stream, stream_handle) = rodio::OutputStream::try_default()?; 21 | let sink = Sink::try_new(&stream_handle).unwrap(); 22 | 23 | let source = rodio::source::SineWave::new(440.0); 24 | sink.append(source); 25 | 26 | sink.sleep_until_end(); 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /application/soundcloud-tui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "soundcloud-tui" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.82" 10 | render = { version = "0.1.0", path = "../../library/render" } 11 | tokio = { version = "1.37.0", features = ["full"] } 12 | -------------------------------------------------------------------------------- /application/soundcloud-tui/README.md: -------------------------------------------------------------------------------- 1 | # SoundCloud TUI 2 | 3 | Terminal UI client that allows you to interact with your soundcloud playlists from directly within your terminal. -------------------------------------------------------------------------------- /application/soundcloud-tui/src/main.rs: -------------------------------------------------------------------------------- 1 | use render::app; 2 | use anyhow::Result; 3 | 4 | #[tokio::main] 5 | async fn main() -> Result<()> { 6 | app::render().await?; 7 | 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /docs/assets/solution-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherbie/soundcloud-tui/c250256fd39c85a4d097751601a7682004595773/docs/assets/solution-diagram.png -------------------------------------------------------------------------------- /library/player/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "player" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.83" 10 | bytes = "1.6.0" 11 | futures = "0.3.30" 12 | m3u8-rs = "6.0.0" 13 | reqwest = "0.12.4" 14 | rodio = "0.17.3" 15 | tokio = {version = "1.37.0", features = ["full"]} 16 | 17 | [dev-dependencies] 18 | mockall = "0.12.1" 19 | tokio-test = "0.4.4" 20 | -------------------------------------------------------------------------------- /library/player/README.md: -------------------------------------------------------------------------------- 1 | # HLS Client Player -------------------------------------------------------------------------------- /library/player/src/device.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use rodio; 3 | 4 | pub trait DeviceService { 5 | fn play(&self) -> Result<(), rodio::PlayError>; 6 | fn pause(&self) -> Result<(), rodio::PlayError>; 7 | fn clear(&self) -> Result<(), rodio::PlayError>; 8 | fn enqueue( 9 | &self, 10 | source: Box + Send>, 11 | ) -> Result<(), rodio::PlayError>; 12 | fn skip(&self) -> Result<(), rodio::PlayError>; 13 | fn is_empty(&self) -> bool; 14 | } 15 | 16 | pub struct DeviceFactory; 17 | 18 | impl DeviceFactory { 19 | pub fn new(sink: rodio::Sink) -> impl DeviceService { 20 | DeviceCore::from_sink(sink) 21 | } 22 | } 23 | struct DeviceCore { 24 | sink: rodio::Sink, 25 | _stream: Option<(rodio::OutputStream, rodio::OutputStreamHandle)>, 26 | } 27 | 28 | impl DeviceCore { 29 | fn from_sink(sink: rodio::Sink) -> Self { 30 | Self { 31 | sink, 32 | _stream: None, 33 | } 34 | } 35 | } 36 | 37 | impl DeviceService for DeviceCore { 38 | fn clear(&self) -> Result<(), rodio::PlayError> { 39 | self.sink.clear(); 40 | 41 | Ok(()) 42 | } 43 | 44 | fn pause(&self) -> Result<(), rodio::PlayError> { 45 | self.sink.pause(); 46 | 47 | Ok(()) 48 | } 49 | 50 | fn play(&self) -> Result<(), rodio::PlayError> { 51 | if self.is_empty() { 52 | return Err(rodio::PlayError::DecoderError( 53 | rodio::decoder::DecoderError::NoStreams, 54 | )); 55 | } 56 | self.sink.play(); 57 | 58 | Ok(()) 59 | } 60 | 61 | fn enqueue( 62 | &self, 63 | source: Box + Send>, 64 | ) -> Result<(), rodio::PlayError> { 65 | self.sink.append(source.into_iter()); 66 | Ok(()) 67 | } 68 | 69 | fn skip(&self) -> Result<(), rodio::PlayError> { 70 | self.sink.skip_one(); 71 | 72 | Ok(()) 73 | } 74 | 75 | fn is_empty(&self) -> bool { 76 | self.sink.empty() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /library/player/src/device_test.rs: -------------------------------------------------------------------------------- 1 | use super::device::*; 2 | 3 | #[test] 4 | fn test_device_play_pause() { 5 | let (sink, _stream) = rodio::Sink::new_idle(); 6 | let device = DeviceFactory::new(sink); 7 | let enqueue_outcome = device.enqueue(Box::new(rodio::source::Empty::new())); 8 | assert!( 9 | enqueue_outcome.is_ok(), 10 | "Expected sound to be enqueued successfully" 11 | ); 12 | assert!(device.play().is_ok(), "Expected play to be Ok"); 13 | assert!(device.pause().is_ok(), "Expected pause to be Ok"); 14 | } 15 | 16 | #[test] 17 | fn test_device_skip_ok() { 18 | let (sink, _stream) = rodio::Sink::new_idle(); 19 | let device = DeviceFactory::new(sink); 20 | assert!( 21 | device 22 | .enqueue(Box::new(rodio::source::Empty::new())) 23 | .is_ok(), 24 | "Expected sound to be enqueued successfully" 25 | ); 26 | assert!( 27 | device 28 | .enqueue(Box::new(rodio::source::Empty::new())) 29 | .is_ok(), 30 | "Expected sound to be enqueued successfully" 31 | ); 32 | 33 | assert!(device.skip().is_ok(), "Expected skip to be Ok"); 34 | } 35 | -------------------------------------------------------------------------------- /library/player/src/internal/client.rs: -------------------------------------------------------------------------------- 1 | use std::io::Error; 2 | 3 | use bytes::Bytes; 4 | #[cfg(test)] 5 | use mockall::automock; 6 | 7 | use super::hls; 8 | 9 | #[cfg_attr(test, automock)] 10 | pub trait ReqClient { 11 | fn fetch(&self, uri: String) -> hls::PinFuture; 12 | } 13 | 14 | pub struct ClientFactory; 15 | 16 | impl ClientFactory { 17 | pub fn new() -> impl ReqClient { 18 | ReqClientInner {} 19 | } 20 | } 21 | 22 | struct ReqClientInner; 23 | 24 | impl ReqClient for ReqClientInner { 25 | fn fetch(&self, uri: String) -> hls::PinFuture { 26 | Box::pin(async move { 27 | match reqwest::get(uri).await { 28 | Ok(response) => { 29 | if let Ok(bytes) = response.bytes().await { 30 | return Ok(Bytes::from(bytes)); 31 | } 32 | } 33 | Err(e) => { 34 | return Err( 35 | Box::new(Error::new(std::io::ErrorKind::Other, e.to_string())).into(), 36 | ); 37 | } 38 | } 39 | 40 | Err(Box::new(Error::new(std::io::ErrorKind::Other, "failed to fetch")).into()) 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /library/player/src/internal/error.rs: -------------------------------------------------------------------------------- 1 | pub type AsyncError = Box; 2 | -------------------------------------------------------------------------------- /library/player/src/internal/hls/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod playlist; 2 | #[cfg(test)] 3 | mod playlist_test; 4 | pub mod track; 5 | #[cfg(test)] 6 | mod track_test; 7 | 8 | use std::pin::Pin; 9 | 10 | use bytes::Bytes; 11 | use futures::{stream::FuturesOrdered, Future}; 12 | 13 | pub type FetchUriFactoryFn = dyn (Fn(String) -> PinFuture) + Send; 14 | type AsyncError = super::error::AsyncError; 15 | pub type PinFuture = Pin> + Send>>; 16 | pub type PlaylistStream = FuturesOrdered>; 17 | -------------------------------------------------------------------------------- /library/player/src/internal/hls/playlist.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use futures::stream::FuturesOrdered; 3 | use m3u8_rs::{self, MasterPlaylist, MediaPlaylist, Playlist}; 4 | use std::future::Future; 5 | use std::io::Error; 6 | use std::pin::Pin; 7 | use std::str::FromStr; 8 | use std::sync; 9 | use std::sync::mpsc; 10 | 11 | #[cfg(test)] 12 | use mockall::automock; 13 | 14 | use super::track::Track; 15 | 16 | use super::*; 17 | 18 | /// 1. source::from_factory 19 | /// a. factory resolves playlist and creates a Track 20 | /// b. a Track consists of samples that have been successfully fetched 21 | /// c. the buffer will contain the entire track then initially. 22 | /// 2. sink::play_sound 23 | 24 | pub fn fetch_playlist(uri: String, fetch_uri: Box>) -> PinFuture { 25 | Box::pin(async move { 26 | let playlist_bytes = fetch_uri(uri).await?; 27 | 28 | match m3u8_rs::parse_playlist(&playlist_bytes) { 29 | Ok((_, Playlist::MasterPlaylist(pl))) => { 30 | return parse_m3u8_master_playlist(pl, fetch_uri).await; 31 | } 32 | Ok((_, Playlist::MediaPlaylist(pl))) => { 33 | let stream = media_playlist_stream_factory(&pl, fetch_uri); 34 | let track = Track::from_media_playlist(pl.to_owned(), stream).await; 35 | 36 | return Ok(track); 37 | } 38 | Err(e) => return Err(Box::new(e.to_owned())), 39 | } 40 | }) 41 | } 42 | 43 | pub fn parse_m3u8_master_playlist( 44 | pl: MasterPlaylist, 45 | fetch_uri: Box>, 46 | ) -> PinFuture { 47 | Box::pin(async move { 48 | // take first variant 49 | // TODO: improve choice of variant 50 | if let Some(variant) = pl.variants.get(0) { 51 | return fetch_playlist(variant.uri.clone(), fetch_uri).await; 52 | } 53 | 54 | Err(Box::new(Error::new( 55 | std::io::ErrorKind::Other, 56 | "no variants in master playlist", 57 | ))) 58 | }) 59 | } 60 | 61 | // make media playlist stream 62 | pub fn media_playlist_stream_factory( 63 | pl: &MediaPlaylist, 64 | fetch_uri: Box>, 65 | ) -> PlaylistStream { 66 | let mut futures = Vec::new(); 67 | for segment in &pl.segments { 68 | futures.push(fetch_uri(segment.uri.clone())); 69 | } 70 | 71 | FuturesOrdered::from_iter(futures) 72 | } 73 | -------------------------------------------------------------------------------- /library/player/src/internal/hls/playlist_test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | mod tests { 4 | use futures::StreamExt; 5 | 6 | use super::playlist::*; 7 | use super::stubs; 8 | 9 | #[tokio::test] 10 | async fn test_process_master_playlist_parsing_err() { 11 | // let mock_m3u8_file = Bytes::from(stubs::MASTER_PLAYLIST_FILE); 12 | let mut fetch_fn = stubs::MockResolveUriFactoryFn::new(); 13 | fetch_fn.set_bytes("invalid m3u8 file"); 14 | 15 | let outcome = fetch_playlist(String::from("https://example.com"), fetch_fn.call()).await; 16 | assert!(outcome.is_err(), "Expected error result, got ok",); 17 | } 18 | 19 | #[tokio::test] 20 | async fn test_process_master_playlist() { 21 | match m3u8_rs::parse_master_playlist(stubs::MASTER_PLAYLIST_FILE.as_bytes()) { 22 | Result::Ok((_, pl)) => { 23 | let mut fetch_fn = stubs::MockResolveUriFactoryFn::new(); 24 | 25 | // return media playlist to avoid infinite loop 26 | fetch_fn.set_bytes(stubs::MEDIA_PLAYLIST_FILE); 27 | 28 | match parse_m3u8_master_playlist(pl, fetch_fn.call()).await { 29 | Ok(_) => { 30 | assert_eq!(fetch_fn.count(), 16, "fetch client was not called the correct number of times. Expected 16, got {}", fetch_fn.count()); 31 | } 32 | Err(e) => { 33 | assert!(false, "future has a result error: {}", e) 34 | } 35 | } 36 | } 37 | Err(e) => { 38 | assert!(false, "unexpected testing error: {}", e) 39 | } 40 | } 41 | } 42 | 43 | #[tokio::test] 44 | async fn test_media_playlist() { 45 | match m3u8_rs::parse_media_playlist(stubs::MEDIA_PLAYLIST_FILE.as_bytes()) { 46 | Ok((_, pl)) => { 47 | let mock_fn = stubs::MockResolveUriFactoryFn::new(); 48 | let stream = media_playlist_stream_factory(&pl, mock_fn.call()); 49 | let stream_len = stream.len(); 50 | assert!(stream_len > 0, "stream is empty"); 51 | stream 52 | .for_each(|f| async move { assert!(f.is_ok(), "future is not okay") }) 53 | .await; 54 | } 55 | Err(e) => { 56 | assert!(false, "unexpected testing error: {}", e) 57 | } 58 | } 59 | } 60 | 61 | #[tokio::test] 62 | async fn test_media_playlist_call_factory_fn() { 63 | match m3u8_rs::parse_media_playlist(stubs::MEDIA_PLAYLIST_FILE.as_bytes()) { 64 | Ok((_, pl)) => { 65 | let fetch_fn = stubs::MockResolveUriFactoryFn::new(); 66 | let stream = media_playlist_stream_factory(&pl, fetch_fn.call()); 67 | let n_items = stream.count().await; 68 | assert_eq!( 69 | fetch_fn.count() as usize, 70 | n_items, 71 | "fetch was not called the correct number of times. Expected {}", 72 | n_items 73 | ); 74 | } 75 | Err(e) => { 76 | assert!(false, "unexpected testing error: {}", e) 77 | } 78 | } 79 | } 80 | } 81 | 82 | mod stubs { 83 | use super::*; 84 | use bytes::Bytes; 85 | use std::sync::{Arc, Mutex}; 86 | 87 | pub struct MockResolveUriFactoryFn { 88 | count: Arc>, 89 | bytes: Bytes, 90 | } 91 | 92 | impl MockResolveUriFactoryFn { 93 | pub fn new() -> Self { 94 | MockResolveUriFactoryFn { 95 | count: Arc::new(Mutex::new(0)), 96 | bytes: Bytes::new(), 97 | } 98 | } 99 | 100 | pub fn set_bytes(&mut self, str: &str) { 101 | self.bytes = Bytes::from(String::from(str)); 102 | } 103 | 104 | pub fn call(&self) -> Box> { 105 | let count = self.count.clone(); 106 | let bytes = self.bytes.clone(); 107 | Box::new(move |_| { 108 | let count = count.clone(); 109 | let bytes = bytes.clone(); 110 | Box::pin(async move { 111 | let count = count.clone(); 112 | let mut c = count.lock().unwrap(); 113 | *c += 1; 114 | Ok(bytes) 115 | }) 116 | }) 117 | } 118 | 119 | pub fn count(&self) -> u32 { 120 | let count_gaurd = self.count.lock().unwrap(); 121 | let count: u32 = count_gaurd.clone(); 122 | 123 | count 124 | } 125 | } 126 | 127 | pub const MASTER_PLAYLIST_FILE: &str = r#"#EXTM3U 128 | #EXT-X-VERSION:3 129 | #EXT-X-STREAM-INF:PROGRAM-ID=9,BANDWIDTH=300000, 130 | chunklist-b300000.m3u8 131 | #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000 132 | chunklist-b600000.m3u8 133 | #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=850000 134 | chunklist-b850000.m3u8 135 | #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000 136 | chunklist-b1000000.m3u8 137 | #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1500000 138 | chunklist-b1500000.m3u8"#; 139 | 140 | pub const MEDIA_PLAYLIST_FILE: &str = r#"#EXTM3U 141 | #EXT-X-VERSION:4 142 | #EXT-X-TARGETDURATION:3 143 | #EXT-X-MEDIA-SEQUENCE:338559 144 | #EXT-X-KEY:METHOD=AES-128,URI="https://secure.domain.com",IV=0xb059217aa2649ce170b734 145 | #EXTINF:2.002,338559 146 | 20140311T113819-01-338559live.ts 147 | #EXTINF:2.002,338560 148 | 20140311T113819-01-338560live.ts 149 | #EXTINF:2.002,338561 150 | 20140311T113819-01-338561live.ts 151 | #EXTINF:2.002,338562 152 | 20140311T113819-01-338562live.ts 153 | #EXTINF:2.002,338563 154 | 20140311T113819-01-338563live.ts 155 | #EXTINF:2.002,338564 156 | 20140311T113819-01-338564live.ts 157 | #EXTINF:2.002,338565 158 | 20140311T113819-01-338565live.ts 159 | #EXTINF:2.002,338566 160 | 20140311T113819-01-338566live.ts 161 | #EXTINF:2.002,338567 162 | 20140311T113819-01-338567live.ts 163 | #EXTINF:2.002,338568 164 | 20140311T113819-01-338568live.ts 165 | #EXTINF:2.002,338569 166 | 20140311T113819-01-338569live.ts 167 | #EXTINF:2.002,338570 168 | 20140311T113819-01-338570live.ts 169 | #EXTINF:2.002,338571 170 | 20140311T113819-01-338571live.ts 171 | #EXTINF:2.002,338572 172 | 20140311T113819-01-338572live.ts 173 | #EXTINF:2.002,338573 174 | 20140311T113819-01-338573live.ts"#; 175 | } 176 | -------------------------------------------------------------------------------- /library/player/src/internal/hls/track.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{Read, Seek}, 3 | pin::Pin, 4 | sync::mpsc, 5 | }; 6 | 7 | use bytes::Bytes; 8 | use futures::{FutureExt, StreamExt}; 9 | use m3u8_rs::{MediaPlaylist, MediaSegment}; 10 | use rodio::{source, Decoder, Sample, Source}; 11 | use std::io; 12 | use std::sync; 13 | 14 | use super::*; 15 | 16 | #[derive(Debug)] 17 | pub struct TrackSample { 18 | sample: Bytes, 19 | } 20 | 21 | impl TrackSample { 22 | fn new(bytes: Bytes) -> Self { 23 | Self { sample: bytes } 24 | } 25 | } 26 | 27 | impl TryInto>> for TrackSample { 28 | type Error = Box; 29 | 30 | fn try_into(self) -> Result>, Self::Error> { 31 | let cursor = io::Cursor::new(self.sample); 32 | let decoder = Decoder::new(cursor)?; 33 | Ok(decoder) 34 | } 35 | } 36 | 37 | impl From for TrackSample { 38 | fn from(bytes: Bytes) -> Self { 39 | TrackSample::new(bytes) 40 | } 41 | } 42 | 43 | impl Into for TrackSample { 44 | fn into(self) -> Bytes { 45 | self.sample 46 | } 47 | } 48 | 49 | #[derive(Debug)] 50 | pub struct Track { 51 | samples: Vec, 52 | media: MediaPlaylist, 53 | } 54 | 55 | impl Track { 56 | pub fn to_decoders( 57 | self, 58 | ) -> Result>>, Box> { 59 | let decoders = self 60 | .samples 61 | .into_iter() 62 | .map(|sample| sample.try_into()) 63 | .collect::>>, Box>>()?; 64 | 65 | Ok(decoders) 66 | } 67 | } 68 | 69 | impl Track { 70 | pub async fn from_media_playlist( 71 | media: MediaPlaylist, 72 | mut segment_stream: PlaylistStream, 73 | ) -> Self { 74 | let mut samples = Vec::new(); 75 | while let Some(segment) = segment_stream.next().await { 76 | let bytes = match segment { 77 | Ok(bytes) => bytes, 78 | Err(e) => panic!("error fetching segment: {}", e), 79 | }; 80 | samples.push(TrackSample::new(bytes)); 81 | } 82 | 83 | Track { samples, media } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /library/player/src/internal/hls/track_test.rs: -------------------------------------------------------------------------------- 1 | use super::track::*; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use std::io::Cursor; 6 | 7 | use bytes::Bytes; 8 | use rodio::Decoder; 9 | 10 | use super::*; 11 | 12 | #[test] 13 | fn test_track_sample_from_bytes() { 14 | let bytes = Bytes::from_static(b"test"); 15 | let sample = TrackSample::from(bytes.clone()); 16 | 17 | let derived_bytes: Bytes = sample.into(); 18 | assert_eq!(bytes, derived_bytes); 19 | } 20 | 21 | #[test] 22 | fn test_track_sample_into_decoder_err() { 23 | let bytes = Bytes::from_static(b"test"); 24 | let sample = TrackSample::from(bytes.clone()); 25 | 26 | let conversion_outcome: Result>, _> = sample.try_into(); 27 | assert!(conversion_outcome.is_err(), "Expected error result, got ok"); 28 | assert_eq!( 29 | "Unrecognized format", 30 | conversion_outcome.err().unwrap().to_string() 31 | ) 32 | } 33 | 34 | #[test] 35 | #[ignore = "The .ts file is invalid as rodio does not support non-audio files"] 36 | fn test_track_sample_into_decoder() { 37 | let file = std::fs::read("testdata/segment_12.ts"); 38 | assert!( 39 | file.is_ok(), 40 | "Error reading sample ts file, {:?}", 41 | file.err() 42 | ); 43 | 44 | let bytes = Bytes::from(file.unwrap()); 45 | let sample = TrackSample::from(bytes); 46 | 47 | let conversion_outcome: Result>, _> = sample.try_into(); 48 | assert!( 49 | conversion_outcome.is_ok(), 50 | "Expected ok result, got error {:?}", 51 | conversion_outcome.err() 52 | ); 53 | } 54 | } 55 | 56 | mod stubs {} 57 | -------------------------------------------------------------------------------- /library/player/src/internal/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod error; 3 | pub mod hls; 4 | pub mod track_lookup; 5 | #[cfg(test)] 6 | mod track_lookup_test; 7 | 8 | pub use client::{ClientFactory, ReqClient}; 9 | pub use track_lookup::{TrackLookupFactory, TrackLookupService}; 10 | -------------------------------------------------------------------------------- /library/player/src/internal/track_lookup.rs: -------------------------------------------------------------------------------- 1 | use super::client; 2 | use super::hls; 3 | 4 | pub trait TrackLookupService { 5 | fn get_track(&self, uri: &str) -> hls::PinFuture; 6 | } 7 | 8 | pub struct TrackLookupFactory; 9 | 10 | impl TrackLookupFactory { 11 | pub fn new(client: std::sync::Arc) -> impl TrackLookupService 12 | where 13 | C: client::ReqClient + Send + Sync + 'static, 14 | { 15 | TrackLookupCore::new(client) 16 | } 17 | } 18 | 19 | struct TrackLookupCore { 20 | client: std::sync::Arc, 21 | } 22 | 23 | impl TrackLookupCore { 24 | pub fn new(client: std::sync::Arc) -> Self { 25 | Self { client } 26 | } 27 | } 28 | 29 | impl TrackLookupService for TrackLookupCore { 30 | fn get_track(&self, uri: &str) -> hls::PinFuture { 31 | let client = self.client.clone(); 32 | let uri_str = uri.to_string(); 33 | 34 | Box::pin(async move { 35 | let track_outcome = 36 | hls::playlist::fetch_playlist(uri_str, Box::new(move |uri| client.fetch(uri))) 37 | .await; 38 | 39 | Ok(track_outcome?) 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /library/player/src/internal/track_lookup_test.rs: -------------------------------------------------------------------------------- 1 | use super::track_lookup::*; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use super::stubs::*; 6 | use super::*; 7 | use mockall::predicate::*; 8 | 9 | #[tokio::test] 10 | #[ignore = "not implemented yet"] 11 | async fn test_track_lookup() { 12 | let mut mock_client = MockReqClient::new(); 13 | mock_client 14 | .expect_fetch() 15 | .returning(|_| Box::pin(async { Ok(Default::default()) })); 16 | 17 | let track_lookup = TrackLookupFactory::new(std::sync::Arc::new(mock_client)); 18 | let track = track_lookup 19 | .get_track("https://www.youtube.com/watch?v=1234") 20 | .await; 21 | 22 | assert!(track.is_ok(), "Expected track lookup to be Ok"); 23 | } 24 | } 25 | 26 | #[cfg(test)] 27 | mod stubs { 28 | pub use super::super::client::MockReqClient; 29 | } 30 | -------------------------------------------------------------------------------- /library/player/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod device; 2 | mod internal; 3 | pub mod player; 4 | 5 | #[cfg(test)] 6 | mod device_test; 7 | #[cfg(test)] 8 | mod player_test; 9 | -------------------------------------------------------------------------------- /library/player/src/player.rs: -------------------------------------------------------------------------------- 1 | use crate::internal::client; 2 | 3 | use super::device; 4 | use super::internal; 5 | 6 | use rodio; 7 | use std::sync::Arc; 8 | use tokio; 9 | 10 | trait TrackPlayerService { 11 | fn enqueue(&self, song_uri: &str); 12 | fn play(&self); 13 | fn pause(&self); 14 | fn skip(&self); 15 | } 16 | 17 | enum DeviceEvent { 18 | Play, 19 | Pause, 20 | Skip, 21 | } 22 | 23 | enum PlayerEvent { 24 | Device(DeviceEvent), 25 | Enqueue(String), 26 | } 27 | 28 | pub struct TrackPlayer { 29 | // buffered songs 30 | event_tx: Box>, 31 | event_rx: Option>, 32 | 33 | // TODO: error signal for worker? ... should probably be the join handle failure 34 | 35 | // singletons 36 | client_service: Arc, 37 | track_lookup_service: Box, 38 | device_service: Box, 39 | } 40 | 41 | impl Default for TrackPlayer { 42 | // TODO: deprecate me 43 | fn default() -> Self { 44 | let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap(); 45 | let sink = rodio::Sink::try_new(&stream_handle).unwrap(); 46 | let device = device::DeviceFactory::new(sink); 47 | 48 | let (tx, rx) = tokio::sync::mpsc::channel::(5); 49 | 50 | let client_service = Arc::new(internal::ClientFactory::new()); 51 | 52 | TrackPlayer { 53 | event_tx: Box::new(tx), 54 | event_rx: Some(rx), 55 | 56 | client_service: client_service.clone(), 57 | track_lookup_service: Box::new(internal::TrackLookupFactory::new( 58 | client_service.clone(), 59 | )), 60 | device_service: Box::new(device), 61 | } 62 | } 63 | } 64 | 65 | impl TrackPlayer { 66 | pub async fn start(&mut self) -> Result<(), internal::error::AsyncError> { 67 | if self.event_rx.is_none() { 68 | return Ok(()); 69 | } 70 | 71 | loop { 72 | match self.event_rx.as_mut().unwrap().recv().await { 73 | Some(PlayerEvent::Enqueue(song)) => { 74 | self.handle_enqueue_event(&song).await?; 75 | } 76 | Some(PlayerEvent::Device(event)) => { 77 | self.handle_device_event(&event).await?; 78 | } 79 | None => { 80 | break; 81 | } 82 | } 83 | } 84 | 85 | Ok(()) 86 | } 87 | 88 | async fn handle_device_event( 89 | &self, 90 | event: &DeviceEvent, 91 | ) -> Result<(), internal::error::AsyncError> { 92 | match event { 93 | DeviceEvent::Play => Ok(self.device_service.play()?), 94 | DeviceEvent::Pause => Ok(self.device_service.pause()?), 95 | DeviceEvent::Skip => Ok(self.device_service.skip()?), 96 | } 97 | } 98 | 99 | async fn handle_enqueue_event( 100 | &mut self, 101 | song: &str, 102 | ) -> Result<(), internal::error::AsyncError> { 103 | // TODO: resolve track with this async service 104 | let track = self.track_lookup_service.get_track(song).await?; 105 | let decoders = track.to_decoders().map_err(|e| { 106 | Box::new(std::io::Error::new( 107 | std::io::ErrorKind::Other, 108 | e.to_string(), 109 | )) 110 | })?; 111 | 112 | use rodio::Source; 113 | let track_sample = 114 | rodio::source::from_iter(Box::new(decoders.into_iter())).convert_samples(); 115 | let _ = self.device_service.enqueue(Box::new(track_sample)); 116 | 117 | Ok(()) 118 | } 119 | } 120 | 121 | impl TrackPlayerService for TrackPlayer { 122 | fn enqueue(&self, song_uri: &str) { 123 | let tx = self.event_tx.clone(); 124 | let song = song_uri.to_string(); 125 | tokio::spawn(async move { tx.send(PlayerEvent::Enqueue(song)).await }); 126 | } 127 | 128 | fn pause(&self) { 129 | let tx = self.event_tx.clone(); 130 | tokio::spawn(async move { tx.send(PlayerEvent::Device(DeviceEvent::Pause)).await }); 131 | } 132 | 133 | fn play(&self) { 134 | let tx = self.event_tx.clone(); 135 | tokio::spawn(async move { tx.send(PlayerEvent::Device(DeviceEvent::Play)).await }); 136 | } 137 | 138 | fn skip(&self) { 139 | let tx = self.event_tx.clone(); 140 | tokio::spawn(async move { tx.send(PlayerEvent::Device(DeviceEvent::Skip)).await }); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /library/player/src/player_test.rs: -------------------------------------------------------------------------------- 1 | mod tests {} 2 | -------------------------------------------------------------------------------- /library/player/testdata/segment_12.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherbie/soundcloud-tui/c250256fd39c85a4d097751601a7682004595773/library/player/testdata/segment_12.ts -------------------------------------------------------------------------------- /library/render/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "render" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.82" 10 | crossterm = "0.27.0" 11 | tokio = "1.37.0" 12 | tui = "0.19.0" 13 | 14 | [dev-dependencies] 15 | tokio-test = "0.4.4" 16 | unimock = "0.6.5" 17 | -------------------------------------------------------------------------------- /library/render/README.md: -------------------------------------------------------------------------------- 1 | # Terminal UI Renderer -------------------------------------------------------------------------------- /library/render/src/app.rs: -------------------------------------------------------------------------------- 1 | use crate::event; 2 | use crate::event::EventServer; 3 | 4 | use super::context::*; 5 | use super::router::draw; 6 | use anyhow::Result; 7 | use crossterm::execute; 8 | use crossterm::terminal::{ 9 | disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, 10 | }; 11 | use std::io; 12 | use tui::backend::CrosstermBackend; 13 | use tui::terminal::Terminal; 14 | 15 | pub async fn render() -> Result<()> { 16 | let mut terminal = init_terminal()?; 17 | let context = Context::default(); 18 | 19 | let mut event_server = event::EventServerCore::default(); 20 | event_server.listen(event::crossterm::CrosstermEventSource); 21 | 22 | loop { 23 | draw(&context, &mut terminal)?; 24 | 25 | match event_server.next() { 26 | Some(event::Event::Input(crossterm::event::Event::Key(_))) => { 27 | break; 28 | } 29 | Some(event::Event::Tick) => {} 30 | _ => {} 31 | }; 32 | } 33 | 34 | event_server.stop(); 35 | exit_ui()?; 36 | 37 | Ok(()) 38 | } 39 | 40 | fn init_terminal() -> Result>> { 41 | let backend: CrosstermBackend = CrosstermBackend::new(io::stdout()); 42 | let mut terminal = Terminal::new(backend)?; 43 | 44 | execute!(io::stdout(), EnterAlternateScreen)?; 45 | enable_raw_mode()?; 46 | terminal.hide_cursor()?; 47 | 48 | Ok(terminal) 49 | } 50 | 51 | fn exit_ui() -> Result<()> { 52 | execute!(io::stdout(), LeaveAlternateScreen)?; 53 | disable_raw_mode()?; 54 | 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /library/render/src/components/component.rs: -------------------------------------------------------------------------------- 1 | use tui::layout::Rect; 2 | use tui::widgets::Widget; 3 | 4 | pub trait Component 5 | where 6 | W: Widget, 7 | { 8 | fn widget(&self) -> W; 9 | fn area(&self, container: Rect) -> Rect; 10 | } 11 | -------------------------------------------------------------------------------- /library/render/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | mod component; 2 | pub mod style; 3 | pub mod widgets; 4 | 5 | pub use component::Component; 6 | -------------------------------------------------------------------------------- /library/render/src/components/style/border.rs: -------------------------------------------------------------------------------- 1 | use std::convert::From; 2 | use std::option::Option; 3 | use tui::style::{Color, Modifier, Style}; 4 | use tui::widgets::{BorderType, Borders}; 5 | 6 | #[derive(Clone, Copy)] 7 | pub struct BorderStyle { 8 | pub borders: Borders, 9 | pub border_type: BorderType, 10 | pub fg: Option, 11 | pub bg: Option, 12 | pub decorations: Option, 13 | } 14 | 15 | impl Default for BorderStyle { 16 | fn default() -> Self { 17 | Self { 18 | borders: Borders::NONE, 19 | border_type: BorderType::Plain, 20 | fg: None, 21 | bg: None, 22 | decorations: None, 23 | } 24 | } 25 | } 26 | 27 | impl From