Add game importing via CLI

This commit is contained in:
Pat Hartl 2024-08-12 19:33:26 -05:00
parent 4009aa3892
commit bf9ef30b4b
7 changed files with 114 additions and 4 deletions

View file

@ -8,6 +8,12 @@ using System.Threading.Tasks;
namespace LANCommander.Launcher.Models
{
public enum ImportArchiveType {
Game,
Redistributable,
Server
}
[Verb("RunScript", HelpText = "Run a script for a game")]
public class RunScriptCommandLineOptions
{
@ -44,8 +50,18 @@ namespace LANCommander.Launcher.Models
public Guid GameId { get; set; }
}
[Verb("Import", HelpText = "Import library items from the server")]
public class ImportCommandLineOptions { }
[Verb("Sync", HelpText = "Sync library items from the server")]
public class SyncCommandLineOptions { }
[Verb("Import", HelpText = "Upload and import an archive to the server (Admin Only)")]
public class ImportCommandLineOptions
{
[Option("Path", HelpText = "Path to the archive file", Required = true)]
public string Path { get; set; }
[Option("Type", HelpText = "The type of archive to import", Required = true)]
public ImportArchiveType Type { get; set; }
}
[Verb("Login", HelpText = "Login to a LANCommander server")]
public class LoginCommandLineOptions

View file

@ -36,7 +36,7 @@ namespace LANCommander.Launcher.Services
RunScriptCommandLineOptions,
InstallCommandLineOptions,
UninstallCommandLineOptions,
ImportCommandLineOptions,
SyncCommandLineOptions,
LoginCommandLineOptions,
LogoutCommandLineOptions,
ChangeAliasCommandLineOptions
@ -45,6 +45,7 @@ namespace LANCommander.Launcher.Services
await result.WithParsedAsync<RunScriptCommandLineOptions>(RunScript);
await result.WithParsedAsync<InstallCommandLineOptions>(Install);
await result.WithParsedAsync<UninstallCommandLineOptions>(Uninstall);
await result.WithParsedAsync<SyncCommandLineOptions>(Sync);
await result.WithParsedAsync<ImportCommandLineOptions>(Import);
await result.WithParsedAsync<LoginCommandLineOptions>(Login);
await result.WithParsedAsync<LogoutCommandLineOptions>(Logout);
@ -120,7 +121,7 @@ namespace LANCommander.Launcher.Services
}
}
private async Task Import(ImportCommandLineOptions options)
private async Task Sync(SyncCommandLineOptions options)
{
Console.WriteLine("Importing games from server...");

View file

@ -6,6 +6,7 @@ using RestSharp;
using RestSharp.Interceptors;
using Semver;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
@ -312,6 +313,41 @@ namespace LANCommander.SDK
return response;
}
internal async Task<Guid> ChunkedUploadRequestAsync(string fileName, Stream stream, bool ignoreVersion = false)
{
var maxChunkSize = 1024 * 1024 * 50;
var initResponse = await PostRequestAsync<UploadInitResponse>("/Upload/Init", ignoreVersion);
var buffer = new byte[maxChunkSize];
while (stream.Position < stream.Length)
{
var chunkRequest = new UploadChunkRequest();
chunkRequest.Start = stream.Position;
if (stream.Position + maxChunkSize > stream.Length)
{
var bytes = stream.Length - stream.Position;
buffer = new byte[bytes];
await stream.ReadAsync(buffer, 0, (int)(stream.Length - stream.Position));
}
else
await stream.ReadAsync(buffer, 0, maxChunkSize);
chunkRequest.End = stream.Position;
chunkRequest.Total = stream.Length;
chunkRequest.File = buffer;
chunkRequest.Key = initResponse.Key;
await PostRequestAsync<object>("/Upload/Chunk", chunkRequest, ignoreVersion);
}
return initResponse.Key;
}
public async Task<AuthToken> AuthenticateAsync(string username, string password, bool ignoreVersion = false)
{
var request = new RestRequest("/api/Auth", Method.Post);

View file

@ -614,6 +614,17 @@ namespace LANCommander.SDK
Reader?.Cancel();
}
public async Task ImportAsync(string archivePath)
{
using (var fs = new FileStream(archivePath, FileMode.Open, FileAccess.Read))
{
var objectKey = await Client.ChunkedUploadRequestAsync("", fs);
if (objectKey != Guid.Empty)
await Client.PostRequestAsync<object>($"/api/Games/Import/{objectKey}");
}
}
public static string GetMetadataDirectoryPath(string installDirectory, Guid gameId)
{
if (String.IsNullOrWhiteSpace(installDirectory))

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LANCommander.SDK.Models
{
internal class UploadChunkRequest
{
internal byte[] File { get; set; }
internal long Start { get; set; }
internal long End { get; set; }
internal long Total { get; set; }
internal Guid Key { get; set; }
}
}

View file

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LANCommander.SDK.Models
{
internal class UploadInitResponse
{
public Guid Key { get; set; }
}
}

View file

@ -110,5 +110,21 @@ namespace LANCommander.Server.Controllers.Api
return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{game.Title.SanitizeFilename()}.zip");
}
[Authorize(Roles = "Administrator")]
[HttpPost("Import/{objectKey}")]
public async Task<IActionResult> Import(Guid objectKey)
{
try
{
var game = await GameService.Import(objectKey);
return Ok();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
}
}