albums list wip

This commit is contained in:
Erica Z 2024-11-07 21:54:01 +01:00
parent 1c4f694157
commit f66262b95f
5 changed files with 92 additions and 56 deletions

View file

@ -27,3 +27,11 @@
#play-queue .playing label.title { #play-queue .playing label.title {
font-weight: bold; font-weight: bold;
} }
gridview.albums child {
margin: 10px;
}
gridview.albums child image {
margin: 10px;
}

View file

@ -83,7 +83,52 @@ template $AudreyUiWindow: Adw.ApplicationWindow {
ScrolledWindow { ScrolledWindow {
vexpand: true; vexpand: true;
GridView {} GridView {
styles [ "albums" ]
single-click-activate: true;
model: NoSelection {
model: bind template.albums-model;
};
factory: BuilderListItemFactory {
template ListItem {
child: Box {
orientation: vertical;
margin-bottom: 6;
Image {
icon-name: "media-optical-cd";
pixel-size: 160;
halign: center;
hexpand: false;
styles [
"frame"
]
}
Label {
label: bind template.item as <$AudreyModelAlbum>.name;
ellipsize: end;
styles [
"heading"
]
}
Label {
label: bind template.item as <$AudreyModelAlbum>.artist;
ellipsize: end;
styles [
"caption"
]
}
};
}
};
}
} }
}; };
} }

View file

@ -1,2 +1,5 @@
mod song; mod song;
pub use song::Song; pub use song::Song;
mod album;
pub use album::Album;

View file

@ -1,8 +1,8 @@
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::RefCell;
use std::cell::{Cell, RefCell};
#[derive(glib::Properties, Default)] #[derive(glib::Properties, Default)]
#[properties(wrapper_type = super::Album)] #[properties(wrapper_type = super::Album)]
@ -12,7 +12,7 @@ mod imp {
#[property(get, set)] #[property(get, set)]
name: RefCell<String>, name: RefCell<String>,
#[property(get, set)] #[property(get, set)]
artist: RefCell<Option<String>>, artist: RefCell<String>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -20,55 +20,13 @@ mod imp {
const NAME: &'static str = "AudreyModelAlbum"; const NAME: &'static str = "AudreyModelAlbum";
type Type = super::Album; type Type = super::Album;
} }
#[glib::derived_properties]
impl ObjectImpl for Album {}
} }
use crate::subsonic; use gtk::glib;
use adw::{prelude::*, subclass::prelude::*};
use glib::Object;
use gtk::{gdk, glib};
use std::rc::Rc;
glib::wrapper! { glib::wrapper! {
pub struct Album(ObjectSubclass<imp::Album>); pub struct Album(ObjectSubclass<imp::Album>);
} }
impl Album {
pub fn from_api(
album: &subsonic::schema::Child,
load_thumbnail: bool,
) -> Self {
let album: Album = Object::builder()
.property("id", &album.id)
.property("title", &album.title)
.property("artist", &album.artist)
.property("album", &album.album)
.property("genre", &album.genre)
.property("duration", album.duration as i64)
.property("track", album.track.map(i64::from).unwrap_or(-1))
.property("stream-url", api.stream_url(&album.id).as_str())
.build();
if load_thumbnail {
let api = Rc::clone(&api);
let id = album.id();
let album_weak = album.downgrade();
album.imp()
.thumbnail_loading
.replace(Some(glib::spawn_future_local(async move {
let bytes = api
.cover_art(&id, Some(50)) // TODO: WidgetExt::scale_factor
.await
.unwrap();
let album = match album_weak.upgrade() {
None => return,
Some(album) => album,
};
album.set_thumbnail(
gdk::Texture::from_bytes(&glib::Bytes::from_owned(bytes)).unwrap(),
);
})));
}
album
}
}

View file

@ -1,5 +1,5 @@
mod imp { mod imp {
use crate::model::Song; use crate::model::{Album, Song};
use crate::{mpris, mpv}; use crate::{mpris, mpv};
use adw::prelude::*; use adw::prelude::*;
use adw::subclass::prelude::*; use adw::subclass::prelude::*;
@ -38,6 +38,9 @@ mod imp {
#[property(get)] #[property(get)]
playlist_model: gio::ListStore, playlist_model: gio::ListStore,
#[property(get)]
albums_model: gio::ListStore,
#[property(type = i64, get = Self::volume, set = Self::set_volume, minimum = 0, maximum = 100)] #[property(type = i64, get = Self::volume, set = Self::set_volume, minimum = 0, maximum = 100)]
_volume: (), _volume: (),
#[property(type = bool, get = Self::mute, set = Self::set_mute)] #[property(type = bool, get = Self::mute, set = Self::set_mute)]
@ -98,7 +101,9 @@ mod imp {
setup: Default::default(), setup: Default::default(),
api: Default::default(), api: Default::default(),
mpv, mpv,
playlist_model: gio::ListStore::new::<Song>(), playlist_model: gio::ListStore::new::<Song>(),
albums_model: gio::ListStore::new::<Album>(),
_volume: (), _volume: (),
_mute: (), _mute: (),
@ -653,20 +658,28 @@ impl Window {
} }
pub fn setup_connected(&self, api: crate::subsonic::Client) { pub fn setup_connected(&self, api: crate::subsonic::Client) {
self.imp().api.replace(Some(Rc::new(api))); if self.imp().api.replace(Some(Rc::new(api))).is_some() {
unimplemented!("changing the api object");
}
self.set_can_click_shuffle_all(true); self.set_can_click_shuffle_all(true);
let api = Rc::clone(self.imp().api.borrow().as_ref().unwrap()); let api = Rc::clone(self.imp().api.borrow().as_ref().unwrap());
let albums_model = self.albums_model().clone();
glib::spawn_future_local(async move { glib::spawn_future_local(async move {
use futures::TryStreamExt; use futures::TryStreamExt;
let mut count = 0;
let mut albums = let mut albums =
std::pin::pin!(api.album_list_full(crate::subsonic::AlbumListType::Newest)); std::pin::pin!(api.album_list_full(crate::subsonic::AlbumListType::Newest));
while albums.try_next().await.unwrap().is_some() { while let Some(album) = albums.try_next().await.unwrap() {
count += 1; albums_model.append(
&glib::Object::builder::<crate::model::Album>()
.property("id", &album.id)
.property("name", &album.name)
.property("artist", &album.artist)
.build(),
);
} }
println!("gathered {count} albums");
}); });
} }
@ -732,4 +745,13 @@ impl Window {
Ordering::Equal => {} Ordering::Equal => {}
} }
} }
pub fn stop(&self) {
self.imp().mpv.command(["stop"]).unwrap();
self.playlist_model().remove_all();
}
pub fn api(&self) -> Rc<crate::subsonic::Client> {
Rc::clone(self.imp().api.borrow().as_ref().unwrap())
}
} }