From b4061a7bdfc8ec356c6a121ac2ffdf389e1b36d1 Mon Sep 17 00:00:00 2001 From: Tichau Date: Thu, 7 Jan 2016 21:47:14 +0100 Subject: [PATCH] Add software update system --- Application/FileConverter/Application.xaml.cs | 204 ++++++++++++------ .../FileConverter/FileConverter.csproj | 15 ++ .../FileConverter/Resources/DownloadIcon.png | Bin 0 -> 2198 bytes .../FileConverter/Settings.Settings.cs | 22 +- Application/FileConverter/Upgrade/Helpers.cs | 131 +++++++++++ .../Upgrade/UpgradeVersionDescription.cs | 51 +++++ Application/FileConverter/Version.cs | 81 ++++++- .../FileConverter/Windows/MainWindow.xaml.cs | 25 ++- .../FileConverter/Windows/SettingsWindow.xaml | 3 +- .../FileConverter/Windows/UpgradeWindow.xaml | 95 ++++++++ .../Windows/UpgradeWindow.xaml.cs | 80 +++++++ FileConverter.sln | 2 +- Middleware/Markdown.Xaml.dll | Bin 0 -> 28160 bytes README.md | 5 +- version.xml | 4 + 15 files changed, 640 insertions(+), 78 deletions(-) create mode 100644 Application/FileConverter/Resources/DownloadIcon.png create mode 100644 Application/FileConverter/Upgrade/Helpers.cs create mode 100644 Application/FileConverter/Upgrade/UpgradeVersionDescription.cs create mode 100644 Application/FileConverter/Windows/UpgradeWindow.xaml create mode 100644 Application/FileConverter/Windows/UpgradeWindow.xaml.cs create mode 100644 Middleware/Markdown.Xaml.dll create mode 100644 version.xml diff --git a/Application/FileConverter/Application.xaml.cs b/Application/FileConverter/Application.xaml.cs index eecada6..ee2a3b1 100644 --- a/Application/FileConverter/Application.xaml.cs +++ b/Application/FileConverter/Application.xaml.cs @@ -19,15 +19,15 @@ namespace FileConverter using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading; + using System.Threading.Tasks; using System.Windows; using FileConverter.ConversionJobs; using FileConverter.Diagnostics; + using FileConverter.Upgrade; public partial class Application : System.Windows.Application { - private int numberOfConversionThread = 1; - private static readonly Version Version = new Version() { Major = 0, @@ -36,28 +36,17 @@ namespace FileConverter private readonly List conversionJobs = new List(); - private bool debugMode; - private bool initialized; + private int numberOfConversionThread = 1; + + private bool needToRunConversionThread; private bool cancelAutoExit; + private UpgradeVersionDescription upgradeVersionDescription = null; public Application() { this.ConvertionJobs = this.conversionJobs.AsReadOnly(); } - protected override void OnStartup(StartupEventArgs e) - { - base.OnStartup(e); - - this.Initialize(); - - if (this.initialized) - { - Thread fileConvertionThread = new Thread(this.ConvertFiles); - fileConvertionThread.Start(); - } - } - public static Version ApplicationVersion { get @@ -94,7 +83,68 @@ namespace FileConverter { this.cancelAutoExit = true; } - + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + this.Initialize(); + + if (this.needToRunConversionThread) + { + Thread fileConvertionThread = new Thread(this.ConvertFiles); + fileConvertionThread.Start(); + } + } + + protected override void OnExit(ExitEventArgs e) + { + base.OnExit(e); + + Debug.Log("Exit application."); + + if (this.upgradeVersionDescription != null && this.upgradeVersionDescription.NeedToUpgrade) + { + Debug.Log("A new version of file converter has been found: {0}.", this.upgradeVersionDescription.LatestVersion); + + if (string.IsNullOrEmpty(this.upgradeVersionDescription.InstallerPath)) + { + Debug.LogError("Invalid installer path."); + } + else + { + Debug.Log("Wait for the end of the installer download."); + while (this.upgradeVersionDescription.InstallerDownloadInProgress) + { + Thread.Sleep(1000); + } + + string installerPath = this.upgradeVersionDescription.InstallerPath; + if (!System.IO.File.Exists(installerPath)) + { + Debug.LogError("Can't find upgrade installer ({0}). Try to restart the application.", installerPath); + return; + } + + // Start process. + Debug.Log("Start file converter upgrade from version {0} to {1}.", ApplicationVersion, this.upgradeVersionDescription.LatestVersion); + + System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(installerPath); + startInfo.UseShellExecute = true; + + Debug.Log("Start upgrade process: {0}{1}.", System.IO.Path.GetFileName(startInfo.FileName), startInfo.Arguments); + System.Diagnostics.Process process = new System.Diagnostics.Process + { + StartInfo = startInfo + }; + + process.Start(); + } + } + + Debug.Release(); + } + private void Initialize() { Diagnostics.Debug.Log("The number of processors on this computer is {0}. Set the default number of conversion threads to {0}", Environment.ProcessorCount); @@ -109,7 +159,7 @@ namespace FileConverter Dispatcher.BeginInvoke((Action)(() => Application.Current.Shutdown())); return; } - + // Retrieve arguments. Debug.Log("Retrieve arguments..."); string[] args = Environment.GetCommandLineArgs(); @@ -117,28 +167,27 @@ namespace FileConverter #if (DEBUG) if (args.Length <= 1) { - this.debugMode = true; System.Array.Resize(ref args, 8); - args[1] = "--conversion-preset"; - args[2] = "To Ogg"; - args[3] = "--verbose"; - - args[4] = @"D:\Test\TrailerV2 compressed.mkv"; - args[4] = @"D:\Test\image.png"; - args[4] = @"E:\Track01.cda"; - args[4] = @"D:\Test\Track01.mp3"; - args[5] = @"D:\Test\Track02.mp3"; - args[6] = @"D:\Test\Track03.mp3"; - args[7] = @"D:\Test\Track04.mp3"; + //args[1] = "--conversion-preset"; + //args[2] = "To Ogg"; + //args[3] = "--verbose"; - //System.Array.Resize(ref args, 2); - //args[1] = "--settings"; + //args[4] = @"D:\Test\TrailerV2 compressed.mkv"; + //args[4] = @"D:\Test\image.png"; + //args[4] = @"E:\Track01.cda"; + //args[4] = @"D:\Test\Track01.mp3"; + //args[5] = @"D:\Test\Track02.mp3"; + //args[6] = @"D:\Test\Track03.mp3"; + //args[7] = @"D:\Test\Track04.mp3"; - System.Array.Resize(ref args, 5); - args[1] = "--conversion-preset"; - args[2] = "To Aac"; - args[3] = "--verbose"; - args[4] = @"D:\Test\02 - Corn on the Cob.flac"; + System.Array.Resize(ref args, 2); + args[1] = "--settings"; + + //System.Array.Resize(ref args, 5); + //args[1] = "--conversion-preset"; + //args[2] = "To Wav"; + //args[3] = "--verbose"; + //args[4] = @"D:\Test\Track01.mp3"; } #endif @@ -150,6 +199,11 @@ namespace FileConverter Debug.Log(string.Empty); + if (args.Length == 1) + { + // TODO: Help windows to explain that this application is a context menu extension. + } + ConversionPreset conversionPreset = null; List filePaths = new List(); @@ -164,9 +218,14 @@ namespace FileConverter switch (parameterTitle) { + case "version": + Console.Write(ApplicationVersion.ToString()); + Dispatcher.BeginInvoke((Action)(() => Application.Current.Shutdown())); + return; + case "settings": this.ShowSettings = true; - return; + break; case "apply-settings": Settings.ApplyTemporarySettings(); @@ -191,7 +250,7 @@ namespace FileConverter index++; continue; - + case "verbose": { this.Verbose = true; @@ -200,7 +259,7 @@ namespace FileConverter break; default: - Debug.LogError("Unknown option {0}.", parameterTitle); + Debug.LogError("Unknown application argument: '--{0}'.", parameterTitle); return; } } @@ -210,41 +269,34 @@ namespace FileConverter } } - if (conversionPreset == null) + if (this.Settings.CheckUpgradeAtStartup) { - Debug.LogError("Can't retrieve the conversion preset from arguments."); - return; + Task task = Upgrade.Helpers.GetLatestVersionDescriptionAsync(this.OnGetLatestVersionDescription); } - // Create convertion jobs. - Debug.Log("Create jobs for conversion preset: '{0}'", conversionPreset.Name); - try + if (conversionPreset != null) { - for (int index = 0; index < filePaths.Count; index++) + // Create convertion jobs. + Debug.Log("Create jobs for conversion preset: '{0}'", conversionPreset.Name); + try { - string inputFilePath = filePaths[index]; - ConversionJob conversionJob = ConversionJobFactory.Create(conversionPreset, inputFilePath); - conversionJob.PrepareConversion(inputFilePath); + for (int index = 0; index < filePaths.Count; index++) + { + string inputFilePath = filePaths[index]; + ConversionJob conversionJob = ConversionJobFactory.Create(conversionPreset, inputFilePath); + conversionJob.PrepareConversion(inputFilePath); - this.conversionJobs.Add(conversionJob); + this.conversionJobs.Add(conversionJob); + } } + catch (Exception exception) + { + Debug.LogError(exception.Message); + throw; + } + + this.needToRunConversionThread = true; } - catch (Exception exception) - { - Debug.LogError(exception.Message); - throw; - } - - this.initialized = true; - } - - protected override void OnExit(ExitEventArgs e) - { - base.OnExit(e); - - Debug.Log("Exit application."); - - Debug.Release(); } private void ConvertFiles() @@ -352,5 +404,21 @@ namespace FileConverter Debug.Log("The conversion job failed but there is an output file that does exists."); } } + + private void OnGetLatestVersionDescription(UpgradeVersionDescription upgradeVersionDescription) + { + if (upgradeVersionDescription == null) + { + return; + } + + if (upgradeVersionDescription.LatestVersion <= ApplicationVersion) + { + return; + } + + this.upgradeVersionDescription = upgradeVersionDescription; + (this.MainWindow as MainWindow).OnNewVersionReleased(upgradeVersionDescription); + } } } diff --git a/Application/FileConverter/FileConverter.csproj b/Application/FileConverter/FileConverter.csproj index 3a6d136..027162a 100644 --- a/Application/FileConverter/FileConverter.csproj +++ b/Application/FileConverter/FileConverter.csproj @@ -46,6 +46,9 @@ + + ..\..\Middleware\Markdown.Xaml.dll + @@ -102,6 +105,8 @@ True + + @@ -128,6 +133,9 @@ SettingsWindow.xaml + + UpgradeWindow.xaml + Designer @@ -156,6 +164,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + @@ -199,6 +211,9 @@ + + + - + + + + diff --git a/Application/FileConverter/Windows/UpgradeWindow.xaml.cs b/Application/FileConverter/Windows/UpgradeWindow.xaml.cs new file mode 100644 index 0000000..abf7eec --- /dev/null +++ b/Application/FileConverter/Windows/UpgradeWindow.xaml.cs @@ -0,0 +1,80 @@ +// License: http://www.gnu.org/licenses/gpl.html GPL version 3. + +namespace FileConverter.Windows +{ + using System.ComponentModel; + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + using System.Windows; + + using FileConverter.Annotations; + using FileConverter.Upgrade; + + public partial class UpgradeWindow : Window, INotifyPropertyChanged + { + private UpgradeVersionDescription upgradeVersionDescription; + private string releaseNoteContent; + + public UpgradeWindow() + { + this.InitializeComponent(); + } + + public event PropertyChangedEventHandler PropertyChanged; + + public UpgradeVersionDescription VersionDescription + { + get + { + return this.upgradeVersionDescription; + } + + set + { + this.upgradeVersionDescription = value; + + Task task = Helpers.GetChangeLogAsync(this.upgradeVersionDescription, this.OnChangeLogRetrieved); + + this.OnPropertyChanged(); + } + } + + public string ReleaseNote + { + get + { + if (string.IsNullOrEmpty(this.releaseNoteContent)) + { + return "###Downloading change log ..."; + } + + return this.releaseNoteContent; + } + + set + { + this.releaseNoteContent = value; + + this.OnPropertyChanged(); + } + } + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private void OnChangeLogRetrieved(UpgradeVersionDescription versionDescription) + { + this.ReleaseNote = versionDescription.ChangeLog; + } + + private void OnInstallButtonClick(object sender, RoutedEventArgs e) + { + this.upgradeVersionDescription.NeedToUpgrade = true; + Helpers.DownloadInstallerAsync(this.upgradeVersionDescription); + this.Close(); + } + } +} diff --git a/FileConverter.sln b/FileConverter.sln index af7cc2b..1eb9171 100644 --- a/FileConverter.sln +++ b/FileConverter.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileConverter", "Application\FileConverter\FileConverter.csproj", "{D27A76D2-43E4-43CC-9DA3-334B0B46F4E5}" EndProject diff --git a/Middleware/Markdown.Xaml.dll b/Middleware/Markdown.Xaml.dll new file mode 100644 index 0000000000000000000000000000000000000000..118156bc365b729eb47d486c5a28d7aa60aec48b GIT binary patch literal 28160 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 l+xQHFABJD59QWV9+h + + http://github.com/Tichau/FileConverter/releases/download/v0.4/FileConverter-0.4-x64-setup.msi + \ No newline at end of file