fix(admin): fixed various issues lmao idk it was a ballache and is probably quite shit. sorry to future me

This commit is contained in:
Fishandchips321 2026-03-05 19:09:26 +00:00
parent 56ea7fb7f0
commit 2cbbc00489
29 changed files with 382 additions and 131 deletions

View file

@ -1,4 +1,5 @@
using JellyGlass.Exceptions; using JellyGlass.Exceptions;
using JellyGlass.Models.ControllerBodies.Auth;
using JellyGlass.Services; using JellyGlass.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -64,20 +65,20 @@ public class AuthController : ControllerBase
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> CreateLogin([FromForm] string username, [FromForm] string password) public async Task<IActionResult> CreateLogin([FromBody] CreateLoginDTO login)
{ {
if (!await IsAdminAuthenticated()) if (!await IsAdminAuthenticated())
{ {
return Forbid(); return Forbid();
} }
var newLogin = await _service.CreateLogin(username, password); var newLogin = await _service.CreateLogin(login.Username, login.Password);
return Ok(newLogin); return Ok(newLogin);
} }
[HttpPut] [HttpPut]
public async Task<IActionResult> UpdateLogin([FromForm] string username, [FromForm] string password, [FromForm] string newPassword) public async Task<IActionResult> UpdateLogin([FromBody] UpdateLoginDTO login)
{ {
if (!await IsAdminAuthenticated()) if (!await IsAdminAuthenticated())
{ {
@ -86,7 +87,7 @@ public class AuthController : ControllerBase
try try
{ {
await _service.UpdateLoginOwnPassword(username, newPassword, password); await _service.UpdateLoginOwnPassword(GetSessionToken()!, login.NewPassword, login.Password); //TODO: should take in session token
} }
catch (AuthNotFoundException) catch (AuthNotFoundException)
{ {
@ -101,7 +102,7 @@ public class AuthController : ControllerBase
} }
[HttpDelete] [HttpDelete]
public async Task<IActionResult> DeleteLogin([FromForm] string username) public async Task<IActionResult> DeleteLogin([FromBody] DeleteLoginDTO login)
{ {
if (!await IsAdminAuthenticated()) if (!await IsAdminAuthenticated())
{ {
@ -110,7 +111,7 @@ public class AuthController : ControllerBase
try try
{ {
var deletedLogin = await _service.DeleteLogin(username); var deletedLogin = await _service.DeleteLogin(login.Username);
return Ok(deletedLogin); return Ok(deletedLogin);
} }

View file

@ -21,7 +21,7 @@ public class SearchController : ControllerBase
} }
[HttpGet] [HttpGet]
public async Task<IActionResult> handleSearch([FromQuery] string searchTerm, string serverId) public async Task<IActionResult> handleSearch([FromQuery] string searchTerm, string serverUrl)
{ {
var sessionToken = Request.Cookies["session"]; var sessionToken = Request.Cookies["session"];
@ -34,7 +34,7 @@ public class SearchController : ControllerBase
try try
{ {
var results = await _service.Search(decodedSearchTerm, serverId); var results = await _service.Search(decodedSearchTerm, serverUrl);
return Ok(results); return Ok(results);
} }
catch (ClientNotFoundException) catch (ClientNotFoundException)

View file

@ -1,4 +1,6 @@
using System.Web;
using JellyGlass.Exceptions; using JellyGlass.Exceptions;
using JellyGlass.Models.ControllerBodies.Servers;
using JellyGlass.Services; using JellyGlass.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -33,11 +35,17 @@ public class ServersController : ControllerBase
} }
[HttpGet] [HttpGet]
public async Task<IActionResult> GetServer([FromQuery] int id) public async Task<IActionResult> GetServer([FromQuery] string url)
{ {
if (!await IsAuthenticated())
{
return Unauthorized();
}
try try
{ {
var server = await _serverService.GetServerByID(id); var decodedUrl = HttpUtility.UrlDecode(url);
var server = await _serverService.GetServerByUrl(decodedUrl);
return Ok(server); return Ok(server);
} }
catch (ServerNotFoundException) catch (ServerNotFoundException)
@ -47,7 +55,7 @@ public class ServersController : ControllerBase
} }
[HttpPut] [HttpPut]
public async Task<IActionResult> UpdateServer([FromBody] int id, string owner, string url, string apiToken) public async Task<IActionResult> UpdateServer([FromBody] UpdateServerDTO server)
{ {
if (!await IsAdminAuthenticated()) if (!await IsAdminAuthenticated())
{ {
@ -56,7 +64,7 @@ public class ServersController : ControllerBase
try try
{ {
var updatedServer = await _serverService.UpdateServer(id, owner, url, apiToken); var updatedServer = await _serverService.UpdateServer(server.Owner, server.Url, server.ApiToken);
return Ok(updatedServer); return Ok(updatedServer);
} }
catch (ServerNotFoundException) catch (ServerNotFoundException)
@ -66,20 +74,7 @@ public class ServersController : ControllerBase
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> AddServer([FromBody] string owner, string url, string apiToken) public async Task<IActionResult> AddServer([FromBody] AddServerDTO server)
{
if (!await IsAdminAuthenticated())
{
return Forbid();
}
var newServer = await _serverService.CreateServer(owner, url, apiToken);
return Ok(newServer);
}
[HttpDelete]
public async Task<IActionResult> DeleteServer([FromBody] int id)
{ {
if (!await IsAdminAuthenticated()) if (!await IsAdminAuthenticated())
{ {
@ -88,7 +83,27 @@ public class ServersController : ControllerBase
try try
{ {
var deletedServer = await _serverService.DeleteServer(id); var newServer = await _serverService.CreateServer(server.Owner, server.Url, server.ApiToken);
return Ok(newServer);
}
catch (ServerAlreadyExistsException)
{
return Conflict("A server with this url already exists");
}
}
[HttpDelete]
public async Task<IActionResult> DeleteServer([FromBody] DeleteServerDTO server)
{
if (!await IsAdminAuthenticated())
{
return Forbid();
}
try
{
var decodedUrl = HttpUtility.UrlDecode(server.Url);
var deletedServer = await _serverService.DeleteServer(decodedUrl);
return Ok(deletedServer); return Ok(deletedServer);
} }
catch (ServerNotFoundException) catch (ServerNotFoundException)

View file

@ -7,7 +7,6 @@ public class AuthServiceException : Exception
public AuthServiceException(string message, Exception inner) : base(message, inner) { } public AuthServiceException(string message, Exception inner) : base(message, inner) { }
} }
[System.Serializable]
public class LoginFailedException : AuthServiceException public class LoginFailedException : AuthServiceException
{ {
public LoginFailedException() { } public LoginFailedException() { }

View file

@ -13,3 +13,10 @@ public class ServerNotFoundException : ServerRepositoryException
public ServerNotFoundException(string message) : base(message) { } public ServerNotFoundException(string message) : base(message) { }
public ServerNotFoundException(string message, Exception inner) : base(message, inner) { } public ServerNotFoundException(string message, Exception inner) : base(message, inner) { }
} }
public class ServerAlreadyExistsException : ServerRepositoryException
{
public ServerAlreadyExistsException() { }
public ServerAlreadyExistsException(string message) : base(message) { }
public ServerAlreadyExistsException(string message, System.Exception inner) : base(message, inner) { }
}

View file

@ -0,0 +1,87 @@
// <auto-generated />
using System;
using JellyGlass.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace JellyGlassBackend.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20260305184556_changedServerId")]
partial class changedServerId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.11");
modelBuilder.Entity("JellyGlass.Models.Server", b =>
{
b.Property<string>("Url")
.HasColumnType("TEXT");
b.Property<string>("ApiToken")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Owner")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Url");
b.ToTable("Servers");
});
modelBuilder.Entity("JellyGlass.Models.UserLogin", b =>
{
b.Property<string>("Username")
.HasColumnType("TEXT");
b.Property<string>("HashedPassword")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("IsAdmin")
.HasColumnType("INTEGER");
b.HasKey("Username");
b.ToTable("Logins");
});
modelBuilder.Entity("JellyGlass.Models.UserSession", b =>
{
b.Property<string>("SessionToken")
.HasColumnType("TEXT");
b.Property<DateTime>("ExpiresOn")
.HasColumnType("TEXT");
b.Property<string>("LoginUsername")
.HasColumnType("TEXT");
b.HasKey("SessionToken");
b.HasIndex("LoginUsername");
b.ToTable("Sessions");
});
modelBuilder.Entity("JellyGlass.Models.UserSession", b =>
{
b.HasOne("JellyGlass.Models.UserLogin", "Login")
.WithMany()
.HasForeignKey("LoginUsername");
b.Navigation("Login");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,48 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JellyGlassBackend.Migrations
{
/// <inheritdoc />
public partial class changedServerId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_Servers",
table: "Servers");
migrationBuilder.DropColumn(
name: "Id",
table: "Servers");
migrationBuilder.AddPrimaryKey(
name: "PK_Servers",
table: "Servers",
column: "Url");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_Servers",
table: "Servers");
migrationBuilder.AddColumn<int>(
name: "Id",
table: "Servers",
type: "INTEGER",
nullable: false,
defaultValue: 0)
.Annotation("Sqlite:Autoincrement", true);
migrationBuilder.AddPrimaryKey(
name: "PK_Servers",
table: "Servers",
column: "Id");
}
}
}

View file

@ -19,9 +19,8 @@ namespace JellyGlassBackend.Migrations
modelBuilder.Entity("JellyGlass.Models.Server", b => modelBuilder.Entity("JellyGlass.Models.Server", b =>
{ {
b.Property<int>("Id") b.Property<string>("Url")
.ValueGeneratedOnAdd() .HasColumnType("TEXT");
.HasColumnType("INTEGER");
b.Property<string>("ApiToken") b.Property<string>("ApiToken")
.IsRequired() .IsRequired()
@ -31,11 +30,7 @@ namespace JellyGlassBackend.Migrations
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Url") b.HasKey("Url");
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Servers"); b.ToTable("Servers");
}); });

View file

@ -0,0 +1,7 @@
namespace JellyGlass.Models.ControllerBodies.Auth;
public class CreateLoginDTO
{
public required string Username { get; set; }
public required string Password { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace JellyGlass.Models.ControllerBodies.Auth;
public class DeleteLoginDTO
{
public required string Username { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace JellyGlass.Models.ControllerBodies.Auth;
public class UpdateLoginDTO
{
public required string Password { get; set; }
public required string NewPassword { get; set; }
}

View file

@ -0,0 +1,8 @@
namespace JellyGlass.Models.ControllerBodies.Servers;
public class AddServerDTO
{
public required string Owner { get; set; }
public required string Url { get; set; }
public required string ApiToken { get; set; }
}

View file

@ -0,0 +1,6 @@
namespace JellyGlass.Models.ControllerBodies.Servers;
public class DeleteServerDTO
{
public required string Url { get; set; }
}

View file

@ -0,0 +1,8 @@
namespace JellyGlass.Models.ControllerBodies.Servers;
public class UpdateServerDTO
{
public required string Owner { get; set; }
public required string Url { get; set; }
public required string ApiToken { get; set; }
}

View file

@ -1,14 +1,11 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace JellyGlass.Models; namespace JellyGlass.Models;
public class Server public class Server
{ {
public string Owner { get; set; } = string.Empty; public string Owner { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty;
[Key] [Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] public string Url { get; set; } = string.Empty;
public int Id { get; set; }
public string ApiToken { get; set; } = string.Empty; public string ApiToken { get; set; } = string.Empty;
} }

View file

@ -8,12 +8,10 @@ public class ServerDTO
{ {
Owner = s.Owner; Owner = s.Owner;
Url = s.Url; Url = s.Url;
Id = s.Id;
} }
public string Owner { get; set; } = string.Empty; public string Owner { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty; public string Url { get; set; } = string.Empty;
public int Id { get; set; }
public string JellyfinServerID { get; set; } = string.Empty; public string JellyfinServerID { get; set; } = string.Empty;
public bool Errored { get; set; } = false; public bool Errored { get; set; } = false;
} }

View file

@ -5,8 +5,8 @@ namespace JellyGlass.Repositories;
public interface IServerRepository public interface IServerRepository
{ {
public Task<Server[]> GetServers(); public Task<Server[]> GetServers();
public Task<Server> GetServerById(int id); public Task<Server> GetServerByUrl(string url);
public Task<Server> CreateServer(string owner, string url, string apiToken); public Task<Server> CreateServer(string owner, string url, string apiToken);
public Task<Server> UpdateServer(int id, string owner, string url, string apiToken); public Task<Server> UpdateServer(string owner, string url, string apiToken);
public Task<Server> DeleteServer(int id); public Task<Server> DeleteServer(string url);
} }

View file

@ -20,9 +20,9 @@ public class ServerRepository : IServerRepository
return servers; return servers;
} }
public async Task<Server> GetServerById(int id) public async Task<Server> GetServerByUrl(string url)
{ {
var server = await _context.Servers.FirstOrDefaultAsync(s => s.Id == id); var server = await _context.Servers.FirstOrDefaultAsync(s => s.Url == url);
if (server == null) if (server == null)
{ {
@ -34,6 +34,10 @@ public class ServerRepository : IServerRepository
public async Task<Server> CreateServer(string owner, string url, string apiToken) public async Task<Server> CreateServer(string owner, string url, string apiToken)
{ {
if (_context.Servers.Where(s => s.Url == url).Any())
{
throw new ServerAlreadyExistsException();
}
var newServer = new Server() var newServer = new Server()
{ {
@ -48,12 +52,11 @@ public class ServerRepository : IServerRepository
return newServer; return newServer;
} }
public async Task<Server> UpdateServer(int id, string owner, string url, string apiToken) public async Task<Server> UpdateServer(string owner, string url, string apiToken)
{ {
var server = await GetServerById(id); var server = await GetServerByUrl(url);
server.Owner = owner; server.Owner = owner;
server.Url = url;
server.ApiToken = apiToken; server.ApiToken = apiToken;
_context.Servers.Update(server); _context.Servers.Update(server);
@ -62,9 +65,9 @@ public class ServerRepository : IServerRepository
return server; return server;
} }
public async Task<Server> DeleteServer(int id) public async Task<Server> DeleteServer(string url)
{ {
var server = await GetServerById(id); var server = await GetServerByUrl(url);
_context.Servers.Remove(server); _context.Servers.Remove(server);
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();

View file

@ -107,16 +107,23 @@ public class AuthService : IAuthService
return new UserLoginDTO(newLogin); return new UserLoginDTO(newLogin);
} }
public async Task UpdateLoginOwnPassword(string username, string newPassword, string oldPassword) public async Task UpdateLoginOwnPassword(string sessionToken, string newPassword, string oldPassword)
{ {
var login = await _loginRepo.GetUserLogin(username); var session = await _sessionRepo.GetUserSession(sessionToken);
if (session == null)
{
throw new LoginFailedException();
}
var login = session.Login;
if (!BCrypt.Net.BCrypt.Verify(oldPassword, login.HashedPassword)) if (!BCrypt.Net.BCrypt.Verify(oldPassword, login.HashedPassword))
{ {
throw new LoginFailedException(); throw new LoginFailedException();
} }
await UpdateLoginPassword(username, newPassword); await UpdateLoginPassword(login.Username, newPassword);
} }
public async Task UpdateLoginPassword(string username, string newPassword) public async Task UpdateLoginPassword(string username, string newPassword)

View file

@ -1,4 +1,5 @@
using JellyGlass.Exceptions; using JellyGlass.Exceptions;
using JellyGlass.Models;
using JellyGlass.Repositories; using JellyGlass.Repositories;
namespace JellyGlass.Services; namespace JellyGlass.Services;
@ -6,7 +7,7 @@ namespace JellyGlass.Services;
public class ClientService : IClientService public class ClientService : IClientService
{ {
private IServerRepository _repository; private IServerRepository _repository;
private static JellyfinApiClient[] _clients = []; private static List<JellyfinApiClient> _clients = new List<JellyfinApiClient>();
private ILogger<ClientService> _logger; private ILogger<ClientService> _logger;
public ClientService(IServerRepository repository, ILogger<ClientService> logger) public ClientService(IServerRepository repository, ILogger<ClientService> logger)
@ -22,59 +23,70 @@ public class ClientService : IClientService
await LoadClients(); await LoadClients();
} }
return _clients; return _clients.ToArray();
} }
public async Task<JellyfinApiClient> GetClientForServerId(string serverId) public async Task<JellyfinApiClient> GetClientFromUrl(string url)
{ {
if (!_clients.Any()) if (!_clients.Any())
{ {
await LoadClients(); await LoadClients();
} }
foreach (var client in _clients) var client = _clients.FirstOrDefault(c => c.InstanceUrl == url);
if (client == null)
{ {
if (client.ID == serverId) throw new Exception($"Could not find client with url {url}");
{
return client;
}
} }
throw new Exception($"Client with ID {serverId} not found"); return client;
}
public async Task LoadNewClient(Server server)
{
var client = new JellyfinApiClient(server.Url, server.ApiToken);
try
{
await client.GetServerInfo();
}
catch (JellyfinApiClientException e)
{
_logger.LogError($"Error authenticating to {server.Url}. Error: {e.Message} Client will not be used");
throw;
}
_clients.Add(client);
}
public void UnloadClient(JellyfinApiClient client)
{
if (_clients.Contains(client))
{
_clients.Remove(client);
}
else
{
throw new Exception($"Haven't loaded Jellyfin api client with ID of {client.ID}");
}
} }
private async Task LoadClients() private async Task LoadClients()
{ {
var servers = await _repository.GetServers(); var servers = await _repository.GetServers();
var clients = new List<JellyfinApiClient>();
foreach (var server in servers) foreach (var server in servers)
{ {
var client = new JellyfinApiClient(server.Url, server.ApiToken);
// try
// {
// await client.Authenticate();
// }
// catch (JellyfinApiClientException e)
// {
// _logger.LogError($"Error authenticating to {server.Url}. Error: {e.Message} Client will not be used");
// continue;
// }
try try
{ {
await client.GetServerInfo(); //test the connection await LoadNewClient(server);
} }
catch (JellyfinApiClientException e) catch (JellyfinApiClientException)
{ {
_logger.LogError($"Error authenticating to {server.Url}. Error: {e.Message} Client will not be used");
continue; continue;
} }
clients.Add(client);
} }
_clients = clients.ToArray();
} }
} }

View file

@ -10,7 +10,7 @@ public interface IAuthService
public Task<UserLoginDTO[]> GetLogins(); public Task<UserLoginDTO[]> GetLogins();
public Task<UserLoginDTO> GetLogin(string username); public Task<UserLoginDTO> GetLogin(string username);
public Task<UserLoginDTO> CreateLogin(string username, string password); public Task<UserLoginDTO> CreateLogin(string username, string password);
public Task UpdateLoginOwnPassword(string username, string newPassword, string oldPassword); public Task UpdateLoginOwnPassword(string sessionToken, string newPassword, string oldPassword);
public Task UpdateLoginPassword(string username, string newPassword); public Task UpdateLoginPassword(string username, string newPassword);
public Task<UserLoginDTO> DeleteLogin(string username); public Task<UserLoginDTO> DeleteLogin(string username);
} }

View file

@ -1,3 +1,4 @@
using JellyGlass.Models;
using JellyGlass.Repositories; using JellyGlass.Repositories;
namespace JellyGlass.Services; namespace JellyGlass.Services;
@ -5,6 +6,7 @@ namespace JellyGlass.Services;
public interface IClientService public interface IClientService
{ {
public Task<JellyfinApiClient[]> GetClients(); public Task<JellyfinApiClient[]> GetClients();
// public JellyfinApiClient GetClientForServer(string url); public Task<JellyfinApiClient> GetClientFromUrl(string url);
public Task<JellyfinApiClient> GetClientForServerId(string serverId); public Task LoadNewClient(Server server);
public void UnloadClient(JellyfinApiClient client);
} }

View file

@ -5,9 +5,9 @@ namespace JellyGlass.Services;
public interface ILibraryService public interface ILibraryService
{ {
public Task<Library[]> GetLibraries(); public Task<Library[]> GetLibraries();
public Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverId); public Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverUrl);
public Task<Library[]> GetLibrariesFromServer(string serverId); public Task<Library[]> GetLibrariesFromServer(string serverUrl);
// public Task<ItemDTO[]> GetChildrenFromItems(ItemDTO[] items); // public Task<ItemDTO[]> GetChildrenFromItems(ItemDTO[] items);

View file

@ -4,5 +4,5 @@ namespace JellyGlass.Services;
public interface ISearchService public interface ISearchService
{ {
public Task<ItemDTO[]> Search(string searchTerm, string serverId); public Task<ItemDTO[]> Search(string searchTerm, string serverUrl);
} }

View file

@ -7,8 +7,8 @@ namespace JellyGlass.Services;
public interface IServerService public interface IServerService
{ {
public Task<ServerDTO[]> GetServers(); public Task<ServerDTO[]> GetServers();
public Task<ServerDTO> GetServerByID(int id); public Task<ServerDTO> GetServerByUrl(string url);
public Task<ServerDTO> CreateServer(string owner, string url, string apiToken); public Task<ServerDTO> CreateServer(string owner, string url, string apiToken);
public Task<ServerDTO> UpdateServer(int id, string owner, string url, string apiToken); public Task<ServerDTO> UpdateServer(string owner, string url, string apiToken);
public Task<ServerDTO> DeleteServer(int id); public Task<ServerDTO> DeleteServer(string url);
} }

View file

@ -44,13 +44,13 @@ public class LibraryService : ILibraryService
return libraries.Values.ToArray(); return libraries.Values.ToArray();
} }
public async Task<Item> GetLibrary(string libraryName, string serverId) public async Task<Item> GetLibrary(string libraryName, string serverUrl)
{ {
var client = await _clientService.GetClientForServerId(serverId); var client = await _clientService.GetClientFromUrl(serverUrl); ;
if (client == null) if (client == null)
{ {
throw new Exception($"Could not find client with ID of {serverId}"); throw new Exception($"Could not find client with url of {serverUrl}");
} }
var libraries = await client.GetInstanceLibraries(); var libraries = await client.GetInstanceLibraries();
@ -66,11 +66,11 @@ public class LibraryService : ILibraryService
throw new Exception("Couldn't find library"); throw new Exception("Couldn't find library");
} }
public async Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverId) public async Task<ItemDTO[]> GetItemsFromLibrary(string libraryName, string serverUrl)
{ {
var client = await _clientService.GetClientForServerId(serverId); var client = await _clientService.GetClientFromUrl(serverUrl);
var library = await GetLibrary(libraryName, serverId); var library = await GetLibrary(libraryName, serverUrl);
var items = await client.GetItemChildren(library.Id); var items = await client.GetItemChildren(library.Id);
@ -84,9 +84,9 @@ public class LibraryService : ILibraryService
return dtos.ToArray(); return dtos.ToArray();
} }
public async Task<Library[]> GetLibrariesFromServer(string serverId) public async Task<Library[]> GetLibrariesFromServer(string serverUrl)
{ {
var client = await _clientService.GetClientForServerId(serverId); var client = await _clientService.GetClientFromUrl(serverUrl);
var libraries = new Dictionary<string, Library>(); var libraries = new Dictionary<string, Library>();

View file

@ -14,9 +14,9 @@ public class SearchService : ISearchService
_clientService = clientService; _clientService = clientService;
} }
public async Task<ItemDTO[]> Search(string searchTerm, string serverId) public async Task<ItemDTO[]> Search(string searchTerm, string serverUrl)
{ {
var client = await _clientService.GetClientForServerId(serverId); var client = await _clientService.GetClientFromUrl(serverUrl);
var items = await client.GetItems(searchTerm: searchTerm); var items = await client.GetItems(searchTerm: searchTerm);
@ -30,25 +30,25 @@ public class SearchService : ISearchService
return dtos.ToArray(); return dtos.ToArray();
} }
public async Task<ItemDTO[]> Search2(string searchTerm, string serverId) // public async Task<ItemDTO[]> Search2(string searchTerm, int serverId)
// {
// var libraries = await _libraryService.GetLibrariesFromServer(serverId);
// var foundItems = new List<ItemDTO>();
// foreach (var library in libraries)
// {
// var found = await SearchLibraryForTerm(searchTerm, serverId, library);
// foundItems.AddRange(found);
// }
// return foundItems.ToArray();
// }
private async Task<ItemDTO[]> SearchLibraryForTerm(string searchTerm, string serverUrl, Library library)
{ {
var libraries = await _libraryService.GetLibrariesFromServer(serverId); var items = await _libraryService.GetItemsFromLibrary(library.Name, serverUrl);
var foundItems = new List<ItemDTO>();
foreach (var library in libraries)
{
var found = await SearchLibraryForTerm(searchTerm, serverId, library);
foundItems.AddRange(found);
}
return foundItems.ToArray();
}
private async Task<ItemDTO[]> SearchLibraryForTerm(string searchTerm, string serverId, Library library)
{
var items = await _libraryService.GetItemsFromLibrary(library.Name, serverId);
var foundItems = new List<ItemDTO>(); var foundItems = new List<ItemDTO>();

View file

@ -1,5 +1,6 @@
using JellyGlass.Exceptions;
using JellyGlass.Models; using JellyGlass.Models;
using JellyGlass.Repositories; using JellyGlass.Repositories;
@ -49,31 +50,68 @@ public class ServerService : IServerService
return dtos.ToArray(); return dtos.ToArray();
} }
public async Task<ServerDTO> GetServerByID(int id) public async Task<ServerDTO> GetServerByUrl(string url)
{ {
var server = await _repository.GetServerById(id); var server = await _repository.GetServerByUrl(url);
return new ServerDTO(server); return await GetServerDTO(server);
} }
public async Task<ServerDTO> CreateServer(string owner, string url, string apiToken) public async Task<ServerDTO> CreateServer(string owner, string url, string apiToken)
{ {
var newServer = await _repository.CreateServer(owner, url, apiToken); var newServer = await _repository.CreateServer(owner, url, apiToken);
var dto = new ServerDTO(newServer);
return new ServerDTO(newServer); try
{
await _service.LoadNewClient(newServer);
}
catch (JellyfinApiClientException)
{
dto.Errored = true;
}
return dto;
} }
public async Task<ServerDTO> UpdateServer(int id, string owner, string url, string apiToken) public async Task<ServerDTO> UpdateServer(string owner, string url, string apiToken)
{ {
var updatedServer = await _repository.UpdateServer(id, owner, url, apiToken); var updatedServer = await _repository.UpdateServer(owner, url, apiToken);
return new ServerDTO(updatedServer); return await GetServerDTO(updatedServer);
} }
public async Task<ServerDTO> DeleteServer(int id) public async Task<ServerDTO> DeleteServer(string url)
{ {
var deletedServer = await _repository.DeleteServer(id); var deletedServer = await _repository.DeleteServer(url);
try
{
var client = await _service.GetClientFromUrl(deletedServer.Url);
_service.UnloadClient(client);
}
catch (Exception)
{
//if it's not loaded, we don't care as we want to unload it anyway
}
return new ServerDTO(deletedServer); return new ServerDTO(deletedServer);
} }
private async Task<ServerDTO> GetServerDTO(Server server)
{
var dto = new ServerDTO(server);
try
{
var client = await _service.GetClientFromUrl(server.Url);
dto.JellyfinServerID = client.ID;
}
catch (Exception)
{
dto.Errored = true;
}
return dto;
}
} }

View file

@ -33,9 +33,9 @@ const ServerSearch = ({ searchTerm, server }: ServerSearchProps) => {
searchResults.length > 0 ? searchResults.length > 0 ?
searchResults.map(result => { searchResults.map(result => {
return ( return (
<tr> <tr key={result!.id}>
<td> <td>
<ServerSearchResult key={result!.id} searchResult={result!} server={server} /> <ServerSearchResult searchResult={result!} server={server} />
</td> </td>
</tr> </tr>
) )