Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,319 changes: 695 additions & 624 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ sha-1 = "0.10"
tokio = {version = "1.44.2", features = ["signal", "rt-multi-thread", "process", "io-std"] }
tokio-stream = "0.1.7"
url = "2.2.2"
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"
librespot-oauth = "0.6"
librespot-audio = { version = "0.7", default-features = false }
librespot-playback = { version = "0.7", default-features = false }
librespot-core = "0.7"
librespot-discovery = "0.7.1"
librespot-connect = "0.7.1"
librespot-metadata = "0.7.1"
librespot-protocol = "0.7.1"
librespot-oauth = "0.7.1"
toml = "0.8.19"
color-eyre = "0.6"
directories = "6.0.0"
Expand Down
6 changes: 3 additions & 3 deletions src/alsa_mixer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ impl AlsaMixer {
}

impl Mixer for AlsaMixer {
fn open(_: MixerConfig) -> AlsaMixer {
AlsaMixer {
fn open(_: MixerConfig) -> Result<AlsaMixer, librespot_core::Error> {
Ok(AlsaMixer {
device: "default".to_string(),
mixer: "Master".to_string(),
linear_scaling: false,
}
})
}

fn volume(&self) -> u16 {
Expand Down
10 changes: 5 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ pub enum DeviceType {
UnknownSpotify,
CarThing,
Observer,
HomeThing,
}

impl From<DeviceType> for LSDeviceType {
Expand All @@ -93,7 +92,6 @@ impl From<DeviceType> for LSDeviceType {
DeviceType::UnknownSpotify => LSDeviceType::UnknownSpotify,
DeviceType::CarThing => LSDeviceType::CarThing,
DeviceType::Observer => LSDeviceType::Observer,
DeviceType::HomeThing => LSDeviceType::HomeThing,
}
}
}
Expand Down Expand Up @@ -626,7 +624,7 @@ pub(crate) struct SpotifydConfig {
pub(crate) audio_device: Option<String>,
pub(crate) audio_format: LSAudioFormat,
pub(crate) volume_controller: VolumeController,
pub(crate) initial_volume: Option<u16>,
pub(crate) initial_volume: u16,
pub(crate) device_name: String,
pub(crate) player_config: PlayerConfig,
pub(crate) session_config: SessionConfig,
Expand Down Expand Up @@ -675,7 +673,8 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
.volume_controller
.unwrap_or(VolumeController::SoftVolume);

let initial_volume: Option<u16> = config
let default_initial_volume = 90;
let initial_volume: u16 = config
.shared_config
.initial_volume
.filter(|val| {
Expand All @@ -686,7 +685,8 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
false
}
})
.map(|volume| (volume as i32 * (u16::MAX as i32) / 100) as u16);
.map(|volume| (volume as i32 * (u16::MAX as i32) / 100) as u16)
.unwrap_or((default_initial_volume * (u16::MAX as i32) / 100) as u16);

let device_name = config
.shared_config
Expand Down
130 changes: 63 additions & 67 deletions src/dbus_mpris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ use futures::{
Future,
task::{Context, Poll},
};
use librespot_connect::spirc::{Spirc, SpircLoadCommand};
use librespot_connect::{LoadContextOptions, LoadRequest, LoadRequestOptions, Spirc};
use librespot_core::{Session, SpotifyId, spotify_id::SpotifyItemType};
use librespot_metadata::audio::AudioItem;
use librespot_playback::player::PlayerEvent;
use librespot_protocol::spirc::TrackRef;
use log::{debug, error, warn};
use log::{debug, warn};
use std::convert::TryFrom;
use std::{
collections::HashMap,
Expand Down Expand Up @@ -140,33 +139,45 @@ impl Position {
#[derive(Clone, Copy, Debug)]
enum RepeatState {
None,
// Track,
Track,
All,
}

impl RepeatState {
fn to_mpris(self) -> &'static str {
match self {
RepeatState::None => "None",
// RepeatState::Track => "Track",
RepeatState::Track => "Track",
RepeatState::All => "Playlist",
}
}

fn track_repeat(self) -> bool {
match self {
RepeatState::Track => true,
_ => false,
}
}
}

impl From<RepeatState> for bool {
fn from(repeat: RepeatState) -> Self {
match repeat {
RepeatState::None => false,
RepeatState::Track => true,
RepeatState::All => true,
}
}
}

impl From<bool> for RepeatState {
fn from(repeat: bool) -> Self {
if repeat {
RepeatState::All
impl From<(bool, bool)> for RepeatState {
fn from((context, track): (bool, bool)) -> Self {
if context {
if track {
RepeatState::Track
} else {
RepeatState::All
}
} else {
RepeatState::None
}
Expand Down Expand Up @@ -273,8 +284,8 @@ impl CurrentStateInner {
self.shuffle = shuffle;
insert_attr(&mut changed, "Shuffle", self.shuffle);
}
PlayerEvent::RepeatChanged { repeat } => {
self.repeat = repeat.into();
PlayerEvent::RepeatChanged { context, track } => {
self.repeat = (context, track).into();
insert_attr(
&mut changed,
"LoopStatus",
Expand All @@ -294,6 +305,9 @@ impl CurrentStateInner {
| PlayerEvent::SessionConnected { .. }
| PlayerEvent::SessionDisconnected { .. }
| PlayerEvent::SessionClientChanged { .. } => (),
PlayerEvent::PositionChanged { position_ms, .. } => {
self.update_position(Duration::milliseconds(position_ms as i64));
}
}

(changed, seeked)
Expand Down Expand Up @@ -642,7 +656,10 @@ fn register_player_interface(
});
let local_spirc = spirc.clone();
b.method("Stop", (), (), move |_, _, (): ()| {
local_spirc.disconnect().map_err(|e| MethodErr::failed(&e))
let pause_playback = false;
local_spirc
.disconnect(pause_playback)
.map_err(|e| MethodErr::failed(&e))
});

let local_spirc = spirc.clone();
Expand Down Expand Up @@ -710,56 +727,21 @@ fn register_player_interface(
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 session = session.clone();

let (playing_track_index, context_uri, tracks) = Handle::current()
let (playing_track_index, context_uri) = 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::Album => (0, uri),
SpotifyItemType::Artist => (0, uri),
SpotifyItemType::Playlist => (0, uri),
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())
(track.number as u32, track.album.id.to_uri()?)
}
SpotifyItemType::Episode => (0, uri),
SpotifyItemType::Show => (0, uri),
SpotifyItemType::Local | SpotifyItemType::Unknown => {
return Err(librespot_core::Error::unimplemented(
"this type of uri is not supported",
Expand All @@ -770,14 +752,23 @@ fn register_player_interface(
.map_err(|e| MethodErr::failed(&e))?;

local_spirc
.load(SpircLoadCommand {
.load(LoadRequest::from_context_uri(
context_uri,
start_playing: true,
shuffle,
repeat: repeat.into(),
playing_track_index,
tracks,
})
LoadRequestOptions {
start_playing: true,
seek_to: 0,
context_options: Some(LoadContextOptions::Options(
librespot_connect::Options {
shuffle,
repeat: repeat.into(),
repeat_track: repeat.track_repeat(),
},
)),
playing_track: Some(librespot_connect::PlayingTrack::Index(
playing_track_index,
)),
},
))
.map_err(|e| MethodErr::failed(&e))
});

Expand Down Expand Up @@ -831,20 +822,25 @@ fn register_player_interface(
Ok(repeat.to_mpris().to_string())
})
.set(move |_, _, value| {
let new_repeat = match value.as_str() {
"None" => false,
"Playlist" => true,
let repeat = match value.as_str() {
"None" => RepeatState::None,
"Playlist" => RepeatState::All,
"Track" => RepeatState::Track,
mode => {
return Err(dbus::MethodErr::failed(&format!(
"unsupported repeat mode: {mode}"
)));
}
};

local_spirc
.repeat(repeat.into())
.map_err(|e| MethodErr::failed(&e))?;
local_spirc
.repeat(new_repeat)
.repeat_track(repeat.track_repeat())
.map_err(|e| MethodErr::failed(&e))?;
// TODO: remove, once librespot sends us updates here
Ok(Some(value))

Ok(None)
});

let local_state = current_state.clone();
Expand Down
8 changes: 4 additions & 4 deletions src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use futures::{
future::{self, Fuse, FusedFuture},
stream::Peekable,
};
use librespot_connect::{config::ConnectConfig, spirc::Spirc};
use librespot_connect::{ConnectConfig, Spirc};
use librespot_core::{
Error, SessionConfig, authentication::Credentials, cache::Cache, config::DeviceType,
session::Session,
Expand Down Expand Up @@ -81,7 +81,7 @@ pub(crate) struct MainLoop {
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) initial_volume: u16,
pub(crate) shell: String,
pub(crate) device_type: DeviceType,
pub(crate) device_name: String,
Expand Down Expand Up @@ -125,9 +125,9 @@ impl MainLoop {
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,
disable_volume: self.has_volume_ctrl,
..ConnectConfig::default()
},
session.clone(),
creds.clone(),
Expand Down
4 changes: 2 additions & 2 deletions src/no_mixer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use librespot_playback::mixer::{Mixer, MixerConfig};
pub struct NoMixer;

impl Mixer for NoMixer {
fn open(_: MixerConfig) -> NoMixer {
NoMixer {}
fn open(_: MixerConfig) -> Result<NoMixer, librespot_core::Error> {
Ok(NoMixer {})
}

fn volume(&self) -> u16 {
Expand Down
10 changes: 8 additions & 2 deletions src/oauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use color_eyre::{
};
use librespot_core::SessionConfig;
use librespot_core::{Session, authentication::Credentials};
use librespot_oauth::OAuthClientBuilder;
use log::info;
use tokio::runtime::Runtime;

Expand Down Expand Up @@ -55,12 +56,17 @@ pub(crate) fn run_oauth(mut cli_config: CliConfig, oauth_port: u16) -> eyre::Res
..Default::default()
};

let token = librespot_oauth::get_access_token(
let oauth_client = OAuthClientBuilder::new(
&session_config.client_id,
&format!("http://127.0.0.1:{oauth_port}/login"),
OAUTH_SCOPES.to_vec(),
)
.wrap_err("token retrieval failed")?;
.build()
.wrap_err("client creation failed")?;

let token = oauth_client
.get_access_token()
.wrap_err("token retrival failed")?;

let creds = Credentials::with_access_token(token.access_token);

Expand Down
Loading