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 <frank.becker@thoughtexchange.com>
This commit is contained in:
Frank Becker
2021-05-14 10:07:32 -07:00
committed by GitHub
parent 885b3ed53f
commit 3ef980bb17
5 changed files with 81 additions and 61 deletions

View File

@@ -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

View File

@@ -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<string> Formats = new HashSet<string>(new[]
private static readonly HashSet<string> WellKnownImageExtensions = new HashSet<string>(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)

View File

@@ -62,14 +62,14 @@
<Reference Include="LibAPNG">
<HintPath>.\LibAPNG.dll</HintPath>
</Reference>
<Reference Include="Magick.NET-Q8-AnyCPU, Version=7.20.0.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=MSIL">
<HintPath>..\..\packages\Magick.NET-Q8-AnyCPU.7.20.0.1\lib\net40\Magick.NET-Q8-AnyCPU.dll</HintPath>
<Reference Include="Magick.NET-Q8-AnyCPU, Version=7.23.4.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=MSIL">
<HintPath>..\..\packages\Magick.NET-Q8-AnyCPU.7.23.4\lib\net40\Magick.NET-Q8-AnyCPU.dll</HintPath>
</Reference>
<Reference Include="Magick.NET.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=MSIL">
<HintPath>..\..\packages\Magick.NET.Core.3.0.0\lib\net40\Magick.NET.Core.dll</HintPath>
<Reference Include="Magick.NET.Core, Version=6.2.0.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=MSIL">
<HintPath>..\..\packages\Magick.NET.Core.6.2.0\lib\net40\Magick.NET.Core.dll</HintPath>
</Reference>
<Reference Include="Magick.NET.SystemWindowsMedia, Version=1.0.2.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=MSIL">
<HintPath>..\..\packages\Magick.NET.SystemWindowsMedia.1.0.2\lib\net40\Magick.NET.SystemWindowsMedia.dll</HintPath>
<Reference Include="Magick.NET.SystemWindowsMedia, Version=3.0.9.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=MSIL">
<HintPath>..\..\packages\Magick.NET.SystemWindowsMedia.3.0.9\lib\net40\Magick.NET.SystemWindowsMedia.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Magick.NET.Core" version="3.0.0" targetFramework="net462" />
<package id="Magick.NET.SystemWindowsMedia" version="1.0.2" targetFramework="net462" />
<package id="Magick.NET-Q8-AnyCPU" version="7.20.0.1" targetFramework="net462" />
<package id="Magick.NET.Core" version="6.2.0" targetFramework="net462" />
<package id="Magick.NET.SystemWindowsMedia" version="3.0.9" targetFramework="net462" />
<package id="Magick.NET-Q8-AnyCPU" version="7.23.4" targetFramework="net462" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net462" />
</packages>

View File

@@ -27,23 +27,19 @@ namespace QuickLook.Plugin.VideoViewer
{
public class Plugin : IViewer
{
private static readonly HashSet<string> Formats = new HashSet<string>(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;
}
}
}