thumbnails in play queue

This commit is contained in:
Erica Z 2024-11-06 12:46:49 +01:00
parent ba3cd74a68
commit db8d99e180
4 changed files with 62 additions and 15 deletions

View file

@ -24,6 +24,8 @@ template $AudreyUiPlayQueueSong: Box {
focusable: false;
halign: end;
justify: right;
margin-top: 1;
margin-bottom: 1;
styles [
"dim-label",
@ -39,7 +41,7 @@ template $AudreyUiPlayQueueSong: Box {
margin-top: 1;
margin-bottom: 1;
pixel-size: 50;
// paintable: bind template.song as <$AudreyModelSong>.thumbnail;
paintable: bind template.song as <$AudreyModelSong>.thumbnail;
}
Box title_box {

View file

@ -1,7 +1,7 @@
mod imp {
use adw::prelude::*;
use gtk::glib;
use gtk::subclass::prelude::*;
use gtk::{gdk, glib};
use std::cell::{Cell, RefCell};
static NEXT_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
@ -31,6 +31,10 @@ mod imp {
cover_art_url: RefCell<String>,
#[property(get, set)]
stream_url: RefCell<String>,
#[property(get, set)]
thumbnail: RefCell<Option<gdk::Paintable>>,
pub(super) thumbnail_loading: RefCell<Option<glib::JoinHandle<()>>>,
}
#[glib::object_subclass]
@ -48,19 +52,31 @@ mod imp {
.set_counter(NEXT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
}
}
impl Drop for Song {
fn drop(&mut self) {
self.thumbnail_loading.take().map(|handle| handle.abort());
}
}
}
use crate::subsonic;
use adw::{prelude::*, subclass::prelude::*};
use glib::Object;
use gtk::glib;
use gtk::{gdk, glib};
use std::rc::Rc;
glib::wrapper! {
pub struct Song(ObjectSubclass<imp::Song>);
}
impl Song {
pub fn from_child(api: &subsonic::Client, song: &subsonic::schema::Child) -> Self {
Object::builder()
pub fn from_child(
api: &Rc<subsonic::Client>,
song: &subsonic::schema::Child,
load_thumbnail: bool,
) -> Self {
let song: Song = Object::builder()
.property("id", &song.id)
.property("title", &song.title)
.property("artist", &song.artist)
@ -68,8 +84,31 @@ impl Song {
.property("genre", &song.genre)
.property("duration", song.duration as i64)
//.property("track", song.track)
.property("cover-art-url", api.cover_art_url(&song.id).as_str())
.property("cover-art-url", api.cover_art_url(&song.id, 0).as_str())
.property("stream-url", api.stream_url(&song.id).as_str())
.build()
.build();
if load_thumbnail {
let api = Rc::clone(&api);
let id = song.id();
let song_weak = song.downgrade();
song.imp()
.thumbnail_loading
.replace(Some(glib::spawn_future_local(async move {
let bytes = api
.cover_art(&id, 50) // TODO: constify thumbnail size, maybe take dpi into account etc
.await
.unwrap();
let song = match song_weak.upgrade() {
None => return,
Some(song) => song,
};
song.set_thumbnail(
gdk::Texture::from_bytes(&glib::Bytes::from_owned(bytes)).unwrap(),
);
})));
}
song
}
}

View file

@ -219,12 +219,19 @@ impl Client {
.map(|response| response.random_songs.song)
}
pub fn cover_art_url(&self, id: &str) -> url::Url {
self.url(&["rest", "getCoverArt"], &[("id", id)])
pub fn cover_art_url(&self, id: &str, size: u32) -> url::Url {
if size == 0 {
self.url(&["rest", "getCoverArt"], &[("id", id)])
} else {
self.url(
&["rest", "getCoverArt"],
&[("id", id), ("size", &size.to_string())],
)
}
}
pub async fn cover_art(&self, id: &str) -> Result<Bytes, Error> {
self.send_bytes(self.client.get(self.cover_art_url(id)))
pub async fn cover_art(&self, id: &str, size: u32) -> Result<Bytes, Error> {
self.send_bytes(self.client.get(self.cover_art_url(id, size)))
.await
}

View file

@ -270,7 +270,7 @@ mod imp {
Rc::clone(api.as_ref().unwrap())
};
for song in api.get_random_songs(10).await.unwrap().into_iter() {
let song = Song::from_child(&api, &song);
let song = Song::from_child(&api, &song, true);
self.mpv
.command(["loadfile", &song.stream_url(), "append-play"])
.unwrap();
@ -472,12 +472,11 @@ mod imp {
let window = self.obj().clone();
let song_id = window.song().unwrap().id();
self
.loading_cover_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)
.cover_art(&song_id, 0) // 0: full size
.await
.expect("could not load cover art for song {song_id}");