Allow media grabber dialog to show results from multiple groups. Show media type in media grabber dialog. Allow modification of search for media grabber dialog.

media
Pat Hartl 2023-11-02 19:27:50 -05:00
parent 499b0c910a
commit d51eab151a
7 changed files with 92 additions and 34 deletions

View File

@ -2,7 +2,7 @@
<div class="image-picker-images"> <div class="image-picker-images">
@foreach (var image in Images) @foreach (var image in Images)
{ {
<div class="image-picker-image" style="width: @(Size)px; height: @(Size)px"> <div class="image-picker-image" style="width: @(Size)px; max-height: @(Size)px">
<input type="radio" id="image-picker-image-@image.Key" checked="@(Value == image.Key)" name="SelectedResult" @onchange="@(() => SelectionChanged(image.Key))" /> <input type="radio" id="image-picker-image-@image.Key" checked="@(Value == image.Key)" name="SelectedResult" @onchange="@(() => SelectionChanged(image.Key))" />
<label for="image-picker-image-@image.Key"></label> <label for="image-picker-image-@image.Key"></label>
<img src="@image.Value" /> <img src="@image.Value" />

View File

@ -3,9 +3,24 @@
@using LANCommander.Models; @using LANCommander.Models;
@inject IMediaGrabberService MediaGrabberService @inject IMediaGrabberService MediaGrabberService
<Slider TValue="double" @bind-Value="Size" DefaultValue="200" Min="50" Max="400" /> <GridRow Justify="space-between">
<GridCol Span="6">
<Search @bind-Value="Search" OnSearch="(x) => GetResults(Type, x)" DefaultValue="@Search" />
</GridCol>
<GridCol Span="12"></GridCol>
<GridCol Span="6">
<Slider TValue="double" @bind-Value="Size" DefaultValue="200" Min="50" Max="400" />
</GridCol>
</GridRow>
<ImagePicker Size="Size" Images="Images" ValueChanged="OnImageSelected" /> @foreach (var group in Results)
{
<div class="media-grabber-group">
<h2>@group.First().Group</h2>
<ImagePicker Size="Size" Images="@group.ToDictionary(r => r.Id, r => r.ThumbnailUrl)" ValueChanged="OnImageSelected" />
</div>
}
@code { @code {
[Parameter] public string Search { get; set; } [Parameter] public string Search { get; set; }
@ -15,16 +30,22 @@
double Size { get; set; } = 200; double Size { get; set; } = 200;
IEnumerable<MediaGrabberResult> Results = new List<MediaGrabberResult>(); IEnumerable<IEnumerable<MediaGrabberResult>> Results = new List<List<MediaGrabberResult>>();
Dictionary<string, string> Images { get; set; } = new Dictionary<string, string>(); Dictionary<string, string> Images { get; set; } = new Dictionary<string, string>();
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnFirstAfterRenderAsync()
{ {
if (firstRender) Type = Options.Type;
{ Search = Options.Search;
Results = await MediaGrabberService.SearchAsync(Options.Type, Options.Search);
Images = Results.ToDictionary(r => r.Id, r => r.ThumbnailUrl); await GetResults(Type, Search);
}
private async Task GetResults(MediaType type, string search)
{
if (!String.IsNullOrWhiteSpace(search))
{
Results = (await MediaGrabberService.SearchAsync(type, search)).GroupBy(r => r.Group);
StateHasChanged(); StateHasChanged();
} }
@ -32,7 +53,7 @@
private void OnImageSelected(string key) private void OnImageSelected(string key)
{ {
Media = Results.FirstOrDefault(r => r.Id == key); Media = Results.SelectMany(g => g).FirstOrDefault(r => r.Id == key);
} }
public override async Task OnFeedbackOkAsync(ModalClosingEventArgs args) public override async Task OnFeedbackOkAsync(ModalClosingEventArgs args)

View File

@ -8,5 +8,6 @@ namespace LANCommander.Models
public MediaType Type { get; set; } public MediaType Type { get; set; }
public string SourceUrl { get; set; } public string SourceUrl { get; set; }
public string ThumbnailUrl { get; set; } public string ThumbnailUrl { get; set; }
public string Group { get; set; }
} }
} }

View File

@ -9,7 +9,7 @@
<PropertyColumn Property="p => p.Id" Title="Preview" Width="100px" Align="ColumnAlign.Center"> <PropertyColumn Property="p => p.Id" Title="Preview" Width="100px" Align="ColumnAlign.Center">
@if (MediaService.FileExists(context)) @if (MediaService.FileExists(context))
{ {
<Image Width="100px" Src="@($"/api/Media/{context.Id}/Download")" /> <Image Width="100px" Src="@($"/api/Media/{context.Id}/Download?fileId={context.FileId}")" />
} }
</PropertyColumn> </PropertyColumn>
<PropertyColumn Property="p => p.Type"> <PropertyColumn Property="p => p.Type">
@ -53,7 +53,8 @@
Values.Add(new Media() Values.Add(new Media()
{ {
GameId = GameId GameId = GameId,
Type = Enum.GetValues<MediaType>().ToList().FirstOrDefault(t => !Values.Any(v => v.Type == t))
}); });
} }
@ -61,7 +62,7 @@
{ {
var modalOptions = new ModalOptions() var modalOptions = new ModalOptions()
{ {
Title = "Search Media", Title = $"Download {media.Type}",
Maximizable = false, Maximizable = false,
DefaultMaximized = true, DefaultMaximized = true,
Closable = true, Closable = true,
@ -81,9 +82,19 @@
modalRef.Config.ConfirmLoading = true; modalRef.Config.ConfirmLoading = true;
media.SourceUrl = result.SourceUrl; media.SourceUrl = result.SourceUrl;
media.FileId = await MediaService.DownloadMediaAsync(result.SourceUrl);
await MediaService.Add(media); if (media.Id == Guid.Empty)
{
media.FileId = await MediaService.DownloadMediaAsync(result.SourceUrl);
await MediaService.Add(media);
}
else
{
MediaService.DeleteLocalMediaFile(media.FileId);
media.FileId = await MediaService.DownloadMediaAsync(result.SourceUrl);
await MediaService.Update(media);
}
Values = MediaService.Get(m => m.GameId == media.GameId).ToList(); Values = MediaService.Get(m => m.GameId == media.GameId).ToList();

View File

@ -18,63 +18,68 @@ namespace LANCommander.Services.MediaGrabbers
public async Task<IEnumerable<MediaGrabberResult>> SearchAsync(MediaType type, string keywords) public async Task<IEnumerable<MediaGrabberResult>> SearchAsync(MediaType type, string keywords)
{ {
var games = await SteamGridDb.SearchForGamesAsync(keywords); var games = await SteamGridDb.SearchForGamesAsync(keywords);
var results = new List<MediaGrabberResult>();
if (games.Length > 0) foreach (var game in games)
{ {
var game = games.FirstOrDefault();
switch (type) switch (type)
{ {
case MediaType.Icon: case MediaType.Icon:
return await GetIconsAsync(game.Id); results.AddRange(await GetIconsAsync(game));
break;
case MediaType.Cover: case MediaType.Cover:
return await GetCoversAsync(game.Id); results.AddRange(await GetCoversAsync(game));
break;
case MediaType.Background: case MediaType.Background:
return await GetBackgroundsAsync(game.Id); results.AddRange(await GetBackgroundsAsync(game));
break;
} }
} }
return new List<MediaGrabberResult>(); return results;
} }
private async Task<IEnumerable<MediaGrabberResult>> GetIconsAsync(int gameId) private async Task<IEnumerable<MediaGrabberResult>> GetIconsAsync(SteamGridDbGame game)
{ {
var icons = await SteamGridDb.GetIconsByGameIdAsync(gameId); var icons = await SteamGridDb.GetIconsByGameIdAsync(game.Id);
return icons.Select(i => new MediaGrabberResult() return icons.Select(i => new MediaGrabberResult()
{ {
Id = i.Id.ToString(), Id = i.Id.ToString(),
Type = MediaType.Icon, Type = MediaType.Icon,
SourceUrl = i.FullImageUrl, SourceUrl = i.FullImageUrl,
ThumbnailUrl = i.ThumbnailImageUrl ThumbnailUrl = i.ThumbnailImageUrl,
Group = game.Name
}); });
} }
private async Task<IEnumerable<MediaGrabberResult>> GetCoversAsync(int gameId) private async Task<IEnumerable<MediaGrabberResult>> GetCoversAsync(SteamGridDbGame game)
{ {
var covers = await SteamGridDb.GetGridsByGameIdAsync(gameId); var covers = await SteamGridDb.GetGridsByGameIdAsync(game.Id);
return covers.Select(c => new MediaGrabberResult() return covers.Select(c => new MediaGrabberResult()
{ {
Id = c.Id.ToString(), Id = c.Id.ToString(),
Type = MediaType.Cover, Type = MediaType.Cover,
SourceUrl = c.FullImageUrl, SourceUrl = c.FullImageUrl,
ThumbnailUrl = c.ThumbnailImageUrl ThumbnailUrl = c.ThumbnailImageUrl,
Group = game.Name
}); });
} }
private async Task<IEnumerable<MediaGrabberResult>> GetBackgroundsAsync(int gameId) private async Task<IEnumerable<MediaGrabberResult>> GetBackgroundsAsync(SteamGridDbGame game)
{ {
var backgrounds = await SteamGridDb.GetHeroesByGameIdAsync(gameId); var backgrounds = await SteamGridDb.GetHeroesByGameIdAsync(game.Id);
return backgrounds.Select(b => new MediaGrabberResult() return backgrounds.Select(b => new MediaGrabberResult()
{ {
Id = b.Id.ToString(), Id = b.Id.ToString(),
Type = MediaType.Background, Type = MediaType.Background,
SourceUrl = b.FullImageUrl, SourceUrl = b.FullImageUrl,
ThumbnailUrl = b.ThumbnailImageUrl ThumbnailUrl = b.ThumbnailImageUrl,
Group = game.Name
}); });
} }
} }

View File

@ -47,9 +47,18 @@ namespace LANCommander.Services
return Path.Combine(Settings.Media.StoragePath, entity.FileId.ToString()); return Path.Combine(Settings.Media.StoragePath, entity.FileId.ToString());
} }
public void DeleteLocalMediaFile(Guid fileId)
{
var path = Path.Combine(Settings.Media.StoragePath, fileId.ToString());
if (File.Exists(path))
File.Delete(path);
}
public async Task<Guid> DownloadMediaAsync(string sourceUrl) public async Task<Guid> DownloadMediaAsync(string sourceUrl)
{ {
var fileId = Guid.NewGuid(); var fileId = Guid.NewGuid();
var path = Path.Combine(Settings.Media.StoragePath, fileId.ToString()); var path = Path.Combine(Settings.Media.StoragePath, fileId.ToString());
using (var http = new HttpClient()) using (var http = new HttpClient())

View File

@ -176,13 +176,11 @@
.image-picker-images { .image-picker-images {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-evenly;
gap: 16px; gap: 16px;
} }
.image-picker-image { .image-picker-image {
position: relative; position: relative;
aspect-ratio: 1/1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -218,6 +216,19 @@
max-height: 100px; max-height: 100px;
} }
.media-grabber-group {
margin-top: 18px;
}
.media-grabber-group + .media-grabber-group {
padding-top: 16px;
border-top: 1px solid rgba(0, 0, 0, 0.85);
}
[data-theme="Dark"] .media-grabber-group + .media-grabber-group {
border-top: 1px solid rgba(255, 255, 255, 0.12);
}
@media screen and (min-width: 768px) { @media screen and (min-width: 768px) {
.mobile-menu { .mobile-menu {
display: none; display: none;