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;
|
focusable: false;
|
||||||
halign: end;
|
halign: end;
|
||||||
justify: right;
|
justify: right;
|
||||||
|
margin-top: 1;
|
||||||
|
margin-bottom: 1;
|
||||||
|
|
||||||
styles [
|
styles [
|
||||||
"dim-label",
|
"dim-label",
|
||||||
|
@ -39,7 +41,7 @@ template $AudreyUiPlayQueueSong: Box {
|
||||||
margin-top: 1;
|
margin-top: 1;
|
||||||
margin-bottom: 1;
|
margin-bottom: 1;
|
||||||
pixel-size: 50;
|
pixel-size: 50;
|
||||||
// paintable: bind template.song as <$AudreyModelSong>.thumbnail;
|
paintable: bind template.song as <$AudreyModelSong>.thumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
Box title_box {
|
Box title_box {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
mod imp {
|
mod imp {
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use gtk::glib;
|
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
|
use gtk::{gdk, glib};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
static NEXT_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
static NEXT_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
||||||
|
@ -31,6 +31,10 @@ mod imp {
|
||||||
cover_art_url: RefCell<String>,
|
cover_art_url: RefCell<String>,
|
||||||
#[property(get, set)]
|
#[property(get, set)]
|
||||||
stream_url: RefCell<String>,
|
stream_url: RefCell<String>,
|
||||||
|
|
||||||
|
#[property(get, set)]
|
||||||
|
thumbnail: RefCell<Option<gdk::Paintable>>,
|
||||||
|
pub(super) thumbnail_loading: RefCell<Option<glib::JoinHandle<()>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -48,19 +52,31 @@ mod imp {
|
||||||
.set_counter(NEXT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
|
.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 crate::subsonic;
|
||||||
|
use adw::{prelude::*, subclass::prelude::*};
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::glib;
|
use gtk::{gdk, glib};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct Song(ObjectSubclass<imp::Song>);
|
pub struct Song(ObjectSubclass<imp::Song>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Song {
|
impl Song {
|
||||||
pub fn from_child(api: &subsonic::Client, song: &subsonic::schema::Child) -> Self {
|
pub fn from_child(
|
||||||
Object::builder()
|
api: &Rc<subsonic::Client>,
|
||||||
|
song: &subsonic::schema::Child,
|
||||||
|
load_thumbnail: bool,
|
||||||
|
) -> Self {
|
||||||
|
let song: Song = Object::builder()
|
||||||
.property("id", &song.id)
|
.property("id", &song.id)
|
||||||
.property("title", &song.title)
|
.property("title", &song.title)
|
||||||
.property("artist", &song.artist)
|
.property("artist", &song.artist)
|
||||||
|
@ -68,8 +84,31 @@ impl Song {
|
||||||
.property("genre", &song.genre)
|
.property("genre", &song.genre)
|
||||||
.property("duration", song.duration as i64)
|
.property("duration", song.duration as i64)
|
||||||
//.property("track", song.track)
|
//.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())
|
.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)
|
.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 {
|
||||||
self.url(&["rest", "getCoverArt"], &[("id", id)])
|
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> {
|
pub async fn cover_art(&self, id: &str, size: u32) -> Result<Bytes, Error> {
|
||||||
self.send_bytes(self.client.get(self.cover_art_url(id)))
|
self.send_bytes(self.client.get(self.cover_art_url(id, size)))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -270,7 +270,7 @@ mod imp {
|
||||||
Rc::clone(api.as_ref().unwrap())
|
Rc::clone(api.as_ref().unwrap())
|
||||||
};
|
};
|
||||||
for song in api.get_random_songs(10).await.unwrap().into_iter() {
|
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
|
self.mpv
|
||||||
.command(["loadfile", &song.stream_url(), "append-play"])
|
.command(["loadfile", &song.stream_url(), "append-play"])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -472,12 +472,11 @@ mod imp {
|
||||||
|
|
||||||
let window = self.obj().clone();
|
let window = self.obj().clone();
|
||||||
let song_id = window.song().unwrap().id();
|
let song_id = window.song().unwrap().id();
|
||||||
self
|
self.loading_cover_handle
|
||||||
.loading_cover_handle
|
|
||||||
.replace(Some(glib::spawn_future_local(async move {
|
.replace(Some(glib::spawn_future_local(async move {
|
||||||
let api = window.imp().api.borrow().as_ref().unwrap().clone();
|
let api = window.imp().api.borrow().as_ref().unwrap().clone();
|
||||||
let bytes = api
|
let bytes = api
|
||||||
.cover_art(&song_id)
|
.cover_art(&song_id, 0) // 0: full size
|
||||||
.await
|
.await
|
||||||
.expect("could not load cover art for song {song_id}");
|
.expect("could not load cover art for song {song_id}");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue