Compare commits

..

No commits in common. "9599ee669dbf44b41cbe1c1344be907cc15676f9" and "25f082ee6f00f70d67c24e3bba416f48f0b91521" have entirely different histories.

4 changed files with 57 additions and 82 deletions

1
Cargo.lock generated
View file

@ -2651,7 +2651,6 @@ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",
"percent-encoding", "percent-encoding",
"serde",
] ]
[[package]] [[package]]

View file

@ -24,7 +24,7 @@ serde = { version = "1.0.214", features = ["derive"] }
tokio = { version = "1", features = ["rt-multi-thread"] } tokio = { version = "1", features = ["rt-multi-thread"] }
tracing = { version = "0.1.40", default-features = false, features = ["attributes", "std"] } tracing = { version = "0.1.40", default-features = false, features = ["attributes", "std"] }
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
url = { version = "2.5.2", features = ["serde"] } url = "2.5.2"
zbus = { version = "5.0.1", features = ["chrono"] } zbus = { version = "5.0.1", features = ["chrono"] }
[build-dependencies] [build-dependencies]

View file

@ -1,6 +1,5 @@
use chrono::{offset::Utc, DateTime}; use chrono::{offset::Utc, DateTime};
use serde::Deserialize; use serde::Deserialize;
use url::Url;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
@ -83,27 +82,3 @@ pub struct AlbumID3 {
pub played: Option<DateTime<Utc>>, pub played: Option<DateTime<Utc>>,
// TODO: opensubsonic extensions? // TODO: opensubsonic extensions?
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchResults3Outer {
pub search_results3: SearchResults3,
}
#[derive(Debug, Deserialize)]
pub struct SearchResults3 {
pub artist: Option<Vec<ArtistID3>>,
pub album: Option<Vec<AlbumID3>>,
pub song: Option<Vec<Child>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArtistID3 {
pub id: String,
pub name: String,
pub cover_art: Option<String>,
pub artist_image_url: Option<Url>,
pub album_count: Option<u32>,
pub starred: Option<DateTime<Utc>>,
}

View file

@ -90,6 +90,7 @@ mod imp {
mpv.observe_property(4, "idle-active").unwrap(); mpv.observe_property(4, "idle-active").unwrap();
mpv.observe_property(6, "playlist-count").unwrap(); mpv.observe_property(6, "playlist-count").unwrap();
mpv.observe_property(7, "duration").unwrap(); mpv.observe_property(7, "duration").unwrap();
mpv.observe_property(8, "path").unwrap();
// "Useful to drain property changes before a new file is loaded." // "Useful to drain property changes before a new file is loaded."
mpv.add_hook(0, "on_before_start_file", 0).unwrap(); mpv.add_hook(0, "on_before_start_file", 0).unwrap();
@ -184,13 +185,11 @@ mod imp {
match event { match event {
Event::PropertyChange(event) => window.imp().on_property_change(event), Event::PropertyChange(event) => window.imp().on_property_change(event),
Event::StartFile(_) => window.imp().on_start_file(),
Event::Hook(event) => window.imp().on_hook(event), Event::Hook(event) => window.imp().on_hook(event),
Event::LogMessage(event) => window.imp().on_log_message(event), Event::LogMessage(event) => window.imp().on_log_message(event),
Event::StartFile(_) => window.imp().on_start_file(),
Event::FileLoaded => window.imp().on_file_loaded(),
Event::PlaybackRestart => window.imp().on_playback_restart(),
Event::Seek => window.imp().on_seek(), Event::Seek => window.imp().on_seek(),
Event::PlaybackRestart => window.imp().on_playback_restart(),
Event::EndFile(event) => window.imp().on_end_file(event), Event::EndFile(event) => window.imp().on_end_file(event),
Event::Unknown(_) => { Event::Unknown(_) => {
@ -455,10 +454,60 @@ mod imp {
self.obj().notify("duration"); self.obj().notify("duration");
} }
8 => {
assert_eq!(event.name, "path");
// sanity check
match self.mpv.get_property::<String>("path") {
Ok(path) => {
assert_eq!(path, self.obj().song().unwrap().stream_url())
}
Err(err) if err.is_property_unavailable() => {}
Err(err) => panic!("{err:?}"),
}
}
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn on_start_file(&self) {
event!(Level::INFO, "start file event");
self.obj().notify("song");
self.buffering_start();
let window = self.obj().clone();
let song_id = window.song().unwrap().id();
if let Some(handle) = self
.loading_cover_handle
.replace(Some(glib::spawn_future_local(async move {
let api = window.imp().api.borrow().as_ref().unwrap().clone();
let bytes = api
.cover_art(&song_id, None) // full size
.await
.expect("could not load cover art for song {song_id}");
match window.song() {
Some(song) if song.id() == song_id => {
let texture = gdk::Texture::from_bytes(&glib::Bytes::from_owned(bytes))
.expect("could not create texture from cover art for {song_id}");
window.set_playing_cover_art(Some(texture));
}
_ => {
event!(
Level::WARN,
"was too late to fetch cover for song {song_id}"
)
}
}
})))
{
handle.abort();
}
// make sure this is reported as 0
self.obj().notify("time-pos");
}
fn on_hook(&self, event: crate::mpv::event::HookEvent) { fn on_hook(&self, event: crate::mpv::event::HookEvent) {
match event.reply_userdata { match event.reply_userdata {
0 => { 0 => {
@ -503,61 +552,13 @@ mod imp {
}; };
} }
fn on_start_file(&self) {
event!(Level::INFO, "StartFile");
self.obj().notify("song");
self.buffering_start();
let window = self.obj().clone();
let song_id = window.song().unwrap().id();
if let Some(handle) = self
.loading_cover_handle
.replace(Some(glib::spawn_future_local(async move {
let api = window.imp().api.borrow().as_ref().unwrap().clone();
let bytes = api
.cover_art(&song_id, None) // full size
.await
.expect("could not load cover art for song {song_id}");
match window.song() {
Some(song) if song.id() == song_id => {
let texture = gdk::Texture::from_bytes(&glib::Bytes::from_owned(bytes))
.expect("could not create texture from cover art for {song_id}");
window.set_playing_cover_art(Some(texture));
}
_ => {
event!(
Level::WARN,
"was too late to fetch cover for song {song_id}"
)
}
}
})))
{
handle.abort();
}
// make sure this is reported as 0
self.obj().notify("time-pos");
}
fn on_file_loaded(&self) {
event!(Level::INFO, "FileLoaded");
// sanity check
// i think "path" is only available after this event is dispatched
assert_eq!(
self.mpv.get_property::<String>("path").unwrap(),
self.obj().song().unwrap().stream_url()
);
}
fn on_seek(&self) { fn on_seek(&self) {
event!(Level::INFO, "Seek"); event!(Level::INFO, "seek event");
self.buffering_start(); self.buffering_start();
} }
fn on_playback_restart(&self) { fn on_playback_restart(&self) {
event!(Level::INFO, "PlaybackRestart"); event!(Level::INFO, "playback restart event");
self.buffering_end(); self.buffering_end();
self.obj().notify("time-pos"); self.obj().notify("time-pos");
@ -569,7 +570,7 @@ mod imp {
} }
fn on_end_file(&self, event: crate::mpv::event::EndFileEvent) { fn on_end_file(&self, event: crate::mpv::event::EndFileEvent) {
event!(Level::INFO, "EndFile: {event:?}"); event!(Level::INFO, "end file event: {event:?}");
self.obj().notify("song"); self.obj().notify("song");
self.buffering_end(); self.buffering_end();