@page "/media/libraries" @using ErsatzTV.Application.Emby @using ErsatzTV.Application.Jellyfin @using ErsatzTV.Application.Libraries @using ErsatzTV.Application.MediaSources @using ErsatzTV.Application.Plex @using ErsatzTV.Core.Interfaces.Metadata @using ErsatzTV.Core.Metadata @using MediatR.Courier @using PlexLibraryViewModel = ErsatzTV.Application.Libraries.PlexLibraryViewModel @implements IDisposable @inject IMediator Mediator @inject IEntityLocker Locker @inject ChannelWriter ScannerWorkerChannel @inject ICourier Courier @inject IScannerProxyService ScannerProxyService
Libraries @if (_showServerNames) { } Library Kind @if (_showServerNames) { Server Name } Library Name Media Kind @context.LibraryKind @if (_showServerNames) { @context.MediaSourceName } @context.Name @switch (context.MediaKind) { case LibraryMediaKind.MusicVideos: @:Music Videos break; case LibraryMediaKind.OtherVideos: @:Other Videos break; case LibraryMediaKind.RemoteStreams: @:Remote Streams break; default: @(context.MediaKind) break; }
@if (IsLibraryLocked(context.Id)) {
@if (_progressByLibrary[context.Id] > 0) { @($"{_progressByLibrary[context.Id]} %") }
} else { if (context is PlexLibraryViewModel or EmbyLibraryViewModel or JellyfinLibraryViewModel) { } else {
} }
External Collections @if (_externalCollections.Any()) { Library Kind @context.LibraryKind
@if (AreCollectionsLocked(context.LibraryKind)) {
} else { if (context.LibraryKind is "Plex" or "Emby" or "Jellyfin") { } else {
} }
}
@code { private CancellationTokenSource _cts; private IList _libraries = new List(); private IList _externalCollections = new List(); private Dictionary _progressByLibrary = new(); private bool _showServerNames; protected override void OnInitialized() { Locker.OnLibraryChanged += LockChanged; Locker.OnEmbyCollectionsChanged += LockChanged; Locker.OnJellyfinCollectionsChanged += LockChanged; Locker.OnPlexCollectionsChanged += LockChanged; Courier.Subscribe(HandleScanProgress); } protected override async Task OnParametersSetAsync() { _cts?.Cancel(); _cts?.Dispose(); _cts = new CancellationTokenSource(); var token = _cts.Token; try { _libraries = await Mediator.Send(new GetConfiguredLibraries(), token); _showServerNames = _libraries.Any(l => l is PlexLibraryViewModel); token.ThrowIfCancellationRequested(); _externalCollections = await Mediator.Send(new GetExternalCollections(), token); foreach (var library in _libraries) { decimal progress = ScannerProxyService.GetProgress(library.Id).IfNone(0); _progressByLibrary[library.Id] = (int)(progress * 100); } } catch (OperationCanceledException) { // do nothing } } private async Task ScanLibrary(LibraryViewModel library, bool deepScan = false) { if (Locker.LockLibrary(library.Id)) { var token = _cts?.Token ?? CancellationToken.None; switch (library) { case LocalLibraryViewModel: await ScannerWorkerChannel.WriteAsync(new ForceScanLocalLibrary(library.Id), token); break; case PlexLibraryViewModel: await ScannerWorkerChannel.WriteAsync(new SynchronizePlexLibraries(library.MediaSourceId), token); await ScannerWorkerChannel.WriteAsync(new ForceSynchronizePlexLibraryById(library.Id, deepScan), token); await ScannerWorkerChannel.WriteAsync(new SynchronizePlexNetworks(library.Id, true), token); break; case JellyfinLibraryViewModel: await ScannerWorkerChannel.WriteAsync(new SynchronizeJellyfinLibraries(library.MediaSourceId), token); await ScannerWorkerChannel.WriteAsync(new ForceSynchronizeJellyfinLibraryById(library.Id, deepScan), token); break; case EmbyLibraryViewModel: await ScannerWorkerChannel.WriteAsync(new SynchronizeEmbyLibraries(library.MediaSourceId), token); await ScannerWorkerChannel.WriteAsync(new ForceSynchronizeEmbyLibraryById(library.Id, deepScan), token); break; } StateHasChanged(); } } private async Task ScanExternalCollections(LibraryViewModel library, bool deepScan = false) { var token = _cts?.Token ?? CancellationToken.None; switch (library.LibraryKind.ToLowerInvariant()) { case "emby": if (Locker.LockEmbyCollections()) { await ScannerWorkerChannel.WriteAsync(new SynchronizeEmbyCollections(library.MediaSourceId, true, deepScan), token); } break; case "jellyfin": if (Locker.LockJellyfinCollections()) { await ScannerWorkerChannel.WriteAsync(new SynchronizeJellyfinCollections(library.MediaSourceId, true, deepScan), token); } break; case "plex": if (Locker.LockPlexCollections()) { await ScannerWorkerChannel.WriteAsync(new SynchronizePlexCollections(library.MediaSourceId, true, deepScan), token); } break; } } private void LockChanged(object sender, EventArgs e) => InvokeAsync(StateHasChanged); private bool AreCollectionsLocked(string libraryKind) { switch (libraryKind.ToLowerInvariant()) { case "emby": return Locker.AreEmbyCollectionsLocked(); case "jellyfin": return Locker.AreJellyfinCollectionsLocked(); case "plex": return Locker.ArePlexCollectionsLocked(); } return false; } private async Task HandleScanProgress(LibraryScanProgress libraryScanProgress, CancellationToken cancellationToken) { try { if (_progressByLibrary != null && _progressByLibrary.ContainsKey(libraryScanProgress.LibraryId)) { _progressByLibrary[libraryScanProgress.LibraryId] = (int)(libraryScanProgress.Progress * 100); await InvokeAsync(StateHasChanged); } } catch (Exception) { // ignore } } void IDisposable.Dispose() { Locker.OnLibraryChanged -= LockChanged; Locker.OnEmbyCollectionsChanged -= LockChanged; Locker.OnJellyfinCollectionsChanged -= LockChanged; Locker.OnPlexCollectionsChanged -= LockChanged; Courier.UnSubscribe(HandleScanProgress); _cts?.Cancel(); _cts?.Dispose(); } private bool IsLibraryLocked(int libraryId) { if (Locker.IsLibraryLocked(libraryId)) { return true; } _progressByLibrary[libraryId] = 0; return false; } }