diff --git a/Manager.App/Components/Layout/NavMenu.razor b/Manager.App/Components/Layout/NavMenu.razor
index da7f50a..dd3ae86 100644
--- a/Manager.App/Components/Layout/NavMenu.razor
+++ b/Manager.App/Components/Layout/NavMenu.razor
@@ -1,9 +1,14 @@
Home
- Channels
- Library
- Playlists
- Development
- Services
+
+ Accounts
+ Channels
+ Playlists
+ Info
+
+
+ Development
+ Services
+
\ No newline at end of file
diff --git a/Manager.App/Components/Pages/Accounts.razor b/Manager.App/Components/Pages/Accounts.razor
new file mode 100644
index 0000000..f693a25
--- /dev/null
+++ b/Manager.App/Components/Pages/Accounts.razor
@@ -0,0 +1,48 @@
+@page "/Accounts"
+@using Manager.App.Models.Settings
+@using Manager.App.Services.System
+@using Microsoft.Extensions.Options
+
+@inject ILibraryService LibraryService
+@inject IDialogService DialogService
+@inject IOptions LibraryOptions
+@inject ClientService ClientService
+@inject ISnackbar Snackbar
+
+Accounts
+
+
+
+
+ Add account
+
+
+
+
+
+ Accounts
+
+
+
+
+ Name
+ Handle
+ ID
+
+
+ @context.Channel?.Name
+ @context.Channel?.Handle
+ @context.Id
+
+
+ No channels found
+
+
+ Loading...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Manager.App/Components/Pages/Accounts.razor.cs b/Manager.App/Components/Pages/Accounts.razor.cs
new file mode 100644
index 0000000..f46ab89
--- /dev/null
+++ b/Manager.App/Components/Pages/Accounts.razor.cs
@@ -0,0 +1,74 @@
+using Manager.App.Components.Dialogs;
+using Manager.App.Models.Library;
+using Manager.Data.Entities.LibraryContext;
+using Microsoft.AspNetCore.Components;
+using MudBlazor;
+
+namespace Manager.App.Components.Pages;
+
+public partial class Accounts : ComponentBase
+{
+ private MudTable? _table;
+ private readonly DialogOptions _dialogOptions = new() { BackdropClick = false, CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.ExtraLarge };
+ private string _search = "";
+
+ private async Task> ServerReload(TableState state, CancellationToken token)
+ {
+ var results = await LibraryService.GetAccountsAsync(_search, state.Page * state.PageSize, state.PageSize, token);
+ return !results.IsSuccess ? new TableData() : new TableData() { Items = results.Value, TotalItems = results.Total };
+ }
+
+ private void OnSearch(string text)
+ {
+ _search = text;
+ _table?.ReloadServerData();
+ }
+
+ private async Task OnAddAccountDialogAsync()
+ {
+ var libSettings = LibraryOptions.Value;
+ var parameters = new DialogParameters { { x => x.DefaultUserAgent, libSettings.DefaultUserAgent } };
+ var dialog = await DialogService.ShowAsync("Add account", parameters, _dialogOptions);
+ var result = await dialog.Result;
+
+ if (result == null || result.Canceled || result.Data == null)
+ {
+ return;
+ }
+
+ var clientChannel = (ClientChannel)result.Data;
+ if (clientChannel?.YouTubeClient == null)
+ {
+ Snackbar.Add("No YouTube client received.", Severity.Error);
+ return;
+ }
+
+ var savedClientResult = await ClientService.SaveClientAsync(clientChannel.YouTubeClient);
+ if (savedClientResult.IsSuccess)
+ {
+ if (_table != null)
+ {
+ await _table.ReloadServerData();
+ }
+ Snackbar.Add($"Client {clientChannel.Channel?.Handle ?? clientChannel.YouTubeClient.Id} saved!", Severity.Success);
+ ClientService.AddClient(clientChannel.YouTubeClient);
+ }
+ else
+ {
+ Snackbar.Add($"Failed to store client: {savedClientResult.Error?.Description ?? "Unknown!"}", Severity.Error);
+ }
+
+ if (clientChannel.Channel == null)
+ {
+ Snackbar.Add("No channel information received!", Severity.Warning);
+ }
+ else
+ {
+ var saveChannelResult = await LibraryService.SaveChannelAsync(clientChannel.Channel);
+ if (!saveChannelResult.IsSuccess)
+ {
+ Snackbar.Add("Failed to save channel information", Severity.Warning);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Manager.App/Components/Pages/Channels.razor b/Manager.App/Components/Pages/Channels.razor
index b8887a4..38d7ab9 100644
--- a/Manager.App/Components/Pages/Channels.razor
+++ b/Manager.App/Components/Pages/Channels.razor
@@ -1,37 +1,26 @@
@page "/Channels"
-@using Manager.App.Models.Settings
-@using Manager.App.Services.System
-@using Microsoft.Extensions.Options
@inject ILibraryService LibraryService
-@inject IDialogService DialogService
-@inject IOptions LibraryOptions
-@inject ClientService ClientService
-@inject ISnackbar Snackbar
Channels
-
-
-
- Add account
-
-
-
- Channels
+ Channels stored in the library
+
+
Name
+ Handle
Channel id
- Has login
- @context.Channel?.Name
+ @context.Name
+ @context.Handle
@context.Id
- @(context.HttpCookies.Any())
No channels found
diff --git a/Manager.App/Components/Pages/Channels.razor.cs b/Manager.App/Components/Pages/Channels.razor.cs
index 6c526ba..bc47493 100644
--- a/Manager.App/Components/Pages/Channels.razor.cs
+++ b/Manager.App/Components/Pages/Channels.razor.cs
@@ -1,5 +1,3 @@
-using Manager.App.Components.Dialogs;
-using Manager.App.Models.Library;
using Manager.Data.Entities.LibraryContext;
using Microsoft.AspNetCore.Components;
using MudBlazor;
@@ -8,60 +6,18 @@ namespace Manager.App.Components.Pages;
public partial class Channels : ComponentBase
{
- private readonly DialogOptions _dialogOptions = new() { BackdropClick = false, CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.ExtraLarge };
- private MudTable? _table;
+ private MudTable? _table;
+ private string _search = "";
- private async Task> ServerReload(TableState state, CancellationToken token)
+ private async Task> ServerReload(TableState state, CancellationToken token)
{
- var results = await LibraryService.GetAccountsAsync(null, state.Page * state.PageSize, state.PageSize, token);
- return !results.IsSuccess ? new TableData() : new TableData { Items = results.Value, TotalItems = results.Total };
+ var results = await LibraryService.GetChannelsAsync(_search, state.Page * state.PageSize, state.PageSize, token);
+ return !results.IsSuccess ? new TableData() : new TableData { Items = results.Value, TotalItems = results.Total };
}
- private async Task OnAddAccountDialogAsync()
+ private void OnSearch(string text)
{
- var libSettings = LibraryOptions.Value;
- var parameters = new DialogParameters { { x => x.DefaultUserAgent, libSettings.DefaultUserAgent } };
- var dialog = await DialogService.ShowAsync("Add account", parameters, _dialogOptions);
- var result = await dialog.Result;
-
- if (result == null || result.Canceled || result.Data == null)
- {
- return;
- }
-
- var clientChannel = (ClientChannel)result.Data;
- if (clientChannel?.YouTubeClient == null)
- {
- Snackbar.Add("No YouTube client received.", Severity.Error);
- return;
- }
-
- var savedClientResult = await ClientService.SaveClientAsync(clientChannel.YouTubeClient);
- if (savedClientResult.IsSuccess)
- {
- if (_table != null)
- {
- await _table.ReloadServerData();
- }
- Snackbar.Add($"Client {clientChannel.Channel?.Handle ?? clientChannel.YouTubeClient.Id} saved!", Severity.Success);
- ClientService.AddClient(clientChannel.YouTubeClient);
- }
- else
- {
- Snackbar.Add($"Failed to store client: {savedClientResult.Error?.Description ?? "Unknown!"}", Severity.Error);
- }
-
- if (clientChannel.Channel == null)
- {
- Snackbar.Add("No channel information received!", Severity.Warning);
- }
- else
- {
- var saveChannelResult = await LibraryService.SaveChannelAsync(clientChannel.Channel);
- if (!saveChannelResult.IsSuccess)
- {
- Snackbar.Add("Failed to save channel information", Severity.Warning);
- }
- }
+ _search = text;
+ _table?.ReloadServerData();
}
}
\ No newline at end of file
diff --git a/Manager.App/Services/ILibraryService.cs b/Manager.App/Services/ILibraryService.cs
index 87ee3b1..e3709b5 100644
--- a/Manager.App/Services/ILibraryService.cs
+++ b/Manager.App/Services/ILibraryService.cs
@@ -14,5 +14,5 @@ public interface ILibraryService
public Task SaveChannelAsync(InnertubeChannel innertubeChannel, CancellationToken cancellationToken = default);
public Task> GetLibraryInfoAsync(CancellationToken cancellationToken = default);
public Task> GetAccountsAsync(string? search, int offset = 0, int total = 20, CancellationToken cancellationToken = default);
- public Task> GetChannelsAsync(int offset = 0, int total = 20, CancellationToken cancellationToken = default);
+ public Task> GetChannelsAsync(string? search, int offset = 0, int total = 20, CancellationToken cancellationToken = default);
}
\ No newline at end of file
diff --git a/Manager.App/Services/LibraryService.cs b/Manager.App/Services/LibraryService.cs
index c3e3643..e5891db 100644
--- a/Manager.App/Services/LibraryService.cs
+++ b/Manager.App/Services/LibraryService.cs
@@ -151,6 +151,7 @@ public class LibraryService : ILibraryService
var channel = await context.Channels
.Include(c => c.ClientAccount)
.ThenInclude(p => p!.HttpCookies)
+ .Include(f => f.Files)
.FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
if (channel == null)
@@ -288,13 +289,33 @@ public class LibraryService : ILibraryService
}
}
- public async Task> GetChannelsAsync(int offset = 0, int total = 20, CancellationToken cancellationToken = default)
+ public async Task> GetChannelsAsync(string? search, int offset = 0, int total = 20, CancellationToken cancellationToken = default)
{
try
{
await using var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
- var orderedAccounts = context.Channels.Include(x => x.ClientAccount).OrderBy(x => x.Id);
- return new ListResultReturn(orderedAccounts.Skip(offset).Take(total).ToList(),orderedAccounts.Count());
+ var orderedAccounts = context.Channels.OrderBy(x => x.Id);
+
+ var totalChannels = orderedAccounts.Count();
+ if (!string.IsNullOrWhiteSpace(search) && orderedAccounts.Any())
+ {
+ var normalizedSearch = $"%{search.ToLower()}%";
+ var searched = orderedAccounts
+ .Where(ca =>
+ EF.Functions.Like(
+ (
+ ca.Id.ToString() + " " +
+ ca.Name + " " +
+ ca.Handle
+ ).ToLower(),
+ normalizedSearch
+ )
+ );
+ totalChannels = searched.Count();
+ orderedAccounts = searched.OrderByDescending(ca => ca.Id);
+ }
+
+ return new ListResultReturn(totalChannels == 0 ? [] : orderedAccounts.Skip(offset).Take(total).ToList(), totalChannels);
}
catch (Exception e)
{
diff --git a/Manager.Data/Contexts/LibraryDbContext.cs b/Manager.Data/Contexts/LibraryDbContext.cs
index 69256d5..c497328 100644
--- a/Manager.Data/Contexts/LibraryDbContext.cs
+++ b/Manager.Data/Contexts/LibraryDbContext.cs
@@ -58,6 +58,9 @@ public sealed class LibraryDbContext : DbContext
.WithOne(x => x.Channel)
.HasForeignKey(e => e.Id)
.IsRequired(false);
+ channel.HasMany(x => x.Files)
+ .WithOne()
+ .HasForeignKey(f => f.ForeignKey);
});
modelBuilder.Entity(cae =>
@@ -71,6 +74,9 @@ public sealed class LibraryDbContext : DbContext
.WithOne(ca => ca.ClientAccount)
.HasForeignKey(ce => ce.Id)
.IsRequired(false);
+ cae.HasMany(x => x.Files)
+ .WithOne()
+ .HasForeignKey(f => f.ForeignKey);
});
modelBuilder.Entity(httpce =>
diff --git a/Manager.Data/Entities/LibraryContext/ChannelEntity.cs b/Manager.Data/Entities/LibraryContext/ChannelEntity.cs
index bf68f9b..209154f 100644
--- a/Manager.Data/Entities/LibraryContext/ChannelEntity.cs
+++ b/Manager.Data/Entities/LibraryContext/ChannelEntity.cs
@@ -17,4 +17,5 @@ public class ChannelEntity : DateTimeBase
public List Media { get; set; } = [];
public List Playlists { get; set; } = [];
public ClientAccountEntity? ClientAccount { get; set; }
+ public List? Files { get; set; }
}
\ No newline at end of file
diff --git a/Manager.Data/Entities/LibraryContext/ClientAccountEntity.cs b/Manager.Data/Entities/LibraryContext/ClientAccountEntity.cs
index 8c30f15..808d86c 100644
--- a/Manager.Data/Entities/LibraryContext/ClientAccountEntity.cs
+++ b/Manager.Data/Entities/LibraryContext/ClientAccountEntity.cs
@@ -12,4 +12,5 @@ public class ClientAccountEntity : DateTimeBase
[MaxLength(DataConstants.DbContext.DefaultDbStringSize)]
public string? UserAgent { get; set; }
public ChannelEntity? Channel { get; set; }
+ public List? Files { get; set; }
}
\ No newline at end of file