|
|
|
|
@ -1,9 +1,17 @@
|
|
|
|
|
use adw::{prelude::*, subclass::prelude::*}; |
|
|
|
|
use gettextrs::gettext; |
|
|
|
|
use gtk::{glib, CompositeTemplate}; |
|
|
|
|
use matrix_sdk::ruma::events::{room::message::MessageType, AnyMessageLikeEventContent}; |
|
|
|
|
|
|
|
|
|
use crate::session::content::room_details::history_viewer::HistoryViewerEvent; |
|
|
|
|
use glib::clone; |
|
|
|
|
use gtk::{gio, glib, CompositeTemplate}; |
|
|
|
|
use log::warn; |
|
|
|
|
use matrix_sdk::ruma::events::{ |
|
|
|
|
room::message::{AudioMessageEventContent, MessageType}, |
|
|
|
|
AnyMessageLikeEventContent, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
use crate::{ |
|
|
|
|
session::{content::room_details::history_viewer::HistoryViewerEvent, Session}, |
|
|
|
|
spawn, spawn_tokio, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
mod imp { |
|
|
|
|
use std::cell::RefCell; |
|
|
|
|
@ -17,6 +25,9 @@ mod imp {
|
|
|
|
|
#[template(resource = "/org/gnome/Fractal/content-audio-history-viewer-row.ui")] |
|
|
|
|
pub struct AudioRow { |
|
|
|
|
pub event: RefCell<Option<HistoryViewerEvent>>, |
|
|
|
|
pub media_file: RefCell<Option<gtk::MediaFile>>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub play_button: TemplateChild<gtk::Button>, |
|
|
|
|
#[template_child] |
|
|
|
|
pub title_label: TemplateChild<gtk::Label>, |
|
|
|
|
#[template_child] |
|
|
|
|
@ -31,6 +42,10 @@ mod imp {
|
|
|
|
|
|
|
|
|
|
fn class_init(klass: &mut Self::Class) { |
|
|
|
|
Self::bind_template(klass); |
|
|
|
|
|
|
|
|
|
klass.install_action("audio-row.toggle-play", None, move |widget, _, _| { |
|
|
|
|
widget.toggle_play(); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn instance_init(obj: &InitializingObject<Self>) { |
|
|
|
|
@ -89,7 +104,7 @@ impl AudioRow {
|
|
|
|
|
if let MessageType::Audio(audio) = content.msgtype { |
|
|
|
|
imp.title_label.set_label(&audio.body); |
|
|
|
|
|
|
|
|
|
if let Some(duration) = audio.info.and_then(|i| i.duration) { |
|
|
|
|
if let Some(duration) = audio.info.as_ref().and_then(|i| i.duration) { |
|
|
|
|
let duration_secs = duration.as_secs(); |
|
|
|
|
let secs = duration_secs % 60; |
|
|
|
|
let mins = (duration_secs % (60 * 60)) / 60; |
|
|
|
|
@ -105,6 +120,11 @@ impl AudioRow {
|
|
|
|
|
} else { |
|
|
|
|
imp.duration_label.set_label(&gettext("Unknown duration")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let session = event.room().unwrap().session(); |
|
|
|
|
spawn!(clone!(@weak self as obj => async move { |
|
|
|
|
obj.download_audio(audio, &session).await; |
|
|
|
|
})); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -116,4 +136,66 @@ impl AudioRow {
|
|
|
|
|
pub fn event(&self) -> Option<HistoryViewerEvent> { |
|
|
|
|
self.imp().event.borrow().clone() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async fn download_audio(&self, audio: AudioMessageEventContent, session: &Session) { |
|
|
|
|
let client = session.client(); |
|
|
|
|
let handle = spawn_tokio!(async move { client.media().get_file(audio, true).await }); |
|
|
|
|
|
|
|
|
|
match handle.await.unwrap() { |
|
|
|
|
Ok(Some(data)) => { |
|
|
|
|
// The GStreamer backend doesn't work with input streams so
|
|
|
|
|
// we need to store the file.
|
|
|
|
|
// See: https://gitlab.gnome.org/GNOME/gtk/-/issues/4062
|
|
|
|
|
let (file, _) = gio::File::new_tmp(Option::<String>::None).unwrap(); |
|
|
|
|
file.replace_contents( |
|
|
|
|
&data, |
|
|
|
|
None, |
|
|
|
|
false, |
|
|
|
|
gio::FileCreateFlags::REPLACE_DESTINATION, |
|
|
|
|
gio::Cancellable::NONE, |
|
|
|
|
) |
|
|
|
|
.unwrap(); |
|
|
|
|
self.prepare_audio(file); |
|
|
|
|
} |
|
|
|
|
Ok(None) => { |
|
|
|
|
warn!("Could not retrieve invalid audio file"); |
|
|
|
|
} |
|
|
|
|
Err(error) => { |
|
|
|
|
warn!("Could not retrieve audio file: {error}"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn prepare_audio(&self, file: gio::File) { |
|
|
|
|
let media_file = gtk::MediaFile::for_file(&file); |
|
|
|
|
|
|
|
|
|
media_file.connect_error_notify(clone!(@weak self as obj => move |media_file| { |
|
|
|
|
if let Some(error) = media_file.error() { |
|
|
|
|
warn!("Error reading audio file: {}", error); |
|
|
|
|
} |
|
|
|
|
})); |
|
|
|
|
media_file.connect_ended_notify(clone!(@weak self as obj => move |media_file| { |
|
|
|
|
if media_file.is_ended() { |
|
|
|
|
obj.imp().play_button.set_icon_name("media-playback-start-symbolic"); |
|
|
|
|
} |
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
self.imp().media_file.replace(Some(media_file)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn toggle_play(&self) { |
|
|
|
|
let imp = self.imp(); |
|
|
|
|
|
|
|
|
|
if let Some(media_file) = self.imp().media_file.borrow().as_ref() { |
|
|
|
|
if media_file.is_playing() { |
|
|
|
|
media_file.pause(); |
|
|
|
|
imp.play_button |
|
|
|
|
.set_icon_name("media-playback-start-symbolic"); |
|
|
|
|
} else { |
|
|
|
|
media_file.play(); |
|
|
|
|
imp.play_button |
|
|
|
|
.set_icon_name("media-playback-pause-symbolic"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|