Repository: Spotifyd/spotifyd (language: rust)

Issue:
MPRIS commands are a bit slow
**Description**
MPRIS commands to spotifyd usually take aroud half a second, sometimes longer. I'd expect it to react almost instantaneously.

**To Reproduce**
For example with `playerctl status`

**Versions**
- OS: Arch Linux
- Spotifyd: 0.3.0-1 from Arch repo
Upgrade to librespot 0.5
There are a number of breaking changes in the Librespot 0.4 to 0.5 release.
There are 3 TODO comments that correspond to future feature work. I tried to do the minimal changes to get this working.
mpris interface unresponsive (maybe due to credential cache panic )
**Description**
the MPRIS interface on systembus becomes unresponsive after some idle time over the night ( the instance is still visible but not introspectable) 

`busctl introspect org.mpris.MediaPlayer2.spotifyd.instance365 /org/mpris/MediaPlayer2 
Failed to introspect object /org/mpris/MediaPlayer2 of service org.mpris.MediaPlayer2.spotifyd.instance365: Connection timed out`

there is an error about credential caching in the log which I do not understand

then I tried this
`busctl call org.mpris.MediaPlayer2.spotifyd.instance365 /rs/spotifyd/Controls rs.spotifyd.Controls TransferPlayback 
Call failed: Connection timed out
`
and then spotifyd crashed and was restarted by my systemd config after that it was working properly again


**To Reproduce**
1. start spotifyd fresh with mpris on systembus
2. play music with the android spotify app and select spotifyd as device
3. pause music and wait 8 hours without letting anything different connect to spotify
4. try to run the first busctl introspect command for your given instance 3 times getting a timeout
5. try to run the second busctl command getting again a timeout
6. see the logs in my case spotifyd had crashed and then started working again

**Expected behavior**
mpris should be instrospectable and responsive all the time
if there is a spotify api login problem it should try to automatically login again

**Logs**
<details><summary>

</summary>

```
Dec 13 09:59:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                 
Dec 13 10:01:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                          
Dec 13 10:03:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                          
Dec 13 10:05:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                          
Dec 13 10:06:34 DietPi spotifyd[365]: Token cache write ignored (not configured)                                                                                                                          
Dec 13 10:06:34 DietPi spotifyd[365]: The application panicked (crashed).                                                                                                                                 
Dec 13 10:06:34 DietPi spotifyd[365]: Message:  RSpotify not authenticated                                                                                                                                
Dec 13 10:06:34 DietPi spotifyd[365]: Location: /root/.cargo/registry/src/index.crates.io-1cd66030c949c28d/rspotify-0.12.0/src/clients/base.rs:106                                                        
Dec 13 10:06:34 DietPi spotifyd[365]: Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.                                                                                    
Dec 13 10:06:34 DietPi spotifyd[365]: Run with RUST_BACKTRACE=full to include source snippets.                                                                                                            
Dec 13 10:07:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                          
Dec 13 10:09:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                          
Dec 13 10:11:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                          
Dec 13 10:13:50 DietPi spotifyd[365]: Session[0] strong=4 weak=2                                                                                                                                          
Dec 13 10:15:14 DietPi spotifyd[365]: kMessageTypePlay "" webapi-44219fb334174bc6b2c634d8f9e4f6eb 1651865768 1702457260546 kPlayStatusPause                                                               
Dec 13 10:15:14 DietPi spotifyd[365]: Sending status to server: [kPlayStatusStop]                                                                                                                         
Dec 13 10:15:14 DietPi spotifyd[365]: The application panicked (crashed).                                                                                                                                 
Dec 13 10:15:14 DietPi spotifyd[365]: Message:  called `Result::unwrap()` on an `Err` value: PoisonError { .. }                                                                                           
Dec 13 10:15:14 DietPi spotifyd[365]: Location: src/dbus_mpris.rs:84                                                                                                                                      
Dec 13 10:15:14 DietPi spotifyd[365]: Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.                                                                                    
Dec 13 10:15:14 DietPi spotifyd[365]: Run with RUST_BACKTRACE=full to include source snippets.                                                                                                            
Dec 13 10:15:14 DietPi spotifyd[365]: drop Spirc[0]                                                                                                                                                       
Dec 13 10:15:14 DietPi spotifyd[365]: Shutting down player thread ...                                                                                                                                     
Dec 13 10:15:14 DietPi spotifyd[365]: drop PlayerInternal[0]                                                                                                                                              
Dec 13 10:15:14 DietPi spotifyd[365]: PlayerInternal thread finished.                                                                                                                                     
Dec 13 10:15:14 DietPi spotifyd[365]: drop Session[0]                                                                                                                                                     
Dec 13 10:15:14 DietPi spotifyd[365]: drop MercuryManager                                                                                                                                                 
Dec 13 10:15:14 DietPi spotifyd[365]: drop Dispatch                                                                                                                                                       
Dec 13 10:15:14 DietPi systemd[1]: spotifyd.service: Main process exited, code=exited, status=101/n/a                                                                                                     
Dec 13 10:15:14 DietPi systemd[1]: spotifyd.service: Failed with result 'exit-code'.                                                                                                                      
Dec 13 10:15:19 DietPi systemd[1]: spotifyd.service: Scheduled restart job, restart counter is at 1.                                                                                                      
Dec 13 10:15:19 DietPi systemd[1]: Stopped Spotifyd (DietPi).                                                                                                                                             
Dec 13 10:15:19 DietPi systemd[1]: Started Spotifyd (DietPi).                                                                                                                                             
Dec 13 10:15:19 DietPi spotifyd[978]: Loading config from "/mnt/dietpi_userdata/spotifyd/spotifyd.conf"                                                                                                   
Dec 13 10:15:19 DietPi spotifyd[978]: CliConfig { config_path: Some("/mnt/dietpi_userdata/spotifyd/spotifyd.conf"), no_daemon: true, verbose: true, pid: None, shared_config: SharedConfigValues { usernam
e: Some("taken out for privacy"), username_cmd: None, password: None, password_cmd: None, use_keyring: false, use_mpris: Some(true), dbus_type: Some(System), on_song_change_hook: None, cache_path: Some(
"/mnt/dietpi_userdata/spotifyd/cache"), no-audio-cache: true, backend: Some(Alsa), volume_controller: Some(SoftVolume), device: None, control: None, mixer: None, device_name: Some("DietPi"), bitrate: So
me(Bitrate320), audio_format: None, initial_volume: Some("40"), volume_normalisation: false, normalisation_pregain: None, zeroconf_port: None, proxy: None, device_type: Some(Speaker), autoplay: true, ma
x_cache_size: None } }                                                                                                                                                                                    
Dec 13 10:15:19 DietPi spotifyd[978]: Found user shell: Some("/bin/sh")                                                                                                                                   
Dec 13 10:15:19 DietPi spotifyd[978]: No password specified. Checking password_cmd                                                                                                                        
Dec 13 10:15:19 DietPi spotifyd[978]: No password_cmd specified                                                                                                                                           
Dec 13 10:15:19 DietPi spotifyd[978]: No proxy specified                                                                                                                                                  
Dec 13 10:15:19 DietPi spotifyd[978]: Using software volume controller.                                                                                                                                   
Dec 13 10:15:19 DietPi spotifyd[978]: Ignoring blacklisted access point ap-gew4.spotify.com:4070                                                                                                          
Dec 13 10:15:19 DietPi spotifyd[978]: Ignoring blacklisted access point ap-gew4.spotify.com:443                                                                                                           
Dec 13 10:15:19 DietPi spotifyd[978]: Ignoring blacklisted access point ap-gew4.spotify.com:80                                                                                                            
Dec 13 10:15:19 DietPi spotifyd[978]: Ignoring blacklisted access point ap-gue1.spotify.com:443                                                                                                           
Dec 13 10:15:19 DietPi spotifyd[978]: Failed to resolve Access Point: Unable to resolve any viable access points.                                                                                         
Dec 13 10:15:19 DietPi spotifyd[978]: Using fallback "ap.spotify.com:443"                                                                                                                                 
Dec 13 10:15:19 DietPi spotifyd[978]: Connecting to AP "ap.spotify.com:443"                                                                                                                               
Dec 13 10:15:20 DietPi spotifyd[978]: Authenticated as "xxxxx" !
Dec 13 10:15:20 DietPi spotifyd[978]: new Session[0]
Dec 13 10:15:20 DietPi spotifyd[978]: Session[0] strong=2 weak=1

<!-- They have to be betwenn the `/summary` and the `/details` HTML tags, and inside the backticks '`' -->  
```

</details>

<!-- if you compiled spotifyd yourself. Alsa backend enabled by default unless compiled with the `--no-default-features` flag -->
**Compilation flags** 
- [x] dbus_mpris
- [ ] dbus_keyring
- [x] alsa_backend
- [ ] portaudio_backend
- [ ] pulseaudio_backend
- [ ] rodio_backend

**Versions (please complete the following information):**
<!-- DO NOT use words like `latest`. Please specify the exact version/commit hash -->
- OS: DietPi 8.0.2 (raspbian bullseye armv6 image) on RaspberryPi 1B
- Spotifyd: 5565f24 armv6 (arm-unknown-linux-gnueabihf version of rust on armv6 raspbian image)
- cargo: cargo 1.74.1 (ecb9851af 2023-10-18)

<!-- **Additional context**
Add any other context about the problem here. -->

Cannot run spotifyd with multiple IPs on the same interface
**Description**
Cannot launch spotifyd with discovery when there are 2 IPs (i.e. 2 IPv4 ) on the same interface. It gives the following error:
```
Message:  called `Result::unwrap()` on an `Err` value: DnsSdError(Os { code: 98, kind: AddrInUse, message: "Address already in use" })
``` 

**To Reproduce**

1. Run `spotifyd --no-daemon` and ensure it works. Close the program.
2. Add an IPv4 to your network interface (e.g. `sudo ip add 192.168.1.123/24 dev enp1s0`)
3. Run `spotifyd --no-daemon` again and it will get the error above.

**Expected behavior**
If the Zeroconf server is already listening on all interfaces with `0.0.0.0` it should not try to bind to the other IP.

**Logs**
<details><summary>Click to show logs</summary>

```
<!-- PLEASE PASTE YOUR LOGS BELOW THIS LINE WHEN REPORTING BUGS. Make sure to run spotifyd using the `--verbose` flag -->
spotifyd --no-daemon --verbose
No config file specified. Running with default values
CliConfig { config_path: None, no_daemon: true, verbose: true, pid: None, shared_config: SharedConfigValues { username: None, username_cmd: None, password: None, password_cmd: None, use_keyring: false, use_mpris: None, dbus_type: None, on_song_change_hook: None, cache_path: None, no-audio-cache: false, backend: None, volume_controller: None, device: None, control: None, mixer: None, device_name: None, bitrate: None, audio_format: None, initial_volume: None, volume_normalisation: false, normalisation_pregain: None, zeroconf_port: None, proxy: None, device_type: None, autoplay: false, max_cache_size: None } }
Found user shell: Some("/run/current-system/sw/bin/bash")
No username specified. Checking username_cmd
No username_cmd specified
No password specified. Checking password_cmd
No password_cmd specified
No proxy specified
registering event source with poller: token=Token(2147483649), interests=READABLE
Using software volume controller.
no usable credentials found, enabling discovery
Using device id '3ad21dfd52db1f2f20d8a9815c37bc58c8d0b643'
registering event source with poller: token=Token(0), interests=READABLE | WRITABLE
Zeroconf server listening on 0.0.0.0:35523
registering event source with poller: token=Token(1), interests=READABLE | WRITABLE
deregistering event source from poller
The application panicked (crashed).
Message:  called `Result::unwrap()` on an `Err` value: DnsSdError(Os { code: 98, kind: AddrInUse, message: "Address already in use" })
Location: src/setup.rs:105

Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.
Shutting down discovery server
deregistering event source from poller
<!-- They have to be betwenn the `/summary` and the `/details` HTML tags, and inside the backticks '`' -->  
```

</details>

<!-- if you compiled spotifyd yourself. Alsa backend enabled by default unless compiled with the `--no-default-features` flag -->
**Compilation flags** 
- [ ] dbus_mpris
- [ ] dbus_keyring
- [x] alsa_backend
- [ ] portaudio_backend
- [ ] pulseaudio_backend
- [ ] rodio_backend

**Versions (please complete the following information):**
<!-- DO NOT use words like `latest`. Please specify the exact version/commit hash -->
- OS: `NixOS 23.11`
- Spotifyd: `spotifyd 0.3.5`
- cargo: <!-- cargo --version -->

<!-- **Additional context**
Add any other context about the problem here. -->



Discussion / hints:
Also worth noting that (at least for me) spotifyd jumps to 100% CPU usage during the delay and then returns to normal usage.

![image](https://user-images.githubusercontent.com/48367298/108641531-71454b00-747e-11eb-851b-971933bbb5eb.png)

i think every mpris command does a network request to the spotify api to get the info, so it makes sense it is slow. A better method should be made for this, either caching or preemptively fetch the info on song change.

@matbme Thats weird and definitly a bug!
FWIW, it seems not to be *all* commands. I've found that `Play` and `PlayPause` (when it works: see #940) are virtually instant, but I'm seeing the half-second delay for others.
Turns out playerctl might be at fault here.

MPRIS commands sent with dbus-send are instantaneous, but the same commands sent with playerctl are uncomfortably slow.

playerctl sends a DBus `GetAll` request  before dispatching the actual MPRIS command, which queries all the properties exposed by the DBus interface (In this case, [org.mpris.MediaPlayer2.Player](https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#properties)), which spotifyd resolves with an obscene amount of HTTP requests.

Maybe spotifyd should implement a workaround for `GetAll`, maybe playerctl should stop dispatching useless requests, you decide ¯\\\_(ツ)\_/¯.

In the meantime, users can substitute playerctl with dbus-send (Don't forget to replace `$PID` and `$COMMAND`):
```
dbus-send --print-reply --dest="org.mpris.MediaPlayer2.spotifyd.instance$PID" /org/mpris/MediaPlayer2 "org.mpris.MediaPlayer2.Player.$COMMAND"
```
Thank you for the investigations! I did a small write-up about the current state of things in `spotifyd`, which might shed some additional light on what is happening behind the scenes [here](https://github.com/Spotifyd/spotifyd/issues/1163#issuecomment-1384583632). Nothing that will solve your problems, unfortunately. ;)
I have to correct myself; playerctl's `GetAll` requests are actually necessary and serve the purpose of finding which DBUS applications are MediaPlayer2 instances.
> In the meantime, users can substitute playerctl with dbus-send (Don't forget to replace `$PID` and `$COMMAND`):
> 
> ```
> dbus-send --print-reply --dest="org.mpris.MediaPlayer2.spotifyd.instance$PID" /org/mpris/MediaPlayer2 "org.mpris.MediaPlayer2.Player.$COMMAND"
> ```

I can also confirm this behavior, so for now I've done the same using the below as a template:

`dbus-send --print-reply --dest="org.mpris.MediaPlayer2.spotifyd.instance$(pgrep spotifyd)" /org/mpris/MediaPlayer2 "org.mpris.MediaPlayer2.Player.PlayPause"`
I have the same problem, but the delay is around 4s (yes, it's really annoying). Even using `dbus-send`do not solve the problem. Any update on this?

Edit: I should have mentioned that I'm on arch and I'm using the `spotifyd`package from the `extra` repository (v 0.3.5)
We're mainly waiting for a librespot release here, since then we'll get access to all necessary information to dispatch commands locally.

I see the same issue. I started playback using the TransferPlayback dbus method and after some time (much less than 8 hours) the log reports a panic, but the process is still alive so it doesn't get restarted.
It didn't crash again when doing introspect and TransferPlayback, but as soon as I opened the Spotify app on another machine I got the second panic and the process died.

Here it is with the backtrace enabled
<details>
   <summary>Backtrace</summary>

```
Couldn't fetch metadata from spotify: Nothing playing at the moment.
Token cache write ignored (not configured)
The application panicked (crashed).
Message:  RSpotify not authenticated
Location: /home/daniel/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rspotify-0.12.0/src/clients/base.rs:106

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                                ⋮ 8 frames hidden ⋮
   9: core::panicking::panic_display::h2c6e530b26584522
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/core/src/panicking.rs:196
  10: core::panicking::panic_str::h4c43cc2df5473fd1
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/core/src/panicking.rs:171
  11: core::option::expect_failed::h39c56eaa3bce6881
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/core/src/option.rs:1988
  12: rspotify::clients::base::BaseClient::auth_headers::hadc1668e0558c207
      at <unknown source file>:<unknown line>
  13: rspotify::clients::base::BaseClient::api_get::h9b23681b5b7dd344
      at <unknown source file>:<unknown line>
  14: spotifyd::dbus_mpris::get_device_id::h325050474d62ce30
      at <unknown source file>:<unknown line>
  15: dbus_crossroads::ifacedesc::IfaceBuilder<T>::method_with_cr::{{closure}}::ha001171c8a86d58c
      at <unknown source file>:<unknown line>
  16: dbus_crossroads::crossroads::Crossroads::handle_message_inner::hdd316cc5c3a84b5e
      at <unknown source file>:<unknown line>
  17: dbus_crossroads::crossroads::Crossroads::handle_message::hda6f3896ef4ed02f
      at <unknown source file>:<unknown line>
  18: spotifyd::dbus_mpris::create_dbus_server::{{closure}}::{{closure}}::h2f08b8943cbbdcd2
      at <unknown source file>:<unknown line>
  19: <dbus::nonblock::SyncConnection as dbus::nonblock::Process>::process_one::h1c8860bd6188ec2d
      at <unknown source file>:<unknown line>
  20: <dbus_tokio::connection::IOResource<C> as core::future::future::Future>::poll::h58d27e434e2502c6
      at <unknown source file>:<unknown line>
  21: spotifyd::dbus_mpris::create_dbus_server::{{closure}}::{{closure}}::h88aa61c939a0c92d
      at <unknown source file>:<unknown line>
  22: tokio::runtime::task::core::Core<T,S>::poll::h2e2c8cdcb2917e71
      at <unknown source file>:<unknown line>
  23: tokio::runtime::task::harness::Harness<T,S>::poll::h9fec6ef6669f3748
      at <unknown source file>:<unknown line>
  24: tokio::runtime::scheduler::multi_thread::worker::Context::run_task::h866b916a77bae828
      at <unknown source file>:<unknown line>
  25: tokio::runtime::scheduler::multi_thread::worker::Context::run::h08ee4a2fe6d3662a
      at <unknown source file>:<unknown line>
  26: tokio::runtime::context::scoped::Scoped<T>::set::h947f25f0a8746071
      at <unknown source file>:<unknown line>
  27: tokio::runtime::context::runtime::enter_runtime::h825bec3258685d62
      at <unknown source file>:<unknown line>
  28: tokio::runtime::scheduler::multi_thread::worker::run::h5c562ff06d9e8e2c
      at <unknown source file>:<unknown line>
  29: <tokio::runtime::blocking::task::BlockingTask<T> as core::future::future::Future>::poll::hba477662a9b8bfe5
      at <unknown source file>:<unknown line>
  30: tokio::runtime::task::core::Core<T,S>::poll::h4ab980a4bdf7aac5
      at <unknown source file>:<unknown line>
  31: tokio::runtime::task::harness::Harness<T,S>::poll::h2c319639f591823e
      at <unknown source file>:<unknown line>
  32: tokio::runtime::blocking::pool::Inner::run::hffcdd47c378bf96a
      at <unknown source file>:<unknown line>
  33: std::sys_common::backtrace::__rust_begin_short_backtrace::ha869a53ca4b5b754
      at <unknown source file>:<unknown line>
  34: core::ops::function::FnOnce::call_once{{vtable.shim}}::h75b4f91f1a148f7f
      at <unknown source file>:<unknown line>
  35: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::h65e29fa99a122141
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/alloc/src/boxed.rs:2015
  36: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::h0e96a7fd708b99fb
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/alloc/src/boxed.rs:2015
  37: std::sys::pal::unix::thread::Thread::new::thread_start::h1a4b48df802ee0a8
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/sys/pal/unix/thread.rs:108
  38: start_thread<unknown>
      at ./nptl/pthread_create.c:442
  39: thread_start<unknown>
      at ./misc/../sysdeps/unix/sysv/linux/aarch64/clone.S:79

Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.
The application panicked (crashed).
Message:  called `Result::unwrap()` on an `Err` value: PoisonError { .. }
Location: src/dbus_mpris.rs:84

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                                ⋮ 9 frames hidden ⋮
  10: <spotifyd::dbus_mpris::DbusServer as core::future::future::Future>::poll::h9b68c668beb53c18
      at <unknown source file>:<unknown line>
  11: <tokio::future::poll_fn::PollFn<F> as core::future::future::Future>::poll::hb787d52c83951b93
      at <unknown source file>:<unknown line>
  12: tokio::runtime::runtime::Runtime::block_on::h5c2d6b3a3959e000
      at <unknown source file>:<unknown line>
  13: spotifyd::main::h9bb04d2e3cab8d76
      at <unknown source file>:<unknown line>
  14: std::sys_common::backtrace::__rust_begin_short_backtrace::h50466c551e020831
      at <unknown source file>:<unknown line>
  15: std::rt::lang_start::{{closure}}::hf00986dc282a29a9
      at <unknown source file>:<unknown line>
  16: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::hdbbf4bc5b948549a
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/core/src/ops/function.rs:284
  17: std::panicking::try::do_call::hd9aa31d1e8310f6a
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/panicking.rs:554
  18: std::panicking::try::h5b4be1ce5148e504
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/panicking.rs:518
  19: std::panic::catch_unwind::h66d85bc43d2d9f6e
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/panic.rs:142
  20: std::rt::lang_start_internal::{{closure}}::h2b8a5b503f27f998
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/rt.rs:148
  21: std::panicking::try::do_call::hd7f77e71a4e73fa4
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/panicking.rs:554
  22: std::panicking::try::h63f06620cf880361
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/panicking.rs:518
  23: std::panic::catch_unwind::h4b03c83abc824798
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/panic.rs:142
  24: std::rt::lang_start_internal::h0ddfe42b2029814a
      at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/rt.rs:148
  25: main<unknown>
      at <unknown source file>:<unknown line>
  26: __libc_start_call_main<unknown>
      at ./csu/../sysdeps/nptl/libc_start_call_main.h:58
  27: __libc_start_main_impl<unknown>
      at ./csu/../csu/libc-start.c:360
 ```
</details>
Hmm, I think I see the problem in the current code: When the application is idle, we currently don't refresh the token and as such, the dbus calls make use of an invalid token.
We are seeing the same thing with our recent build, when MPRIS is polled but spotifyd has been idle for awhile.

```
amplipi authbind[3093]: The application panicked (crashed).
May 06 15:09:59 amplipi authbind[3093]: Message:  called `Result::unwrap()` on an `Err` value: PoisonError { .. }
May 06 15:09:59 amplipi authbind[3093]: Location: src/dbus_mpris.rs:84
May 06 15:09:59 amplipi authbind[3093]: Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.
May 06 15:09:59 amplipi authbind[3093]: Run with RUST_BACKTRACE=full to include source snippets.
```
I am experiencing the same problem and I am willing to take a stab at it. @eladyn any pointers on where/how to solve it?
@2opremio I'm definitely not immersed in the issue right now, but if I recall correctly, the problem is that the refresh token is only refreshed when the Player sends out `PlayerEvents`. When there is no playback and only mpris calls however, no refreshing takes place... If you need more guidance, I can surely have a look tomorrow or you just join the Matrix room so we can discuss over there.
The workaround for us was to subscribe to the `PropertiesChanged` event on the `MPRIS` interface instead of just polling on some interval. Honestly this is probably the better way to do it.  If I recall how this works is by the time the `PropertiesChanged` event is fired the token has already been refreshed. Here's a link to the python we use to interface with MRPIS now: https://github.com/micro-nova/AmpliPi/blob/main/streams/MPRIS_metadata_reader.py
thanks for sharing @linknum23 but I honestly don't understand why this would be a workaround for the issue I described??
The issues does not involve polling and I was not really interested in the changes of properties but I wanted to call DBUS functions and let a simple and custom piece of media-controller software inspect the current MPRIS players, their capabilities and functions available and the identity string that does exist for that client. 

As note/ food for thought for you w.r.t. your use case (track metadata?):
given the indicator that the issue is rooted in invalid credentials/token fetching the metadata via the API and the exposing it via MPRIS I have doubts that the PropertiesChanged event is being triggered after the cache is invalid because it can not fetch the new metadata from the API and therefore would not know about any changes. I agree with you that once the token is fresh it can detect the changes and likely emit the signal, but the token will not refresh on its own, without further external stimuli (e.g. controlling via spotify connect) - that seems to be the issue. But maybe that  external stimulus is inherent to your use case and that could be why it seems to work for you.
Sorry I probably could have given more context on that. You are correct, our use case is tracking the session metadata. For us, using `PropertiesChanged` avoids interfacing to the dbus interface when it is in this bad state. A play request from either Spotify connect or through spotifyd provides the external stimulus in our case.  
I'm also affected by either this or perhaps #247. Perhaps this behavior or zerotier itself conflicts with tailscale?
I think I am affected by this also. I have a virtual machine bridge set up that has 2 IP addresses. Using discovery is important now that user/password is not working anymore. I have been trying both with the Spotify web client and the Spotify Linux client running. Also see #51 
OS: arch linux
Spotifyd: 0.3.5

Using ip -c a

> 2: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master vmbr01 state UP group default qlen 1000
    link/ether d4:5d:64:28:82:35 brd ff:ff:ff:ff:ff:ff
3: vmbr01: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 0a:2e:28:78:f3:81 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.5/24 brd 192.168.1.255 scope global vmbr01
       valid_lft forever preferred_lft forever
    inet 192.168.1.10/24 brd 192.168.1.255 scope global secondary vmbr01
       valid_lft forever preferred_lft forever
    inet6 fe80::82e:28ff:fe78:f381/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

EDIT:  I am confirming that when I removed the 192.168.1.10 address, spotifyd --no-daemon did go into discovery mode and the device showed up in the Spotify client.

I created a PR over at librespot-org/libmdns#58 which should fix this issue. Now all we have to do is wait for it to get merged, adapted in `librespot` and update to the new `librespot` version. But if someone has the time, any testing of this PR would be greatly appreciated. (Just do `cargo run --example register` on the PR)
@eladyn [Release 0.5.0 of librespot](https://github.com/librespot-org/librespot/releases/tag/v0.5.0) has been released which might fix this issue 
Oh, that's great to hear. I'll try to get to it during the weekend, though if someone else is willing to have a go at it until then, always do!

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4a9ce99a..d551d6e8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -55,7 +55,7 @@ jobs:
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        rust: [stable, 1.67]
+        rust: [stable, 1.81]
         os: [macos-latest, ubuntu-latest]
         include:
           - os: macos-latest
diff --git a/Cargo.lock b/Cargo.lock
index c7533d3a..6b177e3f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19,90 +19,34 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
 name = "aes"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
-dependencies = [
- "aes-soft",
- "aesni",
- "cipher 0.2.5",
-]
-
-[[package]]
-name = "aes"
-version = "0.7.5"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
 dependencies = [
  "cfg-if",
- "cipher 0.3.0",
+ "cipher",
  "cpufeatures",
- "opaque-debug",
-]
-
-[[package]]
-name = "aes-ctr"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7729c3cde54d67063be556aeac75a81330d802f0259500ca40cb52967f975763"
-dependencies = [
- "aes-soft",
- "aesni",
- "cipher 0.2.5",
- "ctr",
-]
-
-[[package]]
-name = "aes-soft"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
-dependencies = [
- "cipher 0.2.5",
- "opaque-debug",
-]
-
-[[package]]
-name = "aesni"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
-dependencies = [
- "cipher 0.2.5",
- "opaque-debug",
 ]
 
 [[package]]
 name = "aho-corasick"
-version = "1.0.5"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "alsa"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b"
-dependencies = [
- "alsa-sys",
- "bitflags 1.3.2",
- "libc",
- "nix 0.23.2",
-]
-
-[[package]]
-name = "alsa"
-version = "0.7.1"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47"
+checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
 dependencies = [
  "alsa-sys",
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
+ "cfg-if",
  "libc",
- "nix 0.24.3",
 ]
 
 [[package]]
@@ -140,132 +84,82 @@ dependencies = [
 ]
 
 [[package]]
-name = "async-broadcast"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b"
-dependencies = [
- "event-listener",
- "futures-core",
-]
-
-[[package]]
-name = "async-channel"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
-dependencies = [
- "concurrent-queue",
- "event-listener",
- "futures-core",
-]
-
-[[package]]
-name = "async-executor"
-version = "1.5.1"
+name = "anstream"
+version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
 dependencies = [
- "async-lock",
- "async-task",
- "concurrent-queue",
- "fastrand 1.9.0",
- "futures-lite",
- "slab",
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
 ]
 
 [[package]]
-name = "async-fs"
-version = "1.6.0"
+name = "anstyle"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
-dependencies = [
- "async-lock",
- "autocfg",
- "blocking",
- "futures-lite",
-]
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
 
 [[package]]
-name = "async-io"
-version = "1.13.0"
+name = "anstyle-parse"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
 dependencies = [
- "async-lock",
- "autocfg",
- "cfg-if",
- "concurrent-queue",
- "futures-lite",
- "log",
- "parking",
- "polling",
- "rustix 0.37.26",
- "slab",
- "socket2 0.4.9",
- "waker-fn",
+ "utf8parse",
 ]
 
 [[package]]
-name = "async-lock"
-version = "2.8.0"
+name = "anstyle-query"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
 dependencies = [
- "event-listener",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
-name = "async-process"
-version = "1.7.0"
+name = "anstyle-wincon"
+version = "3.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
 dependencies = [
- "async-io",
- "async-lock",
- "autocfg",
- "blocking",
- "cfg-if",
- "event-listener",
- "futures-lite",
- "rustix 0.37.26",
- "signal-hook",
- "windows-sys",
+ "anstyle",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
-name = "async-recursion"
-version = "1.0.4"
+name = "anyhow"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.29",
-]
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
 
 [[package]]
-name = "async-task"
-version = "4.4.0"
+name = "arrayvec"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
 [[package]]
 name = "async-trait"
-version = "0.1.73"
+version = "0.1.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
+checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "atomic-waker"
-version = "1.1.1"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
 
 [[package]]
 name = "atty"
@@ -273,22 +167,48 @@ version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 dependencies = [
- "hermit-abi 0.1.19",
+ "hermit-abi",
  "libc",
  "winapi",
 ]
 
 [[package]]
 name = "autocfg"
-version = "1.1.0"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "aws-lc-rs"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f409eb70b561706bf8abba8ca9c112729c481595893fd06a2dd9af8ed8441148"
+dependencies = [
+ "aws-lc-sys",
+ "paste",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "8478a5c29ead3f3be14aff8a202ad965cf7da6856860041bfca271becf8ba48b"
+dependencies = [
+ "bindgen 0.69.5",
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+ "libc",
+ "paste",
+]
 
 [[package]]
 name = "backtrace"
-version = "0.3.69"
+version = "0.3.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
 dependencies = [
  "addr2line",
  "cc",
@@ -307,50 +227,74 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "base64"
-version = "0.21.3"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 
 [[package]]
 name = "bindgen"
-version = "0.64.0"
+version = "0.69.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
+checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
  "cexpr",
  "clang-sys",
+ "itertools 0.12.1",
  "lazy_static",
  "lazycell",
- "peeking_take_while",
+ "log",
+ "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
  "rustc-hash",
  "shlex",
- "syn 1.0.109",
+ "syn 2.0.93",
+ "which",
 ]
 
 [[package]]
-name = "bitflags"
-version = "1.3.2"
+name = "bindgen"
+version = "0.70.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
+dependencies = [
+ "bitflags 2.6.0",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.13.0",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 2.0.93",
+]
 
 [[package]]
 name = "bitflags"
-version = "2.4.0"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
-name = "block-buffer"
-version = "0.9.0"
+name = "bitflags"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
-dependencies = [
- "generic-array",
-]
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "block-buffer"
@@ -362,62 +306,38 @@ dependencies = [
 ]
 
 [[package]]
-name = "block-modes"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e"
-dependencies = [
- "block-padding",
- "cipher 0.3.0",
-]
-
-[[package]]
-name = "block-padding"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
-
-[[package]]
-name = "blocking"
-version = "1.3.1"
+name = "bumpalo"
+version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65"
-dependencies = [
- "async-channel",
- "async-lock",
- "async-task",
- "atomic-waker",
- "fastrand 1.9.0",
- "futures-lite",
- "log",
-]
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
-name = "bumpalo"
-version = "3.13.0"
+name = "bytemuck"
+version = "1.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
 
 [[package]]
 name = "byteorder"
-version = "1.4.3"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.4.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
 
 [[package]]
 name = "cc"
-version = "1.0.83"
+version = "1.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
 dependencies = [
  "jobserver",
  "libc",
+ "shlex",
 ]
 
 [[package]]
@@ -443,47 +363,38 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.28"
+version = "0.4.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
  "js-sys",
  "num-traits",
  "serde",
- "time 0.1.45",
  "wasm-bindgen",
- "windows-targets",
-]
-
-[[package]]
-name = "cipher"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
-dependencies = [
- "generic-array",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "cipher"
-version = "0.3.0"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
 dependencies = [
- "generic-array",
+ "crypto-common",
+ "inout",
 ]
 
 [[package]]
 name = "clang-sys"
-version = "1.6.1"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
 dependencies = [
  "glob",
  "libc",
- "libloading 0.7.4",
+ "libloading 0.8.6",
 ]
 
 [[package]]
@@ -501,11 +412,20 @@ dependencies = [
  "vec_map",
 ]
 
+[[package]]
+name = "cmake"
+version = "0.1.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "color-eyre"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
+checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
 dependencies = [
  "backtrace",
  "color-spantrace",
@@ -518,9 +438,9 @@ dependencies = [
 
 [[package]]
 name = "color-spantrace"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
+checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
 dependencies = [
  "once_cell",
  "owo-colors",
@@ -529,57 +449,42 @@ dependencies = [
 ]
 
 [[package]]
-name = "combine"
-version = "4.6.6"
+name = "colorchoice"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
-dependencies = [
- "bytes",
- "memchr",
-]
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
 
 [[package]]
-name = "concurrent-queue"
-version = "2.2.0"
+name = "combine"
+version = "4.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
 dependencies = [
- "crossbeam-utils",
+ "bytes",
+ "memchr",
 ]
 
 [[package]]
-name = "cookie"
-version = "0.17.0"
+name = "const-oid"
+version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
-dependencies = [
- "percent-encoding",
- "time 0.3.36",
- "version_check",
-]
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 
 [[package]]
-name = "cookie_store"
-version = "0.20.0"
+name = "core-foundation"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
 dependencies = [
- "cookie",
- "idna 0.3.0",
- "indexmap 1.9.3",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "time 0.3.36",
- "url",
+ "core-foundation-sys",
+ "libc",
 ]
 
 [[package]]
 name = "core-foundation"
-version = "0.9.3"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -587,73 +492,63 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "coreaudio-rs"
-version = "0.10.0"
+version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88"
+checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace"
 dependencies = [
  "bitflags 1.3.2",
+ "core-foundation-sys",
  "coreaudio-sys",
 ]
 
 [[package]]
 name = "coreaudio-sys"
-version = "0.2.12"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24"
+checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b"
 dependencies = [
- "bindgen",
+ "bindgen 0.70.1",
 ]
 
 [[package]]
 name = "cpal"
-version = "0.13.5"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116"
+checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779"
 dependencies = [
- "alsa 0.6.0",
+ "alsa",
  "core-foundation-sys",
  "coreaudio-rs",
+ "dasp_sample",
  "jack",
  "jni",
  "js-sys",
- "lazy_static",
  "libc",
- "mach",
+ "mach2",
  "ndk",
- "ndk-glue",
- "nix 0.23.2",
+ "ndk-context",
  "oboe",
- "parking_lot 0.11.2",
- "stdweb",
- "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
  "web-sys",
- "winapi",
+ "windows 0.54.0",
 ]
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.9"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
 dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
-dependencies = [
- "cfg-if",
-]
-
 [[package]]
 name = "crypto-common"
 version = "0.1.6"
@@ -664,23 +559,13 @@ dependencies = [
  "typenum",
 ]
 
-[[package]]
-name = "crypto-mac"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
-dependencies = [
- "generic-array",
- "subtle",
-]
-
 [[package]]
 name = "ctr"
-version = "0.6.0"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
 dependencies = [
- "cipher 0.2.5",
+ "cipher",
 ]
 
 [[package]]
@@ -694,9 +579,9 @@ dependencies = [
 
 [[package]]
 name = "darling"
-version = "0.13.4"
+version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
 dependencies = [
  "darling_core",
  "darling_macro",
@@ -704,29 +589,41 @@ dependencies = [
 
 [[package]]
 name = "darling_core"
-version = "0.13.4"
+version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
 dependencies = [
  "fnv",
  "ident_case",
  "proc-macro2",
  "quote",
- "strsim 0.10.0",
- "syn 1.0.109",
+ "strsim 0.11.1",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "darling_macro"
-version = "0.13.4"
+version = "0.20.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.93",
 ]
 
+[[package]]
+name = "dasp_sample"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
+
+[[package]]
+name = "data-encoding"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
+
 [[package]]
 name = "dbus"
 version = "0.9.7"
@@ -749,6 +646,19 @@ dependencies = [
  "dbus",
 ]
 
+[[package]]
+name = "dbus-secret-service"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b"
+dependencies = [
+ "dbus",
+ "futures-util",
+ "num",
+ "once_cell",
+ "rand",
+]
+
 [[package]]
 name = "dbus-tokio"
 version = "0.7.6"
@@ -760,6 +670,17 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "der"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+dependencies = [
+ "const-oid",
+ "pem-rfc7468",
+ "zeroize",
+]
+
 [[package]]
 name = "deranged"
 version = "0.3.11"
@@ -770,23 +691,34 @@ dependencies = [
 ]
 
 [[package]]
-name = "derivative"
-version = "2.2.0"
+name = "derive_builder"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
+dependencies = [
+ "derive_builder_macro",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
 dependencies = [
+ "darling",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.93",
 ]
 
 [[package]]
-name = "digest"
-version = "0.9.0"
+name = "derive_builder_macro"
+version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
 dependencies = [
- "generic-array",
+ "derive_builder_core",
+ "syn 2.0.93",
 ]
 
 [[package]]
@@ -795,7 +727,8 @@ version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 dependencies = [
- "block-buffer 0.10.4",
+ "block-buffer",
+ "const-oid",
  "crypto-common",
  "subtle",
 ]
@@ -818,53 +751,62 @@ dependencies = [
  "libc",
  "option-ext",
  "redox_users",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
-name = "enum_dispatch"
-version = "0.3.12"
+name = "displaydoc"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
- "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
 ]
 
 [[package]]
-name = "enumflags2"
-version = "0.7.7"
+name = "dunce"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2"
-dependencies = [
- "enumflags2_derive",
- "serde",
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
 ]
 
 [[package]]
-name = "enumflags2_derive"
-version = "0.7.7"
+name = "env_filter"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745"
+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.29",
+ "log",
+ "regex",
 ]
 
 [[package]]
 name = "env_logger"
-version = "0.10.0"
+version = "0.11.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
 dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
  "humantime",
- "is-terminal",
  "log",
- "regex",
- "termcolor",
 ]
 
 [[package]]
@@ -875,23 +817,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
-dependencies = [
- "errno-dragonfly",
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
 dependencies = [
- "cc",
  "libc",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -903,17 +834,11 @@ dependencies = [
  "version_check",
 ]
 
-[[package]]
-name = "event-listener"
-version = "2.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
-
 [[package]]
 name = "eyre"
-version = "0.6.8"
+version = "0.6.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
+checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
 dependencies = [
  "indenter",
  "once_cell",
@@ -921,24 +846,15 @@ dependencies = [
 
 [[package]]
 name = "fastrand"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
-dependencies = [
- "instant",
-]
-
-[[package]]
-name = "fastrand"
-version = "2.0.0"
+version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
 name = "fern"
-version = "0.6.2"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
+checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29"
 dependencies = [
  "log",
  "syslog",
@@ -958,18 +874,24 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "form_urlencoded"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
 dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
 [[package]]
 name = "futures"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -982,9 +904,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -992,15 +914,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1009,53 +931,44 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
-
-[[package]]
-name = "futures-lite"
-version = "1.13.0"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
-dependencies = [
- "fastrand 1.9.0",
- "futures-core",
- "futures-io",
- "memchr",
- "parking",
- "pin-project-lite",
- "waker-fn",
-]
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
 
 [[package]]
 name = "futures-task"
-version = "0.3.28"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
 
 [[package]]
 name = "futures-util"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1081,60 +994,111 @@ dependencies = [
 
 [[package]]
 name = "gethostname"
-version = "0.4.3"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30"
 dependencies = [
- "libc",
- "windows-targets",
+ "rustix",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "getrandom"
-version = "0.2.10"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
  "cfg-if",
+ "js-sys",
  "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
+ "wasm-bindgen",
 ]
 
 [[package]]
 name = "gimli"
-version = "0.28.0"
+version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
 name = "glob"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
 
 [[package]]
-name = "hashbrown"
-version = "0.12.3"
+name = "governor"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b"
+dependencies = [
+ "cfg-if",
+ "futures",
+ "futures-timer",
+ "no-std-compat",
+ "nonzero_ext",
+ "parking_lot",
+ "portable-atomic",
+ "rand",
+ "smallvec",
+ "spinning_top",
+]
+
+[[package]]
+name = "h2"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 0.2.12",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.2.0",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
 
 [[package]]
 name = "hashbrown"
-version = "0.14.0"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
 
 [[package]]
 name = "headers"
-version = "0.3.8"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584"
+checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9"
 dependencies = [
- "base64 0.13.1",
- "bitflags 1.3.2",
+ "base64 0.21.7",
  "bytes",
  "headers-core",
- "http",
+ "http 1.2.0",
  "httpdate",
  "mime",
  "sha1",
@@ -1142,11 +1106,11 @@ dependencies = [
 
 [[package]]
 name = "headers-core"
-version = "0.2.0"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429"
+checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
 dependencies = [
- "http",
+ "http 1.2.0",
 ]
 
 [[package]]
@@ -1158,12 +1122,6 @@ dependencies = [
  "unicode-segmentation",
 ]
 
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
 [[package]]
 name = "hermit-abi"
 version = "0.1.19"
@@ -1173,44 +1131,28 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "hermit-abi"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
-
 [[package]]
 name = "hex"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
-[[package]]
-name = "hkdf"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
-dependencies = [
- "hmac 0.12.1",
-]
-
 [[package]]
 name = "hmac"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
 dependencies = [
- "crypto-mac",
- "digest 0.9.0",
+ "digest",
 ]
 
 [[package]]
-name = "hmac"
-version = "0.12.1"
+name = "home"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
 dependencies = [
- "digest 0.10.7",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1224,11 +1166,33 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "hostname"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "windows 0.52.0",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
 [[package]]
 name = "http"
-version = "0.2.9"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
 dependencies = [
  "bytes",
  "fnv",
@@ -1237,20 +1201,43 @@ dependencies = [
 
 [[package]]
 name = "http-body"
-version = "0.4.5"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http 0.2.12",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
 dependencies = [
  "bytes",
- "http",
+ "http 1.2.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
  "pin-project-lite",
 ]
 
 [[package]]
 name = "httparse"
-version = "1.8.0"
+version = "1.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
 
 [[package]]
 name = "httpdate"
@@ -1266,21 +1253,22 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "hyper"
-version = "0.14.27"
+version = "0.14.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
+checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
 dependencies = [
  "bytes",
  "futures-channel",
  "futures-core",
  "futures-util",
- "http",
- "http-body",
+ "h2 0.3.26",
+ "http 0.2.12",
+ "http-body 0.4.6",
  "httparse",
  "httpdate",
  "itoa",
  "pin-project-lite",
- "socket2 0.4.9",
+ "socket2",
  "tokio",
  "tower-service",
  "tracing",
@@ -1288,147 +1276,362 @@ dependencies = [
 ]
 
 [[package]]
-name = "hyper-proxy"
-version = "0.9.1"
+name = "hyper"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc"
+checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
 dependencies = [
  "bytes",
- "futures",
- "headers",
- "http",
- "hyper",
+ "futures-channel",
+ "futures-util",
+ "h2 0.4.7",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
  "tokio",
- "tower-service",
+ "want",
 ]
 
 [[package]]
-name = "iana-time-zone"
-version = "0.1.57"
+name = "hyper-proxy2"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
+checksum = "9043b7b23fb0bc4a1c7014c27b50a4fc42cc76206f71d34fc0dfe5b28ddc3faf"
 dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
- "wasm-bindgen",
- "windows",
+ "bytes",
+ "futures-util",
+ "headers",
+ "http 1.2.0",
+ "hyper 1.5.2",
+ "hyper-rustls 0.26.0",
+ "hyper-util",
+ "pin-project-lite",
+ "rustls-native-certs 0.7.3",
+ "tokio",
+ "tokio-rustls 0.25.0",
+ "tower-service",
+ "webpki",
 ]
 
 [[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.2"
+name = "hyper-rustls"
+version = "0.24.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
 dependencies = [
- "cc",
+ "futures-util",
+ "http 0.2.12",
+ "hyper 0.14.32",
+ "rustls 0.21.12",
+ "tokio",
+ "tokio-rustls 0.24.1",
 ]
 
 [[package]]
-name = "ident_case"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
-
-[[package]]
-name = "idna"
-version = "0.3.0"
+name = "hyper-rustls"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
 dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "futures-util",
+ "http 1.2.0",
+ "hyper 1.5.2",
+ "hyper-util",
+ "log",
+ "rustls 0.22.4",
+ "rustls-native-certs 0.7.3",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.25.0",
+ "tower-service",
 ]
 
 [[package]]
-name = "idna"
-version = "0.4.0"
+name = "hyper-rustls"
+version = "0.27.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
 dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "futures-util",
+ "http 1.2.0",
+ "hyper 1.5.2",
+ "hyper-util",
+ "log",
+ "rustls 0.23.20",
+ "rustls-native-certs 0.8.1",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.26.1",
+ "tower-service",
 ]
 
 [[package]]
-name = "if-addrs"
-version = "0.7.0"
+name = "hyper-util"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
 dependencies = [
- "libc",
- "winapi",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "hyper 1.5.2",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
 ]
 
 [[package]]
-name = "indenter"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
-
-[[package]]
-name = "indexmap"
-version = "1.9.3"
+name = "iana-time-zone"
+version = "0.1.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
 dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core 0.52.0",
 ]
 
 [[package]]
-name = "indexmap"
-version = "2.0.0"
+name = "iana-time-zone-haiku"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
 dependencies = [
- "equivalent",
- "hashbrown 0.14.0",
+ "cc",
 ]
 
 [[package]]
-name = "instant"
-version = "0.1.12"
+name = "icu_collections"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
 dependencies = [
- "cfg-if",
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
 ]
 
 [[package]]
-name = "io-lifetimes"
-version = "1.0.11"
+name = "icu_locid"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
 dependencies = [
- "hermit-abi 0.3.2",
- "libc",
- "windows-sys",
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
 ]
 
 [[package]]
-name = "is-terminal"
-version = "0.4.9"
+name = "icu_locid_transform"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
 dependencies = [
- "hermit-abi 0.3.2",
- "rustix 0.38.10",
- "windows-sys",
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.93",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "if-addrs"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb2a33e9c38988ecbda730c85b0fd9ddcdf83c0305ac7fd21c8bb9f57f2f0cc8"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
+name = "indexmap"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
 ]
 
 [[package]]
 name = "itoa"
-version = "1.0.9"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
 
 [[package]]
 name = "jack"
-version = "0.8.4"
+version = "0.11.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d2ac12f11bb369f3c50d24dbb9fdb00dc987434c9dd622a12c13f618106e153"
+checksum = "0e5a18a3c2aefb354fb77111ade228b20267bdc779de84e7a4ccf7ea96b9a6cd"
 dependencies = [
  "bitflags 1.3.2",
  "jack-sys",
@@ -1439,28 +1642,32 @@ dependencies = [
 
 [[package]]
 name = "jack-sys"
-version = "0.2.3"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b91f2d2d10bc2bab38f4dfa4bc77123a988828af39dd3f30dd9db14d44f2cc1"
+checksum = "6013b7619b95a22b576dfb43296faa4ecbe40abbdb97dfd22ead520775fc86ab"
 dependencies = [
+ "bitflags 1.3.2",
  "lazy_static",
  "libc",
- "libloading 0.6.7",
+ "libloading 0.7.4",
+ "log",
  "pkg-config",
 ]
 
 [[package]]
 name = "jni"
-version = "0.19.0"
+version = "0.21.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
 dependencies = [
  "cesu8",
+ "cfg-if",
  "combine",
  "jni-sys",
  "log",
- "thiserror",
+ "thiserror 1.0.69",
  "walkdir",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -1471,41 +1678,45 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
 
 [[package]]
 name = "jobserver"
-version = "0.1.26"
+version = "0.1.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "js-sys"
-version = "0.3.64"
+version = "0.3.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
 dependencies = [
+ "once_cell",
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "keyring"
-version = "2.3.0"
+version = "3.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b52a1d320b55eacc02d4561fed9714af4e98b7989cf4e696bee192b03fc99720"
+checksum = "2f8fe839464d4e4b37d756d7e910063696af79a7e877282cb1825e4ec5f10833"
 dependencies = [
  "byteorder",
- "lazy_static",
- "linux-keyutils",
- "secret-service",
- "security-framework",
- "winapi",
+ "dbus-secret-service",
+ "log",
+ "security-framework 2.11.1",
+ "security-framework 3.1.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin",
+]
 
 [[package]]
 name = "lazycell"
@@ -1513,22 +1724,11 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
-[[package]]
-name = "lewton"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030"
-dependencies = [
- "byteorder",
- "ogg",
- "tinyvec",
-]
-
 [[package]]
 name = "libc"
-version = "0.2.153"
+version = "0.2.169"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
 
 [[package]]
 name = "libdbus-sys"
@@ -1541,9 +1741,9 @@ dependencies = [
 
 [[package]]
 name = "libloading"
-version = "0.6.7"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
 dependencies = [
  "cfg-if",
  "winapi",
@@ -1551,50 +1751,48 @@ dependencies = [
 
 [[package]]
 name = "libloading"
-version = "0.7.4"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
 dependencies = [
  "cfg-if",
- "winapi",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "libm"
-version = "0.2.7"
+version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
+checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
 
 [[package]]
 name = "libmdns"
-version = "0.7.5"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b04ae6b56b3b19ade26f0e7e7c1360a1713514f326c5ed0797cf2c109c9e010"
+checksum = "48854699e11b111433431b69cee2365fcab0b29b06993f48c257dfbaf6395862"
 dependencies = [
  "byteorder",
  "futures-util",
- "hostname",
+ "hostname 0.4.0",
  "if-addrs",
  "log",
  "multimap",
- "nix 0.23.2",
  "rand",
- "socket2 0.4.9",
- "thiserror",
+ "socket2",
+ "thiserror 1.0.69",
  "tokio",
- "winapi",
 ]
 
 [[package]]
 name = "libpulse-binding"
-version = "2.28.1"
+version = "2.28.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3557a2dfc380c8f061189a01c6ae7348354e0c9886038dc6c171219c08eaff"
+checksum = "b6b1040a6c4c4d1e9e852000f6202df1a02a4f074320de336ab21e4fd317b538"
 dependencies = [
  "bitflags 1.3.2",
  "libc",
  "libpulse-sys",
- "num-derive",
+ "num-derive 0.3.3",
  "num-traits",
  "winapi",
 ]
@@ -1627,38 +1825,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bc19e110fbf42c17260d30f6d3dc545f58491c7830d38ecb9aaca96e26067a9b"
 dependencies = [
  "libc",
- "num-derive",
+ "num-derive 0.3.3",
  "num-traits",
  "pkg-config",
  "winapi",
 ]
 
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.6.0",
+ "libc",
+]
+
 [[package]]
 name = "librespot-audio"
-version = "0.4.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c176a31355e1ea8e0b9c4ced19df4947bfe4770661c25c142b6fba2365940d9d"
+checksum = "7e07566fe7553042936c61bbdd9bedb524114a904aa7f9738e266c641468bab8"
 dependencies = [
- "aes-ctr",
- "byteorder",
+ "aes",
  "bytes",
+ "ctr",
  "futures-util",
+ "http-body-util",
+ "hyper 1.5.2",
+ "hyper-util",
  "librespot-core",
  "log",
+ "parking_lot",
  "tempfile",
+ "thiserror 1.0.69",
  "tokio",
 ]
 
 [[package]]
 name = "librespot-connect"
-version = "0.4.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ffafb6a443e9445ccb3d5d591573b5b1da3c89a9b8846c63ba2c3710210d3ec"
+checksum = "67ce3a5634669ce051a425cff5437a474831111df045fd5a2bb37fabe316fa60"
 dependencies = [
  "form_urlencoded",
  "futures-util",
  "librespot-core",
- "librespot-discovery",
  "librespot-playback",
  "librespot-protocol",
  "log",
@@ -1666,155 +1878,179 @@ dependencies = [
  "rand",
  "serde",
  "serde_json",
+ "thiserror 1.0.69",
  "tokio",
  "tokio-stream",
 ]
 
 [[package]]
 name = "librespot-core"
-version = "0.4.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "046349f25888e644bf02d9c5de0164b2a493d29aa4ce18e1ad0b756da9b55d6d"
+checksum = "c4c76303efcf949a59f9380220ca420c4d72fa32dbb3641a0079f429cc5e44e7"
 dependencies = [
- "aes 0.6.0",
- "base64 0.13.1",
+ "aes",
+ "base64 0.22.1",
  "byteorder",
  "bytes",
+ "data-encoding",
  "form_urlencoded",
  "futures-core",
  "futures-util",
- "hmac 0.11.0",
- "http",
+ "governor",
+ "hmac",
+ "http 1.2.0",
+ "http-body-util",
  "httparse",
- "hyper",
- "hyper-proxy",
+ "hyper 1.5.2",
+ "hyper-proxy2",
+ "hyper-rustls 0.27.5",
+ "hyper-util",
+ "librespot-oauth",
  "librespot-protocol",
  "log",
+ "nonzero_ext",
  "num-bigint",
+ "num-derive 0.4.2",
  "num-integer",
  "num-traits",
  "once_cell",
+ "parking_lot",
  "pbkdf2",
+ "pin-project-lite",
  "priority-queue",
  "protobuf",
+ "quick-xml",
  "rand",
+ "rsa",
  "serde",
  "serde_json",
- "sha-1 0.9.8",
+ "sha1",
  "shannon",
- "thiserror",
+ "sysinfo",
+ "thiserror 1.0.69",
+ "time",
  "tokio",
  "tokio-stream",
+ "tokio-tungstenite",
  "tokio-util",
  "url",
  "uuid",
- "vergen",
+ "vergen-gitcl",
 ]
 
 [[package]]
 name = "librespot-discovery"
-version = "0.4.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aa877d18f6150364012cb4be5682d62d7c712c88bae2d0d01720fd7c15e2f06"
+checksum = "94a952d28c775562b9b6039102c0e3a834cb31157fccb2f2a68a26dc4397fea8"
 dependencies = [
- "aes-ctr",
- "base64 0.13.1",
+ "aes",
+ "base64 0.22.1",
+ "bytes",
+ "ctr",
  "form_urlencoded",
  "futures-core",
- "hmac 0.11.0",
- "hyper",
+ "futures-util",
+ "hmac",
+ "http-body-util",
+ "hyper 1.5.2",
+ "hyper-util",
  "libmdns",
  "librespot-core",
  "log",
  "rand",
  "serde_json",
- "sha-1 0.9.8",
- "thiserror",
+ "serde_repr",
+ "sha1",
+ "thiserror 1.0.69",
  "tokio",
 ]
 
 [[package]]
 name = "librespot-metadata"
-version = "0.4.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b80361fcbcb5092056fd47c08c34d5d51b08385d8efb6941c0d3e46d032c21c"
+checksum = "7cf6d5c46a401b1dd3e062ebdce959aa694bbae1039841756545d2e9c4f8be5f"
 dependencies = [
  "async-trait",
- "byteorder",
+ "bytes",
  "librespot-core",
  "librespot-protocol",
  "log",
  "protobuf",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+ "uuid",
+]
+
+[[package]]
+name = "librespot-oauth"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00dcad779aa6c3b442e493e2a40ca83a5c5fcf38a65c05b026c3587bd4f8d14f"
+dependencies = [
+ "log",
+ "oauth2",
+ "thiserror 1.0.69",
+ "url",
 ]
 
 [[package]]
 name = "librespot-playback"
-version = "0.4.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5190a0b9bcc7f70ee4196a6b4a1c731d405ca130d4a6fcd4c561cfdde8b7cfb7"
+checksum = "4ed1f776a04c8c9275407f8d4df034f2615ea729ec6c6d0fa69f423fc571df64"
 dependencies = [
- "alsa 0.6.0",
- "byteorder",
+ "alsa",
  "cpal",
- "futures-executor",
  "futures-util",
- "lewton",
  "libpulse-binding",
  "libpulse-simple-binding",
  "librespot-audio",
  "librespot-core",
  "librespot-metadata",
  "log",
- "ogg",
- "parking_lot 0.12.1",
+ "parking_lot",
  "portaudio-rs",
  "rand",
  "rand_distr",
  "rodio",
  "shell-words",
- "thiserror",
+ "symphonia",
+ "thiserror 1.0.69",
  "tokio",
  "zerocopy",
 ]
 
 [[package]]
 name = "librespot-protocol"
-version = "0.4.2"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d6d3ac6196ac0ea67bbe039f56d6730a5d8b31502ef9bce0f504ed729dcb39f"
+checksum = "80802f52b5a1b3a2157454e6aca483a627fbf7b942e0a5fea037ebf3ed8b0546"
 dependencies = [
- "glob",
  "protobuf",
- "protobuf-codegen-pure",
-]
-
-[[package]]
-name = "linux-keyutils"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f27bb67f6dd1d0bb5ab582868e4f65052e58da6401188a08f0da09cf512b84b"
-dependencies = [
- "bitflags 1.3.2",
- "libc",
+ "protobuf-codegen",
 ]
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.3.8"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
-name = "linux-raw-sys"
-version = "0.4.5"
+name = "litemap"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
 
 [[package]]
 name = "lock_api"
-version = "0.4.10"
+version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
 dependencies = [
  "autocfg",
  "scopeguard",
@@ -1822,15 +2058,15 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.20"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
-name = "mach"
-version = "0.3.2"
+name = "mach2"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
 dependencies = [
  "libc",
 ]
@@ -1841,40 +2077,11 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
 
-[[package]]
-name = "maybe-async"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "memchr"
-version = "2.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
-
-[[package]]
-name = "memoffset"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "memoffset"
-version = "0.7.1"
+version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
-dependencies = [
- "autocfg",
-]
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
 [[package]]
 name = "mime"
@@ -1890,44 +2097,45 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.1"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
 dependencies = [
  "adler",
 ]
 
 [[package]]
 name = "mio"
-version = "0.8.11"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 dependencies = [
  "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys",
+ "wasi",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "multimap"
-version = "0.8.3"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "ndk"
-version = "0.6.0"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
+checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
  "jni-sys",
+ "log",
  "ndk-sys",
  "num_enum",
- "thiserror",
+ "thiserror 1.0.69",
 ]
 
 [[package]]
@@ -1937,93 +2145,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
 
 [[package]]
-name = "ndk-glue"
-version = "0.6.2"
+name = "ndk-sys"
+version = "0.5.0+25.2.9519653"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f"
+checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
 dependencies = [
- "lazy_static",
- "libc",
- "log",
- "ndk",
- "ndk-context",
- "ndk-macro",
- "ndk-sys",
+ "jni-sys",
 ]
 
 [[package]]
-name = "ndk-macro"
-version = "0.3.0"
+name = "no-std-compat"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
-dependencies = [
- "darling",
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
+checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
 
 [[package]]
-name = "ndk-sys"
-version = "0.3.0"
+name = "nom"
+version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
- "jni-sys",
+ "memchr",
+ "minimal-lexical",
 ]
 
 [[package]]
-name = "nix"
-version = "0.23.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
-dependencies = [
- "bitflags 1.3.2",
- "cc",
- "cfg-if",
- "libc",
- "memoffset 0.6.5",
-]
-
-[[package]]
-name = "nix"
-version = "0.24.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
-dependencies = [
- "bitflags 1.3.2",
- "cfg-if",
- "libc",
-]
-
-[[package]]
-name = "nix"
-version = "0.26.4"
+name = "nonzero_ext"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
-dependencies = [
- "bitflags 1.3.2",
- "cfg-if",
- "libc",
- "memoffset 0.7.1",
-]
+checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
 
 [[package]]
-name = "nom"
-version = "7.1.3"
+name = "ntapi"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
 dependencies = [
- "memchr",
- "minimal-lexical",
+ "winapi",
 ]
 
 [[package]]
 name = "num"
-version = "0.4.1"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
+checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
 dependencies = [
  "num-bigint",
  "num-complex",
@@ -2035,21 +2200,37 @@ dependencies = [
 
 [[package]]
 name = "num-bigint"
-version = "0.4.4"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
 dependencies = [
- "autocfg",
  "num-integer",
  "num-traits",
  "rand",
 ]
 
+[[package]]
+name = "num-bigint-dig"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
+dependencies = [
+ "byteorder",
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand",
+ "smallvec",
+ "zeroize",
+]
+
 [[package]]
 name = "num-complex"
-version = "0.4.4"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
 dependencies = [
  "num-traits",
 ]
@@ -2071,21 +2252,31 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.93",
+]
+
 [[package]]
 name = "num-integer"
-version = "0.1.45"
+version = "0.1.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
 dependencies = [
- "autocfg",
  "num-traits",
 ]
 
 [[package]]
 name = "num-iter"
-version = "0.1.43"
+version = "0.1.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
 dependencies = [
  "autocfg",
  "num-integer",
@@ -2094,11 +2285,10 @@ dependencies = [
 
 [[package]]
 name = "num-rational"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
 dependencies = [
- "autocfg",
  "num-bigint",
  "num-integer",
  "num-traits",
@@ -2106,106 +2296,107 @@ dependencies = [
 
 [[package]]
 name = "num-traits"
-version = "0.2.16"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
  "libm",
 ]
 
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi 0.3.2",
- "libc",
-]
-
 [[package]]
 name = "num_enum"
-version = "0.5.11"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
 dependencies = [
  "num_enum_derive",
 ]
 
 [[package]]
 name = "num_enum_derive"
-version = "0.5.11"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "num_threads"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
 dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "oauth2"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f"
+dependencies = [
+ "base64 0.13.1",
+ "chrono",
+ "getrandom",
+ "http 0.2.12",
+ "rand",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "sha2",
+ "thiserror 1.0.69",
+ "url",
+]
+
 [[package]]
 name = "object"
-version = "0.32.0"
+version = "0.32.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "oboe"
-version = "0.4.6"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1"
+checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"
 dependencies = [
  "jni",
  "ndk",
  "ndk-context",
- "num-derive",
+ "num-derive 0.4.2",
  "num-traits",
  "oboe-sys",
 ]
 
 [[package]]
 name = "oboe-sys"
-version = "0.4.5"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd"
+checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d"
 dependencies = [
  "cc",
 ]
 
-[[package]]
-name = "ogg"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e"
-dependencies = [
- "byteorder",
-]
-
 [[package]]
 name = "once_cell"
-version = "1.18.0"
+version = "1.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 
 [[package]]
-name = "opaque-debug"
-version = "0.3.0"
+name = "openssl-probe"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "option-ext"
@@ -2213,116 +2404,84 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
 
-[[package]]
-name = "ordered-stream"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
-dependencies = [
- "futures-core",
- "pin-project-lite",
-]
-
 [[package]]
 name = "owo-colors"
 version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
 
-[[package]]
-name = "parking"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
-
 [[package]]
 name = "parking_lot"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core 0.8.6",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.12.1"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
 dependencies = [
  "lock_api",
- "parking_lot_core 0.9.8",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
-dependencies = [
- "cfg-if",
- "instant",
- "libc",
- "redox_syscall 0.2.16",
- "smallvec",
- "winapi",
+ "parking_lot_core",
 ]
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.8"
+version = "0.9.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "backtrace",
  "cfg-if",
  "libc",
  "petgraph",
- "redox_syscall 0.3.5",
+ "redox_syscall",
  "smallvec",
  "thread-id",
- "windows-targets",
+ "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
 [[package]]
 name = "pbkdf2"
-version = "0.8.0"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
 dependencies = [
- "crypto-mac",
- "hmac 0.11.0",
+ "digest",
+ "hmac",
 ]
 
 [[package]]
-name = "peeking_take_while"
-version = "0.1.2"
+name = "pem-rfc7468"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+dependencies = [
+ "base64ct",
+]
 
 [[package]]
 name = "percent-encoding"
-version = "2.3.0"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "petgraph"
-version = "0.6.4"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
 dependencies = [
  "fixedbitset",
- "indexmap 2.0.0",
+ "indexmap",
 ]
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.13"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
 
 [[package]]
 name = "pin-utils"
@@ -2330,11 +2489,32 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
+[[package]]
+name = "pkcs1"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
+dependencies = [
+ "der",
+ "pkcs8",
+ "spki",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
 [[package]]
 name = "pkg-config"
-version = "0.3.27"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
 
 [[package]]
 name = "pledge"
@@ -2346,20 +2526,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "polling"
-version = "2.8.0"
+name = "portable-atomic"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
-dependencies = [
- "autocfg",
- "bitflags 1.3.2",
- "cfg-if",
- "concurrent-queue",
- "libc",
- "log",
- "pin-project-lite",
- "windows-sys",
-]
+checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
 
 [[package]]
 name = "portaudio-rs"
@@ -2390,27 +2560,40 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.93",
+]
 
 [[package]]
 name = "priority-queue"
-version = "1.3.2"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fff39edfcaec0d64e8d0da38564fad195d2d51b680940295fcc307366e101e61"
+checksum = "714c75db297bc88a63783ffc6ab9f830698a6705aa0201416931759ef4c8183d"
 dependencies = [
  "autocfg",
- "indexmap 1.9.3",
+ "equivalent",
+ "indexmap",
 ]
 
 [[package]]
 name = "proc-macro-crate"
-version = "1.3.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
 dependencies = [
- "once_cell",
  "toml_edit",
 ]
 
@@ -2440,43 +2623,79 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.66"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "protobuf"
-version = "2.28.0"
+version = "3.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
+checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror 1.0.69",
+]
 
 [[package]]
 name = "protobuf-codegen"
-version = "2.28.0"
+version = "3.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6"
+checksum = "e26b833f144769a30e04b1db0146b2aaa53fd2fd83acf10a6b5f996606c18144"
 dependencies = [
+ "anyhow",
+ "once_cell",
  "protobuf",
+ "protobuf-parse",
+ "regex",
+ "tempfile",
+ "thiserror 1.0.69",
 ]
 
 [[package]]
-name = "protobuf-codegen-pure"
-version = "2.28.0"
+name = "protobuf-parse"
+version = "3.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95a29399fc94bcd3eeaa951c715f7bea69409b2445356b00519740bcd6ddd865"
+checksum = "322330e133eab455718444b4e033ebfac7c6528972c784fcde28d2cc783c6257"
 dependencies = [
+ "anyhow",
+ "indexmap",
+ "log",
  "protobuf",
- "protobuf-codegen",
+ "protobuf-support",
+ "tempfile",
+ "thiserror 1.0.69",
+ "which",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252"
+dependencies = [
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
+dependencies = [
+ "memchr",
+ "serde",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.33"
+version = "1.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
 dependencies = [
  "proc-macro2",
 ]
@@ -2523,47 +2742,29 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
-dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.4.1"
+version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
 ]
 
 [[package]]
 name = "redox_users"
-version = "0.4.3"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
 dependencies = [
  "getrandom",
- "redox_syscall 0.2.16",
- "thiserror",
+ "libredox",
+ "thiserror 1.0.69",
 ]
 
 [[package]]
 name = "regex"
-version = "1.9.4"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -2573,9 +2774,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.3.7"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -2584,168 +2785,244 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.7.5"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "reqwest"
+version = "0.11.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
+checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
+dependencies = [
+ "base64 0.21.7",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.26",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "hyper 0.14.32",
+ "hyper-rustls 0.24.2",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls 0.21.12",
+ "rustls-pemfile 1.0.4",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "system-configuration",
+ "tokio",
+ "tokio-rustls 0.24.1",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "webpki-roots",
+ "winreg",
+]
 
 [[package]]
 name = "ring"
-version = "0.17.3"
+version = "0.17.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
 dependencies = [
  "cc",
+ "cfg-if",
  "getrandom",
  "libc",
  "spin",
  "untrusted",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "rodio"
-version = "0.15.0"
+version = "0.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0939e9f626e6c6f1989adb6226a039c855ca483053f0ee7c98b90e41cf731e"
+checksum = "6006a627c1a38d37f3d3a85c6575418cfe34a5392d60a686d0071e1c8d427acb"
 dependencies = [
  "cpal",
+ "thiserror 1.0.69",
 ]
 
 [[package]]
-name = "rspotify"
-version = "0.12.0"
+name = "rsa"
+version = "0.9.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87c6f1d86b10201655f0cd4002088bafe4abcc75cc610613c995abd719f40fcb"
+checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519"
 dependencies = [
- "base64 0.21.3",
- "chrono",
- "getrandom",
- "log",
- "maybe-async",
- "rspotify-http",
- "rspotify-macros",
- "rspotify-model",
- "serde",
- "serde_json",
- "sha2",
- "thiserror",
- "url",
-]
+ "const-oid",
+ "digest",
+ "num-bigint-dig",
+ "num-integer",
+ "num-traits",
+ "pkcs1",
+ "pkcs8",
+ "rand_core",
+ "signature",
+ "spki",
+ "subtle",
+ "zeroize",
+]
 
 [[package]]
-name = "rspotify-http"
-version = "0.12.0"
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustix"
+version = "0.38.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dad45cd393a8685ee36ec6d2accbb2c955e21ac036a2e4eb175985783f30ed78"
+checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
 dependencies = [
- "log",
- "maybe-async",
- "serde_json",
- "thiserror",
- "ureq",
+ "bitflags 2.6.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
-name = "rspotify-macros"
-version = "0.12.0"
+name = "rustls"
+version = "0.21.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc4892882851a97ee7210e423725ce116e8239157c649af37e208fe93855638a"
+checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki 0.101.7",
+ "sct",
+]
 
 [[package]]
-name = "rspotify-model"
-version = "0.12.0"
+name = "rustls"
+version = "0.22.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bae90ab3d6e4cb4ccd7f2887c4363d19b1419800e132d3fb95e2f9b24c05f4d7"
+checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
 dependencies = [
- "chrono",
- "enum_dispatch",
- "serde",
- "serde_json",
- "strum",
- "thiserror",
+ "log",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki 0.102.8",
+ "subtle",
+ "zeroize",
 ]
 
 [[package]]
-name = "rustc-demangle"
-version = "0.1.23"
+name = "rustls"
+version = "0.23.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
+dependencies = [
+ "aws-lc-rs",
+ "log",
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki 0.102.8",
+ "subtle",
+ "zeroize",
+]
 
 [[package]]
-name = "rustc-hash"
-version = "1.1.0"
+name = "rustls-native-certs"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 2.2.0",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework 2.11.1",
+]
 
 [[package]]
-name = "rustc_version"
-version = "0.4.0"
+name = "rustls-native-certs"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3"
 dependencies = [
- "semver",
+ "openssl-probe",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework 3.1.0",
 ]
 
 [[package]]
-name = "rustix"
-version = "0.37.26"
+name = "rustls-pemfile"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84f3f8f960ed3b5a59055428714943298bf3fa2d4a1d53135084e0544829d995"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
 dependencies = [
- "bitflags 1.3.2",
- "errno",
- "io-lifetimes",
- "libc",
- "linux-raw-sys 0.3.8",
- "windows-sys",
+ "base64 0.21.7",
 ]
 
 [[package]]
-name = "rustix"
-version = "0.38.10"
+name = "rustls-pemfile"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
 dependencies = [
- "bitflags 2.4.0",
- "errno",
- "libc",
- "linux-raw-sys 0.4.5",
- "windows-sys",
+ "rustls-pki-types",
 ]
 
 [[package]]
-name = "rustls"
-version = "0.21.11"
+name = "rustls-pki-types"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
 dependencies = [
- "log",
  "ring",
- "rustls-webpki",
- "sct",
+ "untrusted",
 ]
 
 [[package]]
 name = "rustls-webpki"
-version = "0.101.7"
+version = "0.102.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
 dependencies = [
+ "aws-lc-rs",
  "ring",
+ "rustls-pki-types",
  "untrusted",
 ]
 
 [[package]]
 name = "rustversion"
-version = "1.0.14"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
 
 [[package]]
 name = "ryu"
-version = "1.0.15"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "same-file"
@@ -2756,6 +3033,15 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "schannel"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
@@ -2773,32 +3059,26 @@ dependencies = [
 ]
 
 [[package]]
-name = "secret-service"
-version = "3.0.1"
+name = "security-framework"
+version = "2.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
 dependencies = [
- "aes 0.7.5",
- "block-modes",
- "futures-util",
- "generic-array",
- "hkdf",
- "num",
- "once_cell",
- "rand",
- "serde",
- "sha2",
- "zbus",
+ "bitflags 2.6.0",
+ "core-foundation 0.9.4",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
 ]
 
 [[package]]
 name = "security-framework"
-version = "2.9.2"
+version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
+checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc"
 dependencies = [
- "bitflags 1.3.2",
- "core-foundation",
+ "bitflags 2.6.0",
+ "core-foundation 0.10.0",
  "core-foundation-sys",
  "libc",
  "security-framework-sys",
@@ -2806,82 +3086,86 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.9.1"
+version = "2.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
+checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5"
 dependencies = [
  "core-foundation-sys",
  "libc",
 ]
 
-[[package]]
-name = "semver"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
-
 [[package]]
 name = "serde"
-version = "1.0.188"
+version = "1.0.217"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.188"
+version = "1.0.217"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.105"
+version = "1.0.134"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
+checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
 
 [[package]]
-name = "serde_repr"
+name = "serde_path_to_error"
 version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.3"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
 dependencies = [
  "serde",
 ]
 
 [[package]]
-name = "sha-1"
-version = "0.9.8"
+name = "serde_urlencoded"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
 dependencies = [
- "block-buffer 0.9.0",
- "cfg-if",
- "cpufeatures",
- "digest 0.9.0",
- "opaque-debug",
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
 ]
 
 [[package]]
@@ -2892,29 +3176,29 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
 dependencies = [
  "cfg-if",
  "cpufeatures",
- "digest 0.10.7",
+ "digest",
 ]
 
 [[package]]
 name = "sha1"
-version = "0.10.5"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
 dependencies = [
  "cfg-if",
  "cpufeatures",
- "digest 0.10.7",
+ "digest",
 ]
 
 [[package]]
 name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
 dependencies = [
  "cfg-if",
  "cpufeatures",
- "digest 0.10.7",
+ "digest",
 ]
 
 [[package]]
@@ -2928,9 +3212,9 @@ dependencies = [
 
 [[package]]
 name = "sharded-slab"
-version = "0.1.4"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
 dependencies = [
  "lazy_static",
 ]
@@ -2948,22 +3232,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
 [[package]]
-name = "signal-hook"
-version = "0.3.17"
+name = "signal-hook-registry"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
 dependencies = [
  "libc",
- "signal-hook-registry",
 ]
 
 [[package]]
-name = "signal-hook-registry"
-version = "1.4.1"
+name = "signature"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
 dependencies = [
- "libc",
+ "digest",
+ "rand_core",
 ]
 
 [[package]]
@@ -2977,41 +3261,50 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.11.0"
+version = "1.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
 
 [[package]]
 name = "socket2"
-version = "0.4.9"
+version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
+checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
 dependencies = [
  "libc",
- "winapi",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
-name = "socket2"
-version = "0.5.3"
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "spinning_top"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
+checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
 dependencies = [
- "libc",
- "windows-sys",
+ "lock_api",
 ]
 
 [[package]]
-name = "spin"
-version = "0.9.8"
+name = "spki"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
 
 [[package]]
 name = "spotifyd"
 version = "0.3.5"
 dependencies = [
- "alsa 0.7.1",
+ "alsa",
  "chrono",
  "color-eyre",
  "daemonize",
@@ -3030,14 +3323,17 @@ dependencies = [
  "librespot-connect",
  "librespot-core",
  "librespot-discovery",
+ "librespot-metadata",
  "librespot-playback",
+ "librespot-protocol",
  "log",
  "pledge",
- "rspotify",
  "serde",
- "sha-1 0.10.1",
+ "sha-1",
  "structopt",
  "syslog",
+ "thiserror 2.0.9",
+ "time",
  "tokio",
  "tokio-stream",
  "toml",
@@ -3046,16 +3342,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
-name = "stdweb"
-version = "0.1.3"
+name = "stable_deref_trait"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
 [[package]]
 name = "strsim"
@@ -3065,9 +3355,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
 name = "strsim"
-version = "0.10.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "structopt"
@@ -3086,7 +3376,7 @@ version = "0.4.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
 dependencies = [
- "heck 0.3.3",
+ "heck",
  "proc-macro-error",
  "proc-macro2",
  "quote",
@@ -3094,32 +3384,94 @@ dependencies = [
 ]
 
 [[package]]
-name = "strum"
-version = "0.25.0"
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "symphonia"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9"
+dependencies = [
+ "lazy_static",
+ "symphonia-bundle-mp3",
+ "symphonia-codec-vorbis",
+ "symphonia-core",
+ "symphonia-format-ogg",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "symphonia-bundle-mp3"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
+checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4"
 dependencies = [
- "strum_macros",
+ "lazy_static",
+ "log",
+ "symphonia-core",
+ "symphonia-metadata",
 ]
 
 [[package]]
-name = "strum_macros"
-version = "0.25.2"
+name = "symphonia-codec-vorbis"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059"
+checksum = "5a98765fb46a0a6732b007f7e2870c2129b6f78d87db7987e6533c8f164a9f30"
 dependencies = [
- "heck 0.4.1",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn 2.0.29",
+ "log",
+ "symphonia-core",
+ "symphonia-utils-xiph",
 ]
 
 [[package]]
-name = "subtle"
-version = "2.4.1"
+name = "symphonia-core"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3"
+dependencies = [
+ "arrayvec",
+ "bitflags 1.3.2",
+ "bytemuck",
+ "lazy_static",
+ "log",
+]
+
+[[package]]
+name = "symphonia-format-ogg"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ada3505789516bcf00fc1157c67729eded428b455c27ca370e41f4d785bfa931"
+dependencies = [
+ "log",
+ "symphonia-core",
+ "symphonia-metadata",
+ "symphonia-utils-xiph",
+]
+
+[[package]]
+name = "symphonia-metadata"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c"
+dependencies = [
+ "encoding_rs",
+ "lazy_static",
+ "log",
+ "symphonia-core",
+]
+
+[[package]]
+name = "symphonia-utils-xiph"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe"
+dependencies = [
+ "symphonia-core",
+ "symphonia-metadata",
+]
 
 [[package]]
 name = "syn"
@@ -3134,48 +3486,90 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.29"
+version = "2.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
 dependencies = [
  "proc-macro2",
  "quote",
  "unicode-ident",
 ]
 
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.93",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.31.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+ "memchr",
+ "ntapi",
+ "windows 0.57.0",
+]
+
 [[package]]
 name = "syslog"
-version = "6.1.0"
+version = "6.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7434e95bcccce1215d30f4bf84fe8c00e8de1b9be4fb736d747ca53d36e7f96f"
+checksum = "dfc7e95b5b795122fafe6519e27629b5ab4232c73ebb2428f568e82b1a457ad3"
 dependencies = [
  "error-chain",
- "hostname",
+ "hostname 0.3.1",
  "libc",
  "log",
- "time 0.3.36",
+ "time",
 ]
 
 [[package]]
-name = "tempfile"
-version = "3.8.0"
+name = "system-configuration"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
 dependencies = [
- "cfg-if",
- "fastrand 2.0.0",
- "redox_syscall 0.3.5",
- "rustix 0.38.10",
- "windows-sys",
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
 ]
 
 [[package]]
-name = "termcolor"
-version = "1.2.0"
+name = "system-configuration-sys"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
 dependencies = [
- "winapi-util",
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -3189,40 +3583,59 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.47"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
+dependencies = [
+ "thiserror-impl 2.0.9",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
- "thiserror-impl",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.47"
+version = "2.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
 ]
 
 [[package]]
 name = "thread-id"
-version = "4.2.0"
+version = "4.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79474f573561cdc4871a0de34a51c92f7f5a56039113fbb5b9c9f96bdb756669"
+checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea"
 dependencies = [
  "libc",
- "redox_syscall 0.2.16",
  "winapi",
 ]
 
 [[package]]
 name = "thread_local"
-version = "1.1.7"
+version = "1.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -3230,20 +3643,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
-dependencies = [
- "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
- "winapi",
-]
-
-[[package]]
-name = "time"
-version = "0.3.36"
+version = "0.3.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
 dependencies = [
  "deranged",
  "itoa",
@@ -3264,89 +3666,129 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.18"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
 dependencies = [
  "num-conv",
  "time-core",
 ]
 
 [[package]]
-name = "tinyvec"
-version = "1.6.0"
+name = "tinystr"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
 dependencies = [
- "tinyvec_macros",
+ "displaydoc",
+ "zerovec",
 ]
 
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
 [[package]]
 name = "tokio"
-version = "1.32.0"
+version = "1.42.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
+checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
 dependencies = [
  "backtrace",
  "bytes",
  "libc",
  "mio",
- "num_cpus",
- "parking_lot 0.12.1",
+ "parking_lot",
  "pin-project-lite",
  "signal-hook-registry",
- "socket2 0.5.3",
+ "socket2",
  "tokio-macros",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "tokio-macros"
-version = "2.1.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls 0.21.12",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
+dependencies = [
+ "rustls 0.22.4",
+ "rustls-pki-types",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
+dependencies = [
+ "rustls 0.23.20",
+ "tokio",
 ]
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.14"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
 dependencies = [
  "futures-core",
  "pin-project-lite",
  "tokio",
 ]
 
+[[package]]
+name = "tokio-tungstenite"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9"
+dependencies = [
+ "futures-util",
+ "log",
+ "rustls 0.23.20",
+ "rustls-native-certs 0.8.1",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.26.1",
+ "tungstenite",
+]
+
 [[package]]
 name = "tokio-util"
-version = "0.7.8"
+version = "0.7.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
 dependencies = [
  "bytes",
  "futures-core",
  "futures-sink",
  "pin-project-lite",
  "tokio",
- "tracing",
 ]
 
 [[package]]
 name = "toml"
-version = "0.7.6"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -3356,20 +3798,20 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.3"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.19.14"
+version = "0.22.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
 dependencies = [
- "indexmap 2.0.0",
+ "indexmap",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -3378,38 +3820,25 @@ dependencies = [
 
 [[package]]
 name = "tower-service"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
 
 [[package]]
 name = "tracing"
-version = "0.1.37"
+version = "0.1.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
 dependencies = [
- "cfg-if",
  "pin-project-lite",
- "tracing-attributes",
  "tracing-core",
 ]
 
-[[package]]
-name = "tracing-attributes"
-version = "0.1.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.29",
-]
-
 [[package]]
 name = "tracing-core"
-version = "0.1.31"
+version = "0.1.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
 dependencies = [
  "once_cell",
  "valuable",
@@ -3417,9 +3846,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-error"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
+checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
 dependencies = [
  "tracing",
  "tracing-subscriber",
@@ -3427,9 +3856,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.3.17"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
 dependencies = [
  "sharded-slab",
  "thread_local",
@@ -3438,58 +3867,53 @@ dependencies = [
 
 [[package]]
 name = "try-lock"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
-
-[[package]]
-name = "typenum"
-version = "1.16.0"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
 [[package]]
-name = "uds_windows"
-version = "1.0.2"
+name = "tungstenite"
+version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d"
+checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
 dependencies = [
- "tempfile",
- "winapi",
+ "byteorder",
+ "bytes",
+ "data-encoding",
+ "http 1.2.0",
+ "httparse",
+ "log",
+ "rand",
+ "rustls 0.23.20",
+ "rustls-pki-types",
+ "sha1",
+ "thiserror 1.0.69",
+ "utf-8",
 ]
 
 [[package]]
-name = "unicode-bidi"
-version = "0.3.13"
+name = "typenum"
+version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.11"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
-dependencies = [
- "tinyvec",
-]
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.10.1"
+version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
 
 [[package]]
 name = "unicode-width"
-version = "0.1.10"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
 
 [[package]]
 name = "untrusted"
@@ -3498,42 +3922,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
 
 [[package]]
-name = "ureq"
-version = "2.9.1"
+name = "url"
+version = "2.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
 dependencies = [
- "base64 0.21.3",
- "cookie",
- "cookie_store",
- "log",
- "once_cell",
- "rustls",
- "rustls-webpki",
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
  "serde",
- "serde_json",
- "url",
- "webpki-roots",
 ]
 
 [[package]]
-name = "url"
-version = "2.4.1"
+name = "utf-8"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
-dependencies = [
- "form_urlencoded",
- "idna 0.4.0",
- "percent-encoding",
-]
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "uuid"
-version = "1.4.1"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
+checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
 dependencies = [
  "getrandom",
+ "rand",
 ]
 
 [[package]]
@@ -3550,32 +3981,53 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 
 [[package]]
 name = "vergen"
-version = "3.2.0"
+version = "9.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7141e445af09c8919f1d5f8a20dae0b20c3b57a45dee0d5823c6ed5d237f15a"
+checksum = "31f25fc8f8f05df455c7941e87f093ad22522a9ff33d7a027774815acf6f0639"
 dependencies = [
- "bitflags 1.3.2",
- "chrono",
- "rustc_version",
+ "anyhow",
+ "derive_builder",
+ "rustversion",
+ "time",
+ "vergen-lib",
 ]
 
 [[package]]
-name = "version_check"
-version = "0.9.4"
+name = "vergen-gitcl"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0227006d09f98ab00ea69e9a5e055e676a813cfbed4232986176c86a6080b997"
+dependencies = [
+ "anyhow",
+ "derive_builder",
+ "rustversion",
+ "time",
+ "vergen",
+ "vergen-lib",
+]
 
 [[package]]
-name = "waker-fn"
-version = "1.1.0"
+name = "vergen-lib"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+checksum = "c0c767e6751c09fc85cde58722cf2f1007e80e4c8d5a4321fc90d83dc54ca147"
+dependencies = [
+ "anyhow",
+ "derive_builder",
+ "rustversion",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
 [[package]]
 name = "walkdir"
-version = "2.3.3"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
 dependencies = [
  "same-file",
  "winapi-util",
@@ -3590,12 +4042,6 @@ dependencies = [
  "try-lock",
 ]
 
-[[package]]
-name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
-
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
@@ -3610,34 +4056,47 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.87"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.87"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
 dependencies = [
  "bumpalo",
  "log",
- "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
  "wasm-bindgen-shared",
 ]
 
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.87"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -3645,46 +4104,68 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.87"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.87"
+version = "0.2.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
 
 [[package]]
 name = "web-sys"
-version = "0.3.64"
+version = "0.3.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "webpki"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
 [[package]]
 name = "webpki-roots"
-version = "0.25.3"
+version = "0.25.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
+checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
 
 [[package]]
 name = "whoami"
-version = "1.5.0"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
+checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
 dependencies = [
- "redox_syscall 0.4.1",
+ "redox_syscall",
  "wasite",
  "web-sys",
 ]
@@ -3707,11 +4188,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.5"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "winapi",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -3722,11 +4203,103 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows"
-version = "0.48.0"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+dependencies = [
+ "windows-core 0.52.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows"
+version = "0.54.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
+dependencies = [
+ "windows-core 0.54.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
+dependencies = [
+ "windows-core 0.57.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.54.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
+dependencies = [
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.57.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
 dependencies = [
- "windows-targets",
+ "windows-implement",
+ "windows-interface",
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.93",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.93",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
 ]
 
 [[package]]
@@ -3735,7 +4308,40 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
 ]
 
 [[package]]
@@ -3744,147 +4350,223 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
 ]
 
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
 [[package]]
 name = "winnow"
-version = "0.5.15"
+version = "0.6.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
-name = "xdg-home"
-version = "1.0.0"
+name = "winreg"
+version = "0.50.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
 dependencies = [
- "nix 0.26.4",
- "winapi",
+ "cfg-if",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
-name = "zbus"
-version = "3.14.1"
+name = "write16"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948"
-dependencies = [
- "async-broadcast",
- "async-executor",
- "async-fs",
- "async-io",
- "async-lock",
- "async-process",
- "async-recursion",
- "async-task",
- "async-trait",
- "blocking",
- "byteorder",
- "derivative",
- "enumflags2",
- "event-listener",
- "futures-core",
- "futures-sink",
- "futures-util",
- "hex",
- "nix 0.26.4",
- "once_cell",
- "ordered-stream",
- "rand",
- "serde",
- "serde_repr",
- "sha1",
- "static_assertions",
- "tracing",
- "uds_windows",
- "winapi",
- "xdg-home",
- "zbus_macros",
- "zbus_names",
- "zvariant",
-]
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
 
 [[package]]
-name = "zbus_macros"
-version = "3.14.1"
+name = "yoke"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
 dependencies = [
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "regex",
- "syn 1.0.109",
- "zvariant_utils",
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
 ]
 
 [[package]]
-name = "zbus_names"
-version = "2.6.0"
+name = "yoke-derive"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
 dependencies = [
- "serde",
- "static_assertions",
- "zvariant",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.93",
+ "synstructure",
 ]
 
 [[package]]
 name = "zerocopy"
-version = "0.6.6"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
  "byteorder",
  "zerocopy-derive",
@@ -3892,49 +4574,60 @@ dependencies = [
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.6.6"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.29",
+ "syn 2.0.93",
 ]
 
 [[package]]
-name = "zvariant"
-version = "3.15.0"
+name = "zerofrom"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
 dependencies = [
- "byteorder",
- "enumflags2",
- "libc",
- "serde",
- "static_assertions",
- "zvariant_derive",
+ "zerofrom-derive",
 ]
 
 [[package]]
-name = "zvariant_derive"
-version = "3.15.0"
+name = "zerofrom-derive"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
 dependencies = [
- "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
- "zvariant_utils",
+ "syn 2.0.93",
+ "synstructure",
 ]
 
 [[package]]
-name = "zvariant_utils"
-version = "1.0.1"
+name = "zeroize"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.93",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 4ecf0fdf..66d3e156 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,36 +6,39 @@ description = "A Spotify daemon"
 repository = "https://github.com/Spotifyd/spotifyd"
 license = "GPL-3.0-only"
 version = "0.3.5"
-rust-version = "1.67"
+rust-version = "1.81"
 
 [dependencies]
-alsa = { version = "0.7", optional = true }
+alsa = { version = "0.9.1", optional = true }
 chrono = "0.4"
 dbus = { version = "0.9", optional = true }
 dbus-tokio = { version = "0.7.3", optional = true }
 dbus-crossroads = { version = "0.5.0", optional = true }
-fern = { version = "0.6.0", features = ["syslog-6"] }
+fern = { version = "0.7.0", features = ["syslog-6"] }
 futures = "0.3.15"
-gethostname = "0.4.0"
+gethostname = "0.5.0"
 hex = "0.4"
-keyring = { version = "2.0", optional = true }
+keyring = { version = "3.5", optional = true, features = ["apple-native", "windows-native", "sync-secret-service"] }
 libc = "0.2.82"
 log = "0.4.6"
-rspotify = { version = "0.12.0", features = ["client-ureq", "ureq-rustls-tls"], default-features = false, optional = true }
 serde = { version = "1.0.115", features = ["derive"] }
 sha-1 = "0.10"
 structopt = "0.3.17"
 tokio = {version = "1.26.0", features = ["signal", "rt-multi-thread", "process", "io-std"] }
 tokio-stream = "0.1.7"
 url = "2.2.2"
-librespot-audio = { version = "0.4", default-features = false }
-librespot-playback = { version = "0.4", default-features = false }
-librespot-core = { version = "0.4" }
-librespot-discovery = { version = "0.4" }
-librespot-connect = { version = "0.4" }
-toml = "0.7"
+librespot-audio = { version = "0.6", default-features = false }
+librespot-playback = { version = "0.6", default-features = false }
+librespot-core = "0.6"
+librespot-discovery = "0.6"
+librespot-connect = "0.6"
+librespot-metadata = "0.6"
+librespot-protocol = "0.6"
+toml = "0.8.19"
 color-eyre = "0.6"
 directories = "5.0.1"
+thiserror = "2.0"
+time = { version = "0.3.37", default-features = false, features = ["formatting"] }
 
 [target."cfg(unix)".dependencies]
 daemonize = "0.5"
@@ -48,12 +51,12 @@ whoami = "1"
 pledge = "0.4.2"
 
 [dev-dependencies]
-env_logger = "0.10"
+env_logger = "0.11"
 
 [features]
 alsa_backend = ["librespot-playback/alsa-backend", "alsa"]
 dbus_keyring = ["keyring"]
-dbus_mpris = ["dbus", "dbus-tokio", "dbus-crossroads", "rspotify"]
+dbus_mpris = ["dbus", "dbus-tokio", "dbus-crossroads"]
 pipe_backend = []
 default = ["alsa_backend", "pipe_backend"]
 portaudio_backend = ["librespot-playback/portaudio-backend"]
diff --git a/docs/src/installation/cross-compile-using-docker.md b/docs/src/installation/cross-compile-using-docker.md
index 97277648..53d480f8 100644
--- a/docs/src/installation/cross-compile-using-docker.md
+++ b/docs/src/installation/cross-compile-using-docker.md
@@ -13,22 +13,26 @@ We can also use `docker` to cross compile on every platform and OS that runs `do
 
     If you are **not** using Docker-Desktop you might have to install [QEMU](https://docs.docker.com/build/building/multi-platform/#install-qemu-manually)
 
-2. Create a docker `compose-file.yml`
+2. Create a docker `docker-compose.yml`
 
     Here we are building a `arm64` binary, so we set `platform: linux/arm64`
 
     ```yaml
     services:
       build-container:
-        image: rust:1.79-bookworm
+        image: rust:1-bookworm
         platform: linux/arm64
         command: bash -c "
             apt-get update &&
             apt-get install -y \
               libasound2-dev \
               libssl-dev \
+              jq \
               pkg-config &&
-            curl -sSL https://api.github.com/repos/Spotifyd/spotifyd/tarball/v0.3.5 | tar xz -C /spotifyd --strip-components=1 &&
+            wget -O - https://api.github.com/repos/Spotifyd/spotifyd/tarball/$(\
+                curl -SsL https://api.github.com/repos/Spotifyd/spotifyd/releases/latest \
+                  | jq '.tag_name' -r) \
+              | tar xzv -C /spotifyd --strip-components=1 &&
             cargo build --release &&
             cp /spotifyd/target/release/spotifyd /build/"
         working_dir: /spotifyd
diff --git a/src/config.rs b/src/config.rs
index c52cef92..9c635777 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -5,9 +5,7 @@ use crate::{
 };
 use color_eyre::Report;
 use gethostname::gethostname;
-use librespot_core::{
-    cache::Cache, config::DeviceType as LSDeviceType, config::SessionConfig, version,
-};
+use librespot_core::{cache::Cache, config::DeviceType as LSDeviceType, config::SessionConfig};
 use librespot_playback::{
     config::{AudioFormat as LSAudioFormat, Bitrate as LSBitrate, PlayerConfig},
     dither::{mk_ditherer, DithererBuilder, TriangularDitherer},
@@ -58,7 +56,7 @@ pub enum Backend {
 }
 
 fn default_backend() -> Backend {
-    return Backend::from_str(BACKEND_VALUES.first().unwrap()).unwrap();
+    Backend::from_str(BACKEND_VALUES.first().unwrap()).unwrap()
 }
 
 impl FromStr for Backend {
@@ -750,7 +748,6 @@ pub(crate) struct SpotifydConfig {
     pub(crate) shell: String,
     pub(crate) zeroconf_port: Option<u16>,
     pub(crate) device_type: String,
-    pub(crate) autoplay: bool,
 }
 
 pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
@@ -915,17 +912,17 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
         device_name,
         player_config: pc,
         session_config: SessionConfig {
-            user_agent: version::VERSION_STRING.to_string(),
+            autoplay: Some(autoplay),
             device_id,
             proxy: proxy_url,
             ap_port: Some(443),
+            ..Default::default()
         },
         onevent: config.shared_config.on_song_change_hook,
         pid,
         shell,
         zeroconf_port: config.shared_config.zeroconf_port,
         device_type,
-        autoplay,
     }
 }
 
diff --git a/src/dbus_mpris.rs b/src/dbus_mpris.rs
index d5e221db..ad84aa83 100644
--- a/src/dbus_mpris.rs
+++ b/src/dbus_mpris.rs
@@ -4,184 +4,417 @@ use dbus::{
     arg::{RefArg, Variant},
     channel::{MatchingReceiver, Sender},
     message::{MatchRule, SignalArgs},
+    nonblock::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged,
     MethodErr,
 };
 use dbus_crossroads::{Crossroads, IfaceToken};
-use dbus_tokio::connection;
+use dbus_tokio::connection::{self, IOResourceError};
 use futures::{
-    self,
     task::{Context, Poll},
     Future,
 };
-use librespot_connect::spirc::Spirc;
-use librespot_core::{
-    keymaster::{get_token, Token as LibrespotToken},
-    mercury::MercuryError,
-    session::Session,
-    spotify_id::SpotifyAudioType,
-};
+use librespot_connect::spirc::{Spirc, SpircLoadCommand};
+use librespot_core::{spotify_id::SpotifyItemType, Session, SpotifyId};
+use librespot_metadata::audio::AudioItem;
 use librespot_playback::player::PlayerEvent;
-use log::{error, info, warn};
-use rspotify::{
-    model::{
-        offset::Offset, parse_uri, AlbumId, ArtistId, EpisodeId, IdError, PlayableItem, PlaylistId,
-        RepeatState, ShowId, TrackId, Type,
+use librespot_protocol::spirc::TrackRef;
+use log::{debug, error, warn};
+use std::convert::TryFrom;
+use std::{
+    collections::HashMap,
+    pin::Pin,
+    sync::{Arc, RwLock},
+};
+use thiserror::Error;
+use time::format_description::well_known::Iso8601;
+use tokio::{
+    runtime::Handle,
+    sync::{
+        mpsc::{UnboundedReceiver, UnboundedSender},
+        Mutex,
     },
-    prelude::*,
-    AuthCodeSpotify, Token as RspotifyToken,
 };
-use std::{collections::HashMap, env, pin::Pin, sync::Arc};
-use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
 
-pub struct DbusServer {
-    session: Session,
-    spirc: Arc<Spirc>,
-    spotify_client: Arc<AuthCodeSpotify>,
-    dbus_type: DBusType,
-    #[allow(clippy::type_complexity)]
-    token_request: Option<Pin<Box<dyn Future<Output = Result<LibrespotToken, MercuryError>>>>>,
-    dbus_future: Option<Pin<Box<dyn Future<Output = ()>>>>,
-    device_name: String,
-    event_rx: UnboundedReceiver<PlayerEvent>,
-    event_tx: Option<UnboundedSender<PlayerEvent>>,
+type DbusMap = HashMap<String, Variant<Box<dyn RefArg>>>;
+
+const MPRIS_PATH: &str = "/org/mpris/MediaPlayer2";
+const CONTROLS_PATH: &str = "/rs/spotifyd/Controls";
+
+pub enum ControlMessage {
+    SetSession(Arc<Spirc>, Session),
+    DropSession,
+    Shutdown,
 }
 
-const CLIENT_ID: &str = "2c1ea588dfbc4a989e2426f8385297c3";
-const SCOPE: &str =
-    "user-read-playback-state,user-modify-playback-state,user-read-currently-playing";
+pub(crate) struct DbusServer {
+    dbus_future: Pin<Box<dyn Future<Output = Result<(), DbusError>>>>,
+    control_tx: UnboundedSender<ControlMessage>,
+}
 
 impl DbusServer {
-    pub fn new(
-        session: Session,
-        spirc: Arc<Spirc>,
-        device_name: String,
-        event_rx: UnboundedReceiver<PlayerEvent>,
-        dbus_type: DBusType,
-    ) -> DbusServer {
+    pub fn new(event_rx: UnboundedReceiver<PlayerEvent>, dbus_type: DBusType) -> DbusServer {
+        let (control_tx, control_rx) = tokio::sync::mpsc::unbounded_channel();
+        let dbus_future = Box::pin(create_dbus_server(event_rx, control_rx, dbus_type));
         DbusServer {
-            session,
-            spirc,
-            spotify_client: Default::default(),
-            dbus_type,
-            token_request: None,
-            dbus_future: None,
-            device_name,
-            event_rx,
-            event_tx: None,
+            dbus_future,
+            control_tx,
         }
     }
+
+    pub fn set_session(&self, spirc: Arc<Spirc>, session: Session) -> Result<(), DbusError> {
+        self.control_tx
+            .send(ControlMessage::SetSession(spirc, session))
+            .map_err(|_| DbusError::ControlChannelBroken)
+    }
+
+    pub fn drop_session(&self) -> Result<(), DbusError> {
+        self.control_tx
+            .send(ControlMessage::DropSession)
+            .map_err(|_| DbusError::ControlChannelBroken)
+    }
+
+    /// Sends a shutdown signal and returns false, if the server was already shut down.
+    pub fn shutdown(&self) -> bool {
+        self.control_tx.send(ControlMessage::Shutdown).is_ok()
+    }
+}
+
+#[derive(Debug, Error)]
+pub(crate) enum DbusError {
+    #[error("Failed to initialize D-Bus: {}", .0)]
+    InitFailure(#[from] dbus::Error),
+    #[error("The connection was terminated unexpectedly: {}", .0)]
+    ConnectionFailure(#[from] IOResourceError),
+    #[error("Unexpectedly lost control of dbus server")]
+    ControlChannelBroken,
 }
 
 impl Future for DbusServer {
-    type Output = ();
+    type Output = Result<(), DbusError>;
 
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
-        if self.event_tx.is_some() {
-            if let Poll::Ready(Some(msg)) = self.event_rx.poll_recv(cx) {
-                self.event_tx.as_ref().unwrap().send(msg).unwrap();
-            }
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        self.dbus_future.as_mut().poll(cx)
+    }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+enum PlaybackStatus {
+    Playing,
+    Paused,
+    Stopped,
+}
+
+impl PlaybackStatus {
+    fn to_mpris(self) -> &'static str {
+        match self {
+            PlaybackStatus::Playing => "Playing",
+            PlaybackStatus::Paused => "Paused",
+            PlaybackStatus::Stopped => "Stopped",
         }
-        let needs_token = match *self.spotify_client.get_token().lock().unwrap() {
-            Some(ref token) => token.is_expired(),
-            None => true,
-        };
-
-        if needs_token {
-            if let Some(mut fut) = self.token_request.take() {
-                if let Poll::Ready(token) = fut.as_mut().poll(cx) {
-                    let token = match token {
-                        Ok(token) => token,
-                        Err(_) => {
-                            error!("failed to request a token for the web API");
-                            // shutdown DBus-Server
-                            return Poll::Ready(());
-                        }
-                    };
-
-                    let expires_in = Duration::seconds(token.expires_in as i64);
-                    let api_token = RspotifyToken {
-                        access_token: token.access_token,
-                        expires_in,
-                        expires_at: Some(Utc::now() + expires_in),
-                        ..RspotifyToken::default()
-                    };
-
-                    if self.dbus_future.is_none() {
-                        self.spotify_client = Arc::new(AuthCodeSpotify::from_token(api_token));
-
-                        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
-                        self.event_tx = Some(tx);
-                        self.dbus_future = Some(Box::pin(create_dbus_server(
-                            Arc::clone(&self.spotify_client),
-                            self.spirc.clone(),
-                            self.device_name.clone(),
-                            rx,
-                            self.dbus_type,
-                        )));
-                    } else {
-                        *self.spotify_client.get_token().lock().unwrap() = Some(api_token);
-                    }
-                } else {
-                    self.token_request = Some(fut);
+    }
+}
+
+#[derive(Debug)]
+struct Position {
+    last_position: Duration,
+    last_update: DateTime<Local>,
+}
+
+impl Position {
+    fn new() -> Self {
+        Self {
+            last_position: Duration::zero(),
+            last_update: Local::now(),
+        }
+    }
+    fn update_position(&mut self, new_position: Duration) {
+        self.last_update = Local::now();
+        self.last_position = new_position;
+    }
+
+    fn get_position(&self) -> Duration {
+        (Local::now() - self.last_update) + self.last_position
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+enum RepeatState {
+    None,
+    // Track,
+    All,
+}
+
+impl RepeatState {
+    fn to_mpris(self) -> &'static str {
+        match self {
+            RepeatState::None => "None",
+            // RepeatState::Track => "Track",
+            RepeatState::All => "Playlist",
+        }
+    }
+}
+
+impl From<RepeatState> for bool {
+    fn from(repeat: RepeatState) -> Self {
+        match repeat {
+            RepeatState::None => false,
+            RepeatState::All => true,
+        }
+    }
+}
+
+impl From<bool> for RepeatState {
+    fn from(repeat: bool) -> Self {
+        if repeat {
+            RepeatState::All
+        } else {
+            RepeatState::None
+        }
+    }
+}
+
+#[derive(Debug)]
+struct CurrentStateInner {
+    status: PlaybackStatus,
+    position: Option<Position>,
+    audio_item: Option<Box<AudioItem>>,
+    volume: u16,
+    shuffle: bool,
+    repeat: RepeatState,
+    play_request_id: Option<u64>,
+}
+
+fn insert_attr(map: &mut DbusMap, attr: impl ToString, value: impl RefArg + 'static) {
+    map.insert(attr.to_string(), Variant(Box::new(value)));
+}
+
+impl CurrentStateInner {
+    fn mpris_volume(&self) -> f64 {
+        self.volume as f64 / u16::MAX as f64
+    }
+
+    fn get_position(&self) -> Option<Duration> {
+        let position = self.position.as_ref()?;
+        match self.status {
+            PlaybackStatus::Playing => Some(position.get_position()),
+            PlaybackStatus::Paused => Some(position.last_position),
+            PlaybackStatus::Stopped => None,
+        }
+    }
+
+    fn update_position(&mut self, position: Duration) {
+        self.position
+            .get_or_insert_with(Position::new)
+            .update_position(position);
+    }
+
+    fn handle_event(&mut self, event: PlayerEvent) -> (DbusMap, bool) {
+        let mut changed = DbusMap::new();
+        let mut seeked = false;
+
+        // note that get_play_request_id is None on PlayRequestIdChanged
+        if Option::zip(self.play_request_id, event.get_play_request_id())
+            .is_some_and(|(cur_id, event_id)| cur_id != event_id)
+        {
+            debug!("discarding event due to play_request_id mismatch");
+            return (changed, seeked);
+        }
+
+        debug!("handling event {event:?}");
+        match event {
+            PlayerEvent::VolumeChanged { volume } => {
+                self.volume = volume;
+                insert_attr(&mut changed, "Volume", self.mpris_volume());
+            }
+            PlayerEvent::Stopped { .. } => {
+                self.status = PlaybackStatus::Stopped;
+                self.audio_item = None;
+                insert_attr(
+                    &mut changed,
+                    "PlaybackStatus",
+                    self.status.to_mpris().to_string(),
+                );
+                insert_attr(&mut changed, "Metadata", self.to_metadata());
+            }
+            PlayerEvent::Playing { position_ms, .. } => {
+                if self.status != PlaybackStatus::Playing {
+                    self.status = PlaybackStatus::Playing;
+                    insert_attr(
+                        &mut changed,
+                        "PlaybackStatus",
+                        self.status.to_mpris().to_string(),
+                    );
                 }
-            } else {
-                self.token_request = Some(Box::pin({
-                    let sess = self.session.clone();
-                    // This is more meant as a fast hotfix than anything else!
-                    let client_id =
-                        env::var("SPOTIFYD_CLIENT_ID").unwrap_or_else(|_| CLIENT_ID.to_string());
-                    async move { get_token(&sess, &client_id, SCOPE).await }
-                }));
+                self.update_position(Duration::milliseconds(position_ms as i64));
+                seeked = true;
+            }
+            PlayerEvent::Paused { position_ms, .. } => {
+                if self.status != PlaybackStatus::Paused {
+                    self.status = PlaybackStatus::Paused;
+                    insert_attr(
+                        &mut changed,
+                        "PlaybackStatus",
+                        self.status.to_mpris().to_string(),
+                    )
+                }
+                self.update_position(Duration::milliseconds(position_ms as i64));
+                seeked = true;
+            }
+            PlayerEvent::TrackChanged { audio_item } => {
+                self.audio_item = Some(audio_item);
+                insert_attr(&mut changed, "Metadata", self.to_metadata());
+            }
+            PlayerEvent::PositionCorrection { position_ms, .. }
+            | PlayerEvent::Seeked { position_ms, .. } => {
+                self.update_position(Duration::milliseconds(position_ms as i64));
+                seeked = true;
+            }
+            PlayerEvent::ShuffleChanged { shuffle } => {
+                self.shuffle = shuffle;
+                insert_attr(&mut changed, "Shuffle", self.shuffle);
             }
+            PlayerEvent::RepeatChanged { repeat } => {
+                self.repeat = repeat.into();
+                insert_attr(
+                    &mut changed,
+                    "LoopStatus",
+                    self.repeat.to_mpris().to_string(),
+                )
+            }
+            PlayerEvent::PlayRequestIdChanged { play_request_id } => {
+                self.play_request_id = Some(play_request_id);
+            }
+            PlayerEvent::Preloading { .. }
+            | PlayerEvent::Loading { .. }
+            | PlayerEvent::TimeToPreloadNextTrack { .. }
+            | PlayerEvent::EndOfTrack { .. }
+            | PlayerEvent::Unavailable { .. }
+            | PlayerEvent::AutoPlayChanged { .. }
+            | PlayerEvent::FilterExplicitContentChanged { .. }
+            | PlayerEvent::SessionConnected { .. }
+            | PlayerEvent::SessionDisconnected { .. }
+            | PlayerEvent::SessionClientChanged { .. } => (),
         }
 
-        // Not polling the future here in some cases is fine, since we will poll it
-        // immediately after the token request has completed.
-        // If we would poll the future in any case, we would risk using invalid tokens for API requests.
-        if self.token_request.is_none() {
-            if let Some(ref mut fut) = self.dbus_future {
-                return fut.as_mut().poll(cx);
+        (changed, seeked)
+    }
+
+    fn to_metadata(&self) -> DbusMap {
+        let mut m = HashMap::new();
+
+        insert_attr(
+            &mut m,
+            "mpris:trackid",
+            uri_to_object_path(
+                self.audio_item
+                    .as_deref()
+                    .and_then(|item| item.track_id.to_uri().ok())
+                    .as_deref(),
+            ),
+        );
+
+        if let Some(audio_item) = self.audio_item.as_deref() {
+            if let Some(length) =
+                Duration::milliseconds(audio_item.duration_ms as i64).num_microseconds()
+            {
+                insert_attr(&mut m, "mpris:length", length);
+            }
+
+            if let Some(cover) = audio_item.covers.iter().max_by_key(|im| im.width) {
+                insert_attr(&mut m, "mpris:artUrl", cover.url.clone());
+            }
+
+            insert_attr(&mut m, "xesam:title", audio_item.name.clone());
+
+            use librespot_metadata::audio::UniqueFields::*;
+            match &audio_item.unique_fields {
+                Track {
+                    artists,
+                    album,
+                    album_artists,
+                    popularity,
+                    number,
+                    disc_number,
+                } => {
+                    insert_attr(
+                        &mut m,
+                        "xesam:artist",
+                        artists
+                            .iter()
+                            .map(|artist| artist.name.clone())
+                            .collect::<Vec<String>>(),
+                    );
+                    insert_attr(&mut m, "xesam:album", album.clone());
+                    insert_attr(&mut m, "xesam:albumArtist", album_artists.clone());
+                    insert_attr(&mut m, "xesam:autoRating", (*popularity as f64) / 100.0);
+                    insert_attr(&mut m, "xesam:trackNumber", *number);
+                    insert_attr(&mut m, "xesam:discNumber", *disc_number);
+                }
+                Episode {
+                    description,
+                    publish_time,
+                    show_name,
+                } => {
+                    insert_attr(&mut m, "xesam:artist", vec![show_name.clone()]);
+                    insert_attr(&mut m, "xesam:comment", vec![description.clone()]);
+                    if let Ok(formatted_publish) = publish_time.format(&Iso8601::DEFAULT) {
+                        insert_attr(&mut m, "xesam:contentCreated", formatted_publish);
+                    }
+                }
             }
         }
 
-        Poll::Pending
+        m
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq)]
-enum PlaybackStatus {
-    Playing,
-    Paused,
-    Stopped,
+#[derive(Debug)]
+struct CurrentState(RwLock<CurrentStateInner>);
+
+#[derive(Clone, Copy, Debug, Error)]
+#[error("internal state no longer available due to application error")]
+struct StatePoisonError;
+
+impl From<StatePoisonError> for MethodErr {
+    fn from(value: StatePoisonError) -> Self {
+        MethodErr::failed(&value)
+    }
+}
+
+impl CurrentState {
+    fn new(inner: CurrentStateInner) -> Self {
+        Self(RwLock::new(inner))
+    }
+
+    fn read(&self) -> Result<std::sync::RwLockReadGuard<CurrentStateInner>, StatePoisonError> {
+        self.0.read().map_err(|_| StatePoisonError)
+    }
+
+    fn write(&self) -> Result<std::sync::RwLockWriteGuard<CurrentStateInner>, StatePoisonError> {
+        self.0.write().map_err(|_| StatePoisonError)
+    }
 }
 
 async fn create_dbus_server(
-    spotify_api_client: Arc<AuthCodeSpotify>,
-    spirc: Arc<Spirc>,
-    device_name: String,
     mut event_rx: UnboundedReceiver<PlayerEvent>,
+    mut control_rx: UnboundedReceiver<ControlMessage>,
     dbus_type: DBusType,
-) {
+) -> Result<(), DbusError> {
     let (resource, conn) = match dbus_type {
         DBusType::Session => connection::new_session_sync(),
         DBusType::System => connection::new_system_sync(),
-    }
-    .expect("Failed to initialize DBus connection");
-    tokio::spawn(async {
-        let err = resource.await;
-        panic!("Lost connection to D-Bus: {}", err);
-    });
+    }?;
+    let mut connection_task = tokio::spawn(async { Err::<(), _>(resource.await) });
 
-    let path = format!(
+    // this name will be used, once we can provide the mpris interface
+    let mpris_name = format!(
         "org.mpris.MediaPlayer2.spotifyd.instance{}",
         std::process::id()
     );
+    // this name will always be available to allow easy discovery of the controls interface
+    let spotifyd_name = format!("rs.spotifyd.instance{}", std::process::id());
 
-    // TODO: The first `true` allows us to replace orphaned dbus servers from previous sessions
-    // later. We should instead properly release the name when the session ends.
-    conn.request_name(path, true, true, true)
-        .await
-        .expect("Failed to register dbus player name");
+    conn.request_name(&spotifyd_name, false, true, true).await?;
 
     let mut cr = Crossroads::new();
     cr.set_async_support(Some((
@@ -191,16 +424,158 @@ async fn create_dbus_server(
         }),
     )));
 
+    let current_state = Arc::new(CurrentState::new(CurrentStateInner {
+        status: PlaybackStatus::Stopped,
+        position: None,
+        audio_item: None,
+        volume: u16::MAX,
+        shuffle: false,
+        repeat: RepeatState::None,
+        play_request_id: None,
+    }));
+
+    let (quit_tx, mut quit_rx) = tokio::sync::mpsc::unbounded_channel();
+
+    let cr = Arc::new(Mutex::new(cr));
+
+    let crossroads = cr.clone();
+    conn.start_receive(
+        MatchRule::new_method_call(),
+        Box::new(move |msg, conn| {
+            tokio::task::block_in_place(|| {
+                let mut cr = cr.blocking_lock();
+                cr.handle_message(msg, conn).unwrap();
+            });
+            true
+        }),
+    );
+
+    let mut spirc: Option<Arc<Spirc>> = None;
+    let mut session: Option<Session> = None;
+
+    struct ConnectionData {
+        conn_id: String,
+        seeked_fn: SeekedSignal,
+    }
+    let mut cur_conn: Option<ConnectionData> = None;
+
+    loop {
+        tokio::select! {
+            _ = quit_rx.recv() => {
+                break;
+            }
+            result = &mut connection_task => {
+                result.expect("the dbus connection panicked")?;
+                break;
+            }
+            event = event_rx.recv() => {
+                let event = event.expect("event channel was unexpectedly closed");
+
+                if let PlayerEvent::SessionConnected { connection_id, .. } = event {
+                    let mut cr = crossroads.lock().await;
+                    let seeked_fn = register_player_interface(
+                        &mut cr,
+                        spirc.clone().unwrap(),
+                        session.clone().unwrap(),
+                        current_state.clone(),
+                        quit_tx.clone(),
+                    );
+                    if cur_conn.is_none() {
+                        conn.request_name(&mpris_name, true, true, true).await?;
+                    }
+                    cur_conn = Some(ConnectionData { conn_id: connection_id, seeked_fn });
+                } else if let PlayerEvent::SessionDisconnected { connection_id, .. } = event {
+                    // if this message isn't outdated yet, we vanish from the bus
+                    if cur_conn.as_ref().is_some_and(|d| d.conn_id == connection_id) {
+                        let mut cr = crossroads.lock().await;
+                        conn.release_name(&mpris_name).await?;
+                        cr.remove::<()>(&MPRIS_PATH.into());
+                        cur_conn = None;
+                    }
+                } else {
+                    let (changed, seeked) = current_state
+                        .write()
+                        .expect("state has been poisoned")
+                        .handle_event(event);
+
+                    if seeked {
+                        let position = current_state
+                            .read()
+                            .expect("state has been poisoned")
+                            .get_position();
+                        if let Some((ConnectionData { seeked_fn, .. }, position)) =
+                            Option::zip(cur_conn.as_ref(), position)
+                        {
+                            let msg = seeked_fn(
+                                &MPRIS_PATH.into(),
+                                &(position.num_microseconds().unwrap_or_default(),),
+                            );
+                            conn.send(msg).unwrap();
+                        }
+                    }
+
+                    if !changed.is_empty() {
+                        let msg = PropertiesPropertiesChanged {
+                            interface_name: "org.mpris.MediaPlayer2.Player".to_owned(),
+                            changed_properties: changed,
+                            invalidated_properties: Vec::new(),
+                        };
+                        conn.send(
+                            msg.to_emit_message(&dbus::Path::new(MPRIS_PATH).unwrap()),
+                        )
+                        .unwrap();
+                    }
+                }
+
+            }
+            control = control_rx.recv() => {
+                let control = control.expect("control channel was unexpectedly closed");
+                match control {
+                    ControlMessage::Shutdown => {
+                        break;
+                    },
+                    ControlMessage::SetSession(new_spirc, new_session) => {
+                        let mut cr = crossroads.lock().await;
+                        register_controls_interface(&mut cr, new_spirc.clone());
+                        spirc = Some(new_spirc);
+                        session = Some(new_session);
+                    }
+                    ControlMessage::DropSession => {
+                        let mut cr = crossroads.lock().await;
+                        conn.release_name(&mpris_name).await?;
+                        cr.remove::<()>(&MPRIS_PATH.into());
+                        cr.remove::<()>(&CONTROLS_PATH.into());
+                        spirc = None;
+                        session = None;
+                    }
+                }
+            }
+        }
+    }
+    conn.release_name(&mpris_name).await?;
+    conn.release_name(&spotifyd_name).await?;
+    Ok(())
+}
+
+type SeekedSignal = Box<dyn Fn(&dbus::Path, &(i64,)) -> dbus::Message + Send + Sync + 'static>;
+
+fn register_player_interface(
+    cr: &mut Crossroads,
+    spirc: Arc<Spirc>,
+    session: Session,
+    current_state: Arc<CurrentState>,
+    quit_tx: tokio::sync::mpsc::UnboundedSender<()>,
+) -> SeekedSignal {
     // The following methods and properties are part of the MediaPlayer2 interface.
     // https://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html
-    let media_player2_interface = cr.register("org.mpris.MediaPlayer2", |b| {
+    let media_player2_interface = cr.register("org.mpris.MediaPlayer2", move |b| {
+        let mut quit_tx = Some(quit_tx);
         b.method("Raise", (), (), move |_, _, (): ()| {
             // noop
             Ok(())
         });
-        let local_spirc = spirc.clone();
         b.method("Quit", (), (), move |_, _, (): ()| {
-            local_spirc.shutdown();
+            quit_tx.take().unwrap().send(()).ok();
             Ok(())
         });
         b.property("CanQuit")
@@ -229,232 +604,203 @@ async fn create_dbus_server(
     // The following methods and properties are part of the MediaPlayer2.Player interface.
     // https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html
 
+    let mut seeked_signal = None;
+
     let player_interface: IfaceToken<()> = cr.register("org.mpris.MediaPlayer2.Player", |b| {
+        seeked_signal = Some(b.signal::<(i64,), _>("Seeked", ("Position",)).msg_fn());
         let local_spirc = spirc.clone();
         b.method("VolumeUp", (), (), move |_, _, (): ()| {
-            local_spirc.volume_up();
-            Ok(())
+            local_spirc.volume_up().map_err(|e| MethodErr::failed(&e))
         })
         .deprecated();
         let local_spirc = spirc.clone();
         b.method("VolumeDown", (), (), move |_, _, (): ()| {
-            local_spirc.volume_down();
-            Ok(())
+            local_spirc.volume_down().map_err(|e| MethodErr::failed(&e))
         })
         .deprecated();
         let local_spirc = spirc.clone();
         b.method("Next", (), (), move |_, _, (): ()| {
-            local_spirc.next();
-            Ok(())
+            local_spirc.next().map_err(|e| MethodErr::failed(&e))
         });
         let local_spirc = spirc.clone();
         b.method("Previous", (), (), move |_, _, (): ()| {
-            local_spirc.prev();
-            Ok(())
+            local_spirc.prev().map_err(|e| MethodErr::failed(&e))
         });
         let local_spirc = spirc.clone();
         b.method("Pause", (), (), move |_, _, (): ()| {
-            local_spirc.pause();
-            Ok(())
+            local_spirc.pause().map_err(|e| MethodErr::failed(&e))
         });
         let local_spirc = spirc.clone();
         b.method("PlayPause", (), (), move |_, _, (): ()| {
-            local_spirc.play_pause();
-            Ok(())
+            local_spirc.play_pause().map_err(|e| MethodErr::failed(&e))
         });
         let local_spirc = spirc.clone();
         b.method("Play", (), (), move |_, _, (): ()| {
-            local_spirc.play();
-            Ok(())
+            local_spirc.play().map_err(|e| MethodErr::failed(&e))
         });
         let local_spirc = spirc.clone();
         b.method("Stop", (), (), move |_, _, (): ()| {
-            // TODO: add real stop implementation.
-            local_spirc.pause();
-            Ok(())
+            local_spirc.disconnect().map_err(|e| MethodErr::failed(&e))
         });
 
-        let mv_device_name = device_name.clone();
-        let sp_client = Arc::clone(&spotify_api_client);
-        b.method("Seek", ("offset",), (), move |_, _, (pos,): (i64,)| {
-            if let Ok(Some(playback)) = sp_client.current_playback(None, None::<Vec<_>>) {
-                if playback.device.name == mv_device_name {
-                    let new_pos = playback
-                        .progress
-                        .and_then(|d| d.checked_add(&Duration::milliseconds(pos / 1000)));
-
-                    if let Some(new_pos) = new_pos {
-                        let duration: Duration = match playback.item {
-                            Some(PlayableItem::Track(t)) => t.duration,
-                            Some(PlayableItem::Episode(e)) => e.duration,
-                            None => return Ok(()),
-                        };
-
-                        // MPRIS spec: negative values should be treated as 0
-                        let new_pos = new_pos.max(Duration::zero());
-                        if new_pos <= duration {
-                            let _ = sp_client.seek_track(new_pos, playback.device.id.as_deref());
-                        } else {
-                            // MPRIS spec: values beyond track bounds should act like Next
-                            let _ = sp_client.next_track(playback.device.id.as_deref());
-                        }
-                    }
-                }
+        let local_spirc = spirc.clone();
+        let local_state = current_state.clone();
+        b.method("Seek", ("offset",), (), move |_, _, (offset,): (i64,)| {
+            let Some(position) = local_state.read()?.get_position() else {
+                return Err(dbus::MethodErr::failed(
+                    "cannot seek while playback is stopped",
+                ));
+            };
+            let new_pos = position + Duration::microseconds(offset);
+            let new_pos_ms = u32::try_from(new_pos.num_milliseconds()).map_err(|err| {
+                dbus::MethodErr::invalid_arg(&format!("new position out of bounds: {err}"))
+            })?;
+            if let Err(err) = local_spirc.set_position_ms(new_pos_ms) {
+                warn!("failed to seek by {offset}ms: {err}");
+                return Err(dbus::MethodErr::failed(&err));
             }
             Ok(())
         });
 
-        let mv_device_name = device_name.clone();
-        let sp_client = Arc::clone(&spotify_api_client);
+        let local_spirc = spirc.clone();
+        let local_state = current_state.clone();
         b.method(
             "SetPosition",
             ("track_id", "position"),
             (),
             move |_, _, (track_id, pos): (dbus::Path, i64)| {
-                if let Ok(Some(playback)) = sp_client.current_playback(None, None::<Vec<_>>) {
-                    let (track_matches, duration) = if let Some(item) = playback.item {
-                        let track_matches = item
-                            .id()
-                            .map(|id| uri_to_object_path(id.uri()) == track_id)
-                            .unwrap_or(false);
-                        let duration = match item {
-                            PlayableItem::Track(t) => t.duration.num_microseconds(),
-                            PlayableItem::Episode(e) => e.duration.num_microseconds(),
-                        }
-                        .unwrap_or(i64::MAX);
-                        (track_matches, duration)
-                    } else {
-                        return Ok(());
-                    };
-
-                    // as per MPRIS spec: pos must be in track bounds and track id must match
-                    if playback.device.name == mv_device_name
-                        && track_matches
-                        && (0..=duration).contains(&pos)
-                    {
-                        // pos is in microseconds, seek_track takes milliseconds
-                        let _ = sp_client.seek_track(
-                            Duration::milliseconds(pos / 1000),
-                            playback.device.id.as_deref(),
-                        );
-                    }
+                let Some((current_track_id, duration)) = local_state
+                    .read()?
+                    .audio_item
+                    .as_ref()
+                    .map(|item| (item.track_id, item.duration_ms))
+                else {
+                    return Err(dbus::MethodErr::failed(
+                        "can set position while nothing is playing",
+                    ));
+                };
+                let duration = Duration::milliseconds(duration.into());
+
+                if !track_id.ends_with(&current_track_id.to_base62().unwrap()) {
+                    // as per mpris spec: ignore as stale
+                    return Ok(());
                 }
+                let new_position = Duration::microseconds(pos);
+                if new_position < Duration::zero() || new_position > duration {
+                    // ignore as per spec
+                    return Ok(());
+                }
+                if let Err(err) =
+                    local_spirc.set_position_ms(new_position.num_milliseconds() as u32)
+                {
+                    return Err(dbus::MethodErr::failed(&err));
+                }
+
                 Ok(())
             },
         );
 
-        let mv_device_name = device_name.clone();
-        let sp_client = Arc::clone(&spotify_api_client);
+        let local_spirc = spirc.clone();
+        let local_state = current_state.clone();
         b.method("OpenUri", ("uri",), (), move |_, _, (uri,): (String,)| {
-            enum AnyId<'a> {
-                Playable(PlayableId<'a>),
-                Context(PlayContextId<'a>),
-            }
-
-            fn uri_to_id(uri: &str) -> Result<AnyId<'_>, IdError> {
-                use AnyId::*;
-                Ok(match parse_uri(uri)? {
-                    (Type::Track, id) => Playable(TrackId::from_id(id)?.into()),
-                    (Type::Episode, id) => Playable(EpisodeId::from_id(id)?.into()),
-                    (Type::Artist, id) => Context(ArtistId::from_id(id)?.into()),
-                    (Type::Album, id) => Context(AlbumId::from_id(id)?.into()),
-                    (Type::Playlist, id) => Context(PlaylistId::from_id(id)?.into()),
-                    (Type::Show, id) => Context(ShowId::from_id(id)?.into()),
-                    (Type::User | Type::Collection | Type::Collectionyourepisodes, _) => {
-                        Err(IdError::InvalidType)?
-                    }
-                })
+            let id = SpotifyId::from_uri(&uri).map_err(|e| MethodErr::invalid_arg(&e))?;
+            let CurrentStateInner {
+                shuffle, repeat, ..
+            } = *local_state.read()?;
+
+            fn id_to_trackref(id: &SpotifyId) -> TrackRef {
+                let mut trackref = TrackRef::new();
+                if let Ok(uri) = id.to_uri() {
+                    trackref.set_uri(uri);
+                } else {
+                    trackref.set_gid(id.to_raw().to_vec());
+                }
+                trackref
             }
 
-            let id = uri_to_id(&uri).map_err(|e| MethodErr::invalid_arg(&e))?;
+            let session = session.clone();
 
-            let device_id = get_device_id(&sp_client, &mv_device_name, true);
-
-            if let Some(device_id) = device_id {
-                match id {
-                    AnyId::Playable(id) => {
-                        let _ = sp_client.start_uris_playback(
-                            Some(id),
-                            Some(&device_id),
-                            Some(Offset::Position(Duration::zero())),
-                            None,
-                        );
-                    }
-                    AnyId::Context(id) => {
-                        let _ = sp_client.start_context_playback(
-                            id,
-                            Some(&device_id),
-                            Some(Offset::Position(Duration::zero())),
-                            None,
-                        );
-                    }
-                }
-            }
-            Ok(())
+            let (playing_track_index, context_uri, tracks) = Handle::current()
+                .block_on(async move {
+                    use librespot_metadata::*;
+                    Ok::<_, librespot_core::Error>(match id.item_type {
+                        SpotifyItemType::Album => {
+                            let album = Album::get(&session, &id).await?;
+                            (0, uri, album.tracks().map(id_to_trackref).collect())
+                        }
+                        SpotifyItemType::Artist => {
+                            let artist = Artist::get(&session, &id).await?;
+                            (
+                                0,
+                                uri,
+                                artist
+                                    .top_tracks
+                                    .for_country(&session.country())
+                                    .iter()
+                                    .map(id_to_trackref)
+                                    .collect(),
+                            )
+                        }
+                        SpotifyItemType::Playlist => {
+                            let playlist = Playlist::get(&session, &id).await?;
+                            (0, uri, playlist.tracks().map(id_to_trackref).collect())
+                        }
+                        SpotifyItemType::Track => {
+                            let track = Track::get(&session, &id).await?;
+                            (
+                                track.number as u32,
+                                track.album.id.to_uri()?,
+                                vec![id_to_trackref(&track.id)],
+                            )
+                        }
+                        SpotifyItemType::Episode => (0, uri, vec![id_to_trackref(&id)]),
+                        SpotifyItemType::Show => {
+                            let show = Show::get(&session, &id).await?;
+                            (0, uri, show.episodes.iter().map(id_to_trackref).collect())
+                        }
+                        SpotifyItemType::Local | SpotifyItemType::Unknown => {
+                            return Err(librespot_core::Error::unimplemented(
+                                "this type of uri is not supported",
+                            ));
+                        }
+                    })
+                })
+                .map_err(|e| MethodErr::failed(&e))?;
+
+            local_spirc
+                .load(SpircLoadCommand {
+                    context_uri,
+                    start_playing: true,
+                    shuffle,
+                    repeat: repeat.into(),
+                    playing_track_index,
+                    tracks,
+                })
+                .map_err(|e| MethodErr::failed(&e))
         });
 
-        let mv_device_name = device_name.clone();
-        let sp_client = Arc::clone(&spotify_api_client);
+        let local_state = current_state.clone();
         b.property("PlaybackStatus")
             .emits_changed_false()
             .get(move |_, _| {
-                if let Ok(Some(playback)) = sp_client.current_playback(None, None::<Vec<_>>) {
-                    if playback.device.name == mv_device_name {
-                        if playback.is_playing {
-                            return Ok("Playing".to_string());
-                        } else {
-                            return Ok("Paused".to_string());
-                        }
-                    }
-                }
-                Ok("Stopped".to_string())
+                let playback_state = local_state.read()?.status;
+                Ok(playback_state.to_mpris().to_string())
             });
 
-        let mv_device_name = device_name.clone();
-        let sp_client = Arc::clone(&spotify_api_client);
-        let sp_client2 = Arc::clone(&spotify_api_client);
+        // let local_spirc = spirc.clone();
+        let local_state = current_state.clone();
         b.property("Shuffle")
             .emits_changed_false()
-            .get(move |_, _| {
-                let shuffle_status = sp_client
-                    .current_playback(None, None::<Vec<_>>)
-                    .ok()
-                    .flatten()
-                    .map_or(false, |p| p.shuffle_state);
-                Ok(shuffle_status)
-            })
-            .set(move |_, _, value| {
-                let device_id = get_device_id(&sp_client2, &mv_device_name, true);
-                if let Some(device_id) = device_id {
-                    match sp_client2.shuffle(value, Some(&device_id)) {
-                        Ok(_) => Ok(None),
-                        Err(err) => {
-                            let e = format!("SetShuffle failed: {}", err);
-                            error!("{}", e);
-                            Err(MethodErr::failed(&e))
-                        }
-                    }
-                } else {
-                    let msg = format!("Could not find device with name {}", mv_device_name);
-                    warn!("SetShuffle: {}", msg);
-                    Err(MethodErr::failed(&msg))
-                }
-            });
+            .get(move |_, _| Ok(local_state.read()?.shuffle));
+        // TODO: re-enable, once setting shuffle via spirc works
+        // .set(move |_, _, value| {
+        //     local_spirc
+        //         .shuffle(value)
+        //         .map(|_| None)
+        //         .map_err(|err| dbus::MethodErr::failed(&err))
+        // });
 
         b.property("Rate").emits_changed_const().get(|_, _| Ok(1.0));
-
-        let sp_client = Arc::clone(&spotify_api_client);
-        b.property("Volume").emits_changed_false().get(move |_, _| {
-            let vol = sp_client
-                .current_playback(None, None::<Vec<_>>)
-                .ok()
-                .flatten()
-                .and_then(|p| p.device.volume_percent)
-                .unwrap_or(0) as f64;
-
-            Ok(vol)
-        });
-
         b.property("MaximumRate")
             .emits_changed_const()
             .get(|_, _| Ok(1.0));
@@ -462,59 +808,58 @@ async fn create_dbus_server(
             .emits_changed_const()
             .get(|_, _| Ok(1.0));
 
-        let sp_client = Arc::clone(&spotify_api_client);
-        b.property("LoopStatus")
+        let local_spirc = spirc.clone();
+        let local_state = current_state.clone();
+        b.property("Volume")
             .emits_changed_false()
+            .get(move |_, _| Ok(local_state.read()?.mpris_volume()))
+            .set(move |_, _, value| {
+                if let Err(err) = local_spirc.set_volume((value * u16::MAX as f64) as u16) {
+                    return Err(dbus::MethodErr::failed(&err));
+                }
+                Ok(None)
+            });
+
+        let local_spirc = spirc.clone();
+        let local_state = current_state.clone();
+        b.property("LoopStatus")
+            .emits_changed_true()
             .get(move |_, _| {
-                let status =
-                    if let Ok(Some(player)) = sp_client.current_playback(None, None::<Vec<_>>) {
-                        match player.repeat_state {
-                            RepeatState::Off => "None",
-                            RepeatState::Track => "Track",
-                            RepeatState::Context => "Playlist",
-                        }
-                    } else {
-                        "None"
+                let repeat = local_state.read()?.repeat;
+                Ok(repeat.to_mpris().to_string())
+            })
+            .set(move |_, _, value| {
+                let new_repeat = match value.as_str() {
+                    "None" => false,
+                    "Playlist" => true,
+                    mode => {
+                        return Err(dbus::MethodErr::failed(&format!(
+                            "unsupported repeat mode: {mode}"
+                        )))
                     }
-                    .to_string();
-                Ok(status)
+                };
+                local_spirc
+                    .repeat(new_repeat)
+                    .map_err(|e| MethodErr::failed(&e))?;
+                // TODO: remove, once librespot sends us updates here
+                Ok(Some(value))
             });
 
-        let sp_client = Arc::clone(&spotify_api_client);
+        let local_state = current_state.clone();
         b.property("Position")
             .emits_changed_false()
             .get(move |_, _| {
-                let pos = sp_client
-                    .current_playback(None, None::<Vec<_>>)
-                    .ok()
-                    .flatten()
-                    .and_then(|p| p.progress?.num_microseconds())
-                    .unwrap_or(0);
-
-                Ok(pos)
+                let Some(position) = local_state.read()?.get_position() else {
+                    return Err(dbus::MethodErr::failed("no position available currently"));
+                };
+
+                Ok(position.num_microseconds().unwrap_or_default())
             });
 
-        let sp_client = Arc::clone(&spotify_api_client);
+        let local_state = current_state.clone();
         b.property("Metadata")
             .emits_changed_false()
-            .get(move |_, _| {
-                let mut m: HashMap<String, Variant<Box<dyn RefArg>>> = HashMap::new();
-                let item = match sp_client.current_playing(None, None::<Vec<_>>) {
-                    Ok(playing) => playing.and_then(|playing| playing.item),
-                    Err(e) => {
-                        info!("Couldn't fetch metadata from spotify: {:?}", e);
-                        return Ok(m);
-                    }
-                };
-
-                if let Some(item) = item {
-                    insert_metadata(&mut m, item);
-                } else {
-                    info!("Couldn't fetch metadata from spotify: Nothing playing at the moment.");
-                }
-
-                Ok(m)
-            });
+            .get(move |_, _| Ok(local_state.read()?.to_metadata()));
 
         for prop in [
             "CanPlay",
@@ -528,202 +873,35 @@ async fn create_dbus_server(
         }
     });
 
+    cr.insert(MPRIS_PATH, &[media_player2_interface, player_interface], ());
+
+    seeked_signal.expect("player interface has not been registered")
+}
+
+fn register_controls_interface(cr: &mut Crossroads, spirc: Arc<Spirc>) {
     let spotifyd_ctrls_interface: IfaceToken<()> = cr.register("rs.spotifyd.Controls", |b| {
         let local_spirc = spirc.clone();
         b.method("VolumeUp", (), (), move |_, _, (): ()| {
-            local_spirc.volume_up();
-            Ok(())
+            local_spirc.volume_up().map_err(|e| MethodErr::failed(&e))
         });
         let local_spirc = spirc.clone();
         b.method("VolumeDown", (), (), move |_, _, (): ()| {
-            local_spirc.volume_down();
-            Ok(())
+            local_spirc.volume_down().map_err(|e| MethodErr::failed(&e))
         });
 
-        let mv_device_name = device_name.clone();
-        let sp_client = Arc::clone(&spotify_api_client);
+        let local_spirc = spirc.clone();
         b.method("TransferPlayback", (), (), move |_, _, (): ()| {
-            let device_id = get_device_id(&sp_client, &mv_device_name, false);
-            if let Some(device_id) = device_id {
-                info!("Transferring playback to device {}", device_id);
-                match sp_client.transfer_playback(&device_id, Some(true)) {
-                    Ok(_) => Ok(()),
-                    Err(err) => {
-                        let e = format!("TransferPlayback failed: {}", err);
-                        error!("{}", e);
-                        Err(MethodErr::failed(&e))
-                    }
-                }
-            } else {
-                let msg = format!("Could not find device with name {}", mv_device_name);
-                warn!("TransferPlayback: {}", msg);
-                Err(MethodErr::failed(&msg))
-            }
+            local_spirc.activate().map_err(|e| MethodErr::failed(&e))
         });
     });
 
-    cr.insert(
-        "/org/mpris/MediaPlayer2",
-        &[media_player2_interface, player_interface],
-        (),
-    );
-
-    cr.insert("/rs/spotifyd/Controls", &[spotifyd_ctrls_interface], ());
-
-    conn.start_receive(
-        MatchRule::new_method_call(),
-        Box::new(move |msg, conn| {
-            cr.handle_message(msg, conn).unwrap();
-            true
-        }),
-    );
-
-    // Store current playback state to be able to detect changes
-    let mut last_track_id = None;
-    let mut last_playback_status = None;
-    let mut last_volume = None;
-
-    loop {
-        let event = event_rx
-            .recv()
-            .await
-            .expect("Changed track channel was unexpectedly closed");
-        let mut seeked_position_ms = None;
-
-        // Update playback state from event
-        let (track_id, playback_status, player_volume) = match event {
-            PlayerEvent::VolumeSet { volume } => {
-                (last_track_id, last_playback_status, Some(volume))
-            }
-            PlayerEvent::Playing {
-                track_id,
-                position_ms,
-                ..
-            } => {
-                seeked_position_ms = Some(position_ms);
-                (Some(track_id), Some(PlaybackStatus::Playing), last_volume)
-            }
-            PlayerEvent::Stopped { .. } => {
-                (last_track_id, Some(PlaybackStatus::Stopped), last_volume)
-            }
-            PlayerEvent::Paused { .. } => {
-                (last_track_id, Some(PlaybackStatus::Paused), last_volume)
-            }
-            _ => continue,
-        };
-
-        // if playback_status, track_id or volume have changed, emit a PropertiesChanged signal
-        if last_playback_status != playback_status
-            || last_track_id != track_id
-            || last_volume != player_volume
-        {
-            let mut changed_properties: HashMap<String, Variant<Box<dyn RefArg>>> = HashMap::new();
-
-            if last_volume != player_volume {
-                if let Some(player_volume) = player_volume {
-                    // convert u16 to float
-                    let mut vol_mpris = player_volume as f64;
-                    // max. vol = 1.0 according to mpris spec, round to two decimal places
-                    vol_mpris = (vol_mpris / 65535.0 * 100.0).round() / 100.0;
-                    changed_properties
-                        .insert("Volume".to_owned(), Variant(Box::new(vol_mpris.to_owned())));
-                }
-            } else {
-                if let Some(track_id) = track_id {
-                    let item = match track_id.audio_type {
-                        SpotifyAudioType::Track => {
-                            let track_id = TrackId::from_id(track_id.to_base62().unwrap()).unwrap();
-                            let track = spotify_api_client
-                                .track(track_id, None)
-                                .map(PlayableItem::Track);
-                            Some(track)
-                        }
-                        SpotifyAudioType::Podcast => {
-                            let id = EpisodeId::from_id(track_id.to_base62().unwrap()).unwrap();
-                            let episode = spotify_api_client
-                                .get_an_episode(id, None)
-                                .map(PlayableItem::Episode);
-                            Some(episode)
-                        }
-                        SpotifyAudioType::NonPlayable => None,
-                    };
-
-                    if let Some(item) = item {
-                        match item {
-                            Ok(item) => {
-                                let mut m: HashMap<String, Variant<Box<dyn RefArg>>> =
-                                    HashMap::new();
-                                insert_metadata(&mut m, item);
-
-                                changed_properties
-                                    .insert("Metadata".to_owned(), Variant(Box::new(m)));
-                            }
-                            Err(e) => info!("Couldn't fetch metadata from spotify: {:?}", e),
-                        }
-                    }
-                }
-                if let Some(playback_status) = playback_status {
-                    changed_properties.insert(
-                        "PlaybackStatus".to_owned(),
-                        Variant(Box::new(match playback_status {
-                            PlaybackStatus::Playing => "Playing".to_owned(),
-                            PlaybackStatus::Paused => "Paused".to_owned(),
-                            PlaybackStatus::Stopped => "Stopped".to_owned(),
-                        })),
-                    );
-                }
-            }
-
-            let msg = dbus::nonblock::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged {
-                interface_name: "org.mpris.MediaPlayer2.Player".to_owned(),
-                changed_properties,
-                invalidated_properties: Vec::new(),
-            };
-            conn.send(msg.to_emit_message(&dbus::Path::new("/org/mpris/MediaPlayer2").unwrap()))
-                .unwrap();
-
-            last_playback_status = playback_status;
-            last_track_id = track_id;
-            last_volume = player_volume;
-        }
-
-        // if position in track has changed emit a Seeked signal
-        if let Some(position_ms) = seeked_position_ms {
-            let msg = dbus::message::Message::signal(
-                &dbus::Path::new("/org/mpris/MediaPlayer2").unwrap(),
-                &dbus::strings::Interface::new("org.mpris.MediaPlayer2.Player").unwrap(),
-                &dbus::strings::Member::new("Seeked").unwrap(),
-            )
-            // position should be in microseconds
-            .append1(position_ms as i64 * 1000);
-            conn.send(msg).unwrap();
-        }
-    }
-}
-
-fn get_device_id(
-    sp_client: &AuthCodeSpotify,
-    device_name: &str,
-    only_active: bool,
-) -> Option<String> {
-    let device_result = sp_client.device();
-    match device_result {
-        Ok(devices) => devices.into_iter().find_map(|d| {
-            if d.name == device_name && (d.is_active || !only_active) {
-                info!("Found device: {}, active: {}", d.name, d.is_active);
-                d.id
-            } else {
-                None
-            }
-        }),
-        Err(err) => {
-            error!("Get devices error: {}", err);
-            None
-        }
-    }
+    cr.insert(CONTROLS_PATH, &[spotifyd_ctrls_interface], ());
 }
 
-fn uri_to_object_path(uri: String) -> dbus::Path<'static> {
+fn uri_to_object_path(uri: Option<&str>) -> dbus::Path<'static> {
+    let Some(uri) = uri else {
+        return dbus::Path::new("/org/mpris/MediaPlayer2/TrackList/NoTrack").unwrap();
+    };
     let mut path = String::with_capacity(uri.len() + 1);
     for element in uri.split(':') {
         path.push('/');
@@ -731,121 +909,3 @@ fn uri_to_object_path(uri: String) -> dbus::Path<'static> {
     }
     dbus::Path::new(path).unwrap()
 }
-
-fn insert_metadata(m: &mut HashMap<String, Variant<Box<dyn RefArg>>>, item: PlayableItem) {
-    use rspotify::model::{
-        Image,
-        PlayableItem::{Episode, Track},
-    };
-
-    // some fields that only make sense or only exist for tracks
-    struct TrackFields {
-        artists: Vec<String>,
-        popularity: u32,
-        track_number: u32,
-        disc_number: i32,
-    }
-
-    // a common denominator struct for FullEpisode and FullTrack
-    struct TrackOrEpisode {
-        id: Option<dbus::Path<'static>>,
-        duration: chrono::Duration,
-        images: Vec<Image>,
-        name: String,
-        album_name: String,
-        album_artists: Vec<String>,
-        external_urls: HashMap<String, String>,
-        track_fields: Option<TrackFields>,
-    }
-
-    let item = match item {
-        Track(t) => TrackOrEpisode {
-            id: t.id.map(|t| uri_to_object_path(t.uri())),
-            duration: t.duration,
-            images: t.album.images,
-            name: t.name,
-            album_name: t.album.name,
-            album_artists: t.album.artists.into_iter().map(|a| a.name).collect(),
-            external_urls: t.external_urls,
-            track_fields: Some(TrackFields {
-                artists: t.artists.into_iter().map(|a| a.name).collect(),
-                popularity: t.popularity,
-                track_number: t.track_number,
-                disc_number: t.disc_number,
-            }),
-        },
-        Episode(e) => TrackOrEpisode {
-            id: Some(uri_to_object_path(e.id.uri())),
-            duration: e.duration,
-            images: e.show.images,
-            name: e.name,
-            album_name: e.show.name,
-            album_artists: vec![e.show.publisher],
-            external_urls: e.external_urls,
-            track_fields: None,
-        },
-    };
-
-    m.insert(
-        "mpris:trackid".to_string(),
-        Variant(Box::new(item.id.unwrap_or_default())),
-    );
-
-    m.insert(
-        "mpris:length".to_string(),
-        Variant(Box::new(
-            item.duration.num_microseconds().unwrap_or_default(),
-        )),
-    );
-
-    m.insert(
-        "mpris:artUrl".to_string(),
-        Variant(Box::new(
-            item.images
-                .into_iter()
-                .max_by_key(|i| i.width.unwrap_or(0))
-                .map(|i| i.url)
-                .unwrap_or_default(),
-        )),
-    );
-
-    m.insert("xesam:title".to_string(), Variant(Box::new(item.name)));
-
-    m.insert(
-        "xesam:album".to_string(),
-        Variant(Box::new(item.album_name)),
-    );
-
-    m.insert(
-        "xesam:albumArtist".to_string(),
-        Variant(Box::new(item.album_artists)),
-    );
-
-    if let Some(track) = item.track_fields {
-        m.insert("xesam:artist".to_string(), Variant(Box::new(track.artists)));
-
-        m.insert(
-            "xesam:autoRating".to_string(),
-            Variant(Box::new(f64::from(track.popularity) / 100.0)),
-        );
-
-        m.insert(
-            "xesam:trackNumber".to_string(),
-            Variant(Box::new(track.track_number)),
-        );
-
-        m.insert(
-            "xesam:discNumber".to_string(),
-            Variant(Box::new(track.disc_number)),
-        );
-    }
-
-    // to avoid cloning here, we take the relevant url directly from the HashMap
-    let mut external_urls = item.external_urls;
-    m.insert(
-        "xesam:url".to_string(),
-        Variant(Box::new(
-            external_urls.remove("spotify").unwrap_or_default(),
-        )),
-    );
-}
diff --git a/src/main.rs b/src/main.rs
index deb36d88..a61068c1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -43,9 +43,18 @@ fn setup_logger(log_target: LogTarget, verbose: bool) -> eyre::Result<()> {
 
     let mut logger = fern::Dispatch::new().level(log_level);
 
-    if cfg!(feature = "dbus_mpris") && !verbose {
-        logger = logger.level_for("rspotify_http", LevelFilter::Warn);
-    }
+    logger = if verbose {
+        logger.format(|out, message, record| {
+            out.finish(format_args!(
+                "[{} {}] {}",
+                record.level(),
+                record.target(),
+                message
+            ))
+        })
+    } else {
+        logger.level_for("symphonia_format_ogg::demuxer", LevelFilter::Warn)
+    };
 
     let logger = match log_target {
         LogTarget::Terminal => logger.chain(std::io::stdout()),
@@ -209,7 +218,7 @@ fn main() -> eyre::Result<()> {
 
     let runtime = Runtime::new().unwrap();
     runtime.block_on(async {
-        let mut initial_state = setup::initial_state(internal_config);
+        let initial_state = setup::initial_state(internal_config);
         initial_state.run().await;
     });
 
diff --git a/src/main_loop.rs b/src/main_loop.rs
index 597edcad..0155c4bf 100644
--- a/src/main_loop.rs
+++ b/src/main_loop.rs
@@ -2,18 +2,19 @@ use crate::config::DBusType;
 #[cfg(feature = "dbus_mpris")]
 use crate::dbus_mpris::DbusServer;
 use crate::process::spawn_program_on_event;
+use futures::future::Either;
+#[cfg(not(feature = "dbus_mpris"))]
+use futures::future::Pending;
 use futures::{
     self,
     future::{self, Fuse, FusedFuture},
     stream::Peekable,
     Future, FutureExt, StreamExt,
 };
-use librespot_connect::spirc::Spirc;
+use librespot_connect::{config::ConnectConfig, spirc::Spirc};
 use librespot_core::{
-    authentication::Credentials,
-    cache::Cache,
-    config::{ConnectConfig, DeviceType, SessionConfig},
-    session::{Session, SessionError},
+    authentication::Credentials, cache::Cache, config::DeviceType, session::Session, Error,
+    SessionConfig,
 };
 use librespot_discovery::Discovery;
 use librespot_playback::{
@@ -26,18 +27,8 @@ use log::error;
 use std::pin::Pin;
 use std::sync::Arc;
 
-pub struct AudioSetup {
-    pub mixer: Box<dyn FnMut() -> Box<dyn Mixer>>,
-    pub backend: fn(Option<String>, AudioFormat) -> Box<dyn Sink>,
-    pub audio_device: Option<String>,
-    pub audio_format: AudioFormat,
-}
-
-pub struct SpotifydState {
-    pub cache: Option<Cache>,
-    pub device_name: String,
-    pub player_event_program: Option<String>,
-}
+#[cfg(not(feature = "dbus_mpris"))]
+type DbusServer = Pending<()>;
 
 pub(crate) enum CredentialsProvider {
     Discovery(Peekable<Discovery>),
@@ -73,15 +64,19 @@ impl CredentialsProvider {
 }
 
 pub(crate) struct MainLoop {
-    pub(crate) audio_setup: AudioSetup,
-    pub(crate) spotifyd_state: SpotifydState,
-    pub(crate) player_config: PlayerConfig,
     pub(crate) session_config: SessionConfig,
-    pub(crate) autoplay: bool,
+    pub(crate) player_config: PlayerConfig,
+    pub(crate) cache: Option<Cache>,
+    pub(crate) mixer: Arc<dyn Mixer>,
+    pub(crate) backend: fn(Option<String>, AudioFormat) -> Box<dyn Sink>,
+    pub(crate) audio_device: Option<String>,
+    pub(crate) audio_format: AudioFormat,
     pub(crate) has_volume_ctrl: bool,
     pub(crate) initial_volume: Option<u16>,
     pub(crate) shell: String,
     pub(crate) device_type: DeviceType,
+    pub(crate) device_name: String,
+    pub(crate) player_event_program: Option<String>,
     #[cfg_attr(not(feature = "dbus_mpris"), allow(unused))]
     pub(crate) use_mpris: bool,
     #[cfg_attr(not(feature = "dbus_mpris"), allow(unused))]
@@ -89,31 +84,81 @@ pub(crate) struct MainLoop {
     pub(crate) credentials_provider: CredentialsProvider,
 }
 
+struct ConnectionInfo<SpircTask: Future<Output = ()>> {
+    spirc: Spirc,
+    #[cfg_attr(not(feature = "dbus_mpris"), expect(unused))]
+    session: Session,
+    player: Arc<Player>,
+    spirc_task: SpircTask,
+}
+
 impl MainLoop {
-    async fn get_session(&mut self) -> Result<Session, SessionError> {
+    async fn get_connection(&mut self) -> Result<ConnectionInfo<impl Future<Output = ()>>, Error> {
         let creds = self.credentials_provider.get_credentials().await;
 
-        let session_config = self.session_config.clone();
-        let cache = self.spotifyd_state.cache.clone();
+        let session = Session::new(self.session_config.clone(), self.cache.clone());
+        let player = {
+            let audio_device = self.audio_device.clone();
+            let audio_format = self.audio_format;
+            let backend = self.backend;
+            Player::new(
+                self.player_config.clone(),
+                session.clone(),
+                self.mixer.get_soft_volume(),
+                move || backend(audio_device, audio_format),
+            )
+        };
 
-        Session::connect(session_config, creds, cache, true)
-            .await
-            .map(|(session, _creds)| session)
+        // TODO: expose is_group
+        Spirc::new(
+            ConnectConfig {
+                name: self.device_name.clone(),
+                device_type: self.device_type,
+                is_group: false,
+                initial_volume: self.initial_volume,
+                has_volume_ctrl: self.has_volume_ctrl,
+            },
+            session.clone(),
+            creds,
+            player.clone(),
+            self.mixer.clone(),
+        )
+        .await
+        .map(|(spirc, spirc_task)| ConnectionInfo {
+            spirc,
+            session,
+            player,
+            spirc_task,
+        })
     }
 
-    pub(crate) async fn run(&mut self) {
+    pub(crate) async fn run(mut self) {
         tokio::pin! {
             let ctrl_c = tokio::signal::ctrl_c();
+            // we don't necessarily have a dbus server
+            let dbus_server = Either::<DbusServer, _>::Right(future::pending());
         }
 
+        #[cfg(feature = "dbus_mpris")]
+        let mpris_event_tx = if self.use_mpris {
+            let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
+            *dbus_server.as_mut() = Either::Left(DbusServer::new(rx, self.dbus_type));
+            Some(tx)
+        } else {
+            None
+        };
+
         'mainloop: loop {
-            let session = tokio::select!(
+            let connection = tokio::select!(
                 _ = &mut ctrl_c => {
+                    if let CredentialsProvider::Discovery(stream) = self.credentials_provider {
+                        let _ = stream.into_inner().shutdown().await;
+                    }
                     break 'mainloop;
                 }
-                session = self.get_session() => {
-                    match session {
-                        Ok(session) => session,
+                connection = self.get_connection() => {
+                    match connection {
+                        Ok(connection) => connection,
                         Err(err) => {
                             error!("failed to connect to spotify: {}", err);
                             break 'mainloop;
@@ -122,64 +167,37 @@ impl MainLoop {
                 }
             );
 
-            let mixer = (self.audio_setup.mixer)();
-            let backend = self.audio_setup.backend;
-            let audio_device = self.audio_setup.audio_device.clone();
-            let audio_format = self.audio_setup.audio_format;
-            let (player, mut event_channel) = Player::new(
-                self.player_config.clone(),
-                session.clone(),
-                mixer.get_soft_volume(),
-                move || (backend)(audio_device, audio_format),
-            );
-
-            let (spirc, spirc_task) = Spirc::new(
-                ConnectConfig {
-                    autoplay: self.autoplay,
-                    name: self.spotifyd_state.device_name.clone(),
-                    device_type: self.device_type,
-                    initial_volume: self.initial_volume,
-                    has_volume_ctrl: self.has_volume_ctrl,
-                },
-                session.clone(),
-                player,
-                mixer,
-            );
-
+            let spirc_task = connection.spirc_task;
             tokio::pin!(spirc_task);
 
-            let shared_spirc = Arc::new(spirc);
-
-            // we don't necessarily have a dbus server
-            let mut dbus_server: Pin<Box<dyn Future<Output = ()>>> = Box::pin(future::pending());
+            let shared_spirc = Arc::new(connection.spirc);
 
             #[cfg(feature = "dbus_mpris")]
-            let mpris_event_tx = if self.use_mpris {
-                let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
-                dbus_server = Box::pin(DbusServer::new(
-                    session,
-                    shared_spirc.clone(),
-                    self.spotifyd_state.device_name.clone(),
-                    rx,
-                    self.dbus_type,
-                ));
-                Some(tx)
-            } else {
-                None
-            };
+            if let Either::Left(mut dbus_server) = Either::as_pin_mut(dbus_server.as_mut()) {
+                if let Err(err) = dbus_server
+                    .as_mut()
+                    .set_session(shared_spirc.clone(), connection.session)
+                {
+                    error!("failed to configure dbus server: {err}");
+                    let _ = shared_spirc.shutdown();
+                    break 'mainloop;
+                }
+            }
 
             let mut running_event_program = Box::pin(Fuse::terminated());
 
+            let mut event_channel = connection.player.get_player_event_channel();
+
             loop {
                 tokio::select!(
                     // a new session has been started via the discovery stream
                     _ = self.credentials_provider.incoming_connection() => {
-                        shared_spirc.shutdown();
+                        let _ = shared_spirc.shutdown();
                         break;
                     }
                     // the program should shut down
                     _ = &mut ctrl_c => {
-                        shared_spirc.shutdown();
+                        let _ = shared_spirc.shutdown();
                         break 'mainloop;
                     }
                     // spirc was shut down by some external factor
@@ -187,9 +205,18 @@ impl MainLoop {
                         break;
                     }
                     // dbus stopped unexpectedly
-                    _ = &mut dbus_server => {
-                        shared_spirc.shutdown();
-                        break 'mainloop;
+                    result = &mut dbus_server => {
+                        #[cfg(feature = "dbus_mpris")]
+                        {
+                            if let Err(err) = result {
+                                error!("DBus terminated unexpectedly: {err}");
+                            }
+                            let _ = shared_spirc.shutdown();
+                            *dbus_server.as_mut() = Either::Right(future::pending());
+                            break 'mainloop;
+                        }
+                        #[cfg(not(feature = "dbus_mpris"))]
+                        result // unused variable
                     }
                     // a new player event is available and no program is running
                     event = event_channel.recv(), if running_event_program.is_terminated() => {
@@ -198,7 +225,7 @@ impl MainLoop {
                         if let Some(ref tx) = mpris_event_tx {
                             tx.send(event.clone()).unwrap();
                         }
-                        if let Some(ref cmd) = self.spotifyd_state.player_event_program {
+                        if let Some(ref cmd) = self.player_event_program {
                             match spawn_program_on_event(&self.shell, cmd, event) {
                                 Ok(child) => running_event_program = Box::pin(child.wait().fuse()),
                                 Err(e) => error!("{}", e),
@@ -216,6 +243,21 @@ impl MainLoop {
                     }
                 )
             }
+            #[cfg(feature = "dbus_mpris")]
+            if let Either::Left(dbus_server) = Either::as_pin_mut(dbus_server.as_mut()) {
+                if let Err(err) = dbus_server.drop_session() {
+                    error!("failed to reconfigure dbus server: {err}");
+                    break 'mainloop;
+                }
+            }
+        }
+        #[cfg(feature = "dbus_mpris")]
+        if let Either::Left(dbus_server) = Either::as_pin_mut(dbus_server.as_mut()) {
+            if dbus_server.shutdown() {
+                if let Err(err) = dbus_server.await {
+                    error!("failed to shutdown the dbus server: {err}");
+                }
+            }
         }
     }
 }
diff --git a/src/process.rs b/src/process.rs
index 886187f1..39d92ac4 100644
--- a/src/process.rs
+++ b/src/process.rs
@@ -1,4 +1,5 @@
 use crate::error::Error;
+use librespot_metadata::audio::AudioItem;
 use librespot_playback::player::PlayerEvent;
 use log::info;
 use std::{collections::HashMap, process::Stdio};
@@ -44,6 +45,18 @@ fn spawn_program(shell: &str, cmd: &str, env: HashMap<&str, String>) -> Result<C
     Ok(child)
 }
 
+fn audio_item_to_env(audio_item: Box<AudioItem>, env: &mut HashMap<&str, String>) {
+    env.insert(
+        "TRACK_ID",
+        audio_item.track_id.to_base62().unwrap_or_default(),
+    );
+    env.insert("TRACK_NAME", audio_item.name);
+    env.insert("TRACK_DURATION", audio_item.duration_ms.to_string());
+    if let Some(cover) = audio_item.covers.into_iter().max_by_key(|c| c.width) {
+        env.insert("TRACK_COVER", cover.url);
+    }
+}
+
 /// Spawns provided command in a subprocess using the provided shell.
 /// Various environment variables are included in the subprocess's environment
 /// depending on the `PlayerEvent` that was passed in.
@@ -54,15 +67,11 @@ pub(crate) fn spawn_program_on_event(
 ) -> Result<Child, Error> {
     let mut env = HashMap::new();
     match event {
-        PlayerEvent::Changed {
-            old_track_id,
-            new_track_id,
-        } => {
-            env.insert("OLD_TRACK_ID", old_track_id.to_base62().unwrap());
+        PlayerEvent::TrackChanged { audio_item } => {
             env.insert("PLAYER_EVENT", "change".to_string());
-            env.insert("TRACK_ID", new_track_id.to_base62().unwrap());
+            audio_item_to_env(audio_item, &mut env);
         }
-        PlayerEvent::Started {
+        PlayerEvent::Playing {
             track_id,
             play_request_id,
             position_ms,
@@ -90,29 +99,15 @@ pub(crate) fn spawn_program_on_event(
             env.insert("PLAY_REQUEST_ID", play_request_id.to_string());
             env.insert("POSITION_MS", position_ms.to_string());
         }
-        PlayerEvent::Playing {
-            track_id,
-            play_request_id,
-            position_ms,
-            duration_ms,
-        } => {
-            env.insert("PLAYER_EVENT", "play".to_string());
-            env.insert("TRACK_ID", track_id.to_base62().unwrap());
-            env.insert("PLAY_REQUEST_ID", play_request_id.to_string());
-            env.insert("POSITION_MS", position_ms.to_string());
-            env.insert("DURATION_MS", duration_ms.to_string());
-        }
         PlayerEvent::Paused {
             track_id,
             play_request_id,
             position_ms,
-            duration_ms,
         } => {
             env.insert("PLAYER_EVENT", "pause".to_string());
             env.insert("TRACK_ID", track_id.to_base62().unwrap());
             env.insert("PLAY_REQUEST_ID", play_request_id.to_string());
             env.insert("POSITION_MS", position_ms.to_string());
-            env.insert("DURATION_MS", duration_ms.to_string());
         }
         PlayerEvent::TimeToPreloadNextTrack {
             track_id,
@@ -130,7 +125,7 @@ pub(crate) fn spawn_program_on_event(
             env.insert("TRACK_ID", track_id.to_base62().unwrap());
             env.insert("PLAY_REQUEST_ID", play_request_id.to_string());
         }
-        PlayerEvent::VolumeSet { volume } => {
+        PlayerEvent::VolumeChanged { volume } => {
             env.insert("PLAYER_EVENT", "volumeset".to_string());
             env.insert("VOLUME", volume.to_string());
         }
@@ -146,6 +141,79 @@ pub(crate) fn spawn_program_on_event(
             env.insert("PLAYER_EVENT", "preloading".to_string());
             env.insert("TRACK_ID", track_id.to_base62().unwrap());
         }
+        PlayerEvent::PositionCorrection {
+            play_request_id,
+            track_id,
+            position_ms,
+        } => {
+            env.insert("PLAYER_EVENT", "positioncorrection".to_string());
+            env.insert("TRACK_ID", track_id.to_base62().unwrap());
+            env.insert("POSITION_MS", position_ms.to_string());
+            env.insert("PLAY_REQUEST_ID", play_request_id.to_string());
+        }
+        PlayerEvent::Seeked {
+            play_request_id,
+            track_id,
+            position_ms,
+        } => {
+            env.insert("PLAYER_EVENT", "seeked".to_string());
+            env.insert("TRACK_ID", track_id.to_base62().unwrap());
+            env.insert("POSITION_MS", position_ms.to_string());
+            env.insert("PLAY_REQUEST_ID", play_request_id.to_string());
+        }
+        PlayerEvent::PlayRequestIdChanged { play_request_id } => {
+            env.insert("PLAYER_EVENT", "playrequestid_changed".to_string());
+            env.insert("PLAY_REQUEST_ID", play_request_id.to_string());
+        }
+        PlayerEvent::SessionConnected {
+            connection_id,
+            user_name,
+        } => {
+            env.insert("PLAYER_EVENT", "sessionconnected".to_string());
+            env.insert("USERNAME", user_name);
+            env.insert("CONNECTION_ID", connection_id);
+        }
+        PlayerEvent::SessionDisconnected {
+            connection_id,
+            user_name,
+        } => {
+            env.insert("PLAYER_EVENT", "sessiondisconnected".to_string());
+            env.insert("USERNAME", user_name);
+            env.insert("CONNECTION_ID", connection_id);
+        }
+        PlayerEvent::SessionClientChanged {
+            client_id,
+            client_name,
+            client_brand_name,
+            client_model_name,
+        } => {
+            env.insert("PLAYER_EVENT", "clientchanged".to_string());
+            env.insert("CLIENT_ID", client_id);
+            env.insert("CLIENT_NAME", client_name);
+            env.insert("CLIENT_BRAND", client_brand_name);
+            env.insert("CLIENT_MODEL", client_model_name);
+        }
+        PlayerEvent::ShuffleChanged { shuffle } => {
+            env.insert("PLAYER_EVENT", "shuffle_changed".to_string());
+            env.insert("SHUFFLE", shuffle.to_string());
+        }
+        PlayerEvent::RepeatChanged { repeat } => {
+            env.insert("PLAYER_EVENT", "repeat_changed".to_string());
+            let val = match repeat {
+                true => "all",
+                false => "none",
+            }
+            .to_string();
+            env.insert("REPEAT", val);
+        }
+        PlayerEvent::AutoPlayChanged { auto_play } => {
+            env.insert("PLAYER_EVENT", "autoplay_changed".to_string());
+            env.insert("AUTOPLAY", auto_play.to_string());
+        }
+        PlayerEvent::FilterExplicitContentChanged { filter } => {
+            env.insert("PLAYER_EVENT", "filterexplicit_changed".to_string());
+            env.insert("FILTEREXPLICIT", filter.to_string());
+        }
     }
     spawn_program(shell, cmd, env)
 }
diff --git a/src/setup.rs b/src/setup.rs
index 07a713c3..735c9ca7 100644
--- a/src/setup.rs
+++ b/src/setup.rs
@@ -15,15 +15,14 @@ use librespot_playback::{
 };
 #[allow(unused_imports)] // cfg
 use log::{debug, error, info, warn};
-use std::{str::FromStr, thread, time::Duration};
+use std::{str::FromStr, sync::Arc, thread, time::Duration};
 
 pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLoop {
-    let mixer = {
+    let mixer: Arc<dyn Mixer> = {
         match config.volume_controller {
             config::VolumeController::None => {
                 info!("Using no volume controller.");
-                Box::new(|| Box::new(crate::no_mixer::NoMixer) as Box<dyn Mixer>)
-                    as Box<dyn FnMut() -> Box<dyn Mixer>>
+                Arc::new(crate::no_mixer::NoMixer)
             }
             #[cfg(feature = "alsa_backend")]
             config::VolumeController::Alsa | config::VolumeController::AlsaLinear => {
@@ -35,23 +34,18 @@ pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLo
                     config.volume_controller,
                     config::VolumeController::AlsaLinear
                 );
-                Box::new(move || {
-                    Box::new(alsa_mixer::AlsaMixer {
-                        device: control_device
-                            .clone()
-                            .or_else(|| audio_device.clone())
-                            .unwrap_or_else(|| "default".to_string()),
-                        mixer: mixer.clone().unwrap_or_else(|| "Master".to_string()),
-                        linear_scaling: linear,
-                    }) as Box<dyn mixer::Mixer>
-                }) as Box<dyn FnMut() -> Box<dyn Mixer>>
+                Arc::new(alsa_mixer::AlsaMixer {
+                    device: control_device
+                        .clone()
+                        .or_else(|| audio_device.clone())
+                        .unwrap_or_else(|| "default".to_string()),
+                    mixer: mixer.clone().unwrap_or_else(|| "Master".to_string()),
+                    linear_scaling: linear,
+                })
             }
             _ => {
                 info!("Using software volume controller.");
-                Box::new(move || {
-                    Box::new(mixer::softmixer::SoftMixer::open(MixerConfig::default()))
-                        as Box<dyn Mixer>
-                }) as Box<dyn FnMut() -> Box<dyn Mixer>>
+                Arc::new(mixer::softmixer::SoftMixer::open(MixerConfig::default()))
             }
         }
     };
@@ -60,7 +54,6 @@ pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLo
     let player_config = config.player_config;
     let session_config = config.session_config;
     let backend = config.backend.clone();
-    let autoplay = config.autoplay;
 
     let has_volume_ctrl = !matches!(config.volume_controller, config::VolumeController::None);
 
@@ -100,11 +93,14 @@ pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLo
             let mut retry_counter = 0;
             let mut backoff = Duration::from_secs(5);
             let discovery_stream = loop {
-                match librespot_discovery::Discovery::builder(session_config.device_id.clone())
-                    .name(config.device_name.clone())
-                    .device_type(device_type)
-                    .port(zeroconf_port)
-                    .launch()
+                match librespot_discovery::Discovery::builder(
+                    session_config.device_id.clone(),
+                    session_config.client_id.clone(),
+                )
+                .name(config.device_name.clone())
+                .device_type(device_type)
+                .port(zeroconf_port)
+                .launch()
                 {
                     Ok(discovery_stream) => break discovery_stream,
                     Err(err) => {
@@ -124,26 +120,22 @@ pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLo
         };
 
     let backend = find_backend(backend.as_ref().map(String::as_ref));
+
     main_loop::MainLoop {
         credentials_provider,
-        audio_setup: main_loop::AudioSetup {
-            mixer,
-            backend,
-            audio_device: config.audio_device,
-            audio_format: config.audio_format,
-        },
-        spotifyd_state: main_loop::SpotifydState {
-            cache,
-            device_name: config.device_name,
-            player_event_program: config.onevent,
-        },
-        player_config,
+        mixer,
         session_config,
+        cache,
+        audio_device: config.audio_device,
+        audio_format: config.audio_format,
+        player_config,
+        backend,
         initial_volume: config.initial_volume,
         has_volume_ctrl,
         shell: config.shell,
         device_type,
-        autoplay,
+        device_name: config.device_name,
+        player_event_program: config.onevent,
         use_mpris: config.use_mpris,
         dbus_type: config.dbus_type,
     }
@@ -155,7 +147,9 @@ fn get_credentials(
     password: &Option<String>,
 ) -> Option<Credentials> {
     if let Some(credentials) = cache.as_ref().and_then(Cache::credentials) {
-        if username.as_ref() == Some(&credentials.username) {
+        if Option::zip(username.as_deref(), credentials.username.as_deref())
+            .is_some_and(|(user_config, user_cached)| user_config == user_cached)
+        {
             return Some(credentials);
         }
     }


Test patch: