Remove icon extraction from WinPE files. Add migration from old extracted icons to new media entries.
This commit is contained in:
parent
f0c8296b6e
commit
f275d3478b
11 changed files with 1701 additions and 162 deletions
|
@ -15,7 +15,6 @@ namespace LANCommander.SDK
|
|||
public IEnumerable<string> Publishers { get; set; }
|
||||
public IEnumerable<string> Developers { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public IEnumerable<GameAction> Actions { get; set; }
|
||||
public bool Singleplayer { get; set; }
|
||||
public MultiplayerInfo LocalMultiplayer { get; set; }
|
||||
|
|
|
@ -42,8 +42,6 @@ namespace LANCommander.Controllers.Api
|
|||
{
|
||||
var manifest = await GameService.GetManifest(id);
|
||||
|
||||
manifest.Icon = Url.Action(nameof(GetIcon), new { id = id });
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
|
@ -67,21 +65,5 @@ namespace LANCommander.Controllers.Api
|
|||
|
||||
return File(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{game.Title.SanitizeFilename()}.zip");
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("{id}/Icon.png")]
|
||||
public async Task<IActionResult> GetIcon(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var game = await GameService.Get(id);
|
||||
|
||||
return File(GameService.GetIcon(game), "image/png");
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace LANCommander.Data.Models
|
|||
public string Title { get; set; }
|
||||
[Display(Name = "Sort Title")]
|
||||
public string? SortTitle { get; set; }
|
||||
public string? Icon { get; set; }
|
||||
[Display(Name = "Directory Name")]
|
||||
public string? DirectoryName { get; set; }
|
||||
public string? Description { get; set; }
|
||||
|
|
1624
LANCommander/Migrations/20231103052501_RemoveGameIconProperty.Designer.cs
generated
Normal file
1624
LANCommander/Migrations/20231103052501_RemoveGameIconProperty.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,58 @@
|
|||
using LANCommander.Data.Enums;
|
||||
using LANCommander.Services;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LANCommander.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RemoveGameIconProperty : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var settings = SettingService.GetSettings();
|
||||
|
||||
// Migrate any old icons from the filesystem
|
||||
if (Directory.Exists("Icon"))
|
||||
foreach (var file in Directory.EnumerateFiles("Icon"))
|
||||
{
|
||||
var objectKey = Path.GetFileNameWithoutExtension(file);
|
||||
|
||||
var mediaId = Guid.NewGuid();
|
||||
var fileId = Guid.NewGuid();
|
||||
|
||||
// Probably not an issue, but we'll check to make sure these are valid GUIDs just in case
|
||||
if (Guid.TryParse(objectKey, out var gameId))
|
||||
{
|
||||
var sql = $@"
|
||||
INSERT INTO Media
|
||||
(Id, FileId, Type, SourceUrl, GameId, MimeType, CreatedOn, UpdatedOn)
|
||||
SELECT
|
||||
'{mediaId.ToString().ToUpper()}', '{fileId.ToString().ToUpper()}', '{(int)MediaType.Icon}', '', '{gameId.ToString().ToUpper()}', 'image/png', DateTime('now'), DateTime('now')
|
||||
WHERE EXISTS (SELECT 1 FROM Games WHERE Id = '{gameId.ToString().ToUpper()}')
|
||||
";
|
||||
|
||||
migrationBuilder.Sql(sql);
|
||||
}
|
||||
|
||||
File.Move(file, Path.Combine(settings.Media.StoragePath, fileId.ToString()));
|
||||
}
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Icon",
|
||||
table: "Games");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Icon",
|
||||
table: "Games",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -309,9 +309,6 @@ namespace LANCommander.Migrations
|
|||
b.Property<long?>("IGDBId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Icon")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
|
|
@ -73,9 +73,6 @@
|
|||
<FormItem Label="Sort Title">
|
||||
<Input @bind-Value="@context.SortTitle" />
|
||||
</FormItem>
|
||||
<FormItem Label="Icon">
|
||||
<FilePicker @bind-Value="context.Icon" ArchiveId="@LatestArchiveId" AllowDirectories="true" />
|
||||
</FormItem>
|
||||
<FormItem Label="Notes">
|
||||
<TextArea @bind-Value="@context.Notes" MaxLength=2000 ShowCount />
|
||||
</FormItem>
|
||||
|
@ -260,34 +257,6 @@ else
|
|||
MessageService.Success("Game added!");
|
||||
}
|
||||
|
||||
private async Task BrowseForIcon()
|
||||
{
|
||||
var modalOptions = new ModalOptions()
|
||||
{
|
||||
Title = "Choose Icon",
|
||||
Maximizable = false,
|
||||
DefaultMaximized = true,
|
||||
Closable = true,
|
||||
OkText = "Select File"
|
||||
};
|
||||
|
||||
var browserOptions = new FilePickerOptions()
|
||||
{
|
||||
ArchiveId = Game.Archives.FirstOrDefault().Id,
|
||||
Select = true,
|
||||
Multiple = false
|
||||
};
|
||||
|
||||
var modalRef = await ModalService.CreateModalAsync<FilePickerDialog, FilePickerOptions, IEnumerable<IFileManagerEntry>>(modalOptions, browserOptions);
|
||||
|
||||
modalRef.OnOk = (results) =>
|
||||
{
|
||||
Game.Icon = results.FirstOrDefault().Path;
|
||||
StateHasChanged();
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
private async Task OnGameLookupResultSelected(GameLookupResult result)
|
||||
{
|
||||
Game.Title = result.IGDBMetadata.Name;
|
||||
|
|
|
@ -145,7 +145,12 @@
|
|||
|
||||
private string GetIcon(Game game)
|
||||
{
|
||||
return $"/api/Games/{game.Id}/Icon.png";
|
||||
var media = game?.Media?.FirstOrDefault(m => m.Type == Data.Enums.MediaType.Icon);
|
||||
|
||||
if (media != null)
|
||||
return $"/api/Media/{media.Id}/Download?fileId={media.FileId}";
|
||||
else
|
||||
return "/favicon.ico";
|
||||
}
|
||||
|
||||
private void Add()
|
||||
|
|
|
@ -123,7 +123,12 @@
|
|||
|
||||
private string GetIcon(Game game)
|
||||
{
|
||||
return $"/api/Games/{game?.Id}/Icon.png";
|
||||
var media = game?.Media?.FirstOrDefault(m => m.Type == Data.Enums.MediaType.Icon);
|
||||
|
||||
if (media != null)
|
||||
return $"/api/Media/{media.Id}/Download?fileId={media.FileId}";
|
||||
else
|
||||
return "/favicon.ico";
|
||||
}
|
||||
|
||||
private async Task StartServers()
|
||||
|
|
|
@ -8,14 +8,6 @@
|
|||
<PageHeader Title="Tools" />
|
||||
|
||||
<div style="padding: 0 24px;">
|
||||
<h3>Icon Cache</h3>
|
||||
<p>
|
||||
If your icons look wrong or need to be refreshed, click the button below to clear the icon cache. Note that icons will only be regenerated when requested.
|
||||
</p>
|
||||
<Button Type="@ButtonType.Primary" OnClick="ClearIconCache" Loading="ClearingIconCache">Clear Icon Cache</Button>
|
||||
|
||||
<Divider />
|
||||
|
||||
<h3>Recalculate File Sizes</h3>
|
||||
<p>
|
||||
Some file sizes are cached in the database. Click the button below to scan through all files and recalculate their size.
|
||||
|
@ -37,27 +29,8 @@
|
|||
|
||||
|
||||
@code {
|
||||
bool ClearingIconCache = false;
|
||||
bool RecalculatingFileSizes = false;
|
||||
|
||||
async Task ClearIconCache()
|
||||
{
|
||||
ClearingIconCache = true;
|
||||
|
||||
foreach (var icon in Directory.GetFiles("Icon"))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(icon);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
ClearingIconCache = false;
|
||||
|
||||
await MessageService.Success("Icon cache cleared!");
|
||||
}
|
||||
|
||||
async Task RecalculateFileSizes()
|
||||
{
|
||||
RecalculatingFileSizes = true;
|
||||
|
|
|
@ -12,10 +12,12 @@ namespace LANCommander.Services
|
|||
public class GameService : BaseDatabaseService<Game>
|
||||
{
|
||||
private readonly ArchiveService ArchiveService;
|
||||
private readonly MediaService MediaService;
|
||||
|
||||
public GameService(DatabaseContext dbContext, IHttpContextAccessor httpContextAccessor, ArchiveService archiveService) : base(dbContext, httpContextAccessor)
|
||||
public GameService(DatabaseContext dbContext, IHttpContextAccessor httpContextAccessor, ArchiveService archiveService, MediaService mediaService) : base(dbContext, httpContextAccessor)
|
||||
{
|
||||
ArchiveService = archiveService;
|
||||
MediaService = mediaService;
|
||||
}
|
||||
|
||||
public override async Task Delete(Game game)
|
||||
|
@ -23,8 +25,11 @@ namespace LANCommander.Services
|
|||
foreach (var archive in game.Archives.OrderByDescending(a => a.CreatedOn))
|
||||
{
|
||||
await ArchiveService.Delete(archive);
|
||||
}
|
||||
|
||||
FileHelpers.DeleteIfExists($"Icon/{game.Id}.png".ToPath());
|
||||
foreach (var media in game.Media)
|
||||
{
|
||||
await MediaService.Delete(media);
|
||||
}
|
||||
|
||||
await base.Delete(game);
|
||||
|
@ -44,7 +49,6 @@ namespace LANCommander.Services
|
|||
Description = game.Description,
|
||||
ReleasedOn = game.ReleasedOn.GetValueOrDefault(),
|
||||
Singleplayer = game.Singleplayer,
|
||||
Icon = game.Icon
|
||||
};
|
||||
|
||||
if (game.Genres != null && game.Genres.Count > 0)
|
||||
|
@ -115,81 +119,5 @@ namespace LANCommander.Services
|
|||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
public byte[] GetIcon(Game game)
|
||||
{
|
||||
var cachedPath = $"Icon/{game.Id}.png";
|
||||
|
||||
if (File.Exists(cachedPath))
|
||||
return File.ReadAllBytes(cachedPath);
|
||||
else
|
||||
{
|
||||
#if WINDOWS
|
||||
try
|
||||
{
|
||||
if (game.Archives == null || game.Archives.Count == 0)
|
||||
throw new FileNotFoundException();
|
||||
|
||||
var archive = game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault();
|
||||
|
||||
Bitmap bitmap = null;
|
||||
|
||||
var iconReference = ArchiveService.ReadFile(archive.ObjectKey, game.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);
|
||||
|
||||
File.WriteAllBytes(cachedPath, iconPng);
|
||||
|
||||
return iconPng;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
return File.ReadAllBytes("favicon.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue