[REWORK] Changes saving client and channel info

This commit is contained in:
max
2025-09-18 02:01:45 +02:00
parent 9e173258ed
commit 5250b9f3f9
10 changed files with 201 additions and 153 deletions

View File

@@ -1,23 +1,19 @@
using System.Net;
using DotBased.Logging;
using DotBased.Monads;
using Manager.App.Models.Library;
using Manager.Data.Entities.LibraryContext;
using Manager.YouTube;
using Manager.YouTube.Models.Innertube;
namespace Manager.App.Services.System;
public class ClientService(IServiceScopeFactory scopeFactory, ILogger<ClientService> logger)
: ExtendedBackgroundService(nameof(ClientService), "Managing YouTube clients", logger, TimeSpan.FromMinutes(10))
{
private readonly List<ClientChannel> _loadedClients = [];
private CancellationToken _cancellationToken;
private readonly YouTubeClientCollection _loadedClients = [];
private ILibraryService? _libraryService;
protected override Task InitializeAsync(CancellationToken stoppingToken)
{
_cancellationToken = stoppingToken;
stoppingToken.Register(CancellationRequested);
using var scope = scopeFactory.CreateScope();
_libraryService = scope.ServiceProvider.GetRequiredService<ILibraryService>();
@@ -25,17 +21,79 @@ public class ClientService(IServiceScopeFactory scopeFactory, ILogger<ClientServ
return Task.CompletedTask;
}
protected override Task ExecuteServiceAsync(CancellationToken stoppingToken)
protected override async Task ExecuteServiceAsync(CancellationToken stoppingToken)
{
return Task.CompletedTask;
LogEvent($"Saving {_loadedClients.Count} loaded client(s)");
foreach (var client in _loadedClients)
{
await SaveClientAsync(client, cancellationToken: stoppingToken);
}
}
private void CancellationRequested()
private async void CancellationRequested()
{
// Clear up
foreach (var client in _loadedClients)
{
await SaveClientAsync(client);
client.Dispose();
}
}
public async Task<Result> SaveClientAsync(YouTubeClient client, Channel? channelInfo = null, CancellationToken cancellationToken = default)
public async Task<Result> AddClientByIdAsync(string id, CancellationToken stoppingToken = default)
{
if (_libraryService == null)
{
return ResultError.Fail("Library service is not initialized!.");
}
var clientResult = await _libraryService.GetChannelByIdAsync(id, stoppingToken);
if (!clientResult.IsSuccess)
{
return clientResult;
}
var clientAcc = clientResult.Value.ClientAccount;
if (clientAcc == null)
{
return ResultError.Fail("Client account is not initialized!.");
}
var cookieCollection = new CookieCollection();
foreach (var httpCookie in clientAcc.HttpCookies)
{
var cookie = new Cookie
{
Name = httpCookie.Name,
Value = httpCookie.Value,
Domain = httpCookie.Domain,
Path = httpCookie.Path,
Secure = httpCookie.Secure,
HttpOnly = httpCookie.HttpOnly,
Expires = httpCookie.ExpiresUtc ?? DateTime.MinValue
};
cookieCollection.Add(cookie);
}
var ytClientResult = await YouTubeClient.CreateAsync(cookieCollection, clientAcc.UserAgent ?? "");
if (!ytClientResult.IsSuccess)
{
return ytClientResult;
}
AddClient(ytClientResult.Value);
return Result.Success();
}
public void AddClient(YouTubeClient client)
{
if (_loadedClients.Contains(client))
{
return;
}
_loadedClients.Add(client);
}
public async Task<Result> SaveClientAsync(YouTubeClient client, CancellationToken cancellationToken = default)
{
if (_libraryService == null)
{
@@ -48,121 +106,7 @@ public class ClientService(IServiceScopeFactory scopeFactory, ILogger<ClientServ
return ResultError.Fail("Client does not have an ID, cannot save to library database!");
}
if (channelInfo != null)
{
var imagesResult = await _libraryService.FetchChannelImagesAsync(channelInfo);
if (!imagesResult.IsSuccess)
{
logger.LogWarning("Failed to fetch channel images!");
}
}
var channelResult = await _libraryService.GetChannelByIdAsync(client.Id, cancellationToken);
ChannelEntity? channelEntity;
try
{
if (channelResult.IsSuccess)
{
channelEntity = channelResult.Value;
UpdateChannelEntity(client, channelEntity, channelInfo);
}
else
{
channelEntity = CreateNewChannelFromClient(client, channelInfo);
}
}
catch (Exception e)
{
LogEvent("Failed to save client: " + e.Message, LogSeverity.Warning);
return ResultError.Error(e);
}
var saveResult = await _libraryService.SaveChannelAsync(channelEntity, cancellationToken);
var saveResult = await _libraryService.SaveClientAsync(new ClientAccountEntity { Id = client.Id, UserAgent = client.UserAgent }, cancellationToken);
return saveResult;
}
private void UpdateChannelEntity(YouTubeClient client, ChannelEntity entity, Channel? channelInfo)
{
if (channelInfo != null)
{
entity.Name = channelInfo.ChannelName;
entity.Handle = channelInfo.Handle;
entity.Description = channelInfo.Description;
}
var clientAcc = entity.ClientAccount;
if (clientAcc != null)
{
clientAcc.UserAgent = clientAcc.UserAgent;
var currentCookies = client.CookieContainer.GetAllCookies();
foreach (var cookieEntity in clientAcc.HttpCookies.ToList())
{
var cookie = currentCookies[cookieEntity.Name];
if (cookie == null)
{
clientAcc.HttpCookies.Remove(cookieEntity);
continue;
}
if (!cookie.Domain.Equals(cookieEntity.Domain, StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
cookieEntity.Value = cookie.Value;
cookieEntity.Path = cookie.Path;
cookieEntity.Secure = cookie.Secure;
cookieEntity.HttpOnly = cookie.HttpOnly;
cookieEntity.ExpiresUtc = cookie.Expires == DateTime.MinValue ? null : cookie.Expires;
}
}
}
private ChannelEntity CreateNewChannelFromClient(YouTubeClient client, Channel? channelInfo)
{
if (channelInfo == null)
{
throw new ArgumentNullException(nameof(channelInfo), "Channel information is required to store new client/account.");
}
var cookies = new List<HttpCookieEntity>();
foreach (var cookieObj in client.CookieContainer.GetAllCookies())
{
if (cookieObj is not Cookie cookie)
{
continue;
}
var cookieEntity = new HttpCookieEntity
{
ClientId = client.Id,
Name = cookie.Name,
Value = cookie.Value,
Domain = cookie.Domain,
Path = cookie.Path,
Secure = cookie.Secure,
HttpOnly = cookie.HttpOnly,
ExpiresUtc = cookie.Expires == DateTime.MinValue ? null : cookie.Expires
};
cookies.Add(cookieEntity);
}
var clientAcc = new ClientAccountEntity
{
Id = client.Id,
UserAgent = client.UserAgent,
HttpCookies = cookies
};
var channel = new ChannelEntity
{
Id = channelInfo.Id,
Name = channelInfo.ChannelName,
Handle = channelInfo.Handle,
Description = channelInfo.Description,
ClientAccount = clientAcc
};
return channel;
}
}