diff --git a/.gitignore b/.gitignore index 401267f..7b9e4f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ subtails.toml +src/cover_default.png diff --git a/Cargo.lock b/Cargo.lock index 65bce10..f063dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -958,6 +964,12 @@ dependencies = [ "syn", ] +[[package]] +name = "icy_sixel" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86858ae800284d596cfdefcb0ad435c3493c12f35367431bbe9b2b3858c1155b" + [[package]] name = "ident_case" version = "1.0.1" @@ -1669,6 +1681,22 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "ratatui-image" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a07161c498ddd5066a444a869f27fd97cfc164dbee8c0bdb5ff803b8bb54ce" +dependencies = [ + "base64 0.21.7", + "icy_sixel", + "image", + "rand", + "ratatui", + "rustix", + "thiserror", + "windows", +] + [[package]] name = "rav1e" version = "0.7.1" @@ -1783,7 +1811,7 @@ version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-channel", @@ -2141,6 +2169,7 @@ dependencies = [ "pipewire", "rand", "ratatui", + "ratatui-image", "reqwest", "serde", "serde_json", @@ -2599,6 +2628,51 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-registry" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 2010f92..49bca74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ md5 = "0.7.0" pipewire = "0.8.0" rand = "0.8.5" ratatui = "0.29.0" +ratatui-image = "3.0.0" reqwest = { version = "0.12.9", features = ["blocking", "json"] } serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.133" diff --git a/src/player/current.rs b/src/player/current.rs index 12f3914..e28f7ca 100644 --- a/src/player/current.rs +++ b/src/player/current.rs @@ -1,4 +1,6 @@ -use crate::ssonic::response::Song; +use image::DynamicImage; + +use crate::{ssonic::response::Song, utils::default_cover}; pub struct Metadata { pub name: String, @@ -6,6 +8,7 @@ pub struct Metadata { pub playing: bool, pub duration: i32, pub current_time: i32, + pub cover: DynamicImage, } impl Metadata { @@ -16,6 +19,7 @@ impl Metadata { playing: false, duration: 0, current_time: 0, + cover: default_cover(), } } diff --git a/src/player/mod.rs b/src/player/mod.rs index 647aac4..f1e1cf9 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -52,7 +52,7 @@ pub fn init( player_chan, player_chan_in, playlist: playlist::Playlist::new(), - tui_root: tui::Root::new(), + tui_root: tui::Root::new().unwrap(), }; match player.begin() { Err(err) => { diff --git a/src/player/tui.rs b/src/player/tui.rs index b844226..5a64924 100644 --- a/src/player/tui.rs +++ b/src/player/tui.rs @@ -4,16 +4,19 @@ use ratatui::{ prelude::*, widgets::{Paragraph, Widget, Wrap}, }; +use ratatui_image::{picker::Picker, protocol::StatefulProtocol, StatefulImage}; use crate::utils::Error; use super::{current::Metadata, Player}; pub struct Root { + image_state: StatefulProtocol, + proto_picker: Picker, pub metadata: Metadata, } -impl Widget for &Root { +impl Widget for &mut Root { fn render(self, area: Rect, buf: &mut Buffer) where Self: Sized, @@ -31,12 +34,17 @@ impl Widget for &Root { } impl Root { - pub fn new() -> Self { - Self { - metadata: Metadata::new(), - } + pub fn new() -> Result { + let mut proto_picker = Picker::from_query_stdio()?; + let metadata = Metadata::new(); + let image_state = proto_picker.new_resize_protocol(metadata.cover.clone()); + Ok(Self { + metadata, + image_state, + proto_picker, + }) } - fn render_info(&self, area: Rect, buf: &mut Buffer) { + fn render_info(&mut self, area: Rect, buf: &mut Buffer) { let layout_info = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]); let [cover, track_info] = layout_info.areas(area); self.render_cover(cover, buf); @@ -44,14 +52,11 @@ impl Root { } fn render_progress(&self, area: Rect, buf: &mut Buffer) {} - fn render_cover(&self, area: Rect, buf: &mut Buffer) {} + fn render_cover(&mut self, area: Rect, buf: &mut Buffer) { + StatefulImage::new(None).render(area, buf, &mut self.image_state); + } fn render_track_info(&self, area: Rect, buf: &mut Buffer) { - let layout_track = Layout::vertical([ - Constraint::Min(20), - Constraint::Min(10), - Constraint::Fill(3), - ]) - .flex(Flex::Start); + let layout_track = Layout::vertical([Constraint::Min(20), Constraint::Min(10)]); let [title, artist, _] = layout_track.areas(area); self.render_title(title, buf); self.render_artist(artist, buf); @@ -72,7 +77,7 @@ impl Root { impl Player { pub fn update(&mut self) -> Result<(), Error> { self.terminal - .draw(|frame| frame.render_widget(&self.tui_root, frame.area()))?; + .draw(|frame| frame.render_widget(&mut self.tui_root, frame.area()))?; Ok(()) } } diff --git a/src/utils.rs b/src/utils.rs index 0a47296..0cfc390 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,4 @@ +use image::DynamicImage; use rand::{distributions::Alphanumeric, thread_rng, Rng}; pub type Error = Box; @@ -13,3 +14,9 @@ pub fn generate_random_salt() -> String { pub fn restore() { ratatui::restore() } + +const DEFAULT_COVER_RAW: &'static [u8] = include_bytes!("cover_default.png"); + +pub fn default_cover() -> DynamicImage { + image::load_from_memory(DEFAULT_COVER_RAW).unwrap() +}