1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-04-22 18:15:47 +02:00

Rewrite update system to predownload update installers

This commit is contained in:
chylex 2017-05-17 18:21:06 +02:00
parent 9e44a86be0
commit 0e8c6c066f
14 changed files with 200 additions and 160 deletions

View File

@ -12,6 +12,7 @@ static class Arguments{
// internal args
public const string ArgRestart = "-restart";
public const string ArgImportCookies = "-importcookies";
public const string ArgUpdated = "-updated";
// class data and methods
private static readonly CommandLineArgs Current = CommandLineArgs.FromStringArray('-', Environment.GetCommandLineArgs());
@ -28,7 +29,18 @@ public static CommandLineArgs GetCurrentClean(){
CommandLineArgs args = Current.Clone();
args.RemoveFlag(ArgRestart);
args.RemoveFlag(ArgImportCookies);
args.RemoveFlag(ArgUpdated);
return args;
}
public static CommandLineArgs GetCurrentForInstaller(){
CommandLineArgs args = GetCurrentClean();
args.AddFlag(ArgUpdated);
return args;
}
public static string GetCurrentForInstallerCmd(){
return GetCurrentForInstaller().ToString().Replace("\"", "^\"");
}
}
}

View File

@ -41,6 +41,7 @@ private void InitializeComponent() {
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Activated += new System.EventHandler(this.FormBrowser_Activated);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormBrowser_FormClosed);
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
this.ResumeLayout(false);

View File

@ -247,6 +247,12 @@ private void FormBrowser_FormClosing(object sender, FormClosingEventArgs e){
}
}
private void FormBrowser_FormClosed(object sender, FormClosedEventArgs e){
if (isLoaded && UpdateInstallerPath == null){
updates.CleanupDownload();
}
}
private void Config_MuteToggled(object sender, EventArgs e){
UpdateProperties(PropertyBridge.Properties.MuteNotifications);
}
@ -284,24 +290,14 @@ private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
form.Close();
}
}
updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
if (update.DownloadStatus == UpdateDownloadStatus.Done){
UpdateInstallerPath = update.InstallerPath;
}
Hide();
FormUpdateDownload downloadForm = new FormUpdateDownload(e.UpdateInfo);
downloadForm.MoveToCenter(this);
downloadForm.ShowDialog();
downloadForm.Dispose();
if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Succeeded){
UpdateInstallerPath = downloadForm.InstallerPath;
ForceClose();
}
else if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Manual){
ForceClose();
}
else{
Show();
}
});
}
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){

View File

@ -34,6 +34,7 @@ static class Program{
public static readonly string PluginConfigFilePath = Path.Combine(StoragePath, "TD_PluginConfig.cfg");
private static readonly string ErrorLogFilePath = Path.Combine(StoragePath, "TD_Log.txt");
private static readonly string ConsoleLogFilePath = Path.Combine(StoragePath, "TD_Console.txt");
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
@ -126,6 +127,10 @@ private static void Main(){
if (Arguments.HasFlag(Arguments.ArgImportCookies)){
ExportManager.ImportCookies();
}
if (Arguments.HasFlag(Arguments.ArgUpdated)){
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
}
CefSharpSettings.WcfEnabled = false;
@ -163,7 +168,8 @@ private static void Main(){
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
DismissedUpdate = UserConfig.DismissedUpdate
DismissedUpdate = UserConfig.DismissedUpdate,
InstallerDownloadFolder = InstallerPath
});
Application.Run(mainForm);
@ -172,7 +178,7 @@ private static void Main(){
ExitCleanup();
// ProgramPath has a trailing backslash
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentClean().ToString().Replace("\"", "^\"")+"\""+(IsPortable ? " /PORTABLE=1" : "");
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated);

View File

@ -113,7 +113,7 @@
ele.remove();
if (download){
$TDU.onUpdateAccepted(version, download);
$TDU.onUpdateAccepted();
}
else{
$TDU.openBrowser(updateDownloadFallback);
@ -125,7 +125,7 @@
});
buttonDiv.children(".tdu-btn-dismiss,.tdu-btn-unsupported").click(function(){
$TDU.onUpdateDismissed(version);
$TDU.onUpdateDismissed();
ele.slideUp(function(){ ele.remove(); });
});
@ -161,10 +161,13 @@
if (hasUpdate){
var obj = release.assets.find(asset => asset.name === updateFileName) || { browser_download_url: "" };
displayNotification(tagName, obj.browser_download_url);
if (eventID){ // ignore undefined and 0
$TDU.onUpdateCheckFinished(eventID, tagName, obj.browser_download_url);
}
}
if (eventID){ // ignore undefined and 0
$TDU.onUpdateCheckFinished(eventID, hasUpdate, tagName);
else if (eventID){ // ignore undefined and 0
$TDU.onUpdateCheckFinished(eventID, null, null);
}
});
};

View File

@ -255,6 +255,7 @@
<Compile Include="Core\Utils\NativeMethods.cs" />
<Compile Include="Updates\Events\UpdateAcceptedEventArgs.cs" />
<Compile Include="Updates\Events\UpdateCheckEventArgs.cs" />
<Compile Include="Updates\UpdateDownloadStatus.cs" />
<Compile Include="Updates\UpdateHandler.cs" />
<Compile Include="Updates\UpdateInfo.cs" />
<Compile Include="Program.cs" />

View File

@ -3,13 +3,13 @@
namespace TweetDuck.Updates.Events{
class UpdateCheckEventArgs : EventArgs{
public int EventId { get; }
public bool UpdateAvailable { get; }
public string LatestVersion { get; }
public UpdateInfo UpdateInfo { get; }
public UpdateCheckEventArgs(int eventId, bool updateAvailable, string latestVersion){
public bool UpdateAvailable => UpdateInfo != null;
public UpdateCheckEventArgs(int eventId, UpdateInfo updateInfo){
EventId = eventId;
UpdateAvailable = updateAvailable;
LatestVersion = latestVersion;
UpdateInfo = updateInfo;
}
}
}

View File

@ -23,31 +23,19 @@ protected override void Dispose(bool disposing) {
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormUpdateDownload));
this.progressDownload = new System.Windows.Forms.ProgressBar();
this.btnCancel = new System.Windows.Forms.Button();
this.labelDescription = new System.Windows.Forms.Label();
this.labelStatus = new System.Windows.Forms.Label();
this.timerDownloadCheck = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// progressDownload
//
this.progressDownload.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressDownload.Location = new System.Drawing.Point(12, 32);
this.progressDownload.MarqueeAnimationSpeed = 40;
this.progressDownload.Maximum = 1000;
this.progressDownload.Name = "progressDownload";
this.progressDownload.Size = new System.Drawing.Size(361, 23);
this.progressDownload.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.progressDownload.TabIndex = 0;
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.AutoSize = true;
this.btnCancel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnCancel.Location = new System.Drawing.Point(317, 61);
this.btnCancel.Location = new System.Drawing.Point(195, 34);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCancel.Size = new System.Drawing.Size(56, 23);
@ -65,41 +53,33 @@ private void InitializeComponent() {
this.labelDescription.Size = new System.Drawing.Size(0, 13);
this.labelDescription.TabIndex = 2;
//
// labelStatus
// timerDownloadCheck
//
this.labelStatus.AutoSize = true;
this.labelStatus.Location = new System.Drawing.Point(9, 62);
this.labelStatus.Name = "labelStatus";
this.labelStatus.Size = new System.Drawing.Size(0, 13);
this.labelStatus.TabIndex = 3;
this.timerDownloadCheck.Interval = 500;
this.timerDownloadCheck.Tick += new System.EventHandler(this.timerDownloadCheck_Tick);
//
// FormUpdateDownload
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(385, 96);
this.Controls.Add(this.labelStatus);
this.ClientSize = new System.Drawing.Size(263, 69);
this.Controls.Add(this.labelDescription);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.progressDownload);
this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FormUpdateDownload";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormUpdateDownload_FormClosing);
this.Shown += new System.EventHandler(this.FormUpdateDownload_Shown);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ProgressBar progressDownload;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Label labelDescription;
private System.Windows.Forms.Label labelStatus;
private System.Windows.Forms.Timer timerDownloadCheck;
}
}

View File

@ -1,114 +1,41 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils;
namespace TweetDuck.Updates{
sealed partial class FormUpdateDownload : Form{
private const double BytesToKB = 1024.0;
public string InstallerPath => Path.Combine(Path.GetTempPath(), updateInfo.FileName);
public enum Status{
Waiting, Failed, Cancelled, Manual, Succeeded
}
public Status UpdateStatus { get; private set; }
private readonly WebClient webClient;
private readonly UpdateInfo updateInfo;
public FormUpdateDownload(UpdateInfo info){
InitializeComponent();
this.updateInfo = info;
Text = "Updating "+Program.BrandName;
labelDescription.Text = "Downloading version "+info.VersionTag+"...";
this.updateInfo = info;
this.UpdateStatus = Status.Waiting;
this.webClient = new WebClient{ Proxy = null };
this.webClient.Headers[HttpRequestHeader.UserAgent] = BrowserUtils.HeaderUserAgent;
this.webClient.DownloadProgressChanged += webClient_DownloadProgressChanged;
this.webClient.DownloadFileCompleted += webClient_DownloadFileCompleted;
Disposed += (sender, args) => this.webClient.Dispose();
}
private void FormUpdateDownload_Shown(object sender, EventArgs e){
webClient.DownloadFileAsync(new Uri(updateInfo.DownloadUrl), InstallerPath);
timerDownloadCheck.Start();
}
private void btnCancel_Click(object sender, EventArgs e){
if (webClient.IsBusy){
webClient.CancelAsync();
btnCancel.Enabled = false;
}
else{
UpdateStatus = Status.Cancelled;
Close();
}
private void timerDownloadCheck_Tick(object sender, EventArgs e){
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
timerDownloadCheck.Stop();
DialogResult = DialogResult.OK;
Close();
}
}
else if (updateInfo.DownloadStatus == UpdateDownloadStatus.Failed){
timerDownloadCheck.Stop();
private void FormUpdateDownload_FormClosing(object sender, FormClosingEventArgs e){
if (UpdateStatus == Status.Waiting){
UpdateStatus = e.CloseReason == CloseReason.UserClosing ? Status.Cancelled : Status.Manual; // manual will exit the app
if (webClient.IsBusy){
webClient.CancelAsync();
e.Cancel = true;
if (MessageBox.Show("Could not download the update: "+(updateInfo.DownloadError?.Message ?? "unknown error")+"\r\n\r\nDo you want to open the website and try downloading the update manually?", "Update Has Failed", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1) == DialogResult.Yes){
BrowserUtils.OpenExternalBrowserUnsafe(Program.Website);
DialogResult = DialogResult.OK;
}
}
}
private void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e){
this.InvokeSafe(() => {
if (e.TotalBytesToReceive == -1){
if (progressDownload.Style != ProgressBarStyle.Marquee){
progressDownload.Style = ProgressBarStyle.Continuous;
progressDownload.SetValueInstant(1000);
}
labelStatus.Text = (long)(e.BytesReceived/BytesToKB)+" kB";
}
else{
if (progressDownload.Style != ProgressBarStyle.Continuous){
progressDownload.Style = ProgressBarStyle.Continuous;
}
progressDownload.SetValueInstant(e.ProgressPercentage*10);
labelStatus.Text = (long)(e.BytesReceived/BytesToKB)+" / "+(long)(e.TotalBytesToReceive/BytesToKB)+" kB";
}
});
}
private void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e){
this.InvokeSafe(() => {
if (e.Cancelled){
if (UpdateStatus == Status.Waiting){
UpdateStatus = Status.Cancelled;
}
}
else if (e.Error != null){
Program.Reporter.Log(e.Error.ToString());
if (MessageBox.Show("Could not download the update: "+e.Error.Message+"\r\n\r\nDo you want to open the website and try downloading the update manually?", "Update Has Failed", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1) == DialogResult.Yes){
BrowserUtils.OpenExternalBrowserUnsafe(Program.Website);
UpdateStatus = Status.Manual;
}
else{
UpdateStatus = Status.Failed;
}
}
else{
UpdateStatus = Status.Succeeded;
}
Close();
});
}
}
}
}

View File

@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="timerDownloadCheck.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@ -0,0 +1,8 @@
namespace TweetDuck.Updates{
public enum UpdateDownloadStatus{
None = 0,
InProgress,
Done,
Failed
}
}

View File

@ -1,6 +1,7 @@
using CefSharp;
using CefSharp.WinForms;
using System;
using System.Windows.Forms;
using TweetDuck.Core;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils;
@ -20,6 +21,7 @@ sealed class UpdateHandler{
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
private int lastEventId;
private UpdateInfo lastUpdateInfo;
public UpdateHandler(ChromiumWebBrowser browser, FormBrowser form, UpdaterSettings settings){
this.browser = browser;
@ -55,6 +57,40 @@ public int Check(bool force){
return -1;
}
public void BeginUpdateDownload(Form ownerForm, UpdateInfo updateInfo, Action<UpdateInfo> onSuccess){
if (updateInfo.DownloadStatus == UpdateDownloadStatus.Done){
onSuccess(updateInfo);
}
else{
FormUpdateDownload downloadForm = new FormUpdateDownload(updateInfo);
downloadForm.VisibleChanged += (sender, args) => {
downloadForm.MoveToCenter(ownerForm);
ownerForm.Hide();
};
downloadForm.FormClosed += (sender, args) => {
downloadForm.Dispose();
if (downloadForm.DialogResult == DialogResult.OK){ // success or manual download
onSuccess(updateInfo);
}
else{
ownerForm.Show();
}
};
downloadForm.Show();
}
}
public void CleanupDownload(){
if (lastUpdateInfo != null){
lastUpdateInfo.DeleteInstaller();
lastUpdateInfo = null;
}
}
public void DismissUpdate(string tag){
settings.DismissedUpdate = tag;
UpdateDismissed?.Invoke(this, new UpdateDismissedEventArgs(tag));
@ -90,16 +126,27 @@ public void TriggerUpdateCheck(){
owner.Check(false);
}
public void OnUpdateCheckFinished(int eventId, bool isUpdateAvailable, string latestVersion){
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, isUpdateAvailable, latestVersion));
public void OnUpdateCheckFinished(int eventId, string versionTag, string downloadUrl){
if (versionTag != null && (owner.lastUpdateInfo == null || owner.lastUpdateInfo.VersionTag != versionTag)){
owner.CleanupDownload();
owner.lastUpdateInfo = new UpdateInfo(owner.settings, versionTag, downloadUrl);
owner.lastUpdateInfo.BeginSilentDownload();
}
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, owner.lastUpdateInfo));
}
public void OnUpdateAccepted(string versionTag, string downloadUrl){
owner.TriggerUpdateAcceptedEvent(new UpdateAcceptedEventArgs(new UpdateInfo(versionTag, downloadUrl)));
public void OnUpdateAccepted(){
if (owner.lastUpdateInfo != null){
owner.TriggerUpdateAcceptedEvent(new UpdateAcceptedEventArgs(owner.lastUpdateInfo));
}
}
public void OnUpdateDismissed(string versionTag){
owner.TriggerUpdateDismissedEvent(new UpdateDismissedEventArgs(versionTag));
public void OnUpdateDismissed(){
if (owner.lastUpdateInfo != null){
owner.TriggerUpdateDismissedEvent(new UpdateDismissedEventArgs(owner.lastUpdateInfo.VersionTag));
owner.CleanupDownload();
}
}
public void OpenBrowser(string url){

View File

@ -1,15 +1,70 @@
using TweetDuck.Core.Utils;
using System;
using System.IO;
using System.Net;
using TweetDuck.Core.Utils;
namespace TweetDuck.Updates{
class UpdateInfo{
public readonly string VersionTag;
public readonly string DownloadUrl;
public string VersionTag { get; }
public string InstallerPath { get; }
public string FileName => BrowserUtils.GetFileNameFromUrl(DownloadUrl) ?? Program.BrandName+".Update.exe";
public UpdateDownloadStatus DownloadStatus { get; private set; }
public Exception DownloadError { get; private set; }
private readonly string installerFolder;
private readonly string downloadUrl;
private WebClient currentDownload;
public UpdateInfo(UpdaterSettings settings, string versionTag, string downloadUrl){
this.installerFolder = settings.InstallerDownloadFolder;
this.downloadUrl = downloadUrl;
public UpdateInfo(string versionTag, string downloadUrl){
this.VersionTag = versionTag;
this.DownloadUrl = downloadUrl;
this.InstallerPath = Path.Combine(installerFolder, "TweetDuck."+versionTag+".exe");
}
public void BeginSilentDownload(){
if (DownloadStatus == UpdateDownloadStatus.None || DownloadStatus == UpdateDownloadStatus.Failed){
DownloadStatus = UpdateDownloadStatus.InProgress;
try{
Directory.CreateDirectory(installerFolder);
}catch(Exception e){
DownloadError = e;
DownloadStatus = UpdateDownloadStatus.Failed;
return;
}
if (string.IsNullOrEmpty(downloadUrl)){
DownloadError = new UriFormatException("Could not determine URL of the update installer");
DownloadStatus = UpdateDownloadStatus.Failed;
return;
}
currentDownload = BrowserUtils.DownloadFileAsync(downloadUrl, InstallerPath, () => {
DownloadStatus = UpdateDownloadStatus.Done;
currentDownload = null;
}, e => {
DownloadError = e;
DownloadStatus = UpdateDownloadStatus.Failed;
currentDownload = null;
});
}
}
public void DeleteInstaller(){
DownloadStatus = UpdateDownloadStatus.None;
if (currentDownload != null && currentDownload.IsBusy){
currentDownload.CancelAsync(); // deletes file when cancelled
return;
}
try{
File.Delete(InstallerPath);
}catch{
// rip
}
}
}
}

View File

@ -2,5 +2,6 @@
class UpdaterSettings{
public bool AllowPreReleases { get; set; }
public string DismissedUpdate { get; set; }
public string InstallerDownloadFolder { get; set; }
}
}