Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,25 @@ namespace QuickLook.Plugin.ImageViewer.Webview.Lottie;

internal static class LottieDetector
{
public static bool IsVaild(string path)
public static bool IsVaildFile(string path)
{
try
{
var jsonString = File.ReadAllText(path);
return IsVaildContent(jsonString);
}
catch
{
// If any exception occurs, assume it's not a valid Lottie file
}

return false;
}

public static bool IsVaildContent(string jsonString)
{
try
{
// No exception will be thrown here
var jsonLottie = LottieParser.Parse<Dictionary<string, object>>(jsonString);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

using QuickLook.Plugin.ImageViewer.Webview.Lottie;

namespace QuickLook.Plugin.ImageViewer.Webview.Tgs;

internal static class TgsDetector
{
public static bool IsValidFile(string path)
{
try
{
// Extract JSON content from gzipped TGS file
var jsonString = TgsExtractor.GetJsonContent(path);
return IsVaildContent(jsonString);
}
catch
{
// If any exception occurs, assume it's not a valid TGS file
}

return false;
}

public static bool IsVaildContent(string jsonString)
=> LottieDetector.IsVaildContent(jsonString);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

using System.IO;
using System.IO.Compression;
using System.Text;

namespace QuickLook.Plugin.ImageViewer.Webview.Tgs;

internal static class TgsExtractor
{
public static string GetJsonContent(string path)
{
using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
using var reader = new StreamReader(gzipStream, Encoding.UTF8);
return reader.ReadToEnd();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

using Microsoft.Web.WebView2.Core;
using QuickLook.Plugin.ImageViewer.Webview.Svg;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace QuickLook.Plugin.ImageViewer.Webview.Tgs;

public class TgsImagePanel : SvgImagePanel
{
public override void Preview(string path)
{
FallbackPath = Path.GetDirectoryName(path);

ObjectForScripting ??= new TgsScriptHandler(path);

_homePage = _resources["/lottie2html.html"];
NavigateToUri(new Uri("file://quicklook/"));
}

protected override void WebView_WebResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs args)
{
try
{
var requestedUri = new Uri(args.Request.Uri);

if ((requestedUri.Scheme == "https" || requestedUri.Scheme == "http")
&& requestedUri.AbsolutePath.EndsWith(".tgs", StringComparison.OrdinalIgnoreCase))
{
var localPath = Uri.UnescapeDataString($"{requestedUri.Authority}:{requestedUri.AbsolutePath}".Replace('/', '\\'));

if (localPath.StartsWith(_fallbackPath, StringComparison.OrdinalIgnoreCase))
{
if (File.Exists(localPath))
{
var content = TgsExtractor.GetJsonContent(localPath);
byte[] byteArray = Encoding.UTF8.GetBytes(content);
var stream = new MemoryStream(byteArray);
var response = _webView.CoreWebView2.Environment.CreateWebResourceResponse(
stream, 200, "OK",
$"""
Access-Control-Allow-Origin: *
Content-Type: {MimeTypes.GetMimeType()}
"""
);
args.Response = response;
return;
}
}
}
}
catch (Exception e)
{
// We don't need to feel burdened by any exceptions
Debug.WriteLine(e);
}

base.WebView_WebResourceRequested(sender, args);
}
}

[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public sealed class TgsScriptHandler(string path)
{
public string Path { get; } = path;

public async Task<string> GetPath()
{
return await Task.FromResult(new Uri(Path).AbsolutePath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

using QuickLook.Plugin.ImageViewer.Webview.Lottie;
using System.Collections.Generic;
using System.IO;
using System.Windows;

namespace QuickLook.Plugin.ImageViewer.Webview.Tgs;

public class TgsMetaProvider(string path) : IWebMetaProvider
{
private readonly string _path = path;
private Size _size = Size.Empty;

public Size GetSize()
{
if (_size != Size.Empty)
{
return _size;
}

if (!File.Exists(_path))
{
return _size;
}

try
{
var jsonString = TgsExtractor.GetJsonContent(_path);
var jsonLottie = LottieParser.Parse<Dictionary<string, object>>(jsonString);

if (jsonLottie.ContainsKey("w")
&& jsonLottie.ContainsKey("h")
&& double.TryParse(jsonLottie["w"].ToString(), out double width)
&& double.TryParse(jsonLottie["h"].ToString(), out double height))
{
return _size = new Size(width, height);
}
}
catch
{
// That's fine, just return the default size.
}

return new Size(800, 600);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using QuickLook.Plugin.ImageViewer.Webview.Lottie;
using QuickLook.Plugin.ImageViewer.Webview.Svg;
using QuickLook.Plugin.ImageViewer.Webview.Svga;
using QuickLook.Plugin.ImageViewer.Webview.Tgs;
using System;
using System.IO;
using System.Windows;
Expand All @@ -37,7 +38,8 @@ public static bool TryCanHandle(string path)
{
".svg" => SettingHelper.Get("RenderSvgWeb", true, "QuickLook.Plugin.ImageViewer"),
".svga" or ".lottie" => true,
".json" => LottieDetector.IsVaild(path), // Check for Lottie files
".tgs" => TgsDetector.IsValidFile(path), // Check for TGS files
".json" => LottieDetector.IsVaildFile(path), // Check for Lottie files
_ => false,
};
}
Expand All @@ -47,7 +49,7 @@ public static bool TryPrepare(string path, ContextObject context, out IWebMetaPr
string ext = Path.GetExtension(path).ToLower();

if (ext == ".svg" || ext == ".svga"
|| ext == ".lottie" || ext == ".json")
|| ext == ".lottie" || ext == ".tgs" || ext == ".json")
{
if (ext == ".svg")
{
Expand All @@ -63,6 +65,7 @@ public static bool TryPrepare(string path, ContextObject context, out IWebMetaPr
".svg" => new SvgMetaProvider(path),
".svga" => new SvgaMetaProvider(path),
".lottie" or ".json" => new LottieMetaProvider(path),
".tgs" => new TgsMetaProvider(path),
_ => throw new NotSupportedException($"Unsupported file type: {ext}")
};
var sizeSvg = metaWeb.GetSize();
Expand All @@ -85,7 +88,8 @@ public static bool TryView(string path, ContextObject context, IWebMetaProvider
string ext = Path.GetExtension(path).ToLower();

if (ext == ".svg" || ext == ".svga"
|| ext == ".lottie" || ext == ".json")
|| ext == ".lottie" || ext == ".json"
|| ext == ".tgs")
{
if (ext == ".svg")
{
Expand All @@ -101,6 +105,7 @@ public static bool TryView(string path, ContextObject context, IWebMetaProvider
".svg" => new SvgImagePanel(),
".svga" => new SvgaImagePanel(metaWeb),
".lottie" or ".json" => new LottieImagePanel(),
".tgs" => new TgsImagePanel(),
_ => throw new NotSupportedException($"Unsupported file type: {ext}")
};

Expand Down
3 changes: 3 additions & 0 deletions SUPPORTED_FORMATS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- `.jxl` (JPEG XL)
- `.jxr` (JPEG XR)
- `.k25`, `.kdc` (Kodak RAW image)
- `.lottie` (Lottie animation)
- `.mdc` (MagicDraw UML)
- `.mef` (Mamiya RAW image)
- `.mos` (Leaf RAW image)
Expand All @@ -60,7 +61,9 @@
- `.rw2`, `.rwl`, `.rwz` (Panasonic/Leica RAW image)
- `.sr2`, `.srf`, `.srw` (Sony RAW image)
- `.svg`, `.svgz` (Scalable Vector Graphics)
- `.svga` (SVGA animation)
- `.tga` (Truevision TGA)
- `.tgs` (Telegram sticker, Lottie animation)
- `.tif`, `.tiff` (Tagged Image File Format)
- `.wdp` (Windows Media Photo)
- `.webp` (WebP image)
Expand Down
Loading