use std::{cell::Cell, str::FromStr, sync::Mutex};
use gettextrs::gettext;
use gtk::{gio, glib, prelude::*};
use matrix_sdk::attachment::BaseAudioInfo;
use mime::Mime;
pub mod image;
pub mod video;
pub fn filename_for_mime(mime_type: Option<&str>, fallback: Option<mime::Name>) -> String {
let (type_, extension) =
if let Some(mime) = mime_type.and_then(|m| m.parse::<mime::Mime>().ok()) {
let extension =
mime_guess::get_mime_extensions(&mime).map(|extensions| extensions[0].to_owned());
(Some(mime.type_().as_str().to_owned()), extension)
} else {
(fallback.map(|type_| type_.as_str().to_owned()), None)
};
let name = match type_.as_deref() {
Some("image") => gettext("image"),
Some("video") => gettext("video"),
Some("audio") => gettext("audio"),
_ => gettext("file"),
};
extension
.map(|extension| format!("{name}.{extension}"))
.unwrap_or(name)
}
pub struct FileInfo {
pub mime: Mime,
pub filename: String,
pub size: Option<u32>,
}
pub async fn load_file(file: &gio::File) -> Result<(Vec<u8>, FileInfo), glib::Error> {
let attributes: &[&str] = &[
gio::FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
gio::FILE_ATTRIBUTE_STANDARD_SIZE,
];
let info = file
.query_info_future(
&attributes.join(","),
gio::FileQueryInfoFlags::NONE,
glib::Priority::DEFAULT,
)
.await?;
let mime = info
.content_type()
.and_then(|content_type| Mime::from_str(&content_type).ok())
.unwrap_or(mime::APPLICATION_OCTET_STREAM);
let filename = info.display_name().to_string();
let raw_size = info.size();
let size = if raw_size >= 0 {
Some(raw_size as u32)
} else {
None
};
let (data, _) = file.load_contents_future().await?;
Ok((
data.into(),
FileInfo {
mime,
filename,
size,
},
))
}
async fn load_gstreamer_media_info(file: &gio::File) -> Option<gst_pbutils::DiscovererInfo> {
let timeout = gst::ClockTime::from_seconds(15);
let discoverer = gst_pbutils::Discoverer::new(timeout).ok()?;
let (sender, receiver) = futures_channel::oneshot::channel();
let sender = Mutex::new(Cell::new(Some(sender)));
discoverer.connect_discovered(move |_, info, _| {
if let Some(sender) = sender.lock().unwrap().take() {
sender.send(info.clone()).unwrap();
}
});
discoverer.start();
discoverer.discover_uri_async(&file.uri()).ok()?;
let media_info = receiver.await.unwrap();
discoverer.stop();
Some(media_info)
}
pub async fn load_audio_info(file: &gio::File) -> BaseAudioInfo {
let mut info = BaseAudioInfo {
duration: None,
size: None,
};
let Some(media_info) = load_gstreamer_media_info(file).await else {
return info;
};
info.duration = media_info.duration().map(Into::into);
info
}