Local files playback #1525
Replies: 3 comments 22 replies
-
That's be a yes for me, because it'll make librespot more feature-complete with respect to the official client. |
Beta Was this translation helpful? Give feedback.
-
@photovoltex @roderickvd I have made some progress and identified the capability that allows you to start playing a local file on librespot. On I am not sure how you would prefer to review this work -- this is a tiny change but is it worth making a small incremental PR? Or would you rather the feature be implemented all in one PR, so that librespot isn't lying about its capabilities? I was also thinking about updating the docs/ directory with what I have found out about the connect endpoint, since it would be good to expand the documentation there and also check my own understanding. Is that something you would be open to? |
Beta Was this translation helpful? Give feedback.
-
Now that we have the URI PR merged in I can take another look at this functionality. I've managed to get local file URIs parsed and for the client to report it can play local files, the next tricky bit is matching a URI to a file. I managed to make a snippet to derive a local file URI from a path to an audio file using Symphonia: Symphonia example
use std::fs::File;
use symphonia::core::formats::{FormatOptions};
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::{
MetadataOptions, StandardTagKey, Tag,
};
use symphonia::core::probe::{Hint, ProbeResult};
fn main() {
let path = "/home/jay/Music/Spotify/Dialga's Fight To the Finish!.mp3";
let src = File::open(path).expect("failed to open media");
let mss = MediaSourceStream::new(Box::new(src), Default::default());
let mut hint = Hint::new();
hint.with_extension("mp3");
let meta_opts: MetadataOptions = Default::default();
let fmt_opts: FormatOptions = Default::default();
let mut probed = symphonia::default::get_probe()
.format(&hint, mss, &fmt_opts, &meta_opts)
.expect("unsupported format");
let mut artist: Option<String> = None;
let mut album_title: Option<String> = None;
let mut track_title: Option<String> = None;
let mut duration_secs: Option<u64> = None;
if let Some(tags) = get_tags(&mut probed) {
for tag in tags {
if let Some(std_key) = tag.std_key {
match std_key {
StandardTagKey::Album => {
album_title.replace(tag.value.to_string());
}
StandardTagKey::Artist => {
artist.replace(tag.value.to_string());
}
StandardTagKey::TrackTitle => {
track_title.replace(tag.value.to_string());
}
_ => {
continue;
}
}
}
}
}
if let Some(first_track) = probed.format.tracks().iter().next() {
let tb = first_track.codec_params.time_base;
let time = tb
.expect("tb")
.calc_time(first_track.codec_params.n_frames.unwrap());
duration_secs.replace(time.seconds);
}
let url = format!(
"spotify:local:{}:{}:{}:{}",
artist.as_deref().unwrap_or(""),
album_title.as_deref().unwrap_or(""),
track_title.as_deref().unwrap_or(""),
duration_secs.unwrap_or(0)
);
assert_eq!(url, "spotify:local:Arata Iiyoshi:Explorers of Sky:Dialga's Fight To the Finish!:257");
println!("{}", url);
}
fn get_tags(probed: &mut ProbeResult) -> Option<Vec<Tag>> {
if let Some(metadata_rev) = probed.format.metadata().current() {
return Some(metadata_rev.tags().to_vec());
}
if let Some(metadata_rev) = probed.metadata.get().as_ref().and_then(|m| m.current()) {
return Some(metadata_rev.tags().to_vec());
}
None
} I'm thinking that on initialisation the PlayerInternal can create and store a HashMap of URIs to file paths which can then be used when attempting to play a local file. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Firstly, apologies if there is already a thread about this -- I had a quick look and couldn't find a relevant issue/discussion.
I gather from the enum member
StateError::UnsupportedLocalPlayback
that librespot doesn't currently support playing back local files:librespot/connect/src/state.rs
Lines 57 to 58 in be37402
Predictably when I try to play back a local file it blows up (although interestingly with a 404 Not Found error rather than this specific error).
Is there any interest in supporting local file playback and is it something that would be accepted as a community contribution? I'm aware it's quite a large amount of work and I'm unsure if the local files syncing protocol is supported by the public API.
Beta Was this translation helpful? Give feedback.
All reactions