LANCommander/LANCommander.Server/UI/Pages/Games/Components/MediaEditor.razor
Pat Hartl 56a87354ef
Merge pull request #251 from RattleSN4K3/fix/MediaListOrderType
Fix Media.Type Select not updating
2025-05-03 16:53:41 -05:00

269 lines
9.1 KiB
Text

@using System.Net.Mime
@inject MediaService MediaService
@inject ModalService ModalService
@inject IMessageService MessageService
@inject NavigationManager NavigationManager
@inject ILogger<MediaEditor> Logger
<Flex Vertical Gap="FlexGap.Large" Style="width: 100%" Class="media-editor">
<Table TItem="Media" DataSource="@Values" RowKey="c => c.Id" HidePagination="true" Responsive>
<PropertyColumn Property="p => p.Id" Title="Preview" Width="100px" Align="ColumnAlign.Center">
@if (MediaService.FileExists(context))
{
string originalPath = $"/api/Media/{context.Id}/Download?fileId={context.FileId}";
string thumbnailPath = context.Thumbnail != null
? $"/api/Media/{context.Thumbnail?.Id}/Download?fileId={context.Thumbnail?.FileId}"
: $"/api/Media/{context.Id}/Thumbnail?fileId={context.FileId}";
if (context.Type == MediaType.Manual)
{
<Image Width="100px" Src="@thumbnailPath" PreviewVisible="false" OnClick="() => OpenManual(context)" />
}
else
{
<Image Width="100px" Src="@thumbnailPath" PreviewSrc="@originalPath" />
}
}
</PropertyColumn>
<PropertyColumn Property="p => p.Type">
<Select @key="context.Id" @bind-Value="context.Type" TItem="MediaType" TItemValue="MediaType" DataSource="AllowedMediaTypes" />
</PropertyColumn>
<PropertyColumn Property="p => p.Name">
@if (context.Type == MediaType.Manual)
{
<Input @bind-Value="context.Name" OnBlur="() => Update(context)"/>
}
</PropertyColumn>
<ActionColumn>
<Flex Gap="FlexGap.Small" Justify="FlexJustify.End">
@{
var fileInputId = Guid.NewGuid();
}
<InputFile id="@fileInputId" OnChange="(e) => UploadMedia(e, context)" hidden accept=".jpg,.jpeg,.png,.gif,.pdf,.ico"/>
<label class="ant-btn ant-btn-text ant-btn-icon-only" for="@fileInputId">
<Icon Type="@IconType.Outline.Upload"/>
</label>
@if (String.IsNullOrWhiteSpace(Settings.Media.SteamGridDbApiKey))
{
<Popconfirm OnConfirm="() => NavigateToSettings()" Title="Invalid SteamGridDB credentials. Setup now?">
<Button Type="ButtonType.Text" Icon="@IconType.Outline.Search"/>
</Popconfirm>
}
else
{
<Button OnClick="() => SearchMedia(context)" Type="ButtonType.Text" Icon="@IconType.Outline.Search"/>
}
<Popconfirm OnConfirm="() => RemoveMedia(context)" Title="Are you sure you want to delete this media?">
<Button Type="ButtonType.Text" Danger Icon="@IconType.Outline.Close"/>
</Popconfirm>
</Flex>
</ActionColumn>
</Table>
<GridRow Justify="RowJustify.End">
<GridCol>
<Button OnClick="AddMedia" Type="@ButtonType.Primary">Add Media</Button>
</GridCol>
</GridRow>
</Flex>
@code {
[Parameter] public ICollection<Media> Values { get; set; } = new List<Media>();
[Parameter] public EventCallback<ICollection<Media>> ValuesChanged { get; set; }
[Parameter] public Guid GameId { get; set; }
[Parameter] public string GameTitle { get; set; }
Settings Settings = SettingService.GetSettings();
MediaType[] AllowedMediaTypes = new MediaType[]
{
MediaType.Icon,
MediaType.Cover,
MediaType.Background,
MediaType.Logo,
MediaType.Manual
};
MediaType[] SearchableMediaTypes = new MediaType[]
{
MediaType.Icon,
MediaType.Cover,
MediaType.Background,
MediaType.Logo,
MediaType.Manual
};
string[] ValidMimeTypes = new string[]
{
MediaTypeNames.Image.Png,
MediaTypeNames.Image.Jpeg,
MediaTypeNames.Image.Gif,
MediaTypeNames.Application.Pdf,
MediaTypeNames.Image.Icon
};
private async Task Update(Media media)
{
try
{
if (media.Id != Guid.Empty)
{
await MediaService.UpdateAsync(media);
MessageService.Success($"{media.Type} updated!");
}
}
catch (Exception ex)
{
MessageService.Error($"{media.Type} could not be updated.");
Logger.LogError(ex, $"{media.Type} could not be updated.");
}
}
private async Task AddMedia()
{
if (Values == null)
Values = new List<Media>();
try
{
var types = Enum.GetValues<MediaType>();
var nextType = types.FirstOrDefault(t => !Values.Any(v => v.Type == t) && AllowedMediaTypes.Contains(t));
var defaultStorageLocation = await MediaService.GetDefaultStorageLocationAsync();
var media = new Media
{
GameId = GameId,
Type = nextType == MediaType.Icon && Values.Any(v => v.Type == MediaType.Icon) ? MediaType.Manual : nextType,
StorageLocationId = defaultStorageLocation.Id,
Crc32 = String.Empty
};
media = await MediaService.AddAsync(media);
Values.Add(await MediaService.GetAsync(media.Id));
}
catch (Exception ex)
{
MessageService.Error("Could not add new media!");
Logger.LogError(ex, "Could not add new media!");
}
}
private async Task SearchMedia(Media media)
{
var modalOptions = new ModalOptions()
{
Title = $"Download {media.Type}",
Maximizable = false,
DefaultMaximized = true,
Closable = true,
OkText = "Select",
};
var grabberOptions = new MediaGrabberOptions()
{
Type = media.Type,
Search = GameTitle
};
var modalRef = await ModalService.CreateModalAsync<MediaGrabberDialog, MediaGrabberOptions, MediaGrabberResult>(modalOptions, grabberOptions);
modalRef.OnOk = async (result) =>
{
modalRef.Config.ConfirmLoading = true;
media.SourceUrl = result.SourceUrl;
media.MimeType = result.MimeType;
if (media.Id == Guid.Empty)
{
media = await MediaService.DownloadMediaAsync(result.SourceUrl, media);
await MediaService.AddAsync(media);
}
else
{
MediaService.DeleteLocalMediaFile(media);
media = await MediaService.DownloadMediaAsync(result.SourceUrl, media);
}
Values = await MediaService.GetAsync(m => m.GameId == media.GameId);
if (ValuesChanged.HasDelegate)
await ValuesChanged.InvokeAsync(Values);
};
}
private async Task UploadMedia(InputFileChangeEventArgs e, Media media)
{
if (!ValidMimeTypes.Contains(e.File.ContentType))
{
MessageService.Error("Unsupported file type");
Logger.LogError($"Unsupported file type {e.File.ContentType}");
return;
}
if ((e.File.Size / 1024 / 1024) > Settings.Media.MaxSize)
{
MessageService.Error($"File size must be smaller than {Settings.Media.MaxSize}MB");
return;
}
media.SourceUrl = "";
media.MimeType = e.File.ContentType;
if (media.Id == Guid.Empty)
{
media = await MediaService.UploadMediaAsync(e.File.OpenReadStream(maxAllowedSize: Settings.Media.MaxSize * 1024 * 1024), media);
await MediaService.AddAsync(media);
}
else
{
MediaService.DeleteLocalMediaFile(media);
media = await MediaService.UploadMediaAsync(e.File.OpenReadStream(maxAllowedSize: Settings.Media.MaxSize * 1024 * 1024), media);
await MediaService.UpdateAsync(media);
}
Values = await MediaService.GetAsync(m => m.GameId == media.GameId);
if (ValuesChanged.HasDelegate)
await ValuesChanged.InvokeAsync(Values);
}
private async Task RemoveMedia(Media media)
{
Values.Remove(media);
if (media.Id != Guid.Empty)
await MediaService.DeleteAsync(media);
}
private async Task OpenManual(Media media)
{
var modalOptions = new ModalOptions()
{
Title = media.Name,
Maximizable = true,
DefaultMaximized = true,
Closable = true,
Footer = null,
Draggable = true,
Resizable = true,
WrapClassName = "pdf-reader-dialog",
};
var modalRef = await ModalService.CreateModalAsync<PdfReaderDialog, Media>(modalOptions, media);
}
private void NavigateToSettings()
{
NavigationManager.NavigateTo("/Settings/Media", true);
}
}