diff --git a/CHANGELOG.md b/CHANGELOG.md index 62a3f44..3559556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.2.5 | October 18th 2024 + +- Updated librespot to rel 0.5.0 (was: 0.5.0-dev) +- Fixed an issue where Spoticord would lose connection to Spotify servers (fixed by librespot upgrade) +- Reworked authentication logic, hopefully reducing the amount of "suspicious login" forced password resets + ## 2.2.4 | September 30th 2024 - Added a message for if the Spotify AP connection drops diff --git a/Cargo.lock b/Cargo.lock index 166a884..68dcc67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -146,9 +146,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -157,9 +157,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -211,9 +211,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-rs" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" +checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" dependencies = [ "aws-lc-sys", "mirai-annotations", @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ddc4a5b231dd6958b140ff3151b6412b3f4321fab354f399eec8f14b06df62" +checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" dependencies = [ "bindgen", "cc", @@ -277,9 +277,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags 2.6.0", "cexpr", @@ -333,9 +333,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "byteorder" @@ -382,9 +382,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.22" +version = "1.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" +checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" dependencies = [ "jobserver", "libc", @@ -652,7 +652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -729,9 +729,9 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] @@ -750,9 +750,9 @@ dependencies = [ [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling 0.20.10", "proc-macro2", @@ -762,11 +762,11 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ - "derive_builder_core 0.20.1", + "derive_builder_core 0.20.2", "syn 2.0.79", ] @@ -1061,9 +1061,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1076,9 +1076,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1086,15 +1086,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1103,15 +1103,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1120,15 +1120,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -1138,9 +1138,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1211,9 +1211,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -1283,7 +1283,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", "serde", ] @@ -1297,6 +1297,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hashmap_derive" version = "0.1.0" @@ -1461,9 +1467,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1479,9 +1485,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" dependencies = [ "bytes", "futures-channel", @@ -1503,9 +1509,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -1532,7 +1538,7 @@ dependencies = [ "futures-util", "headers", "http 1.1.0", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-rustls 0.26.0", "hyper-util", "pin-project-lite", @@ -1551,7 +1557,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.31", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", @@ -1565,7 +1571,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "log", "rustls 0.22.4", @@ -1584,10 +1590,10 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "log", - "rustls 0.23.13", + "rustls 0.23.15", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -1607,7 +1613,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.5.0", "pin-project-lite", "socket2", "tokio", @@ -1666,12 +1672,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -1685,9 +1691,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is_terminal_polyfill" @@ -1721,9 +1727,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -1815,9 +1821,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" [[package]] name = "libloading" @@ -1855,8 +1861,8 @@ dependencies = [ [[package]] name = "librespot" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "data-encoding", "env_logger", @@ -1871,28 +1877,24 @@ dependencies = [ "librespot-playback", "librespot-protocol", "log", - "rpassword", "sha1", "sysinfo", "thiserror", "tokio", "url", - "webpki", ] [[package]] name = "librespot-audio" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "aes", - "byteorder", "bytes", "ctr", - "futures-core", "futures-util", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "librespot-core", "log", @@ -1904,8 +1906,8 @@ dependencies = [ [[package]] name = "librespot-connect" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "form_urlencoded", "futures-util", @@ -1924,8 +1926,8 @@ dependencies = [ [[package]] name = "librespot-core" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "aes", "base64 0.22.1", @@ -1940,7 +1942,7 @@ dependencies = [ "http 1.1.0", "http-body-util", "httparse", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-proxy2", "hyper-rustls 0.27.3", "hyper-util", @@ -1955,6 +1957,7 @@ dependencies = [ "once_cell", "parking_lot", "pbkdf2", + "pin-project-lite", "priority-queue", "protobuf", "quick-xml", @@ -1978,20 +1981,19 @@ dependencies = [ [[package]] name = "librespot-discovery" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "aes", "base64 0.22.1", "bytes", - "cfg-if", "ctr", "form_urlencoded", "futures-core", "futures-util", "hmac", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "libmdns", "librespot-core", @@ -2005,11 +2007,10 @@ dependencies = [ [[package]] name = "librespot-metadata" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "async-trait", - "byteorder", "bytes", "librespot-core", "librespot-protocol", @@ -2023,8 +2024,8 @@ dependencies = [ [[package]] name = "librespot-oauth" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "log", "oauth2", @@ -2034,10 +2035,9 @@ dependencies = [ [[package]] name = "librespot-playback" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ - "byteorder", "futures-util", "librespot-audio", "librespot-core", @@ -2055,8 +2055,8 @@ dependencies = [ [[package]] name = "librespot-protocol" -version = "0.5.0-dev" -source = "git+https://github.com/SpoticordMusic/librespot.git#32d17580fcf785062f10a6634d6735d29e5304cc" +version = "0.5.0" +source = "git+https://github.com/SpoticordMusic/librespot.git#32f0c70829388be9968023437a1eb8233bea294a" dependencies = [ "protobuf", "protobuf-codegen", @@ -2401,21 +2401,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -2540,18 +2537,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", @@ -2758,18 +2755,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] [[package]] name = "protobuf" -version = "3.5.1" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bcc343da15609eaecd65f8aa76df8dc4209d325131d8219358c0aaaebab0bf6" +checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" dependencies = [ "once_cell", "protobuf-support", @@ -2778,9 +2775,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.5.1" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d0cde5642ea4df842b13eb9f59ea6fafa26dcb43e3e1ee49120e9757556189" +checksum = "e26b833f144769a30e04b1db0146b2aaa53fd2fd83acf10a6b5f996606c18144" dependencies = [ "anyhow", "once_cell", @@ -2793,9 +2790,9 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.5.1" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0e9b447d099ae2c4993c0cbb03c7a9d6c937b17f2d56cfc0b1550e6fcfdb76" +checksum = "322330e133eab455718444b4e033ebfac7c6528972c784fcde28d2cc783c6257" dependencies = [ "anyhow", "indexmap", @@ -2809,9 +2806,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.5.1" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0766e3675a627c327e4b3964582594b0e8741305d628a98a5de75a1d15f99b9" +checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" dependencies = [ "thiserror", ] @@ -2848,7 +2845,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.13", + "rustls 0.23.15", "socket2", "thiserror", "tokio", @@ -2865,7 +2862,7 @@ dependencies = [ "rand", "ring 0.17.8", "rustc-hash 2.0.0", - "rustls 0.23.13", + "rustls 0.23.15", "slab", "thiserror", "tinyvec", @@ -3043,7 +3040,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.30", + "hyper 0.14.31", "hyper-rustls 0.24.2", "ipnet", "js-sys", @@ -3075,9 +3072,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" dependencies = [ "base64 0.22.1", "bytes", @@ -3086,7 +3083,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-rustls 0.27.3", "hyper-util", "ipnet", @@ -3097,8 +3094,8 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.13", - "rustls-pemfile 2.1.3", + "rustls 0.23.15", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", @@ -3156,17 +3153,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "rpassword" -version = "7.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" -dependencies = [ - "libc", - "rtoolbox", - "windows-sys 0.48.0", -] - [[package]] name = "rsa" version = "0.9.6" @@ -3220,7 +3206,7 @@ dependencies = [ "async-trait", "log", "maybe-async", - "reqwest 0.12.7", + "reqwest 0.12.8", "serde_json", "thiserror", ] @@ -3245,16 +3231,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "rtoolbox" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "rubato" version = "0.15.0" @@ -3362,9 +3338,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" dependencies = [ "aws-lc-rs", "log", @@ -3395,7 +3371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -3408,7 +3384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -3425,19 +3401,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" @@ -3463,9 +3438,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty_pool" @@ -3506,9 +3481,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -3966,14 +3941,15 @@ dependencies = [ [[package]] name = "spoticord" -version = "2.2.4" +version = "2.2.5" dependencies = [ "anyhow", "dotenvy", "env_logger", + "librespot", "log", "poise", - "rustls 0.23.13", + "rustls 0.23.15", "serenity", "songbird", "spoticord_config", @@ -3987,7 +3963,7 @@ dependencies = [ [[package]] name = "spoticord_audio" -version = "2.2.4" +version = "2.2.5" dependencies = [ "librespot", "songbird", @@ -3997,7 +3973,7 @@ dependencies = [ [[package]] name = "spoticord_config" -version = "2.2.4" +version = "2.2.5" dependencies = [ "rspotify", "serenity", @@ -4005,7 +3981,7 @@ dependencies = [ [[package]] name = "spoticord_database" -version = "2.2.4" +version = "2.2.5" dependencies = [ "chrono", "diesel", @@ -4019,7 +3995,7 @@ dependencies = [ [[package]] name = "spoticord_player" -version = "2.2.4" +version = "2.2.5" dependencies = [ "anyhow", "hex", @@ -4034,7 +4010,7 @@ dependencies = [ [[package]] name = "spoticord_session" -version = "2.2.4" +version = "2.2.5" dependencies = [ "anyhow", "base64 0.22.1", @@ -4047,26 +4023,22 @@ dependencies = [ "spoticord_database", "spoticord_player", "spoticord_utils", + "thiserror", "tokio", ] [[package]] name = "spoticord_stats" -version = "2.2.4" +version = "2.2.5" dependencies = [ "redis", ] [[package]] name = "spoticord_utils" -version = "2.2.4" +version = "2.2.5" dependencies = [ - "anyhow", - "base64 0.22.1", - "librespot", - "log", "poise", - "tokio", ] [[package]] @@ -4556,7 +4528,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.13", + "rustls 0.23.15", "rustls-pki-types", "tokio", ] @@ -4624,7 +4596,7 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "rustls 0.23.13", + "rustls 0.23.15", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -4735,9 +4707,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" [[package]] name = "try-lock" @@ -4800,7 +4772,7 @@ dependencies = [ "httparse", "log", "rand", - "rustls 0.23.13", + "rustls 0.23.15", "rustls-pki-types", "sha1", "thiserror", @@ -4870,7 +4842,7 @@ checksum = "5dece5c06268af6a9ff4541788601e560a4284ffebfb357f713d676f13b964db" dependencies = [ "chrono", "dashmap", - "hashbrown", + "hashbrown 0.14.5", "mini-moka", "parking_lot", "secrecy", @@ -4902,9 +4874,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -4923,9 +4895,9 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-segmentation" @@ -4987,9 +4959,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", "rand", @@ -5032,7 +5004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349ed9e45296a581f455bc18039878f409992999bc1d5da12a6800eb18c8752f" dependencies = [ "anyhow", - "derive_builder 0.20.1", + "derive_builder 0.20.2", "rustversion", "time", "vergen-lib", @@ -5045,7 +5017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a3a7f91caabecefc3c249fd864b11d4abe315c166fbdb568964421bccfd2b7a" dependencies = [ "anyhow", - "derive_builder 0.20.1", + "derive_builder 0.20.2", "rustversion", "time", "vergen", @@ -5059,7 +5031,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229eaddb0050920816cf051e619affaf18caa3dd512de8de5839ccbc8e53abb0" dependencies = [ "anyhow", - "derive_builder 0.20.1", + "derive_builder 0.20.2", "rustversion", ] @@ -5102,9 +5074,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -5113,9 +5085,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -5128,9 +5100,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -5140,9 +5112,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5150,9 +5122,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -5163,9 +5135,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-streams" @@ -5182,9 +5154,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 0b780a6..7b38e2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord" -version = "2.2.4" +version = "2.2.5" edition = "2021" rust-version = "1.80.0" @@ -31,6 +31,7 @@ spoticord_session = { path = "./spoticord_session" } spoticord_utils = { path = "./spoticord_utils" } spoticord_stats = { path = "./spoticord_stats", optional = true } +librespot = { git = "https://github.com/SpoticordMusic/librespot.git", default-features = false } anyhow = "1.0.86" dotenvy = "0.15.7" env_logger = "0.11.5" diff --git a/spoticord_audio/Cargo.toml b/spoticord_audio/Cargo.toml index 5a61a3b..e0f03bb 100644 --- a/spoticord_audio/Cargo.toml +++ b/spoticord_audio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord_audio" -version = "2.2.4" +version = "2.2.5" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/spoticord_config/Cargo.toml b/spoticord_config/Cargo.toml index fa21abe..94b8d5f 100644 --- a/spoticord_config/Cargo.toml +++ b/spoticord_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord_config" -version = "2.2.4" +version = "2.2.5" edition = "2021" [dependencies] diff --git a/spoticord_database/Cargo.toml b/spoticord_database/Cargo.toml index c8d8b0c..b9bde42 100644 --- a/spoticord_database/Cargo.toml +++ b/spoticord_database/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord_database" -version = "2.2.4" +version = "2.2.5" edition = "2021" [dependencies] diff --git a/spoticord_database/src/lib.rs b/spoticord_database/src/lib.rs index 14898bd..0694283 100644 --- a/spoticord_database/src/lib.rs +++ b/spoticord_database/src/lib.rs @@ -127,14 +127,14 @@ impl Database { pub async fn update_session_token( &self, _user_id: impl AsRef, - _session_token: impl AsRef, + _session_token: Option, ) -> Result<()> { use schema::account::dsl::*; let mut connection = self.0.get().await?; diesel::update(account) .filter(user_id.eq(_user_id.as_ref())) - .set(session_token.eq(_session_token.as_ref())) + .set(session_token.eq(_session_token.as_deref())) .execute(&mut connection) .await?; diff --git a/spoticord_player/Cargo.toml b/spoticord_player/Cargo.toml index ac0b178..d8f7833 100644 --- a/spoticord_player/Cargo.toml +++ b/spoticord_player/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord_player" -version = "2.2.4" +version = "2.2.5" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/spoticord_player/src/lib.rs b/spoticord_player/src/lib.rs index ca23d93..a318889 100644 --- a/spoticord_player/src/lib.rs +++ b/spoticord_player/src/lib.rs @@ -4,7 +4,10 @@ use anyhow::Result; use info::PlaybackInfo; use librespot::{ connect::{config::ConnectConfig, spirc::Spirc}, - core::{http_client::HttpClientError, Session as SpotifySession, SessionConfig}, + core::{ + connection::AuthenticationError, http_client::HttpClientError, Session as SpotifySession, + SessionConfig, + }, discovery::Credentials, metadata::Lyrics, playback::{ @@ -71,7 +74,7 @@ impl Player { credentials: Credentials, call: Arc>, device_name: impl Into, - ) -> Result<(PlayerHandle, mpsc::Receiver)> { + ) -> Result<(PlayerHandle, mpsc::Receiver, Vec), librespot::core::Error> { let (event_tx, event_rx) = mpsc::channel(16); let mut call_lock = call.lock().await; @@ -80,7 +83,7 @@ impl Player { // Create songbird audio track let adapter = RawAdapter::new(stream.clone(), 44100, 2); let track = call_lock.play_only_input(adapter.into()); - track.pause()?; + _ = track.pause(); // Free call lock before creating session drop(call_lock); @@ -127,11 +130,21 @@ impl Player { { Ok(spirc) => break spirc, Err(why) => { + // Instantly return if our credentials have expired + if let Some(AuthenticationError::LoginFailed( + librespot::protocol::keyexchange::ErrorCode::BadCredentials, + )) = why + .error + .downcast_ref::() + { + return Err(why); + } + tries += 1; if tries > 3 { error!("Failed to connect to Spirc: {why}"); - return Err(why.into()); + return Err(why); } continue; @@ -139,6 +152,9 @@ impl Player { } }; + // Keep auth data to reuse later for faster reconnections and less authentication requests to Spotify + let auth_data = session.auth_data(); + let shutdown = Arc::new(AtomicBool::new(false)); let (tx, rx) = mpsc::channel(16); let player = Self { @@ -169,7 +185,7 @@ impl Player { }); tokio::spawn(player.run()); - Ok((PlayerHandle { commands: tx }, event_rx)) + Ok((PlayerHandle { commands: tx }, event_rx, auth_data)) } async fn run(mut self) { diff --git a/spoticord_session/Cargo.toml b/spoticord_session/Cargo.toml index e9a6c91..66dad40 100644 --- a/spoticord_session/Cargo.toml +++ b/spoticord_session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord_session" -version = "2.2.4" +version = "2.2.5" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,10 +12,11 @@ spoticord_player = { path = "../spoticord_player" } spoticord_utils = { path = "../spoticord_utils" } tokio = { version = "1.39.3", features = ["full"] } -librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false } +librespot = { git = "https://github.com/SpoticordMusic/librespot.git", default-features = false } serenity = "0.12.2" songbird = { version = "0.4.3", features = ["simd-json"] } anyhow = "1.0.86" log = "0.4.22" base64 = "0.22.1" poise = "0.6.1" +thiserror = "1.0.64" diff --git a/spoticord_session/src/error.rs b/spoticord_session/src/error.rs new file mode 100644 index 0000000..6784cd7 --- /dev/null +++ b/spoticord_session/src/error.rs @@ -0,0 +1,30 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + /// The user executed an action inside of a channel that is not supported + #[error("The specified channel is invalid for this operation")] + InvalidChannel, + + /// Generic authentication failure + #[error("Authentication failed")] + AuthenticationFailed, + + /// Cannot perform this action on an active session + #[error("Cannot perform this action on an active session")] + AlreadyActive, + + #[error(transparent)] + Serenity(#[from] serenity::Error), + + #[error(transparent)] + Database(#[from] spoticord_database::error::DatabaseError), + + #[error(transparent)] + JoinError(#[from] songbird::error::JoinError), + + #[error(transparent)] + Librespot(#[from] librespot::core::Error), +} + +pub type Result = ::core::result::Result; diff --git a/spoticord_session/src/lib.rs b/spoticord_session/src/lib.rs index 4e57f56..e9afdcb 100644 --- a/spoticord_session/src/lib.rs +++ b/spoticord_session/src/lib.rs @@ -1,10 +1,16 @@ +pub mod error; pub mod lyrics_embed; pub mod manager; pub mod playback_embed; -use anyhow::{anyhow, Result}; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; -use librespot::{discovery::Credentials, protocol::authentication::AuthenticationType}; +use error::Error; +use error::Result; +use librespot::{ + core::connection, + discovery::Credentials, + protocol::{authentication::AuthenticationType, keyexchange::ErrorCode}, +}; use log::{debug, error, trace}; use lyrics_embed::LyricsEmbed; use manager::{SessionManager, SessionQuery}; @@ -16,9 +22,8 @@ use serenity::{ async_trait, }; use songbird::{model::payload::ClientDisconnect, Call, CoreEvent, Event, EventContext}; -use spoticord_database::Database; use spoticord_player::{Player, PlayerEvent, PlayerHandle}; -use spoticord_utils::{discord::Colors, spotify}; +use spoticord_utils::discord::Colors; use std::{ops::ControlFlow, sync::Arc, time::Duration}; use tokio::{ sync::{mpsc, oneshot, Mutex}, @@ -78,6 +83,8 @@ impl Session { text_channel_id: ChannelId, owner: UserId, ) -> Result { + use Error::*; + // Set up communication channel let (tx, rx) = mpsc::channel(16); let handle = SessionHandle { @@ -93,22 +100,19 @@ impl Session { .to_channel(&context) .await? .guild() - .ok_or(anyhow!("Text channel is not a guild channel"))?; + .ok_or(InvalidChannel)?; // Create channel for internal command communication (timeouts hint hint) // This uses separate channels as to not cause a cyclic dependency let (inner_tx, inner_rx) = mpsc::channel(16); // Grab user credentials and info before joining call - let credentials = - match retrieve_credentials(&session_manager.database(), owner.to_string()).await { - Ok(credentials) => credentials, - Err(why) => { - error!("Failed to retrieve credentials: {why}"); + let account = session_manager + .database() + .get_account(owner.to_string()) + .await?; - return Err(why); - } - }; + // Get user preferences let device_name = match session_manager.database().get_user(owner.to_string()).await { Ok(user) => user.device_name, Err(why) => { @@ -118,20 +122,36 @@ impl Session { } }; - // Hello Discord I'm here - let call = match session_manager - .songbird() - .join(guild_id, voice_channel_id) - .await + let credentials = match account + .session_token + .and_then(|val| BASE64.decode(&val).ok()) { - Ok(call) => call, - Err(why) => { - error!("Failed to join voice channel: {why}"); + Some(token) => Credentials { + username: Some(account.username), + auth_type: AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS, + auth_data: token, + }, + None => { + let access_token = session_manager + .database() + .get_access_token(&account.user_id) + .await?; - return Err(why.into()); + Credentials::with_access_token(access_token) } }; + let credentials_cached = matches!( + credentials.auth_type, + AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS + ); + + // Hello Discord I'm here + let call = session_manager + .songbird() + .join(guild_id, voice_channel_id) + .await?; + // Make sure call guard is dropped or else we can't execute session.run { let mut call = call.lock().await; @@ -144,17 +164,49 @@ impl Session { call.add_global_event(Event::Core(CoreEvent::ClientDisconnect), handle.clone()); } - let (player, events) = match Player::create(credentials, call.clone(), device_name).await { - Ok(player) => player, - Err(why) => { - // Leave call on error, otherwise bot will be stuck in call forever until manually disconnected or taken over - _ = call.lock().await.leave().await; + let (player, events, auth_data) = + match Player::create(credentials, call.clone(), device_name).await { + Ok(player) => player, + Err(why) => { + // Leave call on error, otherwise bot will be stuck in call forever until manually disconnected or taken over + _ = call.lock().await.leave().await; + + error!("Failed to create player: {why}"); + + if let Some(connection::AuthenticationError::LoginFailed( + ErrorCode::BadCredentials, + )) = why.error.downcast_ref::() + { + // Authentication failed, clear tokens in database (depending on which type of auth failed) + + if credentials_cached { + session_manager + .database() + .update_session_token(owner.to_string(), None) + .await + .ok(); + } else { + session_manager + .database() + .delete_account(owner.to_string()) + .await + .ok(); + } - error!("Failed to create player: {why}\n{}", why.backtrace()); + return Err(AuthenticationFailed); + } - return Err(why); - } - }; + return Err(why.into()); + } + }; + + // Store reusable credentials in DB + // We don't care if this fails, we'll just fall back on token login + session_manager + .database() + .update_session_token(owner.to_string(), Some(BASE64.encode(auth_data))) + .await + .ok(); let mut session = Self { session_manager, @@ -362,21 +414,88 @@ impl Session { } async fn reactivate(&mut self, new_owner: UserId) -> Result<()> { + use Error::*; + + let user_id = &*new_owner.to_string(); + if self.active { - return Err(anyhow!("Cannot reactivate session that is already active")); + return Err(AlreadyActive); } - let credentials = - retrieve_credentials(&self.session_manager.database(), new_owner.to_string()).await?; - let device_name = self - .session_manager - .database() - .get_user(new_owner.to_string()) - .await? - .device_name; + // Grab user credentials and info before joining call + let account = self.session_manager.database().get_account(user_id).await?; + + // Get user preferences + let device_name = match self.session_manager.database().get_user(user_id).await { + Ok(user) => user.device_name, + Err(why) => { + error!("Failed to get database user: {why}"); + + return Err(why.into()); + } + }; + + let credentials = match account + .session_token + .and_then(|val| BASE64.decode(val).ok()) + { + Some(token) => Credentials { + username: Some(account.username), + auth_type: AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS, + auth_data: token, + }, + None => { + let access_token = self + .session_manager + .database() + .get_access_token(&account.user_id) + .await?; + + Credentials::with_access_token(access_token) + } + }; + + let credentials_cached = matches!( + credentials.auth_type, + AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS + ); + + let (player, player_events, auth_data) = + match Player::create(credentials, self.call.clone(), device_name).await { + Ok(player) => player, + Err(why) => { + if let Some(connection::AuthenticationError::LoginFailed( + ErrorCode::BadCredentials, + )) = why.error.downcast_ref::() + { + // Authentication failed, clear tokens in database (depending on which type of auth failed) + + if credentials_cached { + self.session_manager + .database() + .update_session_token(user_id, None) + .await + .ok(); + } else { + self.session_manager + .database() + .delete_account(user_id) + .await + .ok(); + } + } + + return Err(why.into()); + } + }; - let (player, player_events) = - Player::create(credentials, self.call.clone(), device_name).await?; + // Store reusable credentials in DB + // We don't care if this fails, we'll just fall back on token login + self.session_manager + .database() + .update_session_token(user_id, Some(BASE64.encode(auth_data))) + .await + .ok(); self.owner = new_owner; self.player = player; @@ -464,7 +583,7 @@ impl SessionHandle { } /// Retrieve the current owner of the session - pub async fn owner(&self) -> Result { + pub async fn owner(&self) -> anyhow::Result { let (tx, rx) = oneshot::channel(); self.commands.send(SessionCommand::GetOwner(tx)).await?; @@ -473,7 +592,7 @@ impl SessionHandle { } /// Retrieve the player handle from the session - pub async fn player(&self) -> Result { + pub async fn player(&self) -> anyhow::Result { let (tx, rx) = oneshot::channel(); self.commands.send(SessionCommand::GetPlayer(tx)).await?; @@ -481,7 +600,7 @@ impl SessionHandle { Ok(result) } - pub async fn active(&self) -> Result { + pub async fn active(&self) -> anyhow::Result { let (tx, rx) = oneshot::channel(); self.commands.send(SessionCommand::GetActive(tx)).await?; @@ -492,13 +611,15 @@ impl SessionHandle { /// Instruct the session to make another user owner. /// /// This will fail if the session still has an active user assigned to it. - pub async fn reactivate(&self, new_owner: UserId) -> Result<()> { + pub async fn reactivate(&self, new_owner: UserId) -> anyhow::Result<()> { let (tx, rx) = oneshot::channel(); self.commands .send(SessionCommand::Reactivate(new_owner, tx)) .await?; - rx.await? + rx.await??; + + Ok(()) } /// Create a playback embed as a response to an interaction @@ -508,7 +629,7 @@ impl SessionHandle { &self, interaction: &CommandInteraction, behavior: playback_embed::UpdateBehavior, - ) -> Result<()> { + ) -> anyhow::Result<()> { self.commands .send(SessionCommand::CreatePlaybackEmbed( self.clone(), @@ -523,7 +644,7 @@ impl SessionHandle { /// Create a lyrics embed as a response to an interaction /// /// This lyrics embed will automatically retrieve the lyrics and update the embed accordingly - pub async fn create_lyrics_embed(&self, interaction: CommandInteraction) -> Result<()> { + pub async fn create_lyrics_embed(&self, interaction: CommandInteraction) -> anyhow::Result<()> { self.commands .send(SessionCommand::CreateLyricsEmbed(self.clone(), interaction)) .await?; @@ -590,50 +711,3 @@ impl songbird::EventHandler for SessionHandle { None } } - -async fn retrieve_credentials(database: &Database, owner: impl AsRef) -> Result { - let account = database.get_account(&owner).await?; - - let token = if let Some(session_token) = &account.session_token { - match spotify::validate_token(&account.username, session_token).await { - Ok(Some(token)) => { - database - .update_session_token(&account.user_id, &token) - .await?; - - Some(token) - } - Ok(None) => Some(session_token.clone()), - Err(_) => None, - } - } else { - None - }; - - // Request new session token if previous one was invalid or missing - let token = match token { - Some(token) => token, - None => { - let access_token = database.get_access_token(&account.user_id).await?; - let credentials = spotify::request_session_token(Credentials { - username: Some(account.username.to_string()), - auth_type: AuthenticationType::AUTHENTICATION_SPOTIFY_TOKEN, - auth_data: access_token.into_bytes(), - }) - .await?; - - let token = BASE64.encode(credentials.auth_data); - database - .update_session_token(&account.user_id, &token) - .await?; - - token - } - }; - - Ok(Credentials { - username: Some(account.username), - auth_type: AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS, - auth_data: BASE64.decode(token)?, - }) -} diff --git a/spoticord_session/src/manager.rs b/spoticord_session/src/manager.rs index 01afb7e..4c47825 100644 --- a/spoticord_session/src/manager.rs +++ b/spoticord_session/src/manager.rs @@ -1,4 +1,5 @@ -use anyhow::Result; +use super::{Session, SessionHandle}; +use crate::error::Result; use serenity::all::{ChannelId, GuildId, UserId}; use songbird::Songbird; use spoticord_database::Database; @@ -7,8 +8,6 @@ use std::{ sync::{Arc, Mutex}, }; -use super::{Session, SessionHandle}; - #[derive(Clone)] pub struct SessionManager { songbird: Arc, diff --git a/spoticord_stats/Cargo.toml b/spoticord_stats/Cargo.toml index ee330c0..037645c 100644 --- a/spoticord_stats/Cargo.toml +++ b/spoticord_stats/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord_stats" -version = "2.2.4" +version = "2.2.5" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/spoticord_utils/Cargo.toml b/spoticord_utils/Cargo.toml index 0ab8c3c..7061116 100644 --- a/spoticord_utils/Cargo.toml +++ b/spoticord_utils/Cargo.toml @@ -1,12 +1,7 @@ [package] name = "spoticord_utils" -version = "2.2.4" +version = "2.2.5" edition = "2021" [dependencies] -librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false } -anyhow = "1.0.86" -base64 = "0.22.1" -log = "0.4.22" poise = "0.6.1" -tokio = { version = "1.40.0", features = ["time"], default-features = false } diff --git a/spoticord_utils/src/lib.rs b/spoticord_utils/src/lib.rs index afc59c3..14f1673 100644 --- a/spoticord_utils/src/lib.rs +++ b/spoticord_utils/src/lib.rs @@ -1,5 +1,4 @@ pub mod discord; -pub mod spotify; use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/spoticord_utils/src/spotify.rs b/spoticord_utils/src/spotify.rs deleted file mode 100644 index f9b0333..0000000 --- a/spoticord_utils/src/spotify.rs +++ /dev/null @@ -1,78 +0,0 @@ -use anyhow::Result; -use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; -use librespot::{ - core::{Session, SessionConfig}, - discovery::Credentials, - protocol::authentication::AuthenticationType, -}; -use log::debug; -use std::time::Duration; - -pub async fn validate_token( - username: impl Into, - token: impl Into, -) -> Result> { - let auth_data = BASE64.decode(token.into())?; - - let credentials = Credentials { - username: Some(username.into()), - auth_type: AuthenticationType::AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS, - auth_data, - }; - - debug!("Validating session token for {:?}", credentials.username); - - let new_credentials = request_session_token(credentials.clone()).await?; - - if credentials.auth_data != new_credentials.auth_data { - debug!("New session token retrieved for {:?}", credentials.username); - - return Ok(Some(BASE64.encode(new_credentials.auth_data))); - } - - Ok(None) -} - -pub async fn request_session_token(credentials: Credentials) -> Result { - debug!("Requesting session token for {:?}", credentials.username); - - let session = Session::new(SessionConfig::default(), None); - let mut tries = 0; - - Ok(loop { - match connect(&session, credentials.clone()).await { - Ok(creds) => break creds, - Err(e) => { - tries += 1; - if tries > 3 { - return Err(e); - } - - tokio::time::sleep(Duration::from_millis(100)).await; - } - } - }) -} - -/// Wrapper around session connecting that times out if an operation is still busy after 3 seconds -async fn connect(session: &Session, credentials: Credentials) -> Result { - const TIMEOUT: Duration = Duration::from_secs(3); - - let (host, port) = - tokio::time::timeout(TIMEOUT, session.apresolver().resolve("accesspoint")).await??; - - // `connect` already has a 3 second timeout internally - let mut transport = librespot::core::connection::connect(&host, port, None).await?; - - let creds = tokio::time::timeout( - TIMEOUT, - librespot::core::connection::authenticate( - &mut transport, - credentials.clone(), - &session.config().device_id, - ), - ) - .await??; - - Ok(creds) -} diff --git a/src/commands/music/join.rs b/src/commands/music/join.rs index d522f1a..a7095fb 100644 --- a/src/commands/music/join.rs +++ b/src/commands/music/join.rs @@ -207,14 +207,18 @@ pub async fn join(ctx: Context<'_>) -> Result<()> { { error!("Failed to create session: {why}"); + let description = if matches!(why, spoticord_session::error::Error::AuthenticationFailed) { + "Unable to authenticate with Spotify. Did you change your password?\n\nThe broken credentials used have been deleted.\n\nYou might need to relink your account using `/link`." + } else { + "An error occured whilst trying to create a session. Please try again." + }; + ctx.send( CreateReply::default() .embed( CreateEmbed::new() .title("Failed to create session") - .description( - "An error occured whilst trying to create a session. Please try again.", - ) + .description(description) .color(Colors::Error), ) .ephemeral(true),