Support .deb

This commit is contained in:
ema
2025-06-04 16:35:51 +08:00
parent 3bd1239457
commit efe28423e9
13 changed files with 307 additions and 39 deletions

View File

@@ -118,7 +118,7 @@
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Version" />
Text="Version Name" />
<TextBlock x:Name="versionName"
Grid.Row="4"
Grid.Column="2"

View File

@@ -70,14 +70,14 @@
</TextBlock.Inlines>
</TextBlock>
</Grid>
<!-- Application Name -->
<TextBlock x:Name="applicationNameTitle"
<!-- Package Name -->
<TextBlock x:Name="packageNameTitle"
Grid.Row="3"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Application Name" />
<TextBlock x:Name="applicationName"
Text="Package Name" />
<TextBlock x:Name="packageName"
Grid.Row="3"
Grid.Column="2"
Margin="8,0,0,0"
@@ -92,7 +92,7 @@
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Version" />
Text="Version Name" />
<TextBlock x:Name="versionName"
Grid.Row="4"
Grid.Column="2"
@@ -102,14 +102,14 @@
Text="Searching..."
TextTrimming="CharacterEllipsis"
TextWrapping="Wrap" />
<!-- Version Code -->
<TextBlock x:Name="versionCodeTitle"
<!-- Maintainer -->
<TextBlock x:Name="maintainerTitle"
Grid.Row="5"
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Version Code" />
<TextBlock x:Name="versionCode"
Text="Maintainer" />
<TextBlock x:Name="maintainer"
Grid.Row="5"
Grid.Column="2"
Margin="8,0,0,0"
@@ -147,8 +147,8 @@
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Searching..."
TextTrimming="CharacterEllipsis" />
<!-- Permissions -->
<GroupBox x:Name="permissionsGroupBox"
<!-- Description -->
<GroupBox x:Name="descriptionGroupBox"
Grid.Row="8"
Grid.Column="1"
Grid.ColumnSpan="2"
@@ -157,7 +157,7 @@
Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Header="Capabilities">
Header="Description">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<Style TargetType="{x:Type TextBox}">
@@ -167,15 +167,12 @@
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</ScrollViewer.Resources>
<ItemsControl x:Name="permissions">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Margin="8,3,16,3"
IsReadOnly="True"
Text="{Binding ., Mode=OneTime}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBox x:Name="description"
Margin="8,3,16,3"
AcceptsReturn="True"
IsReadOnly="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</ScrollViewer>
</GroupBox>
</Grid>

View File

@@ -40,12 +40,12 @@ public partial class DebInfoPanel : UserControl, IAppInfoPanel
InitializeComponent();
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
applicationNameTitle.Text = TranslationHelper.Get("APP_NAME", translationFile);
packageNameTitle.Text = TranslationHelper.Get("PACKAGE_NAME", translationFile);
versionNameTitle.Text = TranslationHelper.Get("APP_VERSION_NAME", translationFile);
versionCodeTitle.Text = TranslationHelper.Get("APP_VERSION_CODE", translationFile);
maintainerTitle.Text = TranslationHelper.Get("MAINTAINER", translationFile);
totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile);
modDateTitle.Text = TranslationHelper.Get("LAST_MODIFIED", translationFile);
permissionsGroupBox.Header = TranslationHelper.Get("PERMISSIONS", translationFile);
descriptionGroupBox.Header = TranslationHelper.Get("DESCRIPTION", translationFile);
}
public void DisplayInfo(string path)
@@ -58,17 +58,17 @@ public partial class DebInfoPanel : UserControl, IAppInfoPanel
if (File.Exists(path))
{
var size = new FileInfo(path).Length;
DebInfo wgtInfo = DebParser.Parse(path);
DebInfo debInfo = DebParser.Parse(path);
var last = File.GetLastWriteTime(path);
Dispatcher.Invoke(() =>
{
//applicationName.Text = wgtInfo.AppNameLocale ?? wgtInfo.AppName;
//versionName.Text = wgtInfo.AppVersionName;
//versionCode.Text = wgtInfo.AppVersionCode;
//totalSize.Text = size.ToPrettySize(2);
//modDate.Text = last.ToString(CultureInfo.CurrentCulture);
//permissions.ItemsSource = wgtInfo.Permissions;
packageName.Text = debInfo.Package;
versionName.Text = debInfo.Version;
maintainer.Text = debInfo.Maintainer;
totalSize.Text = size.ToPrettySize(2);
modDate.Text = last.ToString(CultureInfo.CurrentCulture);
description.Text = debInfo.Description;
_context.IsBusy = false;
});

View File

@@ -168,7 +168,7 @@
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Version" />
Text="Version Name" />
<TextBlock x:Name="versionName"
Grid.Row="5"
Grid.Column="2"

View File

@@ -119,7 +119,7 @@
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Version" />
Text="Version Name" />
<TextBlock x:Name="versionName"
Grid.Row="4"
Grid.Column="2"

View File

@@ -92,7 +92,7 @@
Grid.Column="1"
Padding="3"
Foreground="{DynamicResource WindowTextForegroundAlternative}"
Text="Version" />
Text="Version Name" />
<TextBlock x:Name="versionName"
Grid.Row="4"
Grid.Column="2"

View File

@@ -1,4 +1,21 @@
using System.Collections.Generic;
// 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.Collections.Generic;
using System.IO;
using System.Text;

View File

@@ -17,6 +17,20 @@
namespace QuickLook.Plugin.AppViewer.PackageParsers.Deb;
/// <summary>
/// https://www.debian.org/doc/debian-policy/ch-controlfields.html
/// </summary>
public class DebInfo
{
public string Package { get; set; }
public string Maintainer { get; set; }
public string Uploaders { get; set; }
public string Version { get; set; }
public string Architecture { get; set; }
public string Description { get; set; }
}

View File

@@ -21,7 +21,51 @@ public static class DebParser
{
public static DebInfo Parse(string path)
{
ArEntry[] ar = ArReader.Read(path);
return new();
DebReader reader = new(path);
DebInfo info = new();
{
if (reader.ControlDict.TryGetValue("Package", out string value))
{
info.Package = value;
}
}
{
if (reader.ControlDict.TryGetValue("Maintainer", out string value))
{
info.Maintainer = value;
}
}
{
if (reader.ControlDict.TryGetValue("Uploaders", out string value))
{
info.Uploaders = value;
}
}
{
if (reader.ControlDict.TryGetValue("Version", out string value))
{
info.Version = value;
}
}
{
if (reader.ControlDict.TryGetValue("Architecture", out string value))
{
info.Architecture = value;
}
}
{
if (reader.ControlDict.TryGetValue("Description", out string value))
{
info.Description = value;
}
}
return info;
}
}

View File

@@ -0,0 +1,185 @@
// 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 PureSharpCompress.Archives;
using PureSharpCompress.Compressors;
using PureSharpCompress.Compressors.BZip2;
using PureSharpCompress.Compressors.Deflate;
using PureSharpCompress.Compressors.Deflate64;
using PureSharpCompress.Compressors.LZMA;
using PureSharpCompress.Compressors.Xz;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace QuickLook.Plugin.AppViewer.PackageParsers.Deb;
public class DebReader
{
public ArEntry[] ArEntries { get; set; } = [];
public string Control { get; set; }
public Dictionary<string, string> ControlDict { get; set; } = [];
public DebReader(string path)
{
Open(path);
}
public void Open(string path)
{
ArEntry[] ar = ArReader.Read(path);
var controlEntry = ar.Where(entry => entry.FileName.StartsWith("control.tar"))
.FirstOrDefault();
if (controlEntry != null)
{
ZipCompressionMethod method = GetCompressionMethodFromFileName(controlEntry.FileName);
string control = ExtractControl(controlEntry.Data, method);
if (!string.IsNullOrWhiteSpace(control))
{
Control = control;
TextReader reader = new StringReader(control);
string line;
while ((line = reader.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
{
if (line.StartsWith(" ") || line.IndexOf(':') == -1)
continue;
var split = line.Split([':'], 2);
ControlDict.Add(split[0], split[1].Trim());
}
}
}
}
}
private static string ExtractControl(byte[] data, ZipCompressionMethod method)
{
using var stream = new MemoryStream(data);
using var decompressedTar = new MemoryStream();
using (var decompressor = CreateDecompressionStream(stream, method))
{
decompressor?.CopyTo(decompressedTar);
}
decompressedTar.Position = 0;
using var archive = ArchiveFactory.Open(decompressedTar);
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory)
{
if (entry.Key == "./control")
{
using var reader = new StreamReader(entry.OpenEntryStream());
string content = reader.ReadToEnd();
return content;
}
}
}
return null;
}
private static ZipCompressionMethod GetCompressionMethodFromFileName(string fileName)
{
fileName = fileName.ToLowerInvariant();
if (fileName.EndsWith(".tar.gz") || fileName.EndsWith(".tgz"))
return ZipCompressionMethod.Deflate;
else if (fileName.EndsWith(".tar.xz"))
return ZipCompressionMethod.Xz;
else if (fileName.EndsWith(".tar.bz2"))
return ZipCompressionMethod.BZip2;
else if (fileName.EndsWith(".tar.lzma"))
return ZipCompressionMethod.LZMA;
else if (fileName.EndsWith(".tar.zst"))
return ZipCompressionMethod.ZStd;
return ZipCompressionMethod.None;
}
private static Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method)
{
switch (method)
{
case ZipCompressionMethod.Deflate:
{
return new DeflateStream(stream, CompressionMode.Decompress);
}
case ZipCompressionMethod.Deflate64:
{
return new Deflate64Stream(stream, CompressionMode.Decompress);
}
case ZipCompressionMethod.BZip2:
{
return new BZip2Stream(stream, CompressionMode.Decompress, false);
}
case ZipCompressionMethod.LZMA:
{
var reader = new BinaryReader(stream);
reader.ReadUInt16(); //LZMA version
var props = new byte[reader.ReadUInt16()];
reader.Read(props, 0, props.Length);
return new LzmaStream(
props,
stream,
stream.Length - 4,
-1
);
}
case ZipCompressionMethod.Xz:
{
return new XZStream(stream);
}
case ZipCompressionMethod.ZStd:
{
// Not supported for ZStd
return default;
}
}
return stream;
}
private enum ZipCompressionMethod
{
None = 0,
Shrink = 1,
Reduce1 = 2,
Reduce2 = 3,
Reduce3 = 4,
Reduce4 = 5,
Explode = 6,
Deflate = 8,
Deflate64 = 9,
BZip2 = 12,
LZMA = 14,
ZStd = 93,
Xz = 95,
PPMd = 98,
WinzipAes = 0x63,
}
}

View File

@@ -49,7 +49,7 @@ public class Plugin : IViewer
//".har", // HarmonyOS Archive
// Ubuntu
//".deb", // Debian Package
".deb", // Debian Package
// Others
".wgt", ".wgtu", // UniApp Widget

View File

@@ -77,6 +77,7 @@
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="ApkReader" Version="2.0.1.1" />
<PackageReference Include="PureSharpCompress" Version="0.39.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -24,6 +24,8 @@
<APP_MIN_API_VERSION>Minimum API Version</APP_MIN_API_VERSION>
<APP_TARGET_API_VERSION>Target API Version</APP_TARGET_API_VERSION>
<COMPILE_SDK_VERSION>Compile SDK Version</COMPILE_SDK_VERSION>
<MAINTAINER>Maintainer</MAINTAINER>
<DESCRIPTION>Description</DESCRIPTION>
</en>
<pt-BR>
<PRODUCT_VERSION>Versão do produto</PRODUCT_VERSION>
@@ -48,6 +50,8 @@
<APP_MIN_API_VERSION>Versão mínima do API</APP_MIN_API_VERSION>
<APP_TARGET_API_VERSION>Versão mínima do API</APP_TARGET_API_VERSION>
<COMPILE_SDK_VERSION>Versão do SDK de compilação</COMPILE_SDK_VERSION>
<MAINTAINER>Mantenedor</MAINTAINER>
<DESCRIPTION>Descrição</DESCRIPTION>
</pt-BR>
<zh-CN>
<PRODUCT_VERSION>产品版本</PRODUCT_VERSION>
@@ -72,6 +76,8 @@
<APP_MIN_API_VERSION>最低支持 API 版本</APP_MIN_API_VERSION>
<APP_TARGET_API_VERSION>目标 API 版本</APP_TARGET_API_VERSION>
<COMPILE_SDK_VERSION>编译 SDK 版本</COMPILE_SDK_VERSION>
<MAINTAINER>维护者</MAINTAINER>
<DESCRIPTION>描述</DESCRIPTION>
</zh-CN>
<zh-TW>
<PRODUCT_VERSION>產品版本</PRODUCT_VERSION>
@@ -96,6 +102,8 @@
<APP_MIN_API_VERSION>最低支援 API 版本</APP_MIN_API_VERSION>
<APP_TARGET_API_VERSION>目標 API 版本</APP_TARGET_API_VERSION>
<COMPILE_SDK_VERSION>編譯 SDK 版本</COMPILE_SDK_VERSION>
<MAINTAINER>維護者</MAINTAINER>
<DESCRIPTION>描述</DESCRIPTION>
</zh-TW>
<ja>
<PRODUCT_VERSION>製品バージョン</PRODUCT_VERSION>
@@ -120,5 +128,7 @@
<APP_MIN_API_VERSION>最小APIバージョン</APP_MIN_API_VERSION>
<APP_TARGET_API_VERSION>ターゲットAPIバージョン</APP_TARGET_API_VERSION>
<COMPILE_SDK_VERSION>コンパイルSDKバージョン</COMPILE_SDK_VERSION>
<MAINTAINER>メンテナー</MAINTAINER>
<DESCRIPTION>説明</DESCRIPTION>
</ja>
</Translations>