Show metadata for audio files, part of #56

This commit is contained in:
Jeremy Soller 2025-01-31 15:19:01 -07:00
parent aa9913b2a0
commit 67caa89151
No known key found for this signature in database
GPG key ID: 670FDFB5428E05CA
2 changed files with 118 additions and 82 deletions

View file

@ -1,3 +1,4 @@
album = Album: {$album}
audio = Audio audio = Audio
no-video-or-audio-file-open = No video or audio file open no-video-or-audio-file-open = No video or audio file open
open-file = Open file open-file = Open file

View file

@ -198,6 +198,7 @@ pub struct MprisMeta {
album: String, album: String,
album_art_opt: Option<url::Url>, album_art_opt: Option<url::Url>,
album_artist: String, album_artist: String,
album_year_opt: Option<i32>,
artists: Vec<String>, artists: Vec<String>,
title: String, title: String,
disc_number: i32, disc_number: i32,
@ -266,6 +267,7 @@ pub struct App {
dropdown_opt: Option<DropdownKind>, dropdown_opt: Option<DropdownKind>,
fullscreen: bool, fullscreen: bool,
key_binds: HashMap<KeyBind, Action>, key_binds: HashMap<KeyBind, Action>,
mpris_meta: MprisMeta,
mpris_opt: Option<(MprisMeta, MprisState, mpsc::UnboundedSender<MprisEvent>)>, mpris_opt: Option<(MprisMeta, MprisState, mpsc::UnboundedSender<MprisEvent>)>,
nav_model: segmented_button::SingleSelectModel, nav_model: segmented_button::SingleSelectModel,
projects: Vec<(String, PathBuf)>, projects: Vec<(String, PathBuf)>,
@ -585,7 +587,6 @@ impl App {
} }
fn update_mpris_meta(&mut self) { fn update_mpris_meta(&mut self) {
if let Some((old, _, tx)) = &mut self.mpris_opt {
let mut new = MprisMeta { let mut new = MprisMeta {
//TODO: clear url_opt when file is closed //TODO: clear url_opt when file is closed
url_opt: self.flags.url_opt.clone(), url_opt: self.flags.url_opt.clone(),
@ -601,6 +602,9 @@ impl App {
if let Some(tag) = tags.get::<gst::tags::AlbumArtist>() { if let Some(tag) = tags.get::<gst::tags::AlbumArtist>() {
new.album_artist = tag.get().into(); new.album_artist = tag.get().into();
} }
if let Some(tag) = tags.get::<gst::tags::DateTime>() {
new.album_year_opt = Some(tag.get().year());
}
if let Some(tag) = tags.get::<gst::tags::Artist>() { if let Some(tag) = tags.get::<gst::tags::Artist>() {
//TODO: how are multiple artists handled by gstreamer? //TODO: how are multiple artists handled by gstreamer?
new.artists = vec![tag.get().into()]; new.artists = vec![tag.get().into()];
@ -632,9 +636,7 @@ impl App {
{ {
Ok(mut album_art) => { Ok(mut album_art) => {
match image.write_with_encoder( match image.write_with_encoder(
image::codecs::png::PngEncoder::new( image::codecs::png::PngEncoder::new(&mut album_art),
&mut album_art,
),
) { ) {
Ok(()) => self.album_art_opt = Some(album_art), Ok(()) => self.album_art_opt = Some(album_art),
Err(err) => { Err(err) => {
@ -646,10 +648,7 @@ impl App {
} }
} }
Err(err) => { Err(err) => {
log::warn!( log::warn!("failed to create temporary image: {}", err);
"failed to create temporary image: {}",
err
);
} }
} }
} }
@ -668,11 +667,13 @@ impl App {
new.album_art_opt = url::Url::from_file_path(album_art.path()).ok(); new.album_art_opt = url::Url::from_file_path(album_art.path()).ok();
} }
} }
if let Some((old, _, tx)) = &mut self.mpris_opt {
if new != *old { if new != *old {
*old = new.clone(); *old = new.clone();
let _ = tx.send(MprisEvent::Meta(new)); let _ = tx.send(MprisEvent::Meta(new.clone()));
} }
} }
self.mpris_meta = new;
} }
fn update_mpris_state(&mut self) { fn update_mpris_state(&mut self) {
@ -782,6 +783,7 @@ impl Application for App {
dropdown_opt: None, dropdown_opt: None,
fullscreen: false, fullscreen: false,
key_binds: key_binds(), key_binds: key_binds(),
mpris_meta: MprisMeta::default(),
mpris_opt: None, mpris_opt: None,
nav_model: nav_bar::Model::builder().build(), nav_model: nav_bar::Model::builder().build(),
projects: Vec::new(), projects: Vec::new(),
@ -1222,6 +1224,7 @@ impl Application for App {
let cosmic_theme::Spacing { let cosmic_theme::Spacing {
space_xxs, space_xxs,
space_xs, space_xs,
space_s,
space_m, space_m,
.. ..
} = theme::active().cosmic().spacing; } = theme::active().cosmic().spacing;
@ -1273,14 +1276,46 @@ impl Application for App {
if let Some(album_art) = &self.album_art_opt { if let Some(album_art) = &self.album_art_opt {
if !video.has_video() { if !video.has_video() {
// This is a hack to have the video player running but not visible (since the controls will cover it as an overlay) let mut col = widget::column();
video_player = widget::column::with_children(vec![ col = col.push(widget::vertical_space(Length::Fill));
col = col.push(
widget::image(widget::image::Handle::from_path(album_art.path())) widget::image(widget::image::Handle::from_path(album_art.path()))
.content_fit(ContentFit::ScaleDown) .content_fit(ContentFit::ScaleDown)
.width(Length::Fill) .width(Length::Fill),
.height(Length::Fill) );
col = col.push(widget::vertical_space(space_s));
//TODO: fallback if title missing
col = col.push(widget::text::title4(&self.mpris_meta.title));
for artist in self.mpris_meta.artists.iter() {
col = col.push(widget::text::body(artist));
}
col = col.push(widget::vertical_space(space_s));
if !self.mpris_meta.album.is_empty() {
col = col.push(widget::text::body(fl!(
"album",
album = self.mpris_meta.album.as_str()
)));
}
if let Some(year) = &self.mpris_meta.album_year_opt {
col = col.push(widget::text::body(format!("{}", year)));
}
col = col.push(widget::vertical_space(Length::Fill));
// Space to keep from going under control overlay
let mut control_height = space_xxs + 32 + space_xxs;
if self.core.is_condensed() {
control_height += space_xxs + 32;
}
// This is a hack to have the video player running but not visible (since the controls will cover it as an overlay)
video_player = widget::row::with_children(vec![
widget::horizontal_space(Length::Fill).into(),
widget::container(
col.push(widget::container(video_player).height(control_height)),
)
.width(320)
.into(), .into(),
widget::container(video_player).height(space_m).into(), widget::horizontal_space(Length::Fill).into(),
]) ])
.into(); .into();
} }