@inherits LayoutComponentBase @using System.Reflection @using ErsatzTV.Application.Search @using ErsatzTV.Core.Interfaces.Search @using ErsatzTV.Extensions @implements IDisposable @inject NavigationManager NavigationManager @inject IMediator Mediator @inject SystemStartup SystemStartup @inject ISearchTargets SearchTargets; @if (SystemStartup.IsDatabaseReady && SystemStartup.IsSearchIndexReady) {
@if (!string.IsNullOrWhiteSpace(_query) && _query.Length >= 3) { var matches = _searchTargets.Where(s => s.Name.Contains(_query, StringComparison.CurrentCultureIgnoreCase)).ToList(); if (matches.Any()) { @foreach (SearchTargetViewModel searchTarget in matches) { @searchTarget.Name @( searchTarget.Kind switch { SearchTargetKind.Channel => "Channel", SearchTargetKind.FFmpegProfile => "FFmpeg Profile", SearchTargetKind.ChannelWatermark => "Channel Watermark", SearchTargetKind.Collection => "Collection", SearchTargetKind.MultiCollection => "Multi Collection", SearchTargetKind.SmartCollection => "Smart Collection", SearchTargetKind.Schedule => "Schedule", SearchTargetKind.ScheduleItems => "Schedule Items", _ => string.Empty }) } } }
}
@if (SystemStartup.IsDatabaseReady && SystemStartup.IsSearchIndexReady) { M3U XMLTV } @* API *@
@if (SystemStartup.IsDatabaseReady && SystemStartup.IsSearchIndexReady) { Channels FFmpeg Profiles Watermarks Local Emby Jellyfin Plex Libraries Trash TV Shows Movies Music Other Videos Songs Images Collections Playlists Trakt Lists Filler Presets Schedules Blocks Templates Decos Deco Templates Playouts Settings Health Checks Logs Troubleshooting ErsatzTV Version @InfoVersion } @Body
@code { private static readonly string InfoVersion = Assembly.GetEntryAssembly().GetCustomAttribute()?.InformationalVersion ?? "unknown"; private readonly CancellationTokenSource _cts = new(); private string _query; private record SearchModel; private readonly SearchModel _dummyModel = new(); private bool _drawerIsOpen = true; private bool _isOpen; private List _searchTargets; protected override void OnInitialized() { SystemStartup.OnDatabaseReady += OnStartupProgress; SystemStartup.OnSearchIndexReady += OnStartupProgress; SearchTargets.OnSearchTargetsChanged += OnSearchTargetsChanged; } public void Dispose() { SystemStartup.OnDatabaseReady -= OnStartupProgress; SystemStartup.OnSearchIndexReady -= OnStartupProgress; SearchTargets.OnSearchTargetsChanged -= OnSearchTargetsChanged; _cts.Cancel(); _cts.Dispose(); } private static MudTheme ErsatzTvTheme => new() { PaletteLight = new PaletteLight { ActionDefault = "rgba(255,255,255, 0.80)", Primary = "#009000", Secondary = "#009090", Surface = "#1f1f1f", AppbarBackground = "#121212", AppbarText = "rgba(255,255,255, 0.80)", DrawerBackground = "#1f1f1f", DrawerText = "rgba(255,255,255, 0.80)", Background = "#272727", TextPrimary = "rgba(255,255,255, 0.80)", TextSecondary = "rgba(255,255,255, 0.80)", TextDisabled = "rgba(255,255,255, 0.40)", ActionDisabled = "rgba(255,255,255, 0.40)", TableHover = "rgba(255,255,255, 0.10)", TableLines = "rgba(255,255,255,0.11)", Info = "#00c0c0", Tertiary = "#00c000", White = Colors.Shades.White } }; private string Query { get => _query; set { if (_query == value) { return; } _query = value; _isOpen = true; StateHasChanged(); } } private async void OnStartupProgress(object sender, EventArgs e) { try { await InvokeAsync(StateHasChanged); } catch { // do nothing } } protected override async Task OnParametersSetAsync() { await base.OnParametersSetAsync(); _query = NavigationManager.Uri.GetSearchQuery(); if (SystemStartup.IsDatabaseReady && _searchTargets is null) { _searchTargets = await Mediator.Send(new QuerySearchTargets(), _cts.Token); } } protected async void OnSearchTargetsChanged(object sender, EventArgs e) => _searchTargets = await Mediator.Send(new QuerySearchTargets(), _cts.Token); private void PerformSearch() { NavigationManager.NavigateTo(_query.GetRelativeSearchQuery(), true); StateHasChanged(); } private void OnKeyUp(KeyboardEventArgs args) { switch (args.Key) { case "Enter": case "NumpadEnter": _isOpen = false; break; case "Escape": _isOpen = false; break; } } private void NavigateTo(SearchTargetViewModel searchTarget) => // need to force smart collections to navigate since the query string is all that differs NavigationManager.NavigateTo(UrlFor(searchTarget), searchTarget.Kind is SearchTargetKind.SmartCollection); private string UrlFor(SearchTargetViewModel searchTarget) => searchTarget.Kind switch { SearchTargetKind.Channel => $"channels/{searchTarget.Id}", SearchTargetKind.FFmpegProfile => $"ffmpeg/{searchTarget.Id}", SearchTargetKind.ChannelWatermark => $"watermarks/{searchTarget.Id}", SearchTargetKind.Collection => $"media/collections/{searchTarget.Id}", SearchTargetKind.MultiCollection => $"media/multi-collections/{searchTarget.Id}/edit", SearchTargetKind.SmartCollection when searchTarget is SmartCollectionSearchTargetViewModel sc => sc.Query.GetRelativeSearchQuery(), SearchTargetKind.Schedule => $"schedules/{searchTarget.Id}", SearchTargetKind.ScheduleItems => $"schedules/{searchTarget.Id}/items", _ => null }; private string IptvUrl(string path) { var uri = new Uri(NavigationManager.Uri); return $"{uri.Scheme}://{uri.Host}:{Settings.StreamingPort}/iptv/{path}"; } private void ToggleDrawer() { _drawerIsOpen = !_drawerIsOpen; } }