mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-10-22 11:54:46 +00:00
Support .AppImage
This commit is contained in:
@@ -0,0 +1,241 @@
|
|||||||
|
<UserControl x:Class="QuickLook.Plugin.AppViewer.InfoPanels.AppImageInfoPanel"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:QuickLook.Plugin.AppViewer"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
FontSize="14"
|
||||||
|
UseLayoutRounding="True"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<UserControl.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="GroupBox.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</UserControl.Resources>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="15" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="150" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Image x:Name="image"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Width="120"
|
||||||
|
Height="120"
|
||||||
|
Margin="8"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Opacity="0"
|
||||||
|
RenderOptions.BitmapScalingMode="HighQuality"
|
||||||
|
SnapsToDevicePixels="True"
|
||||||
|
Stretch="Fill"
|
||||||
|
UseLayoutRounding="True">
|
||||||
|
<Image.Style>
|
||||||
|
<Style TargetType="{x:Type Image}">
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Source, ElementName=image}" Value="{x:Null}">
|
||||||
|
<DataTrigger.ExitActions>
|
||||||
|
<BeginStoryboard>
|
||||||
|
<Storyboard>
|
||||||
|
<DoubleAnimation BeginTime="0:0:0"
|
||||||
|
Storyboard.TargetProperty="Opacity"
|
||||||
|
From="0"
|
||||||
|
To="1"
|
||||||
|
Duration="0:0:0.05" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
</DataTrigger.ExitActions>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Image.Style>
|
||||||
|
</Image>
|
||||||
|
<Grid Grid.Row="1" Grid.Column="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="10" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="10" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="10" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.ColumnSpan="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
MaxHeight="60"
|
||||||
|
Padding="3"
|
||||||
|
FontSize="19"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
LineHeight="25"
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="Wrap">
|
||||||
|
<TextBlock.Inlines>
|
||||||
|
<Run x:Name="filename" Text="FilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilename.ext" />
|
||||||
|
</TextBlock.Inlines>
|
||||||
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
<!-- Application Name -->
|
||||||
|
<TextBlock x:Name="applicationNameTitle"
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Application Name" />
|
||||||
|
<TextBlock x:Name="applicationName"
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Searching..."
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<!-- Version -->
|
||||||
|
<TextBlock x:Name="versionTitle"
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Version" />
|
||||||
|
<TextBlock x:Name="version"
|
||||||
|
Grid.Row="4"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Searching..."
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<!-- Architecture -->
|
||||||
|
<TextBlock x:Name="architectureTitle"
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Architecture" />
|
||||||
|
<TextBlock x:Name="architectureName"
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Searching..."
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<!-- Type -->
|
||||||
|
<TextBlock x:Name="typeTitle"
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Type" />
|
||||||
|
<TextBlock x:Name="type"
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Searching..."
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<!-- Terminal -->
|
||||||
|
<TextBlock x:Name="terminalTitle"
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Terminal" />
|
||||||
|
<TextBlock x:Name="terminal"
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Searching..."
|
||||||
|
TextTrimming="CharacterEllipsis"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<!-- Total Size -->
|
||||||
|
<TextBlock x:Name="totalSizeTitle"
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Total Size" />
|
||||||
|
<TextBlock x:Name="totalSize"
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Calculating size..." />
|
||||||
|
<!-- Last Modified -->
|
||||||
|
<TextBlock x:Name="modDateTitle"
|
||||||
|
Grid.Row="9"
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Last Modified" />
|
||||||
|
<TextBlock x:Name="modDate"
|
||||||
|
Grid.Row="9"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Padding="3"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Text="Searching..."
|
||||||
|
TextTrimming="CharacterEllipsis" />
|
||||||
|
<!-- Environment -->
|
||||||
|
<GroupBox x:Name="environmentGroupBox"
|
||||||
|
Grid.Row="10"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Margin="3,3,16,16"
|
||||||
|
Padding="3"
|
||||||
|
Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
|
||||||
|
BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}"
|
||||||
|
Foreground="{DynamicResource WindowTextForegroundAlternative}"
|
||||||
|
Header="Environment"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" Visibility="Collapsed">
|
||||||
|
<ScrollViewer.Resources>
|
||||||
|
<Style TargetType="{x:Type TextBox}">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<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>
|
||||||
|
</ScrollViewer>
|
||||||
|
</GroupBox>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
@@ -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 QuickLook.Common.ExtensionMethods;
|
||||||
|
using QuickLook.Common.Helpers;
|
||||||
|
using QuickLook.Common.Plugin;
|
||||||
|
using QuickLook.Plugin.AppViewer.PackageParsers.AppImage;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
namespace QuickLook.Plugin.AppViewer.InfoPanels;
|
||||||
|
|
||||||
|
public partial class AppImageInfoPanel : UserControl, IAppInfoPanel
|
||||||
|
{
|
||||||
|
private readonly ContextObject _context;
|
||||||
|
|
||||||
|
public AppImageInfoPanel(ContextObject context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
|
||||||
|
DataContext = this;
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
|
||||||
|
applicationNameTitle.Text = TranslationHelper.Get("APP_NAME", translationFile);
|
||||||
|
versionTitle.Text = TranslationHelper.Get("APP_VERSION", translationFile);
|
||||||
|
architectureTitle.Text = TranslationHelper.Get("ARCHITECTURE", translationFile);
|
||||||
|
typeTitle.Text = TranslationHelper.Get("TYPE", translationFile);
|
||||||
|
terminalTitle.Text = TranslationHelper.Get("TERMINAL", translationFile);
|
||||||
|
totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile);
|
||||||
|
modDateTitle.Text = TranslationHelper.Get("LAST_MODIFIED", translationFile);
|
||||||
|
environmentGroupBox.Header = TranslationHelper.Get("ENVIRONMENT", translationFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisplayInfo(string path)
|
||||||
|
{
|
||||||
|
var name = Path.GetFileName(path);
|
||||||
|
filename.Text = string.IsNullOrEmpty(name) ? path : name;
|
||||||
|
|
||||||
|
_ = Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
var size = new FileInfo(path).Length;
|
||||||
|
AppImageInfo appInfo = AppImageParser.Parse(path);
|
||||||
|
var last = File.GetLastWriteTime(path);
|
||||||
|
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
applicationName.Text = appInfo.Name;
|
||||||
|
version.Text = appInfo.Version;
|
||||||
|
architectureName.Text = appInfo.Arch;
|
||||||
|
type.Text = appInfo.Type;
|
||||||
|
terminal.Text = appInfo.Terminal;
|
||||||
|
totalSize.Text = size.ToPrettySize(2);
|
||||||
|
modDate.Text = last.ToString(CultureInfo.CurrentCulture);
|
||||||
|
permissions.ItemsSource = appInfo.Env;
|
||||||
|
|
||||||
|
if (appInfo.HasIcon)
|
||||||
|
{
|
||||||
|
image.Source = appInfo.Logo.ToBitmapSource();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image.Source = new BitmapImage(new Uri("pack://application:,,,/QuickLook.Plugin.AppViewer;component/Resources/linux.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.IsBusy = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
// 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.Drawing;
|
||||||
|
|
||||||
|
namespace QuickLook.Plugin.AppViewer.PackageParsers.AppImage;
|
||||||
|
|
||||||
|
public class AppImageInfo
|
||||||
|
{
|
||||||
|
public string Arch { get; set; }
|
||||||
|
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Exec { get; set; }
|
||||||
|
|
||||||
|
public string Icon { get; set; }
|
||||||
|
|
||||||
|
public Bitmap Logo { get; set; }
|
||||||
|
|
||||||
|
public bool HasIcon => !string.IsNullOrEmpty(Icon) && Logo != null;
|
||||||
|
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
public string Terminal { get; set; }
|
||||||
|
|
||||||
|
public string[] Env { get; set; }
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
namespace QuickLook.Plugin.AppViewer.PackageParsers.AppImage;
|
||||||
|
|
||||||
|
public class AppImageParser
|
||||||
|
{
|
||||||
|
public static AppImageInfo Parse(string path)
|
||||||
|
{
|
||||||
|
var reader = new AppImageReader(path);
|
||||||
|
|
||||||
|
return new AppImageInfo()
|
||||||
|
{
|
||||||
|
Arch = reader.Arch,
|
||||||
|
Version = reader.Version,
|
||||||
|
Name = reader.Name,
|
||||||
|
Exec = reader.Exec,
|
||||||
|
Icon = reader.Icon,
|
||||||
|
Logo = reader.Logo,
|
||||||
|
Type = reader.Type,
|
||||||
|
Terminal = reader.Terminal,
|
||||||
|
Env = reader.Env,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,256 @@
|
|||||||
|
// 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 DiscUtils.SquashFs;
|
||||||
|
using DiscUtils.Streams;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace QuickLook.Plugin.AppViewer.PackageParsers.AppImage;
|
||||||
|
|
||||||
|
public class AppImageReader
|
||||||
|
{
|
||||||
|
public Dictionary<string, Dictionary<string, string>> DesktopEntry { get; private set; } = [];
|
||||||
|
|
||||||
|
public string Arch => DesktopEntry["Desktop Entry"]["X-AppImage-Arch"];
|
||||||
|
|
||||||
|
public string Version => DesktopEntry["Desktop Entry"]["X-AppImage-Version"];
|
||||||
|
|
||||||
|
public string Name => DesktopEntry["Desktop Entry"]["X-AppImage-Name"];
|
||||||
|
|
||||||
|
public string Exec => DesktopEntry["Desktop Entry"]["Exec"];
|
||||||
|
|
||||||
|
public string Icon => DesktopEntry["Desktop Entry"]["Icon"];
|
||||||
|
|
||||||
|
public Bitmap Logo { get; set; }
|
||||||
|
|
||||||
|
public string Type => DesktopEntry["Desktop Entry"]["Type"];
|
||||||
|
|
||||||
|
public string Terminal => DesktopEntry["Desktop Entry"]["Terminal"];
|
||||||
|
|
||||||
|
public string[] Env { get; set; }
|
||||||
|
|
||||||
|
static AppImageReader()
|
||||||
|
{
|
||||||
|
DiscUtils.Complete.SetupHelper.SetupComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppImageReader(Stream stream)
|
||||||
|
{
|
||||||
|
Open(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppImageReader(string path)
|
||||||
|
{
|
||||||
|
using FileStream fs = File.OpenRead(path);
|
||||||
|
Open(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Open(Stream stream)
|
||||||
|
{
|
||||||
|
using SquashFileSystemReader squash = FindSquashFsOffset(stream);
|
||||||
|
ReadFiles(squash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SquashFileSystemReader FindSquashFsOffset(Stream stream)
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[4];
|
||||||
|
|
||||||
|
for (long i = 0; i < stream.Length - 4; i++)
|
||||||
|
{
|
||||||
|
stream.Position = i;
|
||||||
|
stream.ReadExactly(buffer, 0, 4);
|
||||||
|
|
||||||
|
uint magic = BitConverter.ToUInt32(buffer, 0); // little-endian
|
||||||
|
|
||||||
|
if (magic == SuperBlock.SquashFsMagic)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var subStream = new SubStream(stream, i, stream.Length - i);
|
||||||
|
SuperBlock superBlock = new();
|
||||||
|
superBlock.ReadFrom(subStream, superBlock.Size);
|
||||||
|
|
||||||
|
// Supported for ZLib and Xz only
|
||||||
|
if (superBlock.Compression == SquashFileSystemCompressionKind.ZLib
|
||||||
|
|| superBlock.Compression == SquashFileSystemCompressionKind.Xz)
|
||||||
|
{
|
||||||
|
subStream.Position = 0;
|
||||||
|
var squash = new SquashFileSystemReader(subStream);
|
||||||
|
return squash;
|
||||||
|
}
|
||||||
|
else if (Enum.IsDefined(typeof(SquashFileSystemCompressionKind), superBlock.Compression)
|
||||||
|
&& superBlock.Compression != SquashFileSystemCompressionKind.Unknown)
|
||||||
|
{
|
||||||
|
// Unsupported compression
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadFiles(SquashFileSystemReader squash)
|
||||||
|
{
|
||||||
|
byte[] icon = null;
|
||||||
|
Dictionary<string, byte[]> prepareIcons = [];
|
||||||
|
|
||||||
|
foreach (var entry in squash.GetFileSystemEntries(@"\"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(entry);
|
||||||
|
|
||||||
|
if (entry == @"\.DirIcon")
|
||||||
|
continue; // Ignore symlink
|
||||||
|
|
||||||
|
// Cache possible icon files in advance
|
||||||
|
if (entry.EndsWith(".png"))
|
||||||
|
{
|
||||||
|
prepareIcons.Add(entry, squash.ReadBytes(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.EndsWith("AppRun.env"))
|
||||||
|
{
|
||||||
|
string env = squash.ReadString(entry);
|
||||||
|
Env = env?.Split('\n').Where(e => !string.IsNullOrWhiteSpace(e)).ToArray() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.EndsWith(".desktop"))
|
||||||
|
{
|
||||||
|
string desktop = squash.ReadString(entry);
|
||||||
|
DesktopEntry = IniReader.Parse(desktop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DesktopEntry.ContainsKey("Desktop Entry"))
|
||||||
|
{
|
||||||
|
var section = DesktopEntry["Desktop Entry"];
|
||||||
|
|
||||||
|
// Icon Lookup but PNG supported only
|
||||||
|
// https://specifications.freedesktop.org/icon-theme-spec
|
||||||
|
if (section.ContainsKey("Icon"))
|
||||||
|
{
|
||||||
|
string iconEntry = section["Icon"];
|
||||||
|
|
||||||
|
if (prepareIcons.ContainsKey(@$"\{iconEntry}.png"))
|
||||||
|
{
|
||||||
|
icon = prepareIcons[@$"\{iconEntry}.png"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icon == null)
|
||||||
|
{
|
||||||
|
foreach (var entry in squash.GetFileSystemEntries(@$"\usr\share\icons\hicolor\128x128\apps"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (entry == @$"\usr\share\icons\hicolor\128x128\apps\{iconEntry}.png")
|
||||||
|
{
|
||||||
|
prepareIcons.Add(entry, squash.ReadBytes(entry));
|
||||||
|
icon = prepareIcons[@$"\{iconEntry}.png"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icon != null)
|
||||||
|
{
|
||||||
|
using MemoryStream ms = new(icon);
|
||||||
|
Logo = new Bitmap(ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file static class StreamExtension
|
||||||
|
{
|
||||||
|
public static byte[] ReadBytes(this SquashFileSystemReader squash, string path)
|
||||||
|
{
|
||||||
|
using Stream s = squash.OpenFile(path, FileMode.Open);
|
||||||
|
using MemoryStream ms = new();
|
||||||
|
s.CopyTo(ms);
|
||||||
|
byte[] data = ms.ToArray();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadString(this SquashFileSystemReader squash, string path, Encoding encoding = null)
|
||||||
|
{
|
||||||
|
return (encoding ?? Encoding.UTF8).GetString(squash.ReadBytes(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file static class IniReader
|
||||||
|
{
|
||||||
|
public static Dictionary<string, Dictionary<string, string>> Parse(string iniContent)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
Dictionary<string, string> currentSection = null;
|
||||||
|
|
||||||
|
using (var reader = new StringReader(iniContent))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
line = line.Trim();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(line) || line.StartsWith(";") || line.StartsWith("#"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (line.StartsWith("[") && line.EndsWith("]"))
|
||||||
|
{
|
||||||
|
var sectionName = line.Substring(1, line.Length - 2).Trim();
|
||||||
|
currentSection = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
result[sectionName] = currentSection;
|
||||||
|
}
|
||||||
|
else if (currentSection != null)
|
||||||
|
{
|
||||||
|
var separatorIndex = line.IndexOf('=');
|
||||||
|
if (separatorIndex > 0)
|
||||||
|
{
|
||||||
|
var key = line.Substring(0, separatorIndex).Trim();
|
||||||
|
var value = line.Substring(separatorIndex + 1).Trim();
|
||||||
|
currentSection[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@@ -50,6 +50,7 @@ public class Plugin : IViewer
|
|||||||
|
|
||||||
// Ubuntu
|
// Ubuntu
|
||||||
".deb", // Debian Package
|
".deb", // Debian Package
|
||||||
|
".appimage", // AppImage Format
|
||||||
|
|
||||||
// Others
|
// Others
|
||||||
".wgt", ".wgtu", // UniApp Widget
|
".wgt", ".wgtu", // UniApp Widget
|
||||||
@@ -80,6 +81,7 @@ public class Plugin : IViewer
|
|||||||
".msix" or ".msixbundle" or ".appx" or ".appxbundle" => new Size { Width = 560, Height = 328 },
|
".msix" or ".msixbundle" or ".appx" or ".appxbundle" => new Size { Width = 560, Height = 328 },
|
||||||
".deb" => new Size { Width = 600, Height = 345 },
|
".deb" => new Size { Width = 600, Height = 345 },
|
||||||
".dmg" => new Size { Width = 560, Height = 510 },
|
".dmg" => new Size { Width = 560, Height = 510 },
|
||||||
|
".appimage" => new Size { Width = 600, Height = 300 },
|
||||||
".wgt" or ".wgtu" => new Size { Width = 600, Height = 345 },
|
".wgt" or ".wgtu" => new Size { Width = 600, Height = 345 },
|
||||||
_ => throw new NotSupportedException("Extension is not supported."),
|
_ => throw new NotSupportedException("Extension is not supported."),
|
||||||
};
|
};
|
||||||
@@ -102,6 +104,7 @@ public class Plugin : IViewer
|
|||||||
".msix" or ".msixbundle" or ".appx" or ".appxbundle" => new AppxInfoPanel(context),
|
".msix" or ".msixbundle" or ".appx" or ".appxbundle" => new AppxInfoPanel(context),
|
||||||
".deb" => new DebInfoPanel(context),
|
".deb" => new DebInfoPanel(context),
|
||||||
".dmg" => new DmgInfoPanel(context),
|
".dmg" => new DmgInfoPanel(context),
|
||||||
|
".appimage" => new AppImageInfoPanel(context),
|
||||||
".wgt" or ".wgtu" => new WgtInfoPanel(context),
|
".wgt" or ".wgtu" => new WgtInfoPanel(context),
|
||||||
_ => throw new NotSupportedException("Extension is not supported."),
|
_ => throw new NotSupportedException("Extension is not supported."),
|
||||||
};
|
};
|
||||||
|
@@ -77,7 +77,7 @@
|
|||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="ApkReader" Version="2.0.1.1" />
|
<PackageReference Include="ApkReader" Version="2.0.1.1" />
|
||||||
<PackageReference Include="PureSharpCompress" Version="0.39.0" />
|
<PackageReference Include="PureSharpCompress" Version="0.39.0" />
|
||||||
<PackageReference Include="QuickLook.HfsPlus" Version="1.0.0" />
|
<PackageReference Include="QuickLook.DiscUtils" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
BIN
QuickLook.Plugin/QuickLook.Plugin.AppViewer/Resources/linux.png
Normal file
BIN
QuickLook.Plugin/QuickLook.Plugin.AppViewer/Resources/linux.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
@@ -10,6 +10,7 @@
|
|||||||
<PUBLISHER>Publisher</PUBLISHER>
|
<PUBLISHER>Publisher</PUBLISHER>
|
||||||
<CAPABILITIES>Capabilities</CAPABILITIES>
|
<CAPABILITIES>Capabilities</CAPABILITIES>
|
||||||
<APP_NAME>Application</APP_NAME>
|
<APP_NAME>Application</APP_NAME>
|
||||||
|
<APP_VERSION>Version</APP_VERSION>
|
||||||
<APP_VERSION_NAME>Version Name</APP_VERSION_NAME>
|
<APP_VERSION_NAME>Version Name</APP_VERSION_NAME>
|
||||||
<APP_VERSION_CODE>Version Code</APP_VERSION_CODE>
|
<APP_VERSION_CODE>Version Code</APP_VERSION_CODE>
|
||||||
<PERMISSIONS>Permissions</PERMISSIONS>
|
<PERMISSIONS>Permissions</PERMISSIONS>
|
||||||
@@ -27,6 +28,9 @@
|
|||||||
<ARCHITECTURE>Architecture</ARCHITECTURE>
|
<ARCHITECTURE>Architecture</ARCHITECTURE>
|
||||||
<MAINTAINER>Maintainer</MAINTAINER>
|
<MAINTAINER>Maintainer</MAINTAINER>
|
||||||
<DESCRIPTION>Description</DESCRIPTION>
|
<DESCRIPTION>Description</DESCRIPTION>
|
||||||
|
<ENVIRONMENT>Environment</ENVIRONMENT>
|
||||||
|
<TYPE>Type</TYPE>
|
||||||
|
<TERMINAL>Terminal</TERMINAL>
|
||||||
</en>
|
</en>
|
||||||
<pt-BR>
|
<pt-BR>
|
||||||
<PRODUCT_VERSION>Versão do produto</PRODUCT_VERSION>
|
<PRODUCT_VERSION>Versão do produto</PRODUCT_VERSION>
|
||||||
@@ -37,6 +41,7 @@
|
|||||||
<PUBLISHER>Editora</PUBLISHER>
|
<PUBLISHER>Editora</PUBLISHER>
|
||||||
<CAPABILITIES>Recursos</CAPABILITIES>
|
<CAPABILITIES>Recursos</CAPABILITIES>
|
||||||
<APP_NAME>Nome do aplicativo</APP_NAME>
|
<APP_NAME>Nome do aplicativo</APP_NAME>
|
||||||
|
<APP_VERSION>Versão</APP_VERSION>
|
||||||
<APP_VERSION_NAME>Nome da versão</APP_VERSION_NAME>
|
<APP_VERSION_NAME>Nome da versão</APP_VERSION_NAME>
|
||||||
<APP_VERSION_CODE>Código da versão</APP_VERSION_CODE>
|
<APP_VERSION_CODE>Código da versão</APP_VERSION_CODE>
|
||||||
<PERMISSIONS>Permissões</PERMISSIONS>
|
<PERMISSIONS>Permissões</PERMISSIONS>
|
||||||
@@ -54,6 +59,9 @@
|
|||||||
<ARCHITECTURE>Arquitetura</ARCHITECTURE>
|
<ARCHITECTURE>Arquitetura</ARCHITECTURE>
|
||||||
<MAINTAINER>Mantenedor</MAINTAINER>
|
<MAINTAINER>Mantenedor</MAINTAINER>
|
||||||
<DESCRIPTION>Descrição</DESCRIPTION>
|
<DESCRIPTION>Descrição</DESCRIPTION>
|
||||||
|
<ENVIRONMENT>Ambiente</ENVIRONMENT>
|
||||||
|
<TYPE>Tipo</TYPE>
|
||||||
|
<TERMINAL>Terminal</TERMINAL>
|
||||||
</pt-BR>
|
</pt-BR>
|
||||||
<zh-CN>
|
<zh-CN>
|
||||||
<PRODUCT_VERSION>产品版本</PRODUCT_VERSION>
|
<PRODUCT_VERSION>产品版本</PRODUCT_VERSION>
|
||||||
@@ -64,6 +72,7 @@
|
|||||||
<PUBLISHER>发布者</PUBLISHER>
|
<PUBLISHER>发布者</PUBLISHER>
|
||||||
<CAPABILITIES>功能</CAPABILITIES>
|
<CAPABILITIES>功能</CAPABILITIES>
|
||||||
<APP_NAME>应用名称</APP_NAME>
|
<APP_NAME>应用名称</APP_NAME>
|
||||||
|
<APP_VERSION>版本</APP_VERSION>
|
||||||
<APP_VERSION_NAME>版本名称</APP_VERSION_NAME>
|
<APP_VERSION_NAME>版本名称</APP_VERSION_NAME>
|
||||||
<APP_VERSION_CODE>版本号</APP_VERSION_CODE>
|
<APP_VERSION_CODE>版本号</APP_VERSION_CODE>
|
||||||
<PERMISSIONS>权限</PERMISSIONS>
|
<PERMISSIONS>权限</PERMISSIONS>
|
||||||
@@ -81,6 +90,9 @@
|
|||||||
<ARCHITECTURE>架构</ARCHITECTURE>
|
<ARCHITECTURE>架构</ARCHITECTURE>
|
||||||
<MAINTAINER>维护者</MAINTAINER>
|
<MAINTAINER>维护者</MAINTAINER>
|
||||||
<DESCRIPTION>描述</DESCRIPTION>
|
<DESCRIPTION>描述</DESCRIPTION>
|
||||||
|
<ENVIRONMENT>运行环境</ENVIRONMENT>
|
||||||
|
<TYPE>类型</TYPE>
|
||||||
|
<TERMINAL>终端</TERMINAL>
|
||||||
</zh-CN>
|
</zh-CN>
|
||||||
<zh-TW>
|
<zh-TW>
|
||||||
<PRODUCT_VERSION>產品版本</PRODUCT_VERSION>
|
<PRODUCT_VERSION>產品版本</PRODUCT_VERSION>
|
||||||
@@ -91,6 +103,7 @@
|
|||||||
<PUBLISHER>發行者</PUBLISHER>
|
<PUBLISHER>發行者</PUBLISHER>
|
||||||
<CAPABILITIES>功能</CAPABILITIES>
|
<CAPABILITIES>功能</CAPABILITIES>
|
||||||
<APP_NAME>程式名稱</APP_NAME>
|
<APP_NAME>程式名稱</APP_NAME>
|
||||||
|
<APP_VERSION>版本</APP_VERSION>
|
||||||
<APP_VERSION_NAME>版本名稱</APP_VERSION_NAME>
|
<APP_VERSION_NAME>版本名稱</APP_VERSION_NAME>
|
||||||
<APP_VERSION_CODE>版本號</APP_VERSION_CODE>
|
<APP_VERSION_CODE>版本號</APP_VERSION_CODE>
|
||||||
<PERMISSIONS>權限</PERMISSIONS>
|
<PERMISSIONS>權限</PERMISSIONS>
|
||||||
@@ -108,6 +121,9 @@
|
|||||||
<ARCHITECTURE>架構</ARCHITECTURE>
|
<ARCHITECTURE>架構</ARCHITECTURE>
|
||||||
<MAINTAINER>維護者</MAINTAINER>
|
<MAINTAINER>維護者</MAINTAINER>
|
||||||
<DESCRIPTION>描述</DESCRIPTION>
|
<DESCRIPTION>描述</DESCRIPTION>
|
||||||
|
<ENVIRONMENT>執行環境</ENVIRONMENT>
|
||||||
|
<TYPE>類型</TYPE>
|
||||||
|
<TERMINAL>終端機</TERMINAL>
|
||||||
</zh-TW>
|
</zh-TW>
|
||||||
<ja>
|
<ja>
|
||||||
<PRODUCT_VERSION>製品バージョン</PRODUCT_VERSION>
|
<PRODUCT_VERSION>製品バージョン</PRODUCT_VERSION>
|
||||||
@@ -118,6 +134,7 @@
|
|||||||
<PUBLISHER>発行元</PUBLISHER>
|
<PUBLISHER>発行元</PUBLISHER>
|
||||||
<CAPABILITIES>機能</CAPABILITIES>
|
<CAPABILITIES>機能</CAPABILITIES>
|
||||||
<APP_NAME>アプリケーションネーム</APP_NAME>
|
<APP_NAME>アプリケーションネーム</APP_NAME>
|
||||||
|
<APP_VERSION>バージョン</APP_VERSION>
|
||||||
<APP_VERSION_NAME>バージョン名</APP_VERSION_NAME>
|
<APP_VERSION_NAME>バージョン名</APP_VERSION_NAME>
|
||||||
<APP_VERSION_CODE>バージョンコード</APP_VERSION_CODE>
|
<APP_VERSION_CODE>バージョンコード</APP_VERSION_CODE>
|
||||||
<PERMISSIONS>権限</PERMISSIONS>
|
<PERMISSIONS>権限</PERMISSIONS>
|
||||||
@@ -135,6 +152,9 @@
|
|||||||
<ARCHITECTURE>アーキテクチャ</ARCHITECTURE>
|
<ARCHITECTURE>アーキテクチャ</ARCHITECTURE>
|
||||||
<MAINTAINER>メンテナー</MAINTAINER>
|
<MAINTAINER>メンテナー</MAINTAINER>
|
||||||
<DESCRIPTION>説明</DESCRIPTION>
|
<DESCRIPTION>説明</DESCRIPTION>
|
||||||
|
<ENVIRONMENT>実行環境</ENVIRONMENT>
|
||||||
|
<TYPE>種類</TYPE>
|
||||||
|
<TERMINAL>ターミナル</TERMINAL>
|
||||||
</ja>
|
</ja>
|
||||||
<de>
|
<de>
|
||||||
<PRODUCT_VERSION>Produktversion</PRODUCT_VERSION>
|
<PRODUCT_VERSION>Produktversion</PRODUCT_VERSION>
|
||||||
@@ -145,6 +165,7 @@
|
|||||||
<PUBLISHER>Herausgeber</PUBLISHER>
|
<PUBLISHER>Herausgeber</PUBLISHER>
|
||||||
<CAPABILITIES>Funktionen</CAPABILITIES>
|
<CAPABILITIES>Funktionen</CAPABILITIES>
|
||||||
<APP_NAME>Anwendung</APP_NAME>
|
<APP_NAME>Anwendung</APP_NAME>
|
||||||
|
<APP_VERSION>Version</APP_VERSION>
|
||||||
<APP_VERSION_NAME>Bezeichnung der Version</APP_VERSION_NAME>
|
<APP_VERSION_NAME>Bezeichnung der Version</APP_VERSION_NAME>
|
||||||
<APP_VERSION_CODE>Versionscode</APP_VERSION_CODE>
|
<APP_VERSION_CODE>Versionscode</APP_VERSION_CODE>
|
||||||
<PERMISSIONS>Berechtigungen</PERMISSIONS>
|
<PERMISSIONS>Berechtigungen</PERMISSIONS>
|
||||||
@@ -162,5 +183,8 @@
|
|||||||
<ARCHITECTURE>Architektur</ARCHITECTURE>
|
<ARCHITECTURE>Architektur</ARCHITECTURE>
|
||||||
<MAINTAINER>Betreuer</MAINTAINER>
|
<MAINTAINER>Betreuer</MAINTAINER>
|
||||||
<DESCRIPTION>Beschreibung</DESCRIPTION>
|
<DESCRIPTION>Beschreibung</DESCRIPTION>
|
||||||
|
<ENVIRONMENT>Umgebung</ENVIRONMENT>
|
||||||
|
<TYPE>Typ</TYPE>
|
||||||
|
<TERMINAL>Terminal</TERMINAL>
|
||||||
</de>
|
</de>
|
||||||
</Translations>
|
</Translations>
|
||||||
|
Reference in New Issue
Block a user