Basic download and extract of games

dashboard
Pat Hartl 2023-01-06 22:12:03 -06:00
parent 1e63813cd6
commit ffc51f4e0d
8 changed files with 210 additions and 1 deletions

View File

@ -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);
}
}
}
}

View File

@ -31,6 +31,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="ICSharpCode.SharpZipLib, Version=1.4.1.12, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.1.4.1\lib\netstandard2.0\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
@ -47,6 +50,7 @@
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
</Reference>
@ -80,6 +84,7 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="InstallController.cs" />
<Compile Include="LANCommanderClient.cs" />
<Compile Include="PlayniteClient.cs" />
<Compile Include="PlayniteLibraryPlugin.cs" />

View File

@ -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<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> 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<AuthResponse>(new RestRequest("/api/Auth").AddJsonBody(new AuthRequest()
@ -86,5 +106,15 @@ namespace LANCommander.Playnite.Extension
{
return GetRequest<IEnumerable<Game>>("/api/Games");
}
public Game GetGame(Guid id)
{
return GetRequest<Game>($"/api/Games/{id}");
}
public string DownloadArchive(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action<AsyncCompletedEventArgs> completeHandler)
{
return DownloadRequest($"api/Archives/Download/{id}", progressHandler, completeHandler);
}
}
}

View File

@ -77,12 +77,20 @@ namespace LANCommander.Playnite.Extension
return games;
}
catch
catch (Exception ex)
{
return new List<GameMetadata>();
}
}
public override IEnumerable<InstallController> 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;

View File

@ -10,6 +10,14 @@
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.1" newVersion="5.0.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -3,6 +3,7 @@
<package id="Microsoft.Bcl.AsyncInterfaces" version="5.0.0" targetFramework="net462" />
<package id="PlayniteSDK" version="6.8.0" targetFramework="net462" />
<package id="RestSharp" version="106.15.0" targetFramework="net462" />
<package id="SharpZipLib" version="1.4.1" targetFramework="net462" />
<package id="System.Buffers" version="4.5.1" targetFramework="net462" />
<package id="System.Memory" version="4.5.4" targetFramework="net462" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net462" />

View File

@ -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);
}
}
}

View File

@ -19,6 +19,24 @@ namespace LANCommander.Controllers.Api
}
[HttpGet]
public IEnumerable<Archive> Get()
{
using (var repo = new Repository<Archive>(Context, HttpContext))
{
return repo.Get(a => true).ToList();
}
}
[HttpGet("{id}")]
public async Task<Archive> Get(Guid id)
{
using (var repo = new Repository<Archive>(Context, HttpContext))
{
return await repo.Find(id);
}
}
[HttpGet("Download/{id}")]
public async Task<IActionResult> Download(Guid id)
{
using (var repo = new Repository<Archive>(Context, HttpContext))