Added redistributable installing to plugin
parent
e4cf2aabfe
commit
b4405d3034
|
@ -50,7 +50,7 @@ namespace LANCommander.PlaynitePlugin
|
||||||
var result = RetryHelper.RetryOnException<ExtractionResult>(10, TimeSpan.FromMilliseconds(500), new ExtractionResult(), () =>
|
var result = RetryHelper.RetryOnException<ExtractionResult>(10, TimeSpan.FromMilliseconds(500), new ExtractionResult(), () =>
|
||||||
{
|
{
|
||||||
Logger.Trace("Attempting to download and extract game...");
|
Logger.Trace("Attempting to download and extract game...");
|
||||||
return DownloadAndExtract(game);
|
return DownloadAndExtractGame(game);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.Success && !result.Canceled)
|
if (!result.Success && !result.Canceled)
|
||||||
|
@ -88,6 +88,12 @@ namespace LANCommander.PlaynitePlugin
|
||||||
SaveScript(game, result.Directory, ScriptType.NameChange);
|
SaveScript(game, result.Directory, ScriptType.NameChange);
|
||||||
SaveScript(game, result.Directory, ScriptType.KeyChange);
|
SaveScript(game, result.Directory, ScriptType.KeyChange);
|
||||||
|
|
||||||
|
if (game.Redistributables != null && game.Redistributables.Count() > 0)
|
||||||
|
{
|
||||||
|
Logger.Trace("Installing required redistributables...");
|
||||||
|
InstallRedistributables(game);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.Install);
|
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.Install);
|
||||||
|
@ -106,7 +112,7 @@ namespace LANCommander.PlaynitePlugin
|
||||||
InvokeOnInstalled(new GameInstalledEventArgs(installInfo));
|
InvokeOnInstalled(new GameInstalledEventArgs(installInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractionResult DownloadAndExtract(LANCommander.SDK.Models.Game game)
|
private ExtractionResult DownloadAndExtractGame(LANCommander.SDK.Models.Game game)
|
||||||
{
|
{
|
||||||
if (game == null)
|
if (game == null)
|
||||||
{
|
{
|
||||||
|
@ -204,6 +210,152 @@ namespace LANCommander.PlaynitePlugin
|
||||||
return extractionResult;
|
return extractionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InstallRedistributables(LANCommander.SDK.Models.Game game)
|
||||||
|
{
|
||||||
|
foreach (var redistributable in game.Redistributables)
|
||||||
|
{
|
||||||
|
string installScriptTempFile = null;
|
||||||
|
string detectionScriptTempFile = null;
|
||||||
|
string extractTempPath = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var installScript = redistributable.Scripts.FirstOrDefault(s => s.Type == ScriptType.Install);
|
||||||
|
installScriptTempFile = SaveTempScript(installScript);
|
||||||
|
|
||||||
|
var detectionScript = redistributable.Scripts.FirstOrDefault(s => s.Type == ScriptType.DetectInstall);
|
||||||
|
detectionScriptTempFile = SaveTempScript(detectionScript);
|
||||||
|
|
||||||
|
var detectionResult = PowerShellRuntime.RunScript(detectionScriptTempFile, detectionScript.RequiresAdmin);
|
||||||
|
|
||||||
|
if (!detectionResult == 0)
|
||||||
|
{
|
||||||
|
var extractionResult = DownloadAndExtractRedistributable(redistributable);
|
||||||
|
|
||||||
|
if (extractionResult.Success)
|
||||||
|
{
|
||||||
|
extractTempPath = extractionResult.Directory;
|
||||||
|
|
||||||
|
PowerShellRuntime.RunScript(installScriptTempFile, installScript.RequiresAdmin, null, extractTempPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error(ex, $"Redistributable {redistributable.Name} failed to install");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(installScriptTempFile))
|
||||||
|
File.Delete(installScriptTempFile);
|
||||||
|
|
||||||
|
if (File.Exists(detectionScriptTempFile))
|
||||||
|
File.Delete(detectionScriptTempFile);
|
||||||
|
|
||||||
|
if (Directory.Exists(extractTempPath))
|
||||||
|
Directory.Delete(extractTempPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExtractionResult DownloadAndExtractRedistributable(LANCommander.SDK.Models.Redistributable redistributable)
|
||||||
|
{
|
||||||
|
if (redistributable == null)
|
||||||
|
{
|
||||||
|
Logger.Trace("Redistributable failed to download! No redistributable was specified!");
|
||||||
|
|
||||||
|
throw new Exception("Redistributable failed to download!");
|
||||||
|
}
|
||||||
|
|
||||||
|
var destination = Path.Combine(Path.GetTempPath(), redistributable.Name.SanitizeFilename());
|
||||||
|
|
||||||
|
Logger.Trace($"Downloading and extracting \"{redistributable.Name}\" to path {destination}");
|
||||||
|
var result = Plugin.PlayniteApi.Dialogs.ActivateGlobalProgress(progress =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(destination);
|
||||||
|
progress.ProgressMaxValue = 100;
|
||||||
|
progress.CurrentProgressValue = 0;
|
||||||
|
|
||||||
|
using (var redistributableStream = Plugin.LANCommander.StreamRedistributable(redistributable.Id))
|
||||||
|
using (var reader = ReaderFactory.Open(redistributableStream))
|
||||||
|
{
|
||||||
|
progress.ProgressMaxValue = redistributableStream.Length;
|
||||||
|
|
||||||
|
redistributableStream.OnProgress += (pos, len) =>
|
||||||
|
{
|
||||||
|
progress.CurrentProgressValue = pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.EntryExtractionProgress += (object sender, ReaderExtractionEventArgs<IEntry> e) =>
|
||||||
|
{
|
||||||
|
if (progress.CancelToken != null && progress.CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
reader.Cancel();
|
||||||
|
progress.IsIndeterminate = true;
|
||||||
|
|
||||||
|
reader.Dispose();
|
||||||
|
redistributableStream.Dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.WriteAllToDirectory(destination, new ExtractionOptions()
|
||||||
|
{
|
||||||
|
ExtractFullPath = true,
|
||||||
|
Overwrite = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (progress.CancelToken != null && progress.CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Logger.Trace("User cancelled the download");
|
||||||
|
|
||||||
|
if (Directory.Exists(destination))
|
||||||
|
{
|
||||||
|
Logger.Trace("Cleaning up orphaned install files after cancelled install...");
|
||||||
|
|
||||||
|
Directory.Delete(destination, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error(ex, $"Could not extract to path {destination}");
|
||||||
|
|
||||||
|
if (Directory.Exists(destination))
|
||||||
|
{
|
||||||
|
Logger.Trace("Cleaning up orphaned install files after bad install...");
|
||||||
|
|
||||||
|
Directory.Delete(destination, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("The redistributable archive could not be extracted. Please try again or fix the archive!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new GlobalProgressOptions($"Downloading {redistributable.Name}...")
|
||||||
|
{
|
||||||
|
IsIndeterminate = false,
|
||||||
|
Cancelable = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var extractionResult = new ExtractionResult
|
||||||
|
{
|
||||||
|
Canceled = result.Canceled
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!result.Canceled)
|
||||||
|
{
|
||||||
|
extractionResult.Success = true;
|
||||||
|
extractionResult.Directory = destination;
|
||||||
|
Logger.Trace($"Redistributable successfully downloaded and extracted to {destination}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractionResult;
|
||||||
|
}
|
||||||
|
|
||||||
private string Download(LANCommander.SDK.Models.Game game)
|
private string Download(LANCommander.SDK.Models.Game game)
|
||||||
{
|
{
|
||||||
string tempFile = String.Empty;
|
string tempFile = String.Empty;
|
||||||
|
@ -294,6 +446,17 @@ namespace LANCommander.PlaynitePlugin
|
||||||
File.WriteAllText(destination, yaml);
|
File.WriteAllText(destination, yaml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string SaveTempScript(LANCommander.SDK.Models.Script script)
|
||||||
|
{
|
||||||
|
var tempPath = Path.GetTempFileName();
|
||||||
|
|
||||||
|
Logger.Trace($"Writing script {script.Name} to {tempPath}");
|
||||||
|
|
||||||
|
File.WriteAllText(tempPath, script.Contents);
|
||||||
|
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
private void SaveScript(LANCommander.SDK.Models.Game game, string installationDirectory, ScriptType type)
|
private void SaveScript(LANCommander.SDK.Models.Game game, string installationDirectory, ScriptType type)
|
||||||
{
|
{
|
||||||
var script = game.Scripts.FirstOrDefault(s => s.Type == type);
|
var script = game.Scripts.FirstOrDefault(s => s.Type == type);
|
||||||
|
|
|
@ -206,6 +206,11 @@ namespace LANCommander.PlaynitePlugin
|
||||||
return DownloadRequest($"/api/Archives/Download/{id}", progressHandler, completeHandler);
|
return DownloadRequest($"/api/Archives/Download/{id}", progressHandler, completeHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TrackableStream StreamRedistributable(Guid id)
|
||||||
|
{
|
||||||
|
return StreamRequest($"/api/Redistributables/{id}/Download");
|
||||||
|
}
|
||||||
|
|
||||||
public string DownloadSave(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> completeHandler)
|
public string DownloadSave(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> completeHandler)
|
||||||
{
|
{
|
||||||
return DownloadRequest($"/api/Saves/Download/{id}", progressHandler, completeHandler);
|
return DownloadRequest($"/api/Saves/Download/{id}", progressHandler, completeHandler);
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace LANCommander.PlaynitePlugin
|
||||||
File.Delete(tempScript);
|
File.Delete(tempScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunScript(string path, bool asAdmin = false, string arguments = null)
|
public int RunScript(string path, bool asAdmin = false, string arguments = null, string workingDirectory = null)
|
||||||
{
|
{
|
||||||
Logger.Trace($"Executing script at path {path} | Admin: {asAdmin} | Arguments: {arguments}");
|
Logger.Trace($"Executing script at path {path} | Admin: {asAdmin} | Arguments: {arguments}");
|
||||||
|
|
||||||
|
@ -58,6 +58,9 @@ namespace LANCommander.PlaynitePlugin
|
||||||
if (arguments != null)
|
if (arguments != null)
|
||||||
process.StartInfo.Arguments += " " + arguments;
|
process.StartInfo.Arguments += " " + arguments;
|
||||||
|
|
||||||
|
if (workingDirectory != null)
|
||||||
|
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||||
|
|
||||||
if (asAdmin)
|
if (asAdmin)
|
||||||
{
|
{
|
||||||
process.StartInfo.Verb = "runas";
|
process.StartInfo.Verb = "runas";
|
||||||
|
@ -68,6 +71,8 @@ namespace LANCommander.PlaynitePlugin
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
|
|
||||||
Wow64RevertWow64FsRedirection(ref wow64Value);
|
Wow64RevertWow64FsRedirection(ref wow64Value);
|
||||||
|
|
||||||
|
return process.ExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunScript(Game game, ScriptType type, string arguments = null)
|
public void RunScript(Game game, ScriptType type, string arguments = null)
|
||||||
|
|
|
@ -4,7 +4,15 @@
|
||||||
{
|
{
|
||||||
Install,
|
Install,
|
||||||
Uninstall,
|
Uninstall,
|
||||||
|
[Display(Name = "Name Change")]
|
||||||
NameChange,
|
NameChange,
|
||||||
KeyChange
|
[Display(Name = "Key Change")]
|
||||||
|
KeyChange,
|
||||||
|
[Display(Name = "Save Upload")]
|
||||||
|
SaveUpload,
|
||||||
|
[Display(Name = "Save Download")]
|
||||||
|
SaveDownload,
|
||||||
|
[Display(Name = "Detect Install")]
|
||||||
|
DetectInstall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,6 @@ namespace LANCommander.SDK.Models
|
||||||
public virtual Company Developer { get; set; }
|
public virtual Company Developer { get; set; }
|
||||||
public virtual IEnumerable<Archive> Archives { get; set; }
|
public virtual IEnumerable<Archive> Archives { get; set; }
|
||||||
public virtual IEnumerable<Script> Scripts { get; set; }
|
public virtual IEnumerable<Script> Scripts { get; set; }
|
||||||
|
public virtual IEnumerable<Redistributable> Redistributables { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LANCommander.SDK.Models
|
||||||
|
{
|
||||||
|
public class Redistributable : BaseModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
public DateTime ReleasedOn { get; set; }
|
||||||
|
public virtual IEnumerable<Archive> Archives { get; set; }
|
||||||
|
public virtual IEnumerable<Script> Scripts { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
using LANCommander.Data.Models;
|
||||||
|
using LANCommander.Extensions;
|
||||||
|
using LANCommander.Models;
|
||||||
|
using LANCommander.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace LANCommander.Controllers.Api
|
||||||
|
{
|
||||||
|
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class RedistributableController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly RedistributableService RedistributableService;
|
||||||
|
private readonly LANCommanderSettings Settings = SettingService.GetSettings();
|
||||||
|
|
||||||
|
public RedistributableController(RedistributableService redistributableService)
|
||||||
|
{
|
||||||
|
|
||||||
|
RedistributableService = redistributableService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IEnumerable<Redistributable>> Get()
|
||||||
|
{
|
||||||
|
return await RedistributableService.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<Redistributable> Get(Guid id)
|
||||||
|
{
|
||||||
|
return await RedistributableService.Get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/Download")]
|
||||||
|
public async Task<IActionResult> Download(Guid id)
|
||||||
|
{
|
||||||
|
var redistributable = await RedistributableService.Get(id);
|
||||||
|
|
||||||
|
if (redistributable == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
if (redistributable.Archives == null || redistributable.Archives.Count == 0)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var archive = redistributable.Archives.OrderByDescending(a => a.CreatedOn).First();
|
||||||
|
|
||||||
|
var filename = Path.Combine(Settings.Archives.StoragePath, archive.ObjectKey);
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(filename))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{redistributable.Name.SanitizeFilename()}.zip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue