Skip to content

Commit 1d02432

Browse files
committed
Merge branch 'dev' into rodiojack-backend
2 parents 52438b1 + ea8ece3 commit 1d02432

File tree

11 files changed

+143
-33
lines changed

11 files changed

+143
-33
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
matrix:
4242
os: [ubuntu-latest]
4343
toolchain:
44-
- 1.40.0 # MSRV (Minimum supported rust version)
44+
- 1.42.0 # MSRV (Minimum supported rust version)
4545
- stable
4646
- beta
4747
experimental: [false]

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
[![Build Status](https://img.shields.io/github/workflow/status/librespot-org/librespot/test/dev)](https://github.com/librespot-org/librespot/actions)
2-
[![Build Status](https://travis-ci.org/librespot-org/librespot.svg?branch=dev)](https://travis-ci.org/librespot-org/librespot)
1+
[![Build Status](https://github.com/librespot-org/librespot/workflows/test/badge.svg)](https://github.com/librespot-org/librespot/actions)
32
[![Gitter chat](https://badges.gitter.im/librespot-org/librespot.png)](https://gitter.im/librespot-org/spotify-connect-resources)
43
[![Crates.io](https://img.shields.io/crates/v/librespot.svg)](https://crates.io/crates/librespot)
54

@@ -21,7 +20,7 @@ As the origin by [plietar](https://github.com/plietar/) is no longer actively ma
2120
# Documentation
2221
Documentation is currently a work in progress, contributions are welcome!
2322

24-
There is some brief documentation on how the protocol works in the [docs](https://github.com/librespot-org/librespot/tree/master/docs) folder,
23+
There is some brief documentation on how the protocol works in the [docs](https://github.com/librespot-org/librespot/tree/master/docs) folder,
2524

2625
[COMPILING.md](https://github.com/librespot-org/librespot/blob/master/COMPILING.md) contains detailed instructions on setting up a development environment, and compiling librespot. More general usage and compilation information is available on the [wiki](https://github.com/librespot-org/librespot/wiki).
2726
[CONTRIBUTING.md](https://github.com/librespot-org/librespot/blob/master/CONTRIBUTING.md) also contains our contributing guidelines.
@@ -32,7 +31,7 @@ If you wish to learn more about how librespot works overall, the best way is to
3231
If you run into a bug when using librespot, please search the existing issues before opening a new one. Chances are, we've encountered it before, and have provided a resolution. If not, please open a new one, and where possible, include the backtrace librespot generates on crashing, along with anything we can use to reproduce the issue, eg. the Spotify URI of the song that caused the crash.
3332

3433
# Building
35-
A quick walk through of the build process is outlined here, while a detailed compilation guide can be found [here](https://github.com/librespot-org/librespot/blob/master/COMPILING.md).
34+
A quick walk through of the build process is outlined here, while a detailed compilation guide can be found [here](https://github.com/librespot-org/librespot/blob/master/COMPILING.md).
3635

3736
## Additional Dependencies
3837
We recently switched to using [Rodio](https://github.com/tomaka/rodio) for audio playback by default, hence for macOS and Windows, you should just be able to clone and build librespot (with the command below).
@@ -112,4 +111,3 @@ functionality.
112111
- [librespot-java](https://github.com/devgianlu/librespot-java) - A Java port of librespot.
113112
- [ncspot](https://github.com/hrkfdn/ncspot) - Cross-platform ncurses Spotify client.
114113
- [ansible-role-librespot](https://github.com/xMordax/ansible-role-librespot/tree/master) - Ansible role that will build, install and configure Librespot.
115-

audio/src/fetch.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,13 @@ impl AudioFile {
459459
}
460460
}
461461
}
462+
463+
pub fn is_cached(&self) -> bool {
464+
match self {
465+
AudioFile::Cached { .. } => true,
466+
_ => false,
467+
}
468+
}
462469
}
463470

464471
fn request_range(session: &Session, file: FileId, offset: usize, length: usize) -> Channel {

connect/src/discovery.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl Discovery {
7575
"status": 101,
7676
"statusString": "ERROR-OK",
7777
"spotifyError": 0,
78-
"version": "2.1.0",
78+
"version": "2.7.1",
7979
"deviceID": (self.0.device_id),
8080
"remoteName": (self.0.config.name),
8181
"activeUser": "",
@@ -85,6 +85,9 @@ impl Discovery {
8585
"accountReq": "PREMIUM",
8686
"brandDisplayName": "librespot",
8787
"modelDisplayName": "librespot",
88+
"resolverVersion": "0",
89+
"groupStatus": "NONE",
90+
"voiceSupport": "NO",
8891
});
8992

9093
let body = result.to_string();

core/src/authentication.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use sha1::{Digest, Sha1};
77
use std::io::{self, Read};
88

99
use crate::protocol::authentication::AuthenticationType;
10+
use crate::protocol::keyexchange::{APLoginFailed, ErrorCode};
1011

1112
#[derive(Debug, Clone, Serialize, Deserialize)]
1213
pub struct Credentials {
@@ -164,3 +165,37 @@ pub fn get_credentials<F: FnOnce(&String) -> String>(
164165
(None, _, None) => None,
165166
}
166167
}
168+
169+
error_chain! {
170+
types {
171+
AuthenticationError, AuthenticationErrorKind, AuthenticationResultExt, AuthenticationResult;
172+
}
173+
174+
foreign_links {
175+
Io(::std::io::Error);
176+
}
177+
178+
errors {
179+
BadCredentials {
180+
description("Bad credentials")
181+
display("Authentication failed with error: Bad credentials")
182+
}
183+
PremiumAccountRequired {
184+
description("Premium account required")
185+
display("Authentication failed with error: Premium account required")
186+
}
187+
}
188+
}
189+
190+
impl From<APLoginFailed> for AuthenticationError {
191+
fn from(login_failure: APLoginFailed) -> Self {
192+
let error_code = login_failure.get_error_code();
193+
match error_code {
194+
ErrorCode::BadCredentials => Self::from_kind(AuthenticationErrorKind::BadCredentials),
195+
ErrorCode::PremiumAccountRequired => {
196+
Self::from_kind(AuthenticationErrorKind::PremiumAccountRequired)
197+
}
198+
_ => format!("Authentication failed with error: {:?}", error_code).into(),
199+
}
200+
}
201+
}

core/src/cache.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,17 @@ impl Cache {
162162
warn!("Cannot save file to cache: {}", e)
163163
}
164164
}
165+
166+
pub fn remove_file(&self, file: FileId) -> bool {
167+
if let Some(path) = self.file_path(file) {
168+
if let Err(err) = fs::remove_file(path) {
169+
warn!("Unable to remove file from cache: {}", err);
170+
false
171+
} else {
172+
true
173+
}
174+
} else {
175+
false
176+
}
177+
}
165178
}

core/src/config.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ pub enum DeviceType {
3636
AVR = 6,
3737
STB = 7,
3838
AudioDongle = 8,
39+
GameConsole = 9,
40+
CastAudio = 10,
41+
CastVideo = 11,
42+
Automobile = 12,
43+
Smartwatch = 13,
44+
Chromebook = 14,
45+
UnknownSpotify = 100,
46+
CarThing = 101,
47+
Observer = 102,
48+
HomeThing = 103,
3949
}
4050

4151
impl FromStr for DeviceType {
@@ -51,6 +61,14 @@ impl FromStr for DeviceType {
5161
"avr" => Ok(AVR),
5262
"stb" => Ok(STB),
5363
"audiodongle" => Ok(AudioDongle),
64+
"gameconsole" => Ok(GameConsole),
65+
"castaudio" => Ok(CastAudio),
66+
"castvideo" => Ok(CastVideo),
67+
"automobile" => Ok(Automobile),
68+
"smartwatch" => Ok(Smartwatch),
69+
"chromebook" => Ok(Chromebook),
70+
"carthing" => Ok(CarThing),
71+
"homething" => Ok(HomeThing),
5472
_ => Err(()),
5573
}
5674
}
@@ -69,6 +87,16 @@ impl fmt::Display for DeviceType {
6987
AVR => f.write_str("AVR"),
7088
STB => f.write_str("STB"),
7189
AudioDongle => f.write_str("AudioDongle"),
90+
GameConsole => f.write_str("GameConsole"),
91+
CastAudio => f.write_str("CastAudio"),
92+
CastVideo => f.write_str("CastVideo"),
93+
Automobile => f.write_str("Automobile"),
94+
Smartwatch => f.write_str("Smartwatch"),
95+
Chromebook => f.write_str("Chromebook"),
96+
UnknownSpotify => f.write_str("UnknownSpotify"),
97+
CarThing => f.write_str("CarThing"),
98+
Observer => f.write_str("Observer"),
99+
HomeThing => f.write_str("HomeThing"),
72100
}
73101
}
74102
}

core/src/connection/mod.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tokio_core::net::TcpStream;
1313
use tokio_core::reactor::Handle;
1414
use url::Url;
1515

16-
use crate::authentication::Credentials;
16+
use crate::authentication::{AuthenticationError, Credentials};
1717
use crate::version;
1818

1919
use crate::proxytunnel;
@@ -66,7 +66,7 @@ pub fn authenticate(
6666
transport: Transport,
6767
credentials: Credentials,
6868
device_id: String,
69-
) -> Box<dyn Future<Item = (Transport, Credentials), Error = io::Error>> {
69+
) -> Box<dyn Future<Item = (Transport, Credentials), Error = AuthenticationError>> {
7070
use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os};
7171
use crate::protocol::keyexchange::APLoginFailed;
7272

@@ -101,6 +101,7 @@ pub fn authenticate(
101101
transport
102102
.send((cmd, data))
103103
.and_then(|transport| transport.into_future().map_err(|(err, _stream)| err))
104+
.map_err(|io_err| io_err.into())
104105
.and_then(|(packet, transport)| match packet {
105106
Some((0xac, data)) => {
106107
let welcome_data: APWelcome =
@@ -118,10 +119,7 @@ pub fn authenticate(
118119
Some((0xad, data)) => {
119120
let error_data: APLoginFailed =
120121
protobuf::parse_from_bytes(data.as_ref()).unwrap();
121-
panic!(
122-
"Authentication failed with reason: {:?}",
123-
error_data.get_error_code()
124-
)
122+
Err(error_data.into())
125123
}
126124

127125
Some((cmd, _)) => panic!("Unexpected packet {:?}", cmd),

core/src/session.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use crate::config::SessionConfig;
1919
use crate::connection;
2020
use crate::mercury::MercuryManager;
2121

22+
pub use crate::authentication::{AuthenticationError, AuthenticationErrorKind};
23+
2224
struct SessionData {
2325
country: String,
2426
time_delta: i64,
@@ -53,16 +55,18 @@ impl Session {
5355
credentials: Credentials,
5456
cache: Option<Cache>,
5557
handle: Handle,
56-
) -> Box<dyn Future<Item = Session, Error = io::Error>> {
58+
) -> Box<dyn Future<Item = Session, Error = AuthenticationError>> {
5759
let access_point =
5860
apresolve_or_fallback::<io::Error>(&handle, &config.proxy, &config.ap_port);
5961

6062
let handle_ = handle.clone();
6163
let proxy = config.proxy.clone();
62-
let connection = access_point.and_then(move |addr| {
63-
info!("Connecting to AP \"{}\"", addr);
64-
connection::connect(addr, &handle_, &proxy)
65-
});
64+
let connection = access_point
65+
.and_then(move |addr| {
66+
info!("Connecting to AP \"{}\"", addr);
67+
connection::connect(addr, &handle_, &proxy)
68+
})
69+
.map_err(|io_err| io_err.into());
6670

6771
let device_id = config.device_id.clone();
6872
let authentication = connection.and_then(move |connection| {

playback/src/player.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -654,20 +654,24 @@ impl PlayerTrackLoader {
654654
FileFormat::OGG_VORBIS_96,
655655
],
656656
};
657-
let format = formats
658-
.iter()
659-
.find(|format| audio.files.contains_key(format))
660-
.unwrap();
661657

662-
let file_id = match audio.files.get(&format) {
663-
Some(&file_id) => file_id,
658+
let entry = formats.iter().find_map(|format| {
659+
if let Some(&file_id) = audio.files.get(format) {
660+
Some((*format, file_id))
661+
} else {
662+
None
663+
}
664+
});
665+
666+
let (format, file_id) = match entry {
667+
Some(t) => t,
664668
None => {
665-
warn!("<{}> in not available in format {:?}", audio.name, format);
669+
warn!("<{}> is not available in any supported format", audio.name);
666670
return None;
667671
}
668672
};
669673

670-
let bytes_per_second = self.stream_data_rate(*format);
674+
let bytes_per_second = self.stream_data_rate(format);
671675
let play_from_beginning = position_ms == 0;
672676

673677
let key = self.session.audio_key().request(spotify_id, file_id);
@@ -685,6 +689,7 @@ impl PlayerTrackLoader {
685689
return None;
686690
}
687691
};
692+
let is_cached = encrypted_file.is_cached();
688693

689694
let mut stream_loader_controller = encrypted_file.get_stream_loader_controller();
690695

@@ -718,12 +723,31 @@ impl PlayerTrackLoader {
718723

719724
let audio_file = Subfile::new(decrypted_file, 0xa7);
720725

721-
let mut decoder = VorbisDecoder::new(audio_file).unwrap();
726+
let mut decoder = match VorbisDecoder::new(audio_file) {
727+
Ok(decoder) => decoder,
728+
Err(e) if is_cached => {
729+
warn!(
730+
"Unable to read cached audio file: {}. Trying to download it.",
731+
e
732+
);
733+
734+
// unwrap safety: The file is cached, so session must have a cache
735+
if !self.session.cache().unwrap().remove_file(file_id) {
736+
return None;
737+
}
738+
739+
// Just try it again
740+
return self.load_track(spotify_id, position_ms);
741+
}
742+
Err(e) => {
743+
error!("Unable to read audio file: {}", e);
744+
return None;
745+
}
746+
};
722747

723748
if position_ms != 0 {
724-
match decoder.seek(position_ms as i64) {
725-
Ok(_) => (),
726-
Err(err) => error!("Vorbis error: {:?}", err),
749+
if let Err(err) = decoder.seek(position_ms as i64) {
750+
error!("Vorbis error: {}", err);
727751
}
728752
stream_loader_controller.set_stream_mode();
729753
}

src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use futures::{Async, Future, Poll, Stream};
33
use log::{error, info, trace, warn};
44
use sha1::{Digest, Sha1};
55
use std::env;
6-
use std::io::{self, stderr, Write};
6+
use std::io::{stderr, Write};
77
use std::mem;
88
use std::path::Path;
99
use std::process::exit;
@@ -16,7 +16,7 @@ use url::Url;
1616
use librespot::core::authentication::{get_credentials, Credentials};
1717
use librespot::core::cache::Cache;
1818
use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig, VolumeCtrl};
19-
use librespot::core::session::Session;
19+
use librespot::core::session::{AuthenticationError, Session};
2020
use librespot::core::version;
2121

2222
use librespot::connect::discovery::{discovery, DiscoveryStream};
@@ -436,7 +436,7 @@ struct Main {
436436

437437
spirc: Option<Spirc>,
438438
spirc_task: Option<SpircTask>,
439-
connect: Box<dyn Future<Item = Session, Error = io::Error>>,
439+
connect: Box<dyn Future<Item = Session, Error = AuthenticationError>>,
440440

441441
shutdown: bool,
442442
last_credentials: Option<Credentials>,

0 commit comments

Comments
 (0)