Add multiple select support for media grabber
This commit is contained in:
parent
232f86ba85
commit
f0b85de93f
5 changed files with 147 additions and 15 deletions
|
|
@ -1,6 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace LANCommander.Server.Services.Models
|
||||
{
|
||||
public class MediaGrabberDownloadResult
|
||||
{
|
||||
public MediaGrabberResult Result { get; set; }
|
||||
public MediaGrabberDownload Download { get; set; }
|
||||
public List<MediaGrabberDownloadResultEntry> Results { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MediaGrabberDownloadResultEntry
|
||||
{
|
||||
public MediaGrabberResult Result { get; set; }
|
||||
public MediaGrabberDownload Download { get; set; }
|
||||
|
|
|
|||
|
|
@ -6,5 +6,6 @@ namespace LANCommander.Server.Services.Models
|
|||
{
|
||||
public MediaType Type { get; set; }
|
||||
public string Search { get; set; }
|
||||
public bool MultiSelect { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,14 @@
|
|||
var id = Guid.NewGuid();
|
||||
|
||||
<div class="media-picker-item" style="width: @(Size)px; max-height: @(Size)px">
|
||||
<input type="radio" id="media-picker-item-@id" checked="@(Value == image.Key)" name="SelectedResult" @onchange="@(() => SelectionChanged(image.Key))" />
|
||||
@if (MultiSelect)
|
||||
{
|
||||
<input type="checkbox" id="media-picker-item-@id" checked="@(SelectedValues.Contains(image.Key))" name="SelectedResult" @onchange="@(() => ToggleSelection(image.Key))" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="radio" id="media-picker-item-@id" checked="@(Value == image.Key)" name="SelectedResult" @onchange="@(() => SelectionChanged(image.Key))" />
|
||||
}
|
||||
<label for="media-picker-item-@id" @ondblclick="@(() => Pick(image.Key))"></label>
|
||||
<img src="@image.Value" />
|
||||
</div>
|
||||
|
|
@ -19,6 +26,9 @@
|
|||
[Parameter] public EventCallback<string> ValueChanged { get; set; }
|
||||
[Parameter] public EventCallback<string> OnPicked { get; set; }
|
||||
[Parameter] public Dictionary<string, string> Images { get; set; }
|
||||
[Parameter] public bool MultiSelect { get; set; }
|
||||
[Parameter] public HashSet<string> SelectedValues { get; set; } = new();
|
||||
[Parameter] public EventCallback<HashSet<string>> SelectedValuesChanged { get; set; }
|
||||
|
||||
async Task SelectionChanged(string key)
|
||||
{
|
||||
|
|
@ -28,6 +38,15 @@
|
|||
await ValueChanged.InvokeAsync(key);
|
||||
}
|
||||
|
||||
async Task ToggleSelection(string key)
|
||||
{
|
||||
if (!SelectedValues.Add(key))
|
||||
SelectedValues.Remove(key);
|
||||
|
||||
if (SelectedValuesChanged.HasDelegate)
|
||||
await SelectedValuesChanged.InvokeAsync(SelectedValues);
|
||||
}
|
||||
|
||||
async Task Pick(string key)
|
||||
{
|
||||
Value = key;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
<ImagePicker Size="Size" Images="@grabberGroup.Results.ToDictionary(r => r.UniqueKey, r => r.ThumbnailUrl)" ValueChanged="OnImageSelected" OnPicked="OnImagePicked" />
|
||||
<ImagePicker Size="Size" MultiSelect="MultiSelect" SelectedValues="_selectedKeys" SelectedValuesChanged="OnMultiImageSelectionChanged" Images="@grabberGroup.Results.ToDictionary(r => r.UniqueKey, r => r.ThumbnailUrl)" ValueChanged="OnImageSelected" OnPicked="OnImagePicked" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -108,6 +108,9 @@
|
|||
MediaGrabberResult Media { get; set; }
|
||||
MediaGrabberResult _preview { get; set; }
|
||||
|
||||
bool MultiSelect { get; set; }
|
||||
HashSet<string> _selectedKeys = new();
|
||||
|
||||
double Size { get; set; } = 200;
|
||||
bool Loading { get; set; } = true;
|
||||
bool _isDownloading;
|
||||
|
|
@ -122,6 +125,7 @@
|
|||
{
|
||||
Type = Options.Type;
|
||||
Search = Options.Search;
|
||||
MultiSelect = Options.MultiSelect;
|
||||
|
||||
await GetResults(Type, Search);
|
||||
}
|
||||
|
|
@ -138,6 +142,7 @@
|
|||
_preview = null;
|
||||
_allResults.Clear();
|
||||
_groupedResults.Clear();
|
||||
_selectedKeys.Clear();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(search))
|
||||
|
|
@ -198,10 +203,25 @@
|
|||
Media = _allResults.FirstOrDefault(r => r.UniqueKey == uniqueKey);
|
||||
}
|
||||
|
||||
private void OnMultiImageSelectionChanged(HashSet<string> selectedKeys)
|
||||
{
|
||||
_selectedKeys = selectedKeys;
|
||||
}
|
||||
|
||||
private async Task OnImagePicked(string uniqueKey)
|
||||
{
|
||||
OnImageSelected(uniqueKey);
|
||||
await DownloadAndClose();
|
||||
if (MultiSelect)
|
||||
{
|
||||
if (!_selectedKeys.Add(uniqueKey))
|
||||
_selectedKeys.Remove(uniqueKey);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnImageSelected(uniqueKey);
|
||||
await DownloadAndClose();
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewVideo(MediaGrabberResult result)
|
||||
|
|
@ -234,6 +254,12 @@
|
|||
|
||||
private async Task DownloadAndClose()
|
||||
{
|
||||
if (MultiSelect)
|
||||
{
|
||||
await DownloadMultipleAndClose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Media == null)
|
||||
return;
|
||||
|
||||
|
|
@ -272,6 +298,56 @@
|
|||
}
|
||||
}
|
||||
|
||||
private async Task DownloadMultipleAndClose()
|
||||
{
|
||||
var selectedResults = _allResults.Where(r => _selectedKeys.Contains(r.UniqueKey)).ToList();
|
||||
|
||||
if (!selectedResults.Any())
|
||||
return;
|
||||
|
||||
_preview = null;
|
||||
_isDownloading = true;
|
||||
_downloadPercent = 0;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
try
|
||||
{
|
||||
var downloadResult = new MediaGrabberDownloadResult();
|
||||
|
||||
for (int i = 0; i < selectedResults.Count; i++)
|
||||
{
|
||||
var selected = selectedResults[i];
|
||||
_downloadStatus = $"Downloading {i + 1} of {selectedResults.Count}...";
|
||||
_downloadPercent = 0;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
var progress = new Progress<MediaDownloadProgress>(p =>
|
||||
{
|
||||
_downloadPercent = p.Percent ?? 0;
|
||||
InvokeAsync(StateHasChanged);
|
||||
});
|
||||
|
||||
var download = await MediaGrabberService.DownloadAsync(selected, progress);
|
||||
|
||||
downloadResult.Results.Add(new MediaGrabberDownloadResultEntry
|
||||
{
|
||||
Result = selected,
|
||||
Download = download
|
||||
});
|
||||
}
|
||||
|
||||
await base.OkCancelRefWithResult!.OnOk(downloadResult);
|
||||
await CloseFeedbackAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_isDownloading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
MessageService.Error("Download failed. Please try again.");
|
||||
Logger.LogError(ex, "Failed to download media");
|
||||
}
|
||||
}
|
||||
|
||||
private class GroupedResults
|
||||
{
|
||||
public string GrabberName { get; set; }
|
||||
|
|
|
|||
|
|
@ -425,6 +425,8 @@
|
|||
|
||||
async Task SearchMediaByType(MediaType type)
|
||||
{
|
||||
var multiSelect = type == MediaType.Screenshot;
|
||||
|
||||
var modalOptions = new ModalOptions
|
||||
{
|
||||
Title = $"Download {type}",
|
||||
|
|
@ -437,43 +439,68 @@
|
|||
var grabberOptions = new MediaGrabberOptions
|
||||
{
|
||||
Type = type,
|
||||
Search = _game.Title
|
||||
Search = _game.Title,
|
||||
MultiSelect = multiSelect
|
||||
};
|
||||
|
||||
var modalRef = await ModalService.CreateModalAsync<MediaGrabberDialog, MediaGrabberOptions, MediaGrabberDownloadResult>(modalOptions, grabberOptions);
|
||||
|
||||
modalRef.OnOk = async (downloadResult) =>
|
||||
{
|
||||
using var download = downloadResult.Download;
|
||||
if (multiSelect && downloadResult.Results.Any())
|
||||
{
|
||||
foreach (var entry in downloadResult.Results)
|
||||
{
|
||||
using var download = entry.Download;
|
||||
|
||||
var media = new Media
|
||||
{
|
||||
GameId = GameId,
|
||||
Type = type,
|
||||
SourceUrl = entry.Result.SourceUrl,
|
||||
MimeType = download.MimeType,
|
||||
StorageLocation = await MediaService.GetDefaultStorageLocationAsync(),
|
||||
Crc32 = string.Empty,
|
||||
SortOrder = GetNextSortOrder(type)
|
||||
};
|
||||
|
||||
await MediaService.WriteToFileAsync(media, download.Stream);
|
||||
}
|
||||
|
||||
await RefreshMedia();
|
||||
return;
|
||||
}
|
||||
|
||||
using var singleDownload = downloadResult.Download;
|
||||
|
||||
var isSingular = type != MediaType.Screenshot && type != MediaType.Video;
|
||||
var existing = isSingular ? GetMedia(type) : null;
|
||||
|
||||
Media media;
|
||||
Media singleMedia;
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
media = existing;
|
||||
media.SourceUrl = downloadResult.Result.SourceUrl;
|
||||
media.MimeType = download.MimeType;
|
||||
singleMedia = existing;
|
||||
singleMedia.SourceUrl = downloadResult.Result.SourceUrl;
|
||||
singleMedia.MimeType = singleDownload.MimeType;
|
||||
|
||||
MediaService.DeleteLocalMediaFile(media);
|
||||
media = await MediaService.WriteToFileAsync(media, download.Stream);
|
||||
MediaService.DeleteLocalMediaFile(singleMedia);
|
||||
singleMedia = await MediaService.WriteToFileAsync(singleMedia, singleDownload.Stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
media = new Media
|
||||
singleMedia = new Media
|
||||
{
|
||||
GameId = GameId,
|
||||
Type = type,
|
||||
SourceUrl = downloadResult.Result.SourceUrl,
|
||||
MimeType = download.MimeType,
|
||||
MimeType = singleDownload.MimeType,
|
||||
StorageLocation = await MediaService.GetDefaultStorageLocationAsync(),
|
||||
Crc32 = string.Empty,
|
||||
SortOrder = GetNextSortOrder(type)
|
||||
};
|
||||
|
||||
media = await MediaService.WriteToFileAsync(media, download.Stream);
|
||||
singleMedia = await MediaService.WriteToFileAsync(singleMedia, singleDownload.Stream);
|
||||
}
|
||||
|
||||
await RefreshMedia();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue