From ffc51f4e0dd67ab0ba82ca3847be95448481bfde Mon Sep 17 00:00:00 2001 From: Pat Hartl Date: Fri, 6 Jan 2023 22:12:03 -0600 Subject: [PATCH] Basic download and extract of games --- .../InstallController.cs | 124 ++++++++++++++++++ .../LANCommander.Playnite.Extension.csproj | 5 + .../LANCommanderClient.cs | 30 +++++ .../PlayniteLibraryPlugin.cs | 10 +- LANCommander.Playnite.Extension/app.config | 8 ++ .../packages.config | 1 + .../Extensions/StringExtensions.cs | 15 +++ .../Controllers/Api/ArchivesController.cs | 18 +++ 8 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 LANCommander.Playnite.Extension/InstallController.cs create mode 100644 LANCommander.SDK/Extensions/StringExtensions.cs diff --git a/LANCommander.Playnite.Extension/InstallController.cs b/LANCommander.Playnite.Extension/InstallController.cs new file mode 100644 index 0000000..6ef7cee --- /dev/null +++ b/LANCommander.Playnite.Extension/InstallController.cs @@ -0,0 +1,124 @@ +using Playnite.SDK; +using Playnite.SDK.Models; +using Playnite.SDK.Plugins; +using LANCommander.SDK.Extensions; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using ICSharpCode.SharpZipLib.Zip; +using ICSharpCode.SharpZipLib.Core; + +namespace LANCommander.Playnite.Extension +{ + public class LANCommanderInstallController : InstallController + { + private PlayniteLibraryPlugin Plugin; + + public LANCommanderInstallController(PlayniteLibraryPlugin plugin, Game game) : base(game) + { + Name = "Install using LANCommander"; + Plugin = plugin; + + } + + public override void Install(InstallActionArgs args) + { + var tempPath = System.IO.Path.GetTempFileName(); + var gameId = Guid.Parse(Game.GameId); + + var game = Plugin.LANCommander.GetGame(gameId); + + var tempFile = Download(game); + + Extract(game, tempFile); + } + + private string Download(LANCommander.SDK.Models.Game game) + { + string tempFile = String.Empty; + + var archive = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault(); + + if (archive != null) + { + var result = Plugin.PlayniteApi.Dialogs.ActivateGlobalProgress(progress => + { + progress.ProgressMaxValue = 100; + progress.CurrentProgressValue = 0; + + var destination = Plugin.LANCommander.DownloadArchive(archive.Id, (changed) => + { + progress.CurrentProgressValue = changed.ProgressPercentage; + }, (complete) => + { + progress.CurrentProgressValue = 100; + }); + + while (progress.CurrentProgressValue != 100) + { + + } + + tempFile = destination; + }, + new GlobalProgressOptions($"Downloading {game.Title}") + { + IsIndeterminate = false, + Cancelable = false, + }); + + return tempFile; + } + else + throw new Exception("Game failed to download"); + } + + private void Extract(LANCommander.SDK.Models.Game game, string archivePath) + { + var destination = $"C:\\Games\\{game.Title.SanitizeFilename()}"; + + ZipFile file = null; + + try + { + FileStream fs = File.OpenRead(archivePath); + + file = new ZipFile(fs); + + foreach (ZipEntry entry in file) + { + if (!entry.IsFile) + continue; + + byte[] buffer = new byte[4096]; + var zipStream = file.GetInputStream(entry); + + var entryDestination = Path.Combine(destination, entry.Name); + var entryDirectory = Path.GetDirectoryName(entryDestination); + + if (!String.IsNullOrWhiteSpace(entryDirectory)) + Directory.CreateDirectory(entryDirectory); + + using (FileStream streamWriter = File.Create(entryDestination)) + { + StreamUtils.Copy(zipStream, streamWriter, buffer); + } + } + } + finally + { + if (file != null) + { + file.IsStreamOwner = true; + file.Close(); + } + + File.Delete(archivePath); + } + } + } +} diff --git a/LANCommander.Playnite.Extension/LANCommander.Playnite.Extension.csproj b/LANCommander.Playnite.Extension/LANCommander.Playnite.Extension.csproj index cd35c59..49dc42c 100644 --- a/LANCommander.Playnite.Extension/LANCommander.Playnite.Extension.csproj +++ b/LANCommander.Playnite.Extension/LANCommander.Playnite.Extension.csproj @@ -31,6 +31,9 @@ 4 + + ..\packages\SharpZipLib.1.4.1\lib\netstandard2.0\ICSharpCode.SharpZipLib.dll + ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -47,6 +50,7 @@ ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll @@ -80,6 +84,7 @@ + diff --git a/LANCommander.Playnite.Extension/LANCommanderClient.cs b/LANCommander.Playnite.Extension/LANCommanderClient.cs index 8ccd7b0..805db6b 100644 --- a/LANCommander.Playnite.Extension/LANCommanderClient.cs +++ b/LANCommander.Playnite.Extension/LANCommanderClient.cs @@ -1,7 +1,10 @@ using LANCommander.SDK.Models; using RestSharp; +using RestSharp.Extensions; using System; using System.Collections.Generic; +using System.ComponentModel; +using System.IO; using System.Linq; using System.Net; using System.Text; @@ -40,6 +43,23 @@ namespace LANCommander.Playnite.Extension return response.Data; } + private string DownloadRequest(string route, Action progressHandler, Action completeHandler) + { + var client = new WebClient(); + var tempFile = Path.GetTempFileName(); + + client.Headers.Add("Authorization", $"Bearer {Token.AccessToken}"); + client.DownloadProgressChanged += (s, e) => progressHandler(e); + client.DownloadFileCompleted += (s, e) => completeHandler(e); + + var request = new RestRequest(route) + .AddHeader("Authorization", $"Bearer {Token.AccessToken}"); + + client.DownloadFileAsync(new Uri($"{Client.BaseUrl}{route}"), tempFile); + + return tempFile; + } + public AuthResponse Authenticate(string username, string password) { var response = Client.Post(new RestRequest("/api/Auth").AddJsonBody(new AuthRequest() @@ -86,5 +106,15 @@ namespace LANCommander.Playnite.Extension { return GetRequest>("/api/Games"); } + + public Game GetGame(Guid id) + { + return GetRequest($"/api/Games/{id}"); + } + + public string DownloadArchive(Guid id, Action progressHandler, Action completeHandler) + { + return DownloadRequest($"api/Archives/Download/{id}", progressHandler, completeHandler); + } } } diff --git a/LANCommander.Playnite.Extension/PlayniteLibraryPlugin.cs b/LANCommander.Playnite.Extension/PlayniteLibraryPlugin.cs index d2d028f..0262100 100644 --- a/LANCommander.Playnite.Extension/PlayniteLibraryPlugin.cs +++ b/LANCommander.Playnite.Extension/PlayniteLibraryPlugin.cs @@ -77,12 +77,20 @@ namespace LANCommander.Playnite.Extension return games; } - catch + catch (Exception ex) { return new List(); } } + public override IEnumerable GetInstallActions(GetInstallActionsArgs args) + { + if (args.Game.PluginId != Id) + yield break; + + yield return new LANCommanderInstallController(this, args.Game); + } + public override ISettings GetSettings(bool firstRunSettings) { return Settings; diff --git a/LANCommander.Playnite.Extension/app.config b/LANCommander.Playnite.Extension/app.config index 7d2aa9e..b9fa866 100644 --- a/LANCommander.Playnite.Extension/app.config +++ b/LANCommander.Playnite.Extension/app.config @@ -10,6 +10,14 @@ + + + + + + + + \ No newline at end of file diff --git a/LANCommander.Playnite.Extension/packages.config b/LANCommander.Playnite.Extension/packages.config index f991b70..168f677 100644 --- a/LANCommander.Playnite.Extension/packages.config +++ b/LANCommander.Playnite.Extension/packages.config @@ -3,6 +3,7 @@ + diff --git a/LANCommander.SDK/Extensions/StringExtensions.cs b/LANCommander.SDK/Extensions/StringExtensions.cs new file mode 100644 index 0000000..9771b96 --- /dev/null +++ b/LANCommander.SDK/Extensions/StringExtensions.cs @@ -0,0 +1,15 @@ +using System.IO; +using System.Text.RegularExpressions; + +namespace LANCommander.SDK.Extensions +{ + public static class StringExtensions + { + public static string SanitizeFilename(this string filename, string replacement = "") + { + var removeInvalidChars = new Regex($"[{Regex.Escape(new string(Path.GetInvalidFileNameChars()))}]", RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant); + + return removeInvalidChars.Replace(filename, replacement); + } + } +} diff --git a/LANCommander/Controllers/Api/ArchivesController.cs b/LANCommander/Controllers/Api/ArchivesController.cs index c43cdd4..a69f60e 100644 --- a/LANCommander/Controllers/Api/ArchivesController.cs +++ b/LANCommander/Controllers/Api/ArchivesController.cs @@ -19,6 +19,24 @@ namespace LANCommander.Controllers.Api } [HttpGet] + public IEnumerable Get() + { + using (var repo = new Repository(Context, HttpContext)) + { + return repo.Get(a => true).ToList(); + } + } + + [HttpGet("{id}")] + public async Task Get(Guid id) + { + using (var repo = new Repository(Context, HttpContext)) + { + return await repo.Find(id); + } + } + + [HttpGet("Download/{id}")] public async Task Download(Guid id) { using (var repo = new Repository(Context, HttpContext))