Online installer implementation finished

This commit is contained in:
Josef Nemec 2021-07-06 17:27:49 +02:00
parent da3114590b
commit c3dc70ac9b
22 changed files with 705 additions and 326 deletions

View File

@ -11,7 +11,7 @@ environment:
LicensedDependenciesUrl:
secure: ZTSg1DK6QAtwZoCBUDQ94cIaMCKWgSeJMI0dCpVJBPE40+UqG9MNu+UJjjGWjvVa
PlayniteConfigUpdate: https://playnite.link/build/PlayniteProductionConfig.txt
OnlineInstallerConfig: https://playnite.link/build/OnlineInstaller.json
OnlineInstallerConfig: https://playnite.link/build/installer_mirrors.txt
build_script:
- pwsh: .\build\build.ps1 -Package -SdkNuget -LicensedDependenciesUrl $env:LicensedDependenciesUrl -PlayniteConfigUpdate $env:PlayniteConfigUpdate -OnlineInstallerConfig $env:OnlineInstallerConfig
test: off

View File

@ -95,10 +95,10 @@ if (!$SkipBuild)
if ($OnlineInstallerConfig)
{
Write-OperationLog "Updating online installer config..."
$locaConfigPath = "..\source\Tools\PlayniteInstaller\config.json"
$locaConfigPath = "..\source\Tools\PlayniteInstaller\installer_mirrors.txt"
if ($OnlineInstallerConfig.StartsWith("http"))
{
$configFile = Join-Path $env:TEMP "onlineconfig.json"
$configFile = Join-Path $env:TEMP "installer_mirrors.txt"
Invoke-WebRequest $OnlineInstallerConfig -OutFile $locaConfigPath
}
else
@ -195,21 +195,6 @@ if ($SdkNuget)
& .\buildSdkNuget.ps1 -SkipBuild -OutputPath $OutputDir | Out-Null
}
# -------------------------------------------
# Merge online installer
# -------------------------------------------
Write-OperationLog "Merging online installer..."
$ilMerge = (Get-ChildItem "..\source\packages" -Filter "ILMerge.exe" -Recurse | Select -First 1).FullName
$installerOutPath = Join-Path $OutputDir "PlayniteInstaller.exe"
$onlineInstallerDir = Join-Path $OutputDir "Installer"
$mergeRes = StartAndWait $ilMerge "PlayniteInstaller.exe *.dll /out:`"$installerOutPath`" /ndebug /wildcards" -WorkingDir $onlineInstallerDir
if ($mergeRes -ne 0)
{
throw "ILMerge of installer files failed."
}
Remove-Item $onlineInstallerDir -Recurse
# -------------------------------------------
# Build zip package
# -------------------------------------------

View File

@ -23,6 +23,6 @@
<MSBuild
Projects="../source/Tools/PlayniteInstaller/PlayniteInstaller.csproj"
Targets="Build"
Properties="OutputPath=$(OutputPath)/Installer; Configuration=$(Configuration); AllowedReferenceRelatedFileExtensions=none; Platform=AnyCPU" />
Properties="OutputPath=$(OutputPath); Configuration=$(Configuration); AllowedReferenceRelatedFileExtensions=none; Platform=AnyCPU" />
</Target>
</Project>

View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
</startup>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
</startup>
<runtime>
<loadFromRemoteSources enabled="True" />
</runtime>
</configuration>

View File

@ -1,8 +1,9 @@
<Application x:Class="PlayniteInstaller.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
Startup="Application_Startup"
Exit="Application_Exit">
<Application.Resources>
<ResourceDictionary Source="Theme/Classic.xaml"/>
<ResourceDictionary Source="Classic.xaml"/>
</Application.Resources>
</Application>

View File

@ -1,5 +1,5 @@
using Playnite.SDK;
using PlayniteInstaller.ViewModels;
using Playnite.Common;
using Playnite.SDK;
using System;
using System.Collections.Generic;
using System.Configuration;
@ -19,6 +19,7 @@ namespace PlayniteInstaller
private static ILogger logger;
public static string TempDir => Path.Combine(Path.GetTempPath(), "PlayniteInstaller");
public static string InstallerDownloadPath => Path.Combine(Path.GetTempPath(), "PlayniteInstaller", "installer.exe");
private static Version currentVersion;
public static Version CurrentVersion
@ -37,10 +38,11 @@ namespace PlayniteInstaller
private void Application_Startup(object sender, StartupEventArgs e)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
NLogLogger.ConfigureLogger();
LogManager.Init(new NLogLogProvider());
FileSystem.CreateDirectory(App.TempDir);
LogManager.Initialize(Path.Combine(App.TempDir, "installer.log"));
logger = LogManager.GetLogger();
logger.Debug($"Installer started {CurrentVersion}");
var window = new MainWindow();
window.DataContext = new MainViewModel(window);
window.ShowDialog();
@ -49,6 +51,16 @@ namespace PlayniteInstaller
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
logger.Error((Exception)e.ExceptionObject, "Unhandled exception occured.");
MessageBox.Show(
"Unrecoverable error occured and installer will now close.",
"Critical error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
private void Application_Exit(object sender, ExitEventArgs e)
{
LogManager.Dispose();
}
}
}

View File

@ -283,5 +283,80 @@
<Setter Property="FontSize" Value="14" />
<Setter Property="BorderBrush" Value="{DynamicResource PopupBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource PopupBorderThickness}" />
</Style>
</Style>
<Style x:Key="ExpanderDownHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="4" x:Name="CollapsedIcon" FontFamily="Marlett"
VerticalAlignment="Center" HorizontalAlignment="Center"
Style="{DynamicResource BaseTextBlockStyle}"
Foreground="{DynamicResource GlyphBrush}" FontSize="18" MinWidth="20" Margin="0,0,5,0"
Grid.Column="0" TextAlignment="Center" />
<TextBlock Text="6" x:Name="ExpandedIcon" FontFamily="Marlett"
VerticalAlignment="Center" HorizontalAlignment="Center"
Style="{DynamicResource BaseTextBlockStyle}"
Foreground="{DynamicResource GlyphBrush}" FontSize="18" MinWidth="20" Margin="0,0,5,0"
Visibility="Collapsed"
Grid.Column="0" TextAlignment="Center"/>
<ContentPresenter Grid.Column="1" VerticalAlignment="Center"
SnapsToDevicePixels="True" HorizontalAlignment="Stretch" RecognizesAccessKey="True" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Visibility" TargetName="ExpandedIcon" Value="Visible" />
<Setter Property="Visibility" TargetName="CollapsedIcon" Value="Collapsed" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.75" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<DockPanel>
<ToggleButton BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
Style="{DynamicResource ExpanderDownHeaderStyle}"
Padding="{TemplateBinding Padding}"
MinHeight="25" DockPanel.Dock="Top"
Background="{TemplateBinding Background}"
Content="{TemplateBinding Header}"
IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" />
<Border x:Name="Border" DockPanel.Dock="Top" Visibility="Collapsed"
BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter Focusable="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter Property="Visibility" TargetName="Border" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace PlayniteInstaller
{
public class InstallStatusToVisibilityConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (InstallStatus)value == (InstallStatus)parameter ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
public class BooleanToVisibilityConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return Visibility.Collapsed;
}
return ((bool)value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Visibility)value) == Visibility.Visible ? true : false;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
public class InvertedBooleanToVisibilityConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = (bool)value;
return boolValue ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Playnite.SDK
{
public class Logger : ILogger
{
private readonly string loggerName;
public Logger(string loggerName)
{
this.loggerName = loggerName;
}
public void Debug(string message)
{
WriteMessage("|DEBUG: " + message);
}
public void Debug(Exception exception, string message)
{
WriteMessage(exception, "|DEBUG: " + message);
}
public void Error(string message)
{
WriteMessage("|ERROR: " + message);
}
public void Error(Exception exception, string message)
{
WriteMessage(exception, "|ERROR: " + message);
}
public void Info(string message)
{
WriteMessage("|INFO: " + message);
}
public void Info(Exception exception, string message)
{
WriteMessage(exception, "|INFO: " + message);
}
public void Trace(string message)
{
WriteMessage("|TRACE: " + message);
}
public void Trace(Exception exception, string message)
{
WriteMessage(exception, "|TRACE: " + message);
}
public void Warn(string message)
{
WriteMessage("|WARN: " + message);
}
public void Warn(Exception exception, string message)
{
WriteMessage(exception, "|WARN: " + message);
}
private void WriteMessage(string message)
{
LogManager.WriteMessage(loggerName + message);
}
private void WriteMessage(Exception exception, string message)
{
LogManager.WriteMessage(loggerName + message);
LogManager.WriteMessage(exception.ToString());
}
}
public class LogManager
{
private static FileStream logStream;
private static StreamWriter logWriter;
private static object writeLock = new object();
public static void Initialize(string filePath)
{
logStream = new FileStream(filePath, FileMode.Append, FileAccess.Write);
logWriter = new StreamWriter(logStream) { AutoFlush = true };
}
public static void Dispose()
{
logWriter.Dispose();
logStream.Dispose();
}
public static void WriteMessage(string message)
{
lock (writeLock)
{
logWriter.WriteLine(DateTime.Now.ToLongTimeString() + "|" + message);
}
if (Debugger.IsAttached)
{
Console.WriteLine(message);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static ILogger GetLogger()
{
var className = (new StackFrame(1)).GetMethod().DeclaringType.Name;
return GetLogger(className);
}
public static ILogger GetLogger(string loggerName)
{
return new Logger(loggerName);
}
}
}

View File

@ -0,0 +1,286 @@
using Playnite.Common;
using Playnite.SDK;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
namespace PlayniteInstaller
{
public enum InstallStatus
{
Idle,
Downloading,
Installing
}
public class MainViewModel : ObservableObject
{
private static readonly ILogger logger = LogManager.GetLogger();
private readonly Window windowHost;
private readonly List<string> UrlMirrors;
private WebClient webClient;
private InstallStatus status;
public InstallStatus Status
{
get => status;
set
{
status = value;
OnPropertyChanged();
}
}
private double progressValue;
public double ProgressValue
{
get => progressValue;
set
{
progressValue = value;
OnPropertyChanged();
}
}
private string destinationFolder;
public string DestionationFolder
{
get => destinationFolder;
set
{
destinationFolder = value;
OnPropertyChanged();
}
}
private bool portable;
public bool Portable
{
get => portable;
set
{
portable = value;
OnPropertyChanged();
}
}
public RelayCommand<object> BrowseCommand
{
get => new RelayCommand<object>((_) =>
{
Browse();
});
}
public RelayCommand<object> InstallCommand
{
get => new RelayCommand<object>(async (_) =>
{
await Install();
}, (_) => Status == InstallStatus.Idle);
}
public RelayCommand<object> CancelCommand
{
get => new RelayCommand<object>((_) =>
{
Cancel();
}, (_) => Status != InstallStatus.Installing);
}
public static RelayCommand<string> NavigateUrlCommand
{
get => new RelayCommand<string>((url) =>
{
try
{
Process.Start(url);
}
catch (Exception e) when (!Debugger.IsAttached)
{
logger.Error(e, "Failed to open url.");
}
});
}
private List<string> ParseList(string input)
{
return input.Split(new[] { '\r', '\n' }).Where(a => !string.IsNullOrWhiteSpace(a)).ToList();
}
public MainViewModel(Window window)
{
windowHost = window;
DestionationFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Playnite");
UrlMirrors = ParseList(Resources.ReadFileFromResource("PlayniteInstaller.installer_mirrors.txt"));
logger.Debug("Server mirrors in use:");
UrlMirrors.ForEach(a => logger.Debug(a));
}
public void Browse()
{
var dialog = new FolderBrowserDialog()
{
Description = "Select Destination Folder...",
ShowNewFolderButton = true
};
if (dialog.ShowDialog() == DialogResult.OK)
{
DestionationFolder = Path.Combine(dialog.SelectedPath, "Playnite");
}
}
public async Task Install()
{
if (!FileSystem.CanWriteToFolder(DestionationFolder))
{
System.Windows.MessageBox.Show(
"Can't install Playnite to selected directory, use different path.",
"Access error",
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
Status = InstallStatus.Downloading;
try
{
FileSystem.DeleteFile(App.InstallerDownloadPath);
if (webClient != null)
{
webClient.Dispose();
webClient = null;
}
webClient = new WebClient();
var installerUrls = await TryDownloadManifest(UrlMirrors);
webClient.DownloadProgressChanged += WebClient_DownloadProgressChanged;
if (await TryDownloadInstaller(installerUrls) == false)
{
return;
}
Status = InstallStatus.Installing;
var args = string.Format(@"/VERYSILENT /NOCANCEL /DIR=""{0}"" ", DestionationFolder);
args += Portable ? "/PORTABLE" : "";
logger.Info($"Starting:\n{App.InstallerDownloadPath}\n{args}");
await Task.Run(() =>
{
using (var process = Process.Start(App.InstallerDownloadPath, args))
{
process.WaitForExit();
if (process.ExitCode != 0)
{
logger.Error($"Installer failed {process.ExitCode}");
System.Windows.MessageBox.Show(
$"Failed to install Playnite. Error code {process.ExitCode}",
"Installation error",
MessageBoxButton.OK, MessageBoxImage.Error);
Status = InstallStatus.Idle;
return;
}
}
});
var playnitePath = Path.Combine(DestionationFolder, "Playnite.DesktopApp.exe");
if (File.Exists(playnitePath))
{
Process.Start(playnitePath);
}
windowHost.Close();
}
catch (Exception e) when (!Debugger.IsAttached)
{
logger.Error(e, "Failed to download and install Playnite.");
System.Windows.MessageBox.Show(
$"Failed to install Playnite:\n{e.Message}",
"Installation error",
MessageBoxButton.OK, MessageBoxImage.Error);
Status = InstallStatus.Idle;
}
finally
{
webClient?.Dispose();
webClient = null;
}
}
private async Task<List<string>> TryDownloadManifest(List<string> urls)
{
foreach (var url in urls)
{
try
{
return ParseList(await webClient.DownloadStringTaskAsync(url));
}
catch (Exception e)
{
logger.Error(e, $"Failed to download installer manifest from {url}");
}
}
throw new Exception("Failed to download installer manifest.");
}
private async Task<bool> TryDownloadInstaller(List<string> urls)
{
foreach (var url in urls)
{
try
{
await webClient.DownloadFileTaskAsync(url, App.InstallerDownloadPath);
return true;
}
catch (WebException webExp)
{
if (webExp.Status == WebExceptionStatus.RequestCanceled)
{
return false;
}
else
{
logger.Error(webExp, $"Failed to download installer file from {url}");
}
}
catch (Exception e)
{
logger.Error(e, $"Failed to download installer file from {url}");
}
}
throw new Exception("Failed to download installer file.");
}
private void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
ProgressValue = e.ProgressPercentage;
}
public void Cancel()
{
if (Status == InstallStatus.Downloading)
{
webClient.CancelAsync();
webClient.Dispose();
webClient = null;
Status = InstallStatus.Idle;
}
else
{
windowHost.Close();
}
}
}
}

View File

@ -3,53 +3,88 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:PlayniteInstaller"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
Background="#252422"
Title="Playnite Installer" Width="600" SizeToContent="Height">
<Grid Margin="10">
<Grid.Resources>
<StackPanel Margin="10">
<StackPanel.Resources>
<local:InstallStatusToVisibilityConverter x:Key="InstallStatusToVisibilityConverter" />
<Style TargetType="TextBlock" BasedOn="{StaticResource BaseTextBlockStyle}" />
</Grid.Resources>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
</StackPanel.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock FontSize="20" VerticalAlignment="Center" Margin="15,10,10,0">
<TextBlock.Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource BaseTextBlockStyle}">
<Setter Property="Text" Value="Install Playnite" />
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="{x:Static local:InstallStatus.Downloading}">
<Setter Property="Text" Value="Downloading..." />
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="{x:Static local:InstallStatus.Installing}">
<Setter Property="Text" Value="Installing..." />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="Install Playnite" FontSize="20"
VerticalAlignment="Center" Grid.Row="0" Margin="15,10,10,10"/>
<DockPanel Grid.Row="1" Margin="5">
<TextBlock Text="Destionation Folder" DockPanel.Dock="Left" Margin="10,5,10,5" />
<Button Content="Browse..." DockPanel.Dock="Right"
<Expander Header="Options" Margin="10" FontSize="{StaticResource FontSize}"
Visibility="{Binding Status, Converter={StaticResource InstallStatusToVisibilityConverter}, ConverterParameter={x:Static local:InstallStatus.Idle}}">
<StackPanel>
<DockPanel Margin="5">
<TextBlock Text="Destionation Folder" DockPanel.Dock="Left" Margin="10,5,10,5" />
<Button Content="Browse..." DockPanel.Dock="Right"
Command="{Binding BrowseCommand}"
Padding="10,0,10,0" Margin="10,0,10,0"/>
<TextBox DockPanel.Dock="Left" VerticalContentAlignment="Center"
<TextBox DockPanel.Dock="Left" VerticalContentAlignment="Center"
Text="{Binding DestionationFolder}" />
</DockPanel>
<CheckBox Content="Portable" Grid.Row="2" HorizontalAlignment="Left"
IsChecked="{Binding Portable}"
ToolTipService.InitialShowDelay="0"
ToolTip="Installs Playnite in Portable mode.&#x0a;All configuration files and game database will be stored in program folder."
Margin="15,10,10,10"/>
</DockPanel>
<StackPanel Orientation="Horizontal" Grid.Row="3" HorizontalAlignment="Right"
Margin="20,0,15,10">
<Button Content="Install" Padding="20,5,20,5" Margin="0,0,10,0"
IsDefault="True"
Command="{Binding InstallCommand}"/>
<CheckBox Content="Portable" HorizontalAlignment="Left"
IsChecked="{Binding Portable}"
ToolTipService.InitialShowDelay="0"
ToolTip="Installs Playnite in Portable mode.&#x0a;All configuration files and game database will be stored in application's folder."
Margin="15,10,10,10"/>
</StackPanel>
</Expander>
<ProgressBar HorizontalAlignment="Stretch" Height="25" Margin="15,10,15,10"
Minimum="0" Maximum="100" Value="{Binding ProgressValue}">
<ProgressBar.Style>
<Style TargetType="ProgressBar" BasedOn="{StaticResource {x:Type ProgressBar}}">
<Setter Property="IsIndeterminate" Value="False" />
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="{x:Static local:InstallStatus.Installing}">
<Setter Property="IsIndeterminate" Value="True" />
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="{x:Static local:InstallStatus.Downloading}">
<Setter Property="IsIndeterminate" Value="False" />
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Style>
</ProgressBar>
<DockPanel Margin="15,0,15,10">
<Button Content="Cancel" Padding="20,5,20,5"
IsCancel="True"
IsCancel="True" DockPanel.Dock="Right"
Command="{Binding CancelCommand}"/>
</StackPanel>
</Grid>
<Button Content="Install" Padding="20,5,20,5" Margin="0,0,10,0"
IsDefault="True" DockPanel.Dock="Right"
Command="{Binding InstallCommand}"/>
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center">
<Hyperlink Command="{Binding NavigateUrlCommand}"
CommandParameter="https://github.com/JosefNemec/Playnite/wiki/Installation-troubleshooting">
<Run Text="Having issues?" />
</Hyperlink>
</TextBlock>
</DockPanel>
</StackPanel>
</Window>

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlayniteInstaller
{
public class AppConfig
{
public List<string> ServerMirrors { get; set; }
}
}

View File

@ -1,112 +0,0 @@
using NLog;
using NLog.Config;
using NLog.Targets;
using Playnite.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlayniteInstaller
{
public class NLogLogger : Playnite.SDK.ILogger
{
private NLog.Logger logger;
public NLogLogger(string loggerName)
{
logger = NLog.LogManager.GetLogger(loggerName);
}
public void Debug(string message)
{
logger.Debug(message);
}
public void Debug(Exception exception, string message)
{
logger.Debug(exception, message);
}
public void Error(string message)
{
logger.Error(message);
}
public void Error(Exception exception, string message)
{
logger.Error(exception, message);
}
public void Info(string message)
{
logger.Info(message);
}
public void Info(Exception exception, string message)
{
logger.Info(exception, message);
}
public void Warn(string message)
{
logger.Warn(message);
}
public void Warn(Exception exception, string message)
{
logger.Warn(exception, message);
}
public void Trace(string message)
{
logger.Trace(message);
}
public void Trace(Exception exception, string message)
{
logger.Trace(exception, message);
}
public static void ConfigureLogger()
{
var config = new LoggingConfiguration();
#if DEBUG
var consoleTarget = new ColoredConsoleTarget()
{
Layout = @"${message}${onexception:${newline}${exception}}"
};
config.AddTarget("console", consoleTarget);
var rule1 = new LoggingRule("*", LogLevel.Debug, consoleTarget);
config.LoggingRules.Add(rule1);
#endif
FileSystem.CreateDirectory(App.TempDir);
var fileTarget = new FileTarget()
{
FileName = Path.Combine(App.TempDir, "installer.log"),
Layout = "${date:format=dd-MM HH\\:mm\\:ss.fff}|${level:uppercase=true:padding=-5}|${message}${onexception:${newline}${exception:format=toString}}",
KeepFileOpen = false,
Encoding = Encoding.UTF8
};
config.AddTarget("file", fileTarget);
var rule2 = new LoggingRule("*", LogLevel.Debug, fileTarget);
config.LoggingRules.Add(rule2);
LogManager.Configuration = config;
}
}
public class NLogLogProvider : Playnite.SDK.ILogProvider
{
Playnite.SDK.ILogger Playnite.SDK.ILogProvider.GetLogger(string loggerName)
{
return new NLogLogger(loggerName);
}
}
}

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\ILMerge.3.0.41\build\ILMerge.props" Condition="Exists('..\..\packages\ILMerge.3.0.41\build\ILMerge.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -15,6 +14,8 @@
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -36,15 +37,12 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\installer.ico</ApplicationIcon>
<ApplicationIcon>playnite-logo-default.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\packages\NLog.4.7.6\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
@ -77,9 +75,6 @@
<Compile Include="..\..\PlayniteSDK\ILogger.cs">
<Link>Shared\ILogger.cs</Link>
</Compile>
<Compile Include="..\..\PlayniteSDK\LogManager.cs">
<Link>Shared\LogManager.cs</Link>
</Compile>
<Compile Include="..\..\PlayniteSDK\RelayCommand.cs">
<Link>Shared\RelayCommand.cs</Link>
</Compile>
@ -95,9 +90,9 @@
<Compile Include="..\..\Playnite\Common\Resources.cs">
<Link>Shared\Resources.cs</Link>
</Compile>
<Compile Include="Manifests.cs" />
<Compile Include="NLogLogger.cs" />
<Compile Include="ViewModels\MainViewModel.cs" />
<Compile Include="Converters.cs" />
<Compile Include="Logger.cs" />
<Compile Include="MainViewModel.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -110,7 +105,7 @@
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="Theme\Classic.xaml">
<Page Include="Classic.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
@ -133,20 +128,24 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="packages.config" />
<None Include="app.manifest">
<SubType>Designer</SubType>
</None>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="App.config" />
<None Include="App.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="config.json" />
<EmbeddedResource Include="installer_mirrors.txt" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\installer.ico" />
<Resource Include="playnite-logo-default.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -49,4 +49,4 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

View File

@ -1,104 +0,0 @@
using Newtonsoft.Json;
using Playnite.Common;
using Playnite.SDK;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
namespace PlayniteInstaller.ViewModels
{
public class MainViewModel : ObservableObject
{
private static readonly ILogger logger = LogManager.GetLogger();
private readonly Window windowHost;
private readonly AppConfig appConfig;
private string destinationFolder;
public string DestionationFolder
{
get => destinationFolder;
set
{
destinationFolder = value;
OnPropertyChanged();
}
}
private bool portable;
public bool Portable
{
get => portable;
set
{
portable = value;
OnPropertyChanged();
}
}
public RelayCommand<object> BrowseCommand
{
get => new RelayCommand<object>((a) =>
{
Browse();
});
}
public RelayCommand<object> InstallCommand
{
get => new RelayCommand<object>((a) =>
{
Install();
});
}
public RelayCommand<object> CancelCommand
{
get => new RelayCommand<object>((a) =>
{
Cancel();
});
}
public MainViewModel(Window window)
{
DestionationFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Playnite");
windowHost = window;
var appConfigStr = Resources.ReadFileFromResource("PlayniteInstaller.config.json");
logger.Info(appConfigStr);
appConfig = JsonConvert.DeserializeObject<AppConfig>(appConfigStr);
}
public void Browse()
{
var dialog = new FolderBrowserDialog()
{
Description = "Select Destination Folder...",
ShowNewFolderButton = true
};
if (dialog.ShowDialog() == DialogResult.OK)
{
DestionationFolder = dialog.SelectedPath;
}
}
public void Install()
{
windowHost.Close();
}
public void Cancel()
{
windowHost.Close();
}
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="Playnite.DesktopApp"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

View File

@ -1,6 +0,0 @@
{
"ServerMirrors": [
"https://localhost/installer/",
"https://localhost:8081/installer/"
]
}

View File

@ -0,0 +1,2 @@
https://raw.githubusercontent.com/JosefNemec/PlayniteWeb/master/manifests/installer_mirrors.txt
https://playnite.link/download/installer_mirrors.txt

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILMerge" version="3.0.41" targetFramework="net462" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net462" />
<package id="NLog" version="4.7.6" targetFramework="net462" />
</packages>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB