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(), () =>
|
||||
{
|
||||
Logger.Trace("Attempting to download and extract game...");
|
||||
return DownloadAndExtract(game);
|
||||
return DownloadAndExtractGame(game);
|
||||
});
|
||||
|
||||
if (!result.Success && !result.Canceled)
|
||||
|
@ -88,6 +88,12 @@ namespace LANCommander.PlaynitePlugin
|
|||
SaveScript(game, result.Directory, ScriptType.NameChange);
|
||||
SaveScript(game, result.Directory, ScriptType.KeyChange);
|
||||
|
||||
if (game.Redistributables != null && game.Redistributables.Count() > 0)
|
||||
{
|
||||
Logger.Trace("Installing required redistributables...");
|
||||
InstallRedistributables(game);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PowerShellRuntime.RunScript(PlayniteGame, ScriptType.Install);
|
||||
|
@ -106,7 +112,7 @@ namespace LANCommander.PlaynitePlugin
|
|||
InvokeOnInstalled(new GameInstalledEventArgs(installInfo));
|
||||
}
|
||||
|
||||
private ExtractionResult DownloadAndExtract(LANCommander.SDK.Models.Game game)
|
||||
private ExtractionResult DownloadAndExtractGame(LANCommander.SDK.Models.Game game)
|
||||
{
|
||||
if (game == null)
|
||||
{
|
||||
|
@ -204,6 +210,152 @@ namespace LANCommander.PlaynitePlugin
|
|||
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)
|
||||
{
|
||||
string tempFile = String.Empty;
|
||||
|
@ -294,6 +446,17 @@ namespace LANCommander.PlaynitePlugin
|
|||
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)
|
||||
{
|
||||
var script = game.Scripts.FirstOrDefault(s => s.Type == type);
|
||||
|
|
|
@ -206,6 +206,11 @@ namespace LANCommander.PlaynitePlugin
|
|||
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)
|
||||
{
|
||||
return DownloadRequest($"/api/Saves/Download/{id}", progressHandler, completeHandler);
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace LANCommander.PlaynitePlugin
|
|||
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}");
|
||||
|
||||
|
@ -58,6 +58,9 @@ namespace LANCommander.PlaynitePlugin
|
|||
if (arguments != null)
|
||||
process.StartInfo.Arguments += " " + arguments;
|
||||
|
||||
if (workingDirectory != null)
|
||||
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||
|
||||
if (asAdmin)
|
||||
{
|
||||
process.StartInfo.Verb = "runas";
|
||||
|
@ -68,6 +71,8 @@ namespace LANCommander.PlaynitePlugin
|
|||
process.WaitForExit();
|
||||
|
||||
Wow64RevertWow64FsRedirection(ref wow64Value);
|
||||
|
||||
return process.ExitCode;
|
||||
}
|
||||
|
||||
public void RunScript(Game game, ScriptType type, string arguments = null)
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
{
|
||||
Install,
|
||||
Uninstall,
|
||||
[Display(Name = "Name Change")]
|
||||
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 IEnumerable<Archive> Archives { 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