From 3ef980bb1706a7ffa4f67ab7e96e5f3ef74a4d14 Mon Sep 17 00:00:00 2001 From: Frank Becker Date: Fri, 14 May 2021 10:07:32 -0700 Subject: [PATCH] Replace supported image extension list with image detection via MagickImageInfo. (#818) * Replace supported image extension list with image detection via MagickImageInfo. * Change ImageViewer priority to -4. Change VideoViewer priority to -3 and detect audio/video via MediaInfo instead of file extensions. * Make mediaInfo a class static and initialize once. Add some notes about MediaInfo Open and Close. * Remove try/catch from Prepare and let it be handled in caller. If there was an exception due to MediaInfo it would have already occurred in CanHandle. * Upgrade ImageMagick to latest * Only check extension for well known image and animated image types. For other image formats, let ImageMagick try to detect by file content. Upgrade to latest Magick.NET Co-authored-by: Frank Becker --- .../Providers/ImageMagickProvider.cs | 2 +- .../QuickLook.Plugin.ImageViewer/Plugin.cs | 33 +++++-- .../QuickLook.Plugin.ImageViewer.csproj | 12 +-- .../packages.config | 6 +- .../QuickLook.Plugin.VideoViewer/Plugin.cs | 89 ++++++++++--------- 5 files changed, 81 insertions(+), 61 deletions(-) diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/Providers/ImageMagickProvider.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/Providers/ImageMagickProvider.cs index 41a7f4b..c748a2c 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/Providers/ImageMagickProvider.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/AnimatedImage/Providers/ImageMagickProvider.cs @@ -23,7 +23,7 @@ using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using ImageMagick; -using ImageMagick.Formats.Dng; +using ImageMagick.Formats; using QuickLook.Common.Helpers; namespace QuickLook.Plugin.ImageViewer.AnimatedImage.Providers diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs index e7af3c3..13049d2 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/Plugin.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Windows; +using ImageMagick; using QuickLook.Common.Helpers; using QuickLook.Common.Plugin; using QuickLook.Plugin.ImageViewer.AnimatedImage.Providers; @@ -27,19 +28,16 @@ namespace QuickLook.Plugin.ImageViewer { public class Plugin : IViewer { - private static readonly HashSet Formats = new HashSet(new[] + private static readonly HashSet WellKnownImageExtensions = new HashSet(new[] { - ".apng", ".ari", ".arw", ".avif", ".bay", ".bmp", ".cap", ".cr2", ".cr3", ".crw", ".dcr", ".dcs", ".dng", - ".drf", ".eip", ".emf", ".erf", ".exr", ".fff", ".gif", ".hdr", ".heic", ".heif", ".ico", ".icon", ".iiq", - ".jfif", ".jpeg", ".jpg", ".k25", ".kdc", ".mdc", ".mef", ".mos", ".mrw", ".nef", ".nrw", ".obm", ".orf", - ".pbm", ".pef", ".pgm", ".png", ".pnm", ".ppm", ".psd", ".ptx", ".pxn", ".r3d", ".raf", ".raw", ".rw2", - ".rwl", ".rwz", ".sr2", ".srf", ".srw", ".svg", ".tga", ".tif", ".tiff", ".wdp", ".webp", ".wmf", ".x3f" + ".apng", ".bmp", ".gif", ".ico", ".icon", ".jfif", ".jpeg", ".jpg", ".png", ".psd", + ".svg", ".tga", ".tif", ".tiff", ".webp", ".wmf", }); private ImagePanel _ip; private MetaProvider _meta; - public int Priority => 0; + public int Priority => -4; public void Init() { @@ -57,9 +55,28 @@ namespace QuickLook.Plugin.ImageViewer typeof(ImageMagickProvider))); } + private bool IsWellKnownImageExtension(string path) + { + return WellKnownImageExtensions.Contains(Path.GetExtension(path.ToLower())); + } + + private bool IsImageMagickSupported(string path) + { + try + { + return new MagickImageInfo(path).Format != MagickFormat.Unknown; + } + catch + { + return false; + } + } + public bool CanHandle(string path) { - return !Directory.Exists(path) && Formats.Contains(Path.GetExtension(path.ToLower())); + // Only check extension for well known image and animated image types. + // For other image formats, let ImageMagick try to detect by file content. + return !Directory.Exists(path) && (IsWellKnownImageExtension(path) || IsImageMagickSupported(path)); } public void Prepare(string path, ContextObject context) diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj index ade2717..6bfbeac 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj @@ -62,14 +62,14 @@ .\LibAPNG.dll - - ..\..\packages\Magick.NET-Q8-AnyCPU.7.20.0.1\lib\net40\Magick.NET-Q8-AnyCPU.dll + + ..\..\packages\Magick.NET-Q8-AnyCPU.7.23.4\lib\net40\Magick.NET-Q8-AnyCPU.dll - - ..\..\packages\Magick.NET.Core.3.0.0\lib\net40\Magick.NET.Core.dll + + ..\..\packages\Magick.NET.Core.6.2.0\lib\net40\Magick.NET.Core.dll - - ..\..\packages\Magick.NET.SystemWindowsMedia.1.0.2\lib\net40\Magick.NET.SystemWindowsMedia.dll + + ..\..\packages\Magick.NET.SystemWindowsMedia.3.0.9\lib\net40\Magick.NET.SystemWindowsMedia.dll diff --git a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config index 26128e3..46bf5d6 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config +++ b/QuickLook.Plugin/QuickLook.Plugin.ImageViewer/packages.config @@ -1,7 +1,7 @@  - - - + + + \ No newline at end of file diff --git a/QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Plugin.cs index a2c353a..55b8d0c 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Plugin.cs @@ -27,23 +27,19 @@ namespace QuickLook.Plugin.VideoViewer { public class Plugin : IViewer { - private static readonly HashSet Formats = new HashSet(new[] - { - // video - ".3g2", ".3gp", ".3gp2", ".3gpp", ".amv", ".asf", ".avi", ".flv", ".m4v", ".mkv", ".mov", ".mp4", ".mp4v", - ".mpeg", ".mpg", ".mts", ".m2ts", ".mxf", ".ogv", ".qt", ".tp", ".ts", ".vob", ".webm", ".wmv", - // audio - ".3gp", ".aa", ".aac", ".aax", ".act", ".aif", ".aiff", ".amr", ".ape", ".au", ".awb", ".dct", ".dss", ".dvf", - ".flac", ".gsm", ".iklax", ".ivs", ".m4a", ".m4b", ".m4p", ".m4r", ".mka", ".mmf", ".mp3", ".mpc", ".msv", - ".ogg", ".oga", ".mogg", ".opus", ".ra", ".raw", ".rm", ".tta", ".vox", ".wav", ".webm", ".wma", ".wv" - }); - - private ContextObject _context; - private MediaInfo.MediaInfo _mediaInfo; + private static MediaInfo.MediaInfo _mediaInfo; private ViewerPanel _vp; - public int Priority => -10; // make it lower than TextViewer + public int Priority => -3; + + static Plugin() + { + _mediaInfo = new MediaInfo.MediaInfo(Path.Combine( + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), + Environment.Is64BitProcess ? "MediaInfo-x64\\" : "MediaInfo-x86\\")); + _mediaInfo.Option("Cover_Data", "base64"); + } public void Init() { @@ -52,36 +48,46 @@ namespace QuickLook.Plugin.VideoViewer public bool CanHandle(string path) { - return !Directory.Exists(path) && Formats.Contains(Path.GetExtension(path)?.ToLower()); + if (!Directory.Exists(path)) + { + try + { + _mediaInfo.Open(path); + string videoCodec = _mediaInfo.Get(StreamKind.Video, 0, "Format"); + string audioCodec = _mediaInfo.Get(StreamKind.Audio, 0, "Format"); + // Note MediaInfo.Close seems to close the dll and you have to re-create the MediaInfo + // object like in the static class constructor above. Any call to Get methods etc. + // will result in a "Unable to load MediaInfo library" error. + // Ref: https://github.com/MediaArea/MediaInfoLib/blob/master/Source/MediaInfoDLL/MediaInfoDLL.cs + // Pretty sure it doesn't leak when opening another file as the c++ code calls Close on Open + // Ref: https://github.com/MediaArea/MediaInfoLib/blob/master/Source/MediaInfo/MediaInfo_Internal.cpp + if (videoCodec == "Unable to load MediaInfo library") // should not happen + { + return false; + } + if (!string.IsNullOrWhiteSpace(videoCodec) || !string.IsNullOrWhiteSpace(audioCodec)) + { + return true; + } + } + catch + { + // return false; + } + } + + return false; } public void Prepare(string path, ContextObject context) { - _context = context; - - try + string videoCodec = _mediaInfo.Get(StreamKind.Video, 0, "Format"); + if (!string.IsNullOrWhiteSpace(videoCodec)) // video { - _mediaInfo = new MediaInfo.MediaInfo(Path.Combine( - Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), - Environment.Is64BitProcess ? "MediaInfo-x64\\" : "MediaInfo-x86\\")); - _mediaInfo.Option("Cover_Data", "base64"); + int.TryParse(_mediaInfo.Get(StreamKind.Video, 0, "Width"), out var width); + int.TryParse(_mediaInfo.Get(StreamKind.Video, 0, "Height"), out var height); + double.TryParse(_mediaInfo.Get(StreamKind.Video, 0, "Rotation"), out var rotation); - _mediaInfo.Open(path); - } - catch (Exception) - { - _mediaInfo?.Dispose(); - _mediaInfo = null; - } - - context.TitlebarOverlap = true; - - if (_mediaInfo == null || - !string.IsNullOrWhiteSpace(_mediaInfo.Get(StreamKind.General, 0, "VideoCount"))) // video - { - int.TryParse(_mediaInfo?.Get(StreamKind.Video, 0, "Width"), out var width); - int.TryParse(_mediaInfo?.Get(StreamKind.Video, 0, "Height"), out var height); - double.TryParse(_mediaInfo?.Get(StreamKind.Video, 0, "Rotation"), out var rotation); // Correct rotation: on some machine the value "90" becomes "90000" by some reason if (rotation > 360) rotation /= 1e3; @@ -110,6 +116,8 @@ namespace QuickLook.Plugin.VideoViewer context.TitlebarBlurVisibility = false; context.TitlebarColourVisibility = false; } + + context.TitlebarOverlap = true; } public void View(string path, ContextObject context) @@ -127,11 +135,6 @@ namespace QuickLook.Plugin.VideoViewer { _vp?.Dispose(); _vp = null; - - _mediaInfo?.Dispose(); - _mediaInfo = null; - - _context = null; } } }