metadata: Drop unreliable mime-type
Only use a reliable mime-type. This requires that we load images earlier in the pipe. This also fixes warnings about mime types not being available on network drives like SMB. Fixes #513 Issue #534
This commit is contained in:
parent
40d68e898d
commit
3b11b0534e
8 changed files with 57 additions and 68 deletions
1
news.d/50.alpha/fixed-wrong-image-format
Normal file
1
news.d/50.alpha/fixed-wrong-image-format
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Showing wrong mime type in error message if the file ending for an image is wrong.
|
||||||
|
|
@ -124,7 +124,11 @@ impl UpdateSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_error(&self, err: glycin::ErrorCtx) {
|
pub fn send_error(&self, err: glycin::ErrorCtx) {
|
||||||
if err.unsupported_format().is_some() {
|
if let Some(mime_type) = err.unsupported_format() {
|
||||||
|
let mut metadata = Metadata::default();
|
||||||
|
metadata.set_mime_type(mime_type);
|
||||||
|
|
||||||
|
self.send(DecoderUpdate::Metadata(Box::new(metadata)));
|
||||||
self.send(DecoderUpdate::SpecificError(
|
self.send(DecoderUpdate::SpecificError(
|
||||||
DecoderError::UnsupportedFormat,
|
DecoderError::UnsupportedFormat,
|
||||||
));
|
));
|
||||||
|
|
@ -150,6 +154,7 @@ pub struct Decoder {
|
||||||
enum FormatDecoder {
|
enum FormatDecoder {
|
||||||
Glycin(Glycin),
|
Glycin(Glycin),
|
||||||
Svg(Svg),
|
Svg(Svg),
|
||||||
|
Failed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder {
|
impl Decoder {
|
||||||
|
|
@ -159,7 +164,6 @@ impl Decoder {
|
||||||
/// The renderer should listen to updates from the returned receiver.
|
/// The renderer should listen to updates from the returned receiver.
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
file: gio::File,
|
file: gio::File,
|
||||||
mime_type: Option<String>,
|
|
||||||
tiles: Arc<SharedFrameBuffer>,
|
tiles: Arc<SharedFrameBuffer>,
|
||||||
) -> (Self, mpsc::Receiver<DecoderUpdate>) {
|
) -> (Self, mpsc::Receiver<DecoderUpdate>) {
|
||||||
let (sender, receiver) = mpsc::unbounded();
|
let (sender, receiver) = mpsc::unbounded();
|
||||||
|
|
@ -167,7 +171,26 @@ impl Decoder {
|
||||||
let update_sender = UpdateSender { sender };
|
let update_sender = UpdateSender { sender };
|
||||||
tiles.set_update_sender(update_sender.clone());
|
tiles.set_update_sender(update_sender.clone());
|
||||||
|
|
||||||
let format_decoder = Self::format_decoder(update_sender.clone(), file, mime_type, tiles);
|
log::trace!("Setting up loader");
|
||||||
|
let mut loader = glycin::Loader::new(file);
|
||||||
|
loader.apply_transformations(false);
|
||||||
|
|
||||||
|
let image = match loader.load().await {
|
||||||
|
Ok(image) => image,
|
||||||
|
Err(err) => {
|
||||||
|
update_sender.send_error(err);
|
||||||
|
return (
|
||||||
|
Self {
|
||||||
|
decoder: FormatDecoder::Failed,
|
||||||
|
update_sender,
|
||||||
|
},
|
||||||
|
receiver,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let format_decoder = Self::format_decoder(update_sender.clone(), image, tiles);
|
||||||
|
|
||||||
let decoder = Self {
|
let decoder = Self {
|
||||||
decoder: format_decoder,
|
decoder: format_decoder,
|
||||||
update_sender,
|
update_sender,
|
||||||
|
|
@ -178,20 +201,19 @@ impl Decoder {
|
||||||
|
|
||||||
fn format_decoder(
|
fn format_decoder(
|
||||||
update_sender: UpdateSender,
|
update_sender: UpdateSender,
|
||||||
file: gio::File,
|
image: glycin::Image,
|
||||||
mime_type: Option<String>,
|
|
||||||
tiles: Arc<SharedFrameBuffer>,
|
tiles: Arc<SharedFrameBuffer>,
|
||||||
) -> FormatDecoder {
|
) -> FormatDecoder {
|
||||||
if let Some(mime_type) = mime_type {
|
let mime_type = image.mime_type().to_string();
|
||||||
// Known things we want to match here are
|
|
||||||
// - image/svg+xml
|
|
||||||
// - image/svg+xml-compressed
|
|
||||||
if mime_type.split('+').next() == Some("image/svg") {
|
|
||||||
return FormatDecoder::Svg(Svg::new(file, update_sender, tiles));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FormatDecoder::Glycin(Glycin::new(file, update_sender, tiles))
|
// Known things we want to match here are
|
||||||
|
// - image/svg+xml
|
||||||
|
// - image/svg+xml-compressed
|
||||||
|
if mime_type.split('+').next() == Some("image/svg") {
|
||||||
|
return FormatDecoder::Svg(Svg::new(image, update_sender, tiles));
|
||||||
|
} else {
|
||||||
|
FormatDecoder::Glycin(Glycin::new(image, update_sender, tiles))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request missing tiles
|
/// Request missing tiles
|
||||||
|
|
|
||||||
|
|
@ -42,22 +42,12 @@ impl Drop for Glycin {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Glycin {
|
impl Glycin {
|
||||||
pub fn new(file: gio::File, updater: UpdateSender, tiles: Arc<SharedFrameBuffer>) -> Self {
|
pub fn new(image: glycin::Image, updater: UpdateSender, tiles: Arc<SharedFrameBuffer>) -> Self {
|
||||||
let cancellable = gio::Cancellable::new();
|
let cancellable = image.cancellable();
|
||||||
let cancellable_ = cancellable.clone();
|
|
||||||
|
|
||||||
let (next_frame_send, next_frame_recv) = async_channel::bounded(2);
|
let (next_frame_send, next_frame_recv) = async_channel::bounded(2);
|
||||||
|
|
||||||
updater.clone().spawn_error_handled(async move {
|
updater.clone().spawn_error_handled(async move {
|
||||||
log::trace!("Setting up loader");
|
|
||||||
let mut loader = glycin::Loader::new(file);
|
|
||||||
loader.apply_transformations(false);
|
|
||||||
|
|
||||||
loader.cancellable(cancellable_);
|
|
||||||
|
|
||||||
let image = loader.load().await?;
|
|
||||||
log::trace!("Image info received");
|
|
||||||
|
|
||||||
let mut metadata: Metadata = Metadata::default();
|
let mut metadata: Metadata = Metadata::default();
|
||||||
metadata.set_image_info(image.details().clone());
|
metadata.set_image_info(image.details().clone());
|
||||||
metadata.set_mime_type(image.mime_type().to_string());
|
metadata.set_mime_type(image.mime_type().to_string());
|
||||||
|
|
|
||||||
|
|
@ -61,21 +61,14 @@ impl Drop for Svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Svg {
|
impl Svg {
|
||||||
pub fn new(file: gio::File, updater: UpdateSender, tiles: Arc<SharedFrameBuffer>) -> Self {
|
pub fn new(image: glycin::Image, updater: UpdateSender, tiles: Arc<SharedFrameBuffer>) -> Self {
|
||||||
let current_request: Arc<std::sync::RwLock<Request>> = Default::default();
|
let current_request: Arc<std::sync::RwLock<Request>> = Default::default();
|
||||||
let request_store = current_request.clone();
|
let request_store = current_request.clone();
|
||||||
let cancellable = gio::Cancellable::new();
|
let cancellable = image.cancellable();
|
||||||
let cancellable_ = cancellable.clone();
|
let cancellable_ = cancellable.clone();
|
||||||
let (wakeup, wakeup_resv) = mpsc::unbounded();
|
let (wakeup, wakeup_resv) = mpsc::unbounded();
|
||||||
|
|
||||||
updater.clone().spawn_error_handled(async move {
|
updater.clone().spawn_error_handled(async move {
|
||||||
log::trace!("Setting up SVG loader");
|
|
||||||
let mut loader = glycin::Loader::new(file);
|
|
||||||
|
|
||||||
loader.cancellable(cancellable.clone());
|
|
||||||
|
|
||||||
let image = loader.load().await?;
|
|
||||||
|
|
||||||
let mut metadata: Metadata = Metadata::default();
|
let mut metadata: Metadata = Metadata::default();
|
||||||
metadata.set_image_info(image.details().clone());
|
metadata.set_image_info(image.details().clone());
|
||||||
metadata.set_mime_type(image.mime_type().to_string());
|
metadata.set_mime_type(image.mime_type().to_string());
|
||||||
|
|
|
||||||
|
|
@ -116,25 +116,11 @@ impl Metadata {
|
||||||
self.mime_type.clone()
|
self.mime_type.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unreliable mime type
|
|
||||||
///
|
|
||||||
/// Before the image is successfully loaded, this mime type might be wrong
|
|
||||||
/// since it is not yet based on the more complex mime type detection used
|
|
||||||
/// by glyin.
|
|
||||||
pub fn unreliable_mime_type(&self) -> Option<String> {
|
|
||||||
self.mime_type().or_else(|| {
|
|
||||||
self.file_info
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|x| x.mime_type.as_ref())
|
|
||||||
.map(|x| x.to_string())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format_name(&self) -> Option<String> {
|
pub fn format_name(&self) -> Option<String> {
|
||||||
self.image_info
|
self.image_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|x| x.info_format_name().map(|x| x.to_string()))
|
.and_then(|x| x.info_format_name().map(|x| x.to_string()))
|
||||||
.or_else(|| self.unreliable_mime_type())
|
.or_else(|| self.mime_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alpha_channel(&self) -> Option<bool> {
|
pub fn alpha_channel(&self) -> Option<bool> {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ const ATTRIBUTE_HOST_PATH: &str = "xattr::document-portal.host-path";
|
||||||
pub struct FileInfo {
|
pub struct FileInfo {
|
||||||
pub(super) display_name: glib::GString,
|
pub(super) display_name: glib::GString,
|
||||||
pub(super) file_size: Option<u64>,
|
pub(super) file_size: Option<u64>,
|
||||||
pub(super) mime_type: Option<glib::GString>,
|
|
||||||
pub(super) created: Option<glib::DateTime>,
|
pub(super) created: Option<glib::DateTime>,
|
||||||
pub(super) modified: Option<glib::DateTime>,
|
pub(super) modified: Option<glib::DateTime>,
|
||||||
pub(super) host_path: Option<glib::GString>,
|
pub(super) host_path: Option<glib::GString>,
|
||||||
|
|
@ -50,9 +49,6 @@ impl FileInfo {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
display_name: file_info.display_name(),
|
display_name: file_info.display_name(),
|
||||||
file_size,
|
file_size,
|
||||||
mime_type: file_info
|
|
||||||
.content_type()
|
|
||||||
.and_then(|x| gio::content_type_get_mime_type(&x)),
|
|
||||||
created: file_info.creation_date_time(),
|
created: file_info.creation_date_time(),
|
||||||
modified: file_info.modification_date_time(),
|
modified: file_info.modification_date_time(),
|
||||||
host_path: file_info.attribute_as_string(ATTRIBUTE_HOST_PATH),
|
host_path: file_info.attribute_as_string(ATTRIBUTE_HOST_PATH),
|
||||||
|
|
|
||||||
|
|
@ -32,17 +32,22 @@ impl imp::LpImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set filename after filename changed
|
/// Set filename after filename changed
|
||||||
async fn set_file_changed(&self, file: &gio::File) {
|
async fn set_file_changed(&self, old_file: &gio::File, new_file: &gio::File) {
|
||||||
let obj = self.obj();
|
let obj = self.obj();
|
||||||
let prev_mime_type = obj.metadata().unreliable_mime_type();
|
let old_filename_ext = old_file
|
||||||
|
.basename()
|
||||||
|
.map(|x| x.extension().map(|x| x.to_owned()));
|
||||||
|
let new_filename_ext = new_file
|
||||||
|
.basename()
|
||||||
|
.map(|x| x.extension().map(|x| x.to_owned()));
|
||||||
|
|
||||||
self.file.replace(Some(file.clone()));
|
self.file.replace(Some(new_file.clone()));
|
||||||
self.reload_file_info().await;
|
self.reload_file_info().await;
|
||||||
|
|
||||||
// Reload if mime type changed since other decoding path might be responsible
|
// Reload if file extension changed since other decoding path might be responsible
|
||||||
// now
|
// now
|
||||||
if obj.metadata().unreliable_mime_type() != prev_mime_type {
|
if old_filename_ext != new_filename_ext {
|
||||||
obj.load(file).await;
|
obj.load(new_file).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,6 +228,7 @@ impl imp::LpImage {
|
||||||
obj.load(&file).await;
|
obj.load(&file).await;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
let old_file = file_a.clone();
|
||||||
glib::spawn_future_local(async move {
|
glib::spawn_future_local(async move {
|
||||||
if obj.error().is_some() {
|
if obj.error().is_some() {
|
||||||
// Tmp files might be renamed quickly after creation. In this case
|
// Tmp files might be renamed quickly after creation. In this case
|
||||||
|
|
@ -230,7 +236,7 @@ impl imp::LpImage {
|
||||||
// Therefore reload on name change loading caused an error.
|
// Therefore reload on name change loading caused an error.
|
||||||
obj.load(&file).await;
|
obj.load(&file).await;
|
||||||
} else {
|
} else {
|
||||||
obj.imp().set_file_changed(&file).await;
|
obj.imp().set_file_changed(&old_file, &file).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -365,12 +371,7 @@ impl LpImage {
|
||||||
// Reset background color for reloads
|
// Reset background color for reloads
|
||||||
imp.set_background_color(None);
|
imp.set_background_color(None);
|
||||||
|
|
||||||
let (decoder, decoder_update) = Decoder::new(
|
let (decoder, decoder_update) = Decoder::new(file.clone(), tiles.clone()).await;
|
||||||
file.clone(),
|
|
||||||
self.metadata().unreliable_mime_type(),
|
|
||||||
tiles.clone(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let weak_obj = self.downgrade();
|
let weak_obj = self.downgrade();
|
||||||
glib::spawn_future_local(async move {
|
glib::spawn_future_local(async move {
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ impl LpImagePage {
|
||||||
imp.stack.set_visible_child(&*imp.error_page);
|
imp.stack.set_visible_child(&*imp.error_page);
|
||||||
imp.error_more_info.set_visible(false);
|
imp.error_more_info.set_visible(false);
|
||||||
} else if image.specific_error() == DecoderError::UnsupportedFormat {
|
} else if image.specific_error() == DecoderError::UnsupportedFormat {
|
||||||
let mime_type = image.metadata().unreliable_mime_type().unwrap_or_default();
|
let mime_type = image.metadata().mime_type().unwrap_or_default();
|
||||||
let content_type = gio::content_type_from_mime_type(&mime_type).unwrap_or_default();
|
let content_type = gio::content_type_from_mime_type(&mime_type).unwrap_or_default();
|
||||||
let description = gio::content_type_get_description(&content_type).to_string();
|
let description = gio::content_type_get_description(&content_type).to_string();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue