Add some support for reading icons from archives.

dashboard
Pat Hartl 2023-01-08 01:34:38 -06:00
parent ac87659b2a
commit b2ffdb9f23
6 changed files with 161 additions and 1 deletions

1
.gitignore vendored
View File

@ -349,3 +349,4 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder # Ionide (cross platform F# VS Code tools) working folder
.ionide/ .ionide/
Upload/ Upload/
LANCommander/Icon/

View File

@ -49,6 +49,8 @@ namespace LANCommander.Playnite.Extension
{ {
var existingGame = PlayniteApi.Database.Games.FirstOrDefault(g => g.GameId == game.Id.ToString() && g.PluginId == Id && g.IsInstalled); var existingGame = PlayniteApi.Database.Games.FirstOrDefault(g => g.GameId == game.Id.ToString() && g.PluginId == Id && g.IsInstalled);
var iconUri = new Uri(new Uri(Settings.ServerAddress), $"Games/GetIcon/{game.Id}");
var metadata = new GameMetadata() var metadata = new GameMetadata()
{ {
IsInstalled = existingGame != null, IsInstalled = existingGame != null,
@ -57,7 +59,8 @@ namespace LANCommander.Playnite.Extension
Description = game.Description, Description = game.Description,
GameId = game.Id.ToString(), GameId = game.Id.ToString(),
ReleaseDate = new ReleaseDate(game.ReleasedOn), ReleaseDate = new ReleaseDate(game.ReleasedOn),
Version = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault().Version, Version = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault().Version,
Icon = new MetadataFile(iconUri.ToString())
}; };
gameMetadata.Add(metadata); gameMetadata.Add(metadata);

View File

@ -9,6 +9,8 @@ using Microsoft.EntityFrameworkCore;
using LANCommander.Data; using LANCommander.Data;
using LANCommander.Data.Models; using LANCommander.Data.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using LANCommander.Services;
using System.Drawing;
namespace LANCommander.Controllers namespace LANCommander.Controllers
{ {
@ -153,10 +155,14 @@ namespace LANCommander.Controllers
foreach (var archive in game.Archives.OrderByDescending(a => a.CreatedOn)) foreach (var archive in game.Archives.OrderByDescending(a => a.CreatedOn))
{ {
var archiveFile = Path.Combine("Upload", archive.ObjectKey); var archiveFile = Path.Combine("Upload", archive.ObjectKey);
var iconFile = Path.Combine("Icon", $"{game.Id}.png");
if (System.IO.File.Exists(archiveFile)) if (System.IO.File.Exists(archiveFile))
System.IO.File.Delete(archiveFile); System.IO.File.Delete(archiveFile);
if (System.IO.File.Exists(iconFile))
System.IO.File.Delete(iconFile);
archiveRepo.Delete(archive); archiveRepo.Delete(archive);
} }
@ -172,6 +178,82 @@ namespace LANCommander.Controllers
} }
} }
public async Task<IActionResult> GetIcon(Guid id)
{
var cachedPath = Path.Combine("Icon", $"{id}.png");
if (!System.IO.Directory.Exists("Icon"))
System.IO.Directory.CreateDirectory("Icon");
if (System.IO.File.Exists(cachedPath))
{
return File(System.IO.File.ReadAllBytes(cachedPath), "image/png");
}
else
{
using (var repo = new Repository<Game>(Context, HttpContext))
{
var game = await repo.Find(id);
if (game.Archives == null || game.Archives.Count == 0)
return NotFound();
var archive = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault();
Bitmap bitmap = null;
var manifest = ArchiveService.ReadManifest(archive.ObjectKey);
var iconReference = ArchiveService.ReadFile(archive.ObjectKey, manifest.Icon);
if (IsWinPEFile(iconReference))
{
var tmp = System.IO.Path.GetTempFileName();
System.IO.File.WriteAllBytes(tmp, iconReference);
var icon = System.Drawing.Icon.ExtractAssociatedIcon(tmp);
bitmap = icon.ToBitmap();
}
else
{
using (var ms = new MemoryStream(iconReference))
{
bitmap = (Bitmap)Bitmap.FromStream(ms);
}
}
var iconPng = ConvertToPng(bitmap);
System.IO.File.WriteAllBytes(cachedPath, iconPng);
return File(iconPng, "image/png");
}
}
}
private static bool IsWinPEFile(byte[] file)
{
var mz = new byte[2];
using (var ms = new MemoryStream(file))
{
ms.Read(mz, 0, 2);
}
return System.Text.Encoding.UTF8.GetString(mz) == "MZ";
}
private static byte[] ConvertToPng(Image img)
{
using (var stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
return stream.ToArray();
}
}
private bool GameExists(Guid id) private bool GameExists(Guid id)
{ {
return (Context.Games?.Any(e => e.Id == id)).GetValueOrDefault(); return (Context.Games?.Any(e => e.Id == id)).GetValueOrDefault();

View File

@ -0,0 +1,65 @@
using LANCommander.Models;
using System.IO.Compression;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace LANCommander.Services
{
public static class ArchiveService
{
public static GameManifest ReadManifest(string objectKey)
{
var upload = Path.Combine("Upload", objectKey);
string manifestContents = String.Empty;
if (!System.IO.File.Exists(upload))
throw new FileNotFoundException(upload);
using (ZipArchive zip = ZipFile.OpenRead(upload))
{
var entry = zip.Entries.FirstOrDefault(e => e.FullName == "_manifest.yml");
if (entry == null)
throw new FileNotFoundException("Manifest not found");
using (StreamReader sr = new StreamReader(entry.Open()))
{
manifestContents = sr.ReadToEnd();
}
}
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
var manifest = deserializer.Deserialize<GameManifest>(manifestContents);
return manifest;
}
public static byte[] ReadFile(string objectKey, string path)
{
var upload = Path.Combine("Upload", objectKey);
if (!System.IO.File.Exists(upload))
throw new FileNotFoundException(upload);
using (ZipArchive zip = ZipFile.OpenRead(upload))
{
var entry = zip.Entries.FirstOrDefault(e => e.FullName == path);
if (entry == null)
throw new FileNotFoundException(path);
using (var ms = new MemoryStream())
{
entry.Open().CopyTo(ms);
return ms.ToArray();
}
}
}
}
}

View File

@ -33,6 +33,7 @@
<table class="table table-vcenter table-mobile-md card-table"> <table class="table table-vcenter table-mobile-md card-table">
<thead> <thead>
<tr> <tr>
<th></th>
<th> <th>
@Html.DisplayNameFor(model => model.Title) @Html.DisplayNameFor(model => model.Title)
</th> </th>
@ -65,6 +66,7 @@
@foreach (var item in Model) @foreach (var item in Model)
{ {
<tr> <tr>
<td><img src="@Url.Action("GetIcon", "Games", new { id = item.Id })" /></td>
<td> <td>
@Html.DisplayFor(modelItem => item.Title) @Html.DisplayFor(modelItem => item.Title)
</td> </td>

View File

@ -0,0 +1,7 @@
{
"runtimeOptions": {
"configProperties": {
"System.Drawing.EnableUnixSupport": true
}
}
}