thumbnails in play queue
This commit is contained in:
parent
ba3cd74a68
commit
db8d99e180
4 changed files with 62 additions and 15 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,12 +219,19 @@ impl Client {
|
|||
.map(|response| response.random_songs.song)
|
||||
}
|
||||
|
||||
pub fn cover_art_url(&self, id: &str) -> url::Url {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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}");
|
||||
|
||||
|
|
Loading…
Reference in a new issue