Compare commits

...

6 Commits

Author SHA1 Message Date
ema
e4e014d9d7 Refactor Lottie and TGS file validation methods
Renamed and split validation methods for Lottie and TGS files to improve clarity and code reuse. Added IsVaildContent for both detectors and updated WebHandler to use the new method names.
2025-09-22 04:56:16 +08:00
copilot-swe-agent[bot]
cafd2413be Remove TgsExtractor.cs from Lottie folder
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
2025-09-20 20:47:00 +00:00
copilot-swe-agent[bot]
0b0e9e9a7a Refactor TGS support into separate Tgs folder structure
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
2025-09-20 20:27:55 +00:00
copilot-swe-agent[bot]
2ca513f2ab Update documentation to include TGS and Lottie formats
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
2025-09-20 17:12:18 +00:00
copilot-swe-agent[bot]
e7a65387b5 Implement TGS support for Telegram stickers
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
2025-09-20 17:09:05 +00:00
copilot-swe-agent[bot]
dd04a18c2c Initial plan 2025-09-20 17:04:27 +00:00
7 changed files with 254 additions and 4 deletions

View File

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

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -20,6 +20,7 @@ using QuickLook.Common.Plugin;
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;
@@ -37,7 +38,8 @@ internal static class WebHandler
{
".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,
};
}
@@ -47,7 +49,7 @@ internal static class WebHandler
string ext = Path.GetExtension(path).ToLower();
if (ext == ".svg" || ext == ".svga"
|| ext == ".lottie" || ext == ".json")
|| ext == ".lottie" || ext == ".tgs" || ext == ".json")
{
if (ext == ".svg")
{
@@ -63,6 +65,7 @@ internal static class WebHandler
".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();
@@ -85,7 +88,8 @@ internal static class WebHandler
string ext = Path.GetExtension(path).ToLower();
if (ext == ".svg" || ext == ".svga"
|| ext == ".lottie" || ext == ".json")
|| ext == ".lottie" || ext == ".json"
|| ext == ".tgs")
{
if (ext == ".svg")
{
@@ -101,6 +105,7 @@ internal static class WebHandler
".svg" => new SvgImagePanel(),
".svga" => new SvgaImagePanel(metaWeb),
".lottie" or ".json" => new LottieImagePanel(),
".tgs" => new TgsImagePanel(),
_ => throw new NotSupportedException($"Unsupported file type: {ext}")
};

View File

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