diff --git a/backend/src/Application/Application.csproj b/backend/src/Application/Application.csproj
deleted file mode 100644
index 64eb85d..0000000
--- a/backend/src/Application/Application.csproj
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
- net9.0
- enable
- enable
-
-
-
diff --git a/backend/src/Application/Services/ItemService.cs b/backend/src/Application/Services/ItemService.cs
deleted file mode 100644
index ac5e840..0000000
--- a/backend/src/Application/Services/ItemService.cs
+++ /dev/null
@@ -1 +0,0 @@
-namespace JellyGlass.Application.Services;
\ No newline at end of file
diff --git a/backend/src/Controllers/LibraryController.cs b/backend/src/Controllers/LibraryController.cs
new file mode 100644
index 0000000..f3fc142
--- /dev/null
+++ b/backend/src/Controllers/LibraryController.cs
@@ -0,0 +1,51 @@
+using JellyGlass.Models;
+using JellyGlass.Services;
+using Microsoft.AspNetCore.Mvc;
+
+namespace JellyGlass.Controllers;
+
+[ApiController]
+[Route("/api/[controller]")]
+public class LibraryController : ControllerBase
+{
+ private ILogger _logger;
+ private ILibraryService _service;
+
+ public LibraryController(ILogger logger, ILibraryService service)
+ {
+ _logger = logger;
+ _service = service;
+ }
+
+ [HttpGet()]
+ public async Task GetLibrares()
+ {
+ var libraries = await _service.GetLibraries();
+
+ return Ok(libraries);
+ }
+
+ [HttpGet("{libraryName}")]
+ public async Task GetLibraryItems([FromRoute] string libraryName)
+ {
+ throw new NotImplementedException();
+ }
+
+ // [HttpGet("{libraryName}/Item/{itemName}")]
+ // public async Task GetLibraryItem([FromRoute] string libraryName, string itemName)
+ // {
+
+ // }
+
+ // [HttpGet("TvShows/{seriesName}")]
+ // public async Task GetSeasonsForTvSeries([FromRoute] string seriesName)
+ // {
+
+ // }
+
+ // [HttpGet("TvShows/{seriesName}/Season/{seasonName}")]
+ // public async Task GetEpisodesForTvSeriesSeason([FromRoute] string seriesName, string seasonName)
+ // {
+
+ // }
+}
\ No newline at end of file
diff --git a/backend/src/Application/Services/LibraryService.cs b/backend/src/Controllers/MoviesController.cs
similarity index 100%
rename from backend/src/Application/Services/LibraryService.cs
rename to backend/src/Controllers/MoviesController.cs
diff --git a/backend/src/Application/Services/ServerService.cs b/backend/src/Controllers/TvShowsController.cs
similarity index 100%
rename from backend/src/Application/Services/ServerService.cs
rename to backend/src/Controllers/TvShowsController.cs
diff --git a/backend/src/Core/Core.csproj b/backend/src/Core/Core.csproj
deleted file mode 100644
index 125f4c9..0000000
--- a/backend/src/Core/Core.csproj
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- net9.0
- enable
- enable
-
-
-
diff --git a/backend/src/Core/Entities/Item.cs b/backend/src/Core/Entities/Item.cs
deleted file mode 100644
index 0352276..0000000
--- a/backend/src/Core/Entities/Item.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace JellyGlass.Core.Entities;
-
-public class Item
-{
- public string ID { get; set; }
- public string Name { get; set; }
- public Uri ThumbnailUrl { get; set; }
- public Uri Url { get; set; }
-}
\ No newline at end of file
diff --git a/backend/src/Core/Entities/Library.cs b/backend/src/Core/Entities/Library.cs
deleted file mode 100644
index cf0ee35..0000000
--- a/backend/src/Core/Entities/Library.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace JellyGlass.Core.Entities;
-
-public class Library
-{
- public string ID { get; set; }
- public string Name { get; set; }
- public Uri ThumbnailUrl { get; set; }
-}
\ No newline at end of file
diff --git a/backend/src/Core/Entities/Server.cs b/backend/src/Core/Entities/Server.cs
deleted file mode 100644
index 91181d0..0000000
--- a/backend/src/Core/Entities/Server.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace JellyGlass.Core.Entities;
-
-public class Server
-{
- public string Owner { get; set; }
- public Uri Url { get; set; }
-}
\ No newline at end of file
diff --git a/backend/src/Core/Interfaces/IItemRepository.cs b/backend/src/Core/Interfaces/IItemRepository.cs
deleted file mode 100644
index 3502a94..0000000
--- a/backend/src/Core/Interfaces/IItemRepository.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using JellyGlass.Core.Entities;
-
-namespace JellyGlass.Core.Interfaces;
-
-public interface IItemRepository
-{
- public Task- GetItemsFromLibrary(string libraryId);
-}
\ No newline at end of file
diff --git a/backend/src/Core/Interfaces/IItemService.cs b/backend/src/Core/Interfaces/IItemService.cs
deleted file mode 100644
index 3d32dbb..0000000
--- a/backend/src/Core/Interfaces/IItemService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using JellyGlass.Core.Entities;
-
-namespace JellyGlass.Core.Interfaces;
-
-public interface IItemService
-{
- public Task
- GetItemsFromLibrary(string libraryId);
-}
\ No newline at end of file
diff --git a/backend/src/Core/Interfaces/ILibraryRepository.cs b/backend/src/Core/Interfaces/ILibraryRepository.cs
deleted file mode 100644
index 1c47580..0000000
--- a/backend/src/Core/Interfaces/ILibraryRepository.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using JellyGlass.Core.Entities;
-
-namespace JellyGlass.Core.Interfaces;
-
-public interface ILibraryRepository
-{
- public Task GetLibraries();
-}
\ No newline at end of file
diff --git a/backend/src/Core/Interfaces/ILibraryService.cs b/backend/src/Core/Interfaces/ILibraryService.cs
deleted file mode 100644
index b775085..0000000
--- a/backend/src/Core/Interfaces/ILibraryService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using JellyGlass.Core.Entities;
-
-namespace JellyGlass.Core.Interfaces;
-
-public interface ILibraryService
-{
- public Task GetLibraries();
-}
\ No newline at end of file
diff --git a/backend/src/Core/Interfaces/IServerService.cs b/backend/src/Core/Interfaces/IServerService.cs
deleted file mode 100644
index 238da9c..0000000
--- a/backend/src/Core/Interfaces/IServerService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using JellyGlass.Core.Entities;
-
-namespace JellyGlass.Core.Interfaces;
-
-public interface IServerService
-{
- public Task GetServers();
-}
\ No newline at end of file
diff --git a/backend/src/Exceptions/JellyfinApiClientExceptions.cs b/backend/src/Exceptions/JellyfinApiClientExceptions.cs
new file mode 100644
index 0000000..d979004
--- /dev/null
+++ b/backend/src/Exceptions/JellyfinApiClientExceptions.cs
@@ -0,0 +1,9 @@
+namespace JellyGlass.Exceptions;
+
+[Serializable]
+public class JellyfinApiClientException : Exception
+{
+ public JellyfinApiClientException() { }
+ public JellyfinApiClientException(string message) : base(message) { }
+ public JellyfinApiClientException(string message, Exception inner) : base(message, inner) { }
+}
\ No newline at end of file
diff --git a/backend/src/Infrastructure/Infrastructure.csproj b/backend/src/Infrastructure/Infrastructure.csproj
deleted file mode 100644
index 8a49582..0000000
--- a/backend/src/Infrastructure/Infrastructure.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
- net9.0
- enable
- enable
-
-
-
diff --git a/backend/src/Infrastructure/Repositories/ItemRepository.cs b/backend/src/Infrastructure/Repositories/ItemRepository.cs
deleted file mode 100644
index 129e431..0000000
--- a/backend/src/Infrastructure/Repositories/ItemRepository.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using JellyGlass.Core.Entities;
-using JellyGlass.Core.Interfaces;
-
-namespace JellyGlass.Infrastructure.Repositories;
\ No newline at end of file
diff --git a/backend/src/Infrastructure/Repositories/LibraryRepository.cs b/backend/src/Infrastructure/Repositories/LibraryRepository.cs
deleted file mode 100644
index 49c02d5..0000000
--- a/backend/src/Infrastructure/Repositories/LibraryRepository.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using JellyGlass.Core.Entities;
-using JellyGlass.Core.Interfaces;
-
-namespace JellyGlass.Infrastructure.Repositories;
-
-public class LibraryRepository : ILibraryRepository
-{
- public Task GetLibraries()
- {
- throw new NotImplementedException();
- }
-}
\ No newline at end of file
diff --git a/backend/src/Infrastructure/Repositories/ServerRepository.cs b/backend/src/Infrastructure/Repositories/ServerRepository.cs
deleted file mode 100644
index 129e431..0000000
--- a/backend/src/Infrastructure/Repositories/ServerRepository.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using JellyGlass.Core.Entities;
-using JellyGlass.Core.Interfaces;
-
-namespace JellyGlass.Infrastructure.Repositories;
\ No newline at end of file
diff --git a/backend/src/Infrastructure/Services/JellyfinApiClient.cs b/backend/src/Infrastructure/Services/JellyfinApiClient.cs
deleted file mode 100644
index 572a1a2..0000000
--- a/backend/src/Infrastructure/Services/JellyfinApiClient.cs
+++ /dev/null
@@ -1 +0,0 @@
-namespace JellyGlass.Infrastructure.Services;
\ No newline at end of file
diff --git a/backend/src/JellyGlass-Backend.csproj b/backend/src/JellyGlass-Backend.csproj
new file mode 100644
index 0000000..2bc8cc1
--- /dev/null
+++ b/backend/src/JellyGlass-Backend.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
diff --git a/backend/src/JellyGlass-test.db b/backend/src/JellyGlass-test.db
new file mode 100644
index 0000000..0b21577
Binary files /dev/null and b/backend/src/JellyGlass-test.db differ
diff --git a/backend/src/JellyGlass.sln b/backend/src/JellyGlass.sln
deleted file mode 100644
index 2d83437..0000000
--- a/backend/src/JellyGlass.sln
+++ /dev/null
@@ -1,40 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31903.59
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi", "WebApi\WebApi.csproj", "{870696BA-371D-455E-B39A-7B3B14FDE62D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Application\Application.csproj", "{5C6D5937-49DA-4758-9AE5-C1290975DBB4}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{DFE2D8D3-3174-44D9-AC91-C129D208A37B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{7BA1276D-3AF5-4E09-B996-2D91556F8939}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {870696BA-371D-455E-B39A-7B3B14FDE62D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {870696BA-371D-455E-B39A-7B3B14FDE62D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {870696BA-371D-455E-B39A-7B3B14FDE62D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {870696BA-371D-455E-B39A-7B3B14FDE62D}.Release|Any CPU.Build.0 = Release|Any CPU
- {5C6D5937-49DA-4758-9AE5-C1290975DBB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5C6D5937-49DA-4758-9AE5-C1290975DBB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5C6D5937-49DA-4758-9AE5-C1290975DBB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5C6D5937-49DA-4758-9AE5-C1290975DBB4}.Release|Any CPU.Build.0 = Release|Any CPU
- {DFE2D8D3-3174-44D9-AC91-C129D208A37B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DFE2D8D3-3174-44D9-AC91-C129D208A37B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DFE2D8D3-3174-44D9-AC91-C129D208A37B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DFE2D8D3-3174-44D9-AC91-C129D208A37B}.Release|Any CPU.Build.0 = Release|Any CPU
- {7BA1276D-3AF5-4E09-B996-2D91556F8939}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7BA1276D-3AF5-4E09-B996-2D91556F8939}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7BA1276D-3AF5-4E09-B996-2D91556F8939}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7BA1276D-3AF5-4E09-B996-2D91556F8939}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/backend/src/Migrations/20251224181902_initial.Designer.cs b/backend/src/Migrations/20251224181902_initial.Designer.cs
new file mode 100644
index 0000000..08accd9
--- /dev/null
+++ b/backend/src/Migrations/20251224181902_initial.Designer.cs
@@ -0,0 +1,50 @@
+//
+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("20251224181902_initial")]
+ partial class initial
+ {
+ ///
+ 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("Id")
+ .HasColumnType("TEXT");
+
+ b.Property("Owner")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Username")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Servers");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/backend/src/Migrations/20251224181902_initial.cs b/backend/src/Migrations/20251224181902_initial.cs
new file mode 100644
index 0000000..18bdaea
--- /dev/null
+++ b/backend/src/Migrations/20251224181902_initial.cs
@@ -0,0 +1,36 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace JellyGlassBackend.Migrations
+{
+ ///
+ public partial class initial : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Servers",
+ columns: table => new
+ {
+ Id = table.Column(type: "TEXT", nullable: false),
+ Owner = table.Column(type: "TEXT", nullable: false),
+ Url = table.Column(type: "TEXT", nullable: false),
+ Password = table.Column(type: "TEXT", nullable: false),
+ Username = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Servers", x => x.Id);
+ });
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Servers");
+ }
+ }
+}
diff --git a/backend/src/Migrations/DatabaseContextModelSnapshot.cs b/backend/src/Migrations/DatabaseContextModelSnapshot.cs
new file mode 100644
index 0000000..335317c
--- /dev/null
+++ b/backend/src/Migrations/DatabaseContextModelSnapshot.cs
@@ -0,0 +1,47 @@
+//
+using JellyGlass.Repositories;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace JellyGlassBackend.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ partial class DatabaseContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "9.0.11");
+
+ modelBuilder.Entity("JellyGlass.Models.Server", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT");
+
+ b.Property("Owner")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Username")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Servers");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/backend/src/Models/ItemDTO.cs b/backend/src/Models/ItemDTO.cs
new file mode 100644
index 0000000..6cde21f
--- /dev/null
+++ b/backend/src/Models/ItemDTO.cs
@@ -0,0 +1,29 @@
+using JellyGlass.Models.JellyfinApi;
+
+namespace JellyGlass.Models;
+
+public class ItemDTO
+{
+ public ItemDTO() { }
+
+ public ItemDTO(Item item, string ServerUrl)
+ {
+ ID = item.Id;
+ Name = item.Name;
+ ServerID = item.ServerId;
+ this.ServerUrl = ServerUrl;
+ Type = item.Type;
+ Index = item.IndexNumber;
+ ParentId = item.ParentId;
+ ThumbnailUrl = $"{this.ServerUrl}/Items/{ID}/Images/Primary";
+ }
+
+ public string ID { get; set; } = string.Empty;
+ public string Name { get; set; } = string.Empty;
+ public string ServerID { get; set; } = string.Empty;
+ public string ServerUrl { get; set; } = string.Empty;
+ public string Type { get; set; } = string.Empty;
+ public int? Index { get; set; }
+ public string? ParentId { get; set; }
+ public string? ThumbnailUrl { get; set; }
+}
\ No newline at end of file
diff --git a/backend/src/Models/JellyfinApi/AuthResponseModels.cs b/backend/src/Models/JellyfinApi/AuthResponseModels.cs
new file mode 100644
index 0000000..02e686a
--- /dev/null
+++ b/backend/src/Models/JellyfinApi/AuthResponseModels.cs
@@ -0,0 +1,14 @@
+namespace JellyGlass.Models.JellyfinApi;
+
+public class AuthResponse
+{
+ public required User User { get; set; }
+ public required string AccessToken { get; set; }
+ public required string ServerId { get; set; }
+}
+
+public class User
+{
+ public required string Id { get; set; }
+}
+
diff --git a/backend/src/Models/JellyfinApi/Item.cs b/backend/src/Models/JellyfinApi/Item.cs
new file mode 100644
index 0000000..9d0bc38
--- /dev/null
+++ b/backend/src/Models/JellyfinApi/Item.cs
@@ -0,0 +1,24 @@
+namespace JellyGlass.Models.JellyfinApi;
+
+public class Item
+{
+ public string Name { get; set; } = string.Empty;
+ public string ServerId { get; set; } = string.Empty;
+ public string Id { get; set; } = string.Empty;
+ public DateTime DateCreated { get; set; }
+ public bool CanDownload { get; set; }
+ public string SortName { get; set; } = string.Empty;
+ public int? IndexNumber { get; set; }
+ public bool IsFolder { get; set; } //Whether item has children or not
+ public string? ParentId { get; set; }
+ public int? ParentIndexNumber { get; set; }
+ public string? PremiereDate { get; set; }
+ public int? ProductionYear { get; set; }
+ public string? SeriesName { get; set; }
+ public string? SeriesId { get; set; }
+ public string? SeasonId { get; set; }
+ public string? SeasonName { get; set; }
+ public string Type { get; set; } = string.Empty;
+ public double PrimaryImageAspectRatio { get; set; }
+ public string? CollectionType { get; set; }
+}
\ No newline at end of file
diff --git a/backend/src/Models/JellyfinApi/ItemsResponse.cs b/backend/src/Models/JellyfinApi/ItemsResponse.cs
new file mode 100644
index 0000000..8f38aef
--- /dev/null
+++ b/backend/src/Models/JellyfinApi/ItemsResponse.cs
@@ -0,0 +1,8 @@
+namespace JellyGlass.Models.JellyfinApi;
+
+public class ItemResponse
+{
+ public List
- Items { get; set; } = [];
+ public int TotalRecordCount { get; set; }
+ public int StartIndex { get; set; }
+}
\ No newline at end of file
diff --git a/backend/src/Models/Library.cs b/backend/src/Models/Library.cs
new file mode 100644
index 0000000..3f9c2a1
--- /dev/null
+++ b/backend/src/Models/Library.cs
@@ -0,0 +1,8 @@
+
+namespace JellyGlass.Models;
+
+public class Library
+{
+ public string Name { get; set; } = string.Empty;
+ public string ThumbnailUrl { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/backend/src/Models/Server.cs b/backend/src/Models/Server.cs
new file mode 100644
index 0000000..0b31f5f
--- /dev/null
+++ b/backend/src/Models/Server.cs
@@ -0,0 +1,10 @@
+namespace JellyGlass.Models;
+
+public class Server
+{
+ public string Owner { get; set; } = string.Empty;
+ public string Url { get; set; } = string.Empty;
+ public string Id { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
+ public string Username { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/backend/src/Program.cs b/backend/src/Program.cs
new file mode 100644
index 0000000..79e7ec8
--- /dev/null
+++ b/backend/src/Program.cs
@@ -0,0 +1,41 @@
+using JellyGlass.Repositories;
+using JellyGlass.Services;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
+builder.Services.AddOpenApi();
+builder.Services.AddControllers();
+
+builder.Configuration.GetSection("TestingLogin");
+
+string dbConnectionString;
+
+if (builder.Environment.IsDevelopment())
+{
+ dbConnectionString = "Data Source=JellyGlass-test.db;";
+}
+else
+{
+ dbConnectionString = "Data Source./JellyGlass.db;";
+}
+
+builder.Services.AddSqlite(dbConnectionString);
+
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddScoped();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.MapOpenApi();
+}
+
+app.UseHttpsRedirection();
+app.MapControllers();
+
+app.Run();
diff --git a/backend/src/WebApi/Properties/launchSettings.json b/backend/src/Properties/launchSettings.json
similarity index 80%
rename from backend/src/WebApi/Properties/launchSettings.json
rename to backend/src/Properties/launchSettings.json
index 89549ba..6206ac3 100644
--- a/backend/src/WebApi/Properties/launchSettings.json
+++ b/backend/src/Properties/launchSettings.json
@@ -5,7 +5,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
- "applicationUrl": "http://localhost:5152",
+ "applicationUrl": "http://localhost:5092",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@@ -14,7 +14,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
- "applicationUrl": "https://localhost:7106;http://localhost:5152",
+ "applicationUrl": "https://localhost:7226;http://localhost:5092",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
diff --git a/backend/src/Repositories/DatabaseContext.cs b/backend/src/Repositories/DatabaseContext.cs
new file mode 100644
index 0000000..c6e3ae1
--- /dev/null
+++ b/backend/src/Repositories/DatabaseContext.cs
@@ -0,0 +1,15 @@
+using JellyGlass.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace JellyGlass.Repositories;
+
+public class DatabaseContext : DbContext
+{
+ public DatabaseContext(DbContextOptions options)
+ : base(options)
+ {
+
+ }
+
+ public DbSet Servers { get; set; }
+}
\ No newline at end of file
diff --git a/backend/src/Core/Interfaces/IServerRepository.cs b/backend/src/Repositories/IServerRepository.cs
similarity index 52%
rename from backend/src/Core/Interfaces/IServerRepository.cs
rename to backend/src/Repositories/IServerRepository.cs
index 3f4fbd8..489985f 100644
--- a/backend/src/Core/Interfaces/IServerRepository.cs
+++ b/backend/src/Repositories/IServerRepository.cs
@@ -1,6 +1,6 @@
-using JellyGlass.Core.Entities;
+using JellyGlass.Models;
-namespace JellyGlass.Core.Interfaces;
+namespace JellyGlass.Repositories;
public interface IServerRepository
{
diff --git a/backend/src/Repositories/JellyfinApiClient.cs b/backend/src/Repositories/JellyfinApiClient.cs
new file mode 100644
index 0000000..42928f7
--- /dev/null
+++ b/backend/src/Repositories/JellyfinApiClient.cs
@@ -0,0 +1,149 @@
+using System.Net;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Text.Json;
+using JellyGlass.Exceptions;
+using JellyGlass.Models;
+using JellyGlass.Models.JellyfinApi;
+
+namespace JellyGlass.Repositories;
+
+public class JellyfinApiClient
+{
+ private string _apiKey = string.Empty;
+ public readonly string InstanceUrl;
+ private readonly HttpClient _client;
+ private readonly string _username, _password;
+
+ public JellyfinApiClient(string instanceUrl, string username, string password)
+ {
+ InstanceUrl = instanceUrl;
+ _client = new HttpClient();
+ _client.DefaultRequestHeaders.Clear();
+ _username = username;
+ _password = password;
+ }
+
+ public async Task GetInstanceLibraries()
+ {
+ try
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{InstanceUrl}/Library/MediaFolders");
+ var response = await MakeRequest(request);
+
+ response.EnsureSuccessStatusCode();
+
+ var apiResponse = await response.Content.ReadFromJsonAsync();
+
+ return apiResponse!;
+ }
+ catch (HttpRequestException e)
+ {
+ throw new JellyfinApiClientException(e.Message);
+ }
+ }
+
+ public async Task GetItemChildren(string itemId)
+ {
+ try
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{InstanceUrl}/items?ParentId={itemId}");
+
+ var response = await MakeRequest(request);
+
+ response.EnsureSuccessStatusCode();
+
+ var apiResponse = await response.Content.ReadFromJsonAsync();
+
+ return apiResponse!;
+ }
+ catch (HttpRequestException e)
+ {
+ throw new JellyfinApiClientException(e.Message);
+ }
+ }
+
+ public async Task GetItems(string searchTerm = "", string years = "", string itemTypes = "", string limit = "", string parentId = "")
+ {
+ var query = new Dictionary();
+
+ if (searchTerm != String.Empty)
+ {
+ query.Add("SearchTerm", searchTerm);
+ }
+
+ throw new NotImplementedException();
+ }
+
+ public async Task Authenticate()
+ {
+ var request = new HttpRequestMessage(HttpMethod.Post, $"{InstanceUrl}/Users/AuthenticateByName");
+
+ request.Headers.Authorization = new AuthenticationHeaderValue("MediaBrowser", GetAuthHeader());
+
+ var body = new
+ {
+ Username = _username,
+ Pw = _password
+ };
+
+ request.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
+
+ try
+ {
+ var response = await _client.SendAsync(request);
+
+ response.EnsureSuccessStatusCode();
+
+ var authResponse = await response.Content.ReadFromJsonAsync();
+
+ _apiKey = authResponse!.AccessToken;
+ }
+ catch (HttpRequestException e)
+ {
+ //TODO: What to do on an exception
+ throw new JellyfinApiClientException(e.Message);
+ }
+ }
+
+ private async Task MakeRequest(HttpRequestMessage request)
+ {
+ request.Headers.Authorization = new AuthenticationHeaderValue("MediaBrowser", GetAuthHeader());
+
+ HttpResponseMessage response;
+
+ try
+ {
+ response = await _client.SendAsync(request);
+ }
+ catch (HttpRequestException e)
+ {
+ if (e.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ await Authenticate();
+
+ request.Headers.Authorization = new AuthenticationHeaderValue("MediaBrowser", GetAuthHeader());
+
+ response = await _client.SendAsync(request);
+ }
+ else
+ {
+ throw new JellyfinApiClientException(e.Message);
+ }
+ }
+
+ return response;
+ }
+
+ private string GetAuthHeader()
+ {
+ var header = "Client=Test, Device=Test, DeviceId=Test, Version=1";
+
+ if (_apiKey != String.Empty)
+ {
+ header += ", Token=" + _apiKey;
+ }
+
+ return header;
+ }
+}
\ No newline at end of file
diff --git a/backend/src/Repositories/ServerRepository.cs b/backend/src/Repositories/ServerRepository.cs
new file mode 100644
index 0000000..4fdb60d
--- /dev/null
+++ b/backend/src/Repositories/ServerRepository.cs
@@ -0,0 +1,21 @@
+using JellyGlass.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace JellyGlass.Repositories;
+
+public class ServerRepository : IServerRepository
+{
+ private DatabaseContext _context;
+
+ public ServerRepository(DatabaseContext context)
+ {
+ _context = context;
+ }
+
+ public async Task GetServers()
+ {
+ var servers = await _context.Servers.ToArrayAsync();
+
+ return servers;
+ }
+}
\ No newline at end of file
diff --git a/backend/src/Services/ILibraryService.cs b/backend/src/Services/ILibraryService.cs
new file mode 100644
index 0000000..72a9e6f
--- /dev/null
+++ b/backend/src/Services/ILibraryService.cs
@@ -0,0 +1,14 @@
+using JellyGlass.Models;
+
+namespace JellyGlass.Services;
+
+public interface ILibraryService
+{
+ public Task GetLibraries();
+ public Task GetItemsFromLibrary(string libraryName);
+
+
+ // public Task GetChildrenFromItems(ItemDTO[] items);
+ // public Task GetItemsByName(string name, string itemType);
+ // public Task GetItemsByType(string itemType);
+}
\ No newline at end of file
diff --git a/backend/src/Services/IServerService.cs b/backend/src/Services/IServerService.cs
new file mode 100644
index 0000000..82c633b
--- /dev/null
+++ b/backend/src/Services/IServerService.cs
@@ -0,0 +1,10 @@
+using JellyGlass.Repositories;
+
+namespace JellyGlass.Services;
+
+public interface IServerService
+{
+ public Task GetJellyfinClients();
+ // public JellyfinApiClient GetClientForServer(string url);
+ // public JellyfinApiClient GetClientForServerId(string serverId);
+}
\ No newline at end of file
diff --git a/backend/src/Services/LibraryService.cs b/backend/src/Services/LibraryService.cs
new file mode 100644
index 0000000..efdabd1
--- /dev/null
+++ b/backend/src/Services/LibraryService.cs
@@ -0,0 +1,74 @@
+using JellyGlass.Models;
+
+namespace JellyGlass.Services;
+
+public class LibraryService : ILibraryService
+{
+ private IServerService _serverService;
+
+ public LibraryService(IServerService serverService)
+ {
+ _serverService = serverService;
+ }
+
+ public async Task GetLibraries()
+ {
+ var clients = await _serverService.GetJellyfinClients();
+
+ var libraries = new Dictionary();
+
+ foreach (var client in clients)
+ {
+ var clientLibraries = await client.GetInstanceLibraries();
+
+ foreach (var library in clientLibraries.Items)
+ {
+ if (!libraries.ContainsKey(library.Name))
+ {
+
+ libraries.Add(library.Name, new Library()
+ {
+ Name = library.Name,
+ ThumbnailUrl = $"{client.InstanceUrl}/Items/{library.Id}/Primary"
+ });
+ }
+ }
+ }
+
+ return libraries.Values.ToArray();
+ }
+
+ public async Task GetItemsFromLibrary(string libraryName)
+ {
+ throw new NotImplementedException();
+ }
+
+ // public async Task GetChildrenFromItems(ItemDTO[] items)
+ // {
+ // var children = new List();
+
+ // foreach (var item in items)
+ // {
+ // var client = _serverService.GetClientForServerId(item.ServerID);
+
+ // var itemChildren = await client.GetItemChildren(item.ID);
+
+ // foreach (var child in itemChildren.Items)
+ // {
+ // children.Add(new ItemDTO(child, client.InstanceUrl));
+ // }
+ // }
+
+ // return children.ToArray();
+ // }
+
+ // public async Task GetItemsByName(string name, string itemType)
+ // {
+
+ // }
+
+ // public async Task GetItemsByType(string itemType)
+ // {
+
+ // }
+}
\ No newline at end of file
diff --git a/backend/src/Services/ServerService.cs b/backend/src/Services/ServerService.cs
new file mode 100644
index 0000000..935d1b8
--- /dev/null
+++ b/backend/src/Services/ServerService.cs
@@ -0,0 +1,52 @@
+using JellyGlass.Exceptions;
+using JellyGlass.Models;
+using JellyGlass.Repositories;
+
+namespace JellyGlass.Services;
+
+public class ServerService : IServerService
+{
+ private IServerRepository _repository;
+ private static JellyfinApiClient[] _clients = [];
+ private ILogger _logger;
+
+ public ServerService(IServerRepository repository, ILogger logger)
+ {
+ _repository = repository;
+ _logger = logger;
+ }
+
+ public async Task GetJellyfinClients()
+ {
+ if (!_clients.Any())
+ {
+ await LoadClients();
+ }
+
+ return _clients;
+ }
+
+ private async Task LoadClients()
+ {
+ var servers = await _repository.GetServers();
+ var clients = new List();
+
+ foreach (var server in servers)
+ {
+ var client = new JellyfinApiClient(server.Url, server.Username, server.Password);
+
+ try
+ {
+ await client.Authenticate();
+ }
+ catch (JellyfinApiClientException e)
+ {
+
+ }
+
+ clients.Add(client);
+ }
+
+ _clients = clients.ToArray();
+ }
+}
\ No newline at end of file
diff --git a/backend/src/WebApi/Controllers/ItemController.cs b/backend/src/WebApi/Controllers/ItemController.cs
deleted file mode 100644
index 289e173..0000000
--- a/backend/src/WebApi/Controllers/ItemController.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using JellyGlass.Core.Interfaces;
-
-namespace JellyGlass.WebApi.Controllers;
-
-[ApiController]
-[Route("[controller]")]
-public class ItemController : ControllerBase
-{
- private ILogger _logger;
- private IItemService _service;
-
- public ItemController(ILogger logger, IItemService service)
- {
- _service = service;
- _logger = logger;
- }
-
- public async Task GetItemsForLibrary(string libraryId)
- {
- var items = await _service.GetItemsFromLibrary(libraryId);
-
- return Ok(items);
- }
-}
\ No newline at end of file
diff --git a/backend/src/WebApi/Controllers/LibraryController.cs b/backend/src/WebApi/Controllers/LibraryController.cs
deleted file mode 100644
index b10fb75..0000000
--- a/backend/src/WebApi/Controllers/LibraryController.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using JellyGlass.Core.Interfaces;
-
-namespace JellyGlass.WebApi.Controllers;
-
-[ApiController]
-[Route("[controller]")]
-public class LibraryController : ControllerBase
-{
- private ILogger _logger;
- private ILibraryService _service;
-
- public LibraryController(ILogger logger, ILibraryService service)
- {
- _logger = logger;
- _service = service;
- }
-
- [HttpGet]
- public async Task GetLibraries()
- {
- var libraries = await _service.GetLibraries();
-
- return Ok(libraries);
- }
-}
\ No newline at end of file
diff --git a/backend/src/WebApi/Controllers/ServerController.cs b/backend/src/WebApi/Controllers/ServerController.cs
deleted file mode 100644
index 9aa7147..0000000
--- a/backend/src/WebApi/Controllers/ServerController.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using JellyGlass.Core.Interfaces;
-
-namespace JellyGlass.WebApi.Controllers;
-
-[ApiController]
-[Route("[controller]")]
-public class ServerController : ControllerBase
-{
- private ILogger _logger;
- private IServerService _service;
-
- public ServerController(ILogger logger, IServerService service)
- {
- _logger = logger;
- _service = service;
- }
-
- public async Task GetServers()
- {
- var servers = await _service.GetServers();
-
- return Ok(servers);
- }
-}
\ No newline at end of file
diff --git a/backend/src/WebApi/Program.cs b/backend/src/WebApi/Program.cs
deleted file mode 100644
index 1829f66..0000000
--- a/backend/src/WebApi/Program.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-var builder = WebApplication.CreateBuilder(args);
-
-// Add services to the container.
-// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
-builder.Services.AddOpenApi();
-builder.Services.AddControllers();
-
-var app = builder.Build();
-
-// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
-{
- app.MapOpenApi();
-}
-
-app.UseHttpsRedirection();
-app.MapControllers();
-
-app.Run();
diff --git a/backend/src/WebApi/WebApi.csproj b/backend/src/WebApi/WebApi.csproj
deleted file mode 100644
index b5cfb60..0000000
--- a/backend/src/WebApi/WebApi.csproj
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- net9.0
- enable
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/backend/src/WebApi/WebApi.http b/backend/src/WebApi/WebApi.http
deleted file mode 100644
index f3e4d2c..0000000
--- a/backend/src/WebApi/WebApi.http
+++ /dev/null
@@ -1,6 +0,0 @@
-@WebApi_HostAddress = http://localhost:5152
-
-GET {{WebApi_HostAddress}}/weatherforecast/
-Accept: application/json
-
-###
diff --git a/backend/src/WebApi/appsettings.Development.json b/backend/src/WebApi/appsettings.Development.json
deleted file mode 100644
index 0c208ae..0000000
--- a/backend/src/WebApi/appsettings.Development.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- }
-}
diff --git a/backend/src/appsettings.Development.json b/backend/src/appsettings.Development.json
new file mode 100644
index 0000000..75e7744
--- /dev/null
+++ b/backend/src/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
\ No newline at end of file
diff --git a/backend/src/WebApi/appsettings.json b/backend/src/appsettings.json
similarity index 100%
rename from backend/src/WebApi/appsettings.json
rename to backend/src/appsettings.json
diff --git a/backend/src/src.http b/backend/src/src.http
new file mode 100644
index 0000000..fffe865
--- /dev/null
+++ b/backend/src/src.http
@@ -0,0 +1,6 @@
+@src_HostAddress = http://localhost:5092
+
+GET {{src_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/pythonBackend/venv/bin/Activate.ps1 b/pythonBackend/venv/bin/Activate.ps1
new file mode 100644
index 0000000..b49d77b
--- /dev/null
+++ b/pythonBackend/venv/bin/Activate.ps1
@@ -0,0 +1,247 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies:
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+ [Parameter(Mandatory = $false)]
+ [String]
+ $VenvDir,
+ [Parameter(Mandatory = $false)]
+ [String]
+ $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+ # Revert to original values
+
+ # The prior prompt:
+ if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+ Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+ Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+ }
+
+ # The prior PYTHONHOME:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+ Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+ }
+
+ # The prior PATH:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+ Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+ }
+
+ # Just remove the VIRTUAL_ENV altogether:
+ if (Test-Path -Path Env:VIRTUAL_ENV) {
+ Remove-Item -Path env:VIRTUAL_ENV
+ }
+
+ # Just remove VIRTUAL_ENV_PROMPT altogether.
+ if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
+ Remove-Item -Path env:VIRTUAL_ENV_PROMPT
+ }
+
+ # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+ if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+ Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+ }
+
+ # Leave deactivate function in the global namespace if requested:
+ if (-not $NonDestructive) {
+ Remove-Item -Path function:deactivate
+ }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+ [String]
+ $ConfigDir
+) {
+ Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+ # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+ $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+ # An empty map will be returned if no config file is found.
+ $pyvenvConfig = @{ }
+
+ if ($pyvenvConfigPath) {
+
+ Write-Verbose "File exists, parse `key = value` lines"
+ $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+ $pyvenvConfigContent | ForEach-Object {
+ $keyval = $PSItem -split "\s*=\s*", 2
+ if ($keyval[0] -and $keyval[1]) {
+ $val = $keyval[1]
+
+ # Remove extraneous quotations around a string value.
+ if ("'""".Contains($val.Substring(0, 1))) {
+ $val = $val.Substring(1, $val.Length - 2)
+ }
+
+ $pyvenvConfig[$keyval[0]] = $val
+ Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+ }
+ }
+ }
+ return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+ Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+ Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+ $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+ Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+ Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+ Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+ if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+ Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+ $Prompt = $pyvenvCfg['prompt'];
+ }
+ else {
+ Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
+ Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+ $Prompt = Split-Path -Path $venvDir -Leaf
+ }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+ Write-Verbose "Setting prompt to '$Prompt'"
+
+ # Set the prompt to include the env name
+ # Make sure _OLD_VIRTUAL_PROMPT is global
+ function global:_OLD_VIRTUAL_PROMPT { "" }
+ Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+ New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+ function global:prompt {
+ Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+ _OLD_VIRTUAL_PROMPT
+ }
+ $env:VIRTUAL_ENV_PROMPT = $Prompt
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+ Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+ Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
diff --git a/pythonBackend/venv/bin/activate b/pythonBackend/venv/bin/activate
new file mode 100644
index 0000000..d7469ec
--- /dev/null
+++ b/pythonBackend/venv/bin/activate
@@ -0,0 +1,70 @@
+# This file must be used with "source bin/activate" *from bash*
+# You cannot run it directly
+
+deactivate () {
+ # reset old environment variables
+ if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+ PATH="${_OLD_VIRTUAL_PATH:-}"
+ export PATH
+ unset _OLD_VIRTUAL_PATH
+ fi
+ if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+ PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+ export PYTHONHOME
+ unset _OLD_VIRTUAL_PYTHONHOME
+ fi
+
+ # Call hash to forget past commands. Without forgetting
+ # past commands the $PATH changes we made may not be respected
+ hash -r 2> /dev/null
+
+ if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+ PS1="${_OLD_VIRTUAL_PS1:-}"
+ export PS1
+ unset _OLD_VIRTUAL_PS1
+ fi
+
+ unset VIRTUAL_ENV
+ unset VIRTUAL_ENV_PROMPT
+ if [ ! "${1:-}" = "nondestructive" ] ; then
+ # Self destruct!
+ unset -f deactivate
+ fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+# on Windows, a path can contain colons and backslashes and has to be converted:
+if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
+ # transform D:\path\to\venv to /d/path/to/venv on MSYS
+ # and to /cygdrive/d/path/to/venv on Cygwin
+ export VIRTUAL_ENV=$(cygpath /home/fox/Documents/programming/Projects/JellyGlass/pythonBackend/venv)
+else
+ # use the path as-is
+ export VIRTUAL_ENV=/home/fox/Documents/programming/Projects/JellyGlass/pythonBackend/venv
+fi
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/"bin":$PATH"
+export PATH
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+ _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+ unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+ _OLD_VIRTUAL_PS1="${PS1:-}"
+ PS1='(venv) '"${PS1:-}"
+ export PS1
+ VIRTUAL_ENV_PROMPT='(venv) '
+ export VIRTUAL_ENV_PROMPT
+fi
+
+# Call hash to forget past commands. Without forgetting
+# past commands the $PATH changes we made may not be respected
+hash -r 2> /dev/null
diff --git a/pythonBackend/venv/bin/activate.csh b/pythonBackend/venv/bin/activate.csh
new file mode 100644
index 0000000..1ea5a84
--- /dev/null
+++ b/pythonBackend/venv/bin/activate.csh
@@ -0,0 +1,27 @@
+# This file must be used with "source bin/activate.csh" *from csh*.
+# You cannot run it directly.
+
+# Created by Davide Di Blasi .
+# Ported to Python 3.3 venv by Andrew Svetlov
+
+alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+setenv VIRTUAL_ENV /home/fox/Documents/programming/Projects/JellyGlass/pythonBackend/venv
+
+set _OLD_VIRTUAL_PATH="$PATH"
+setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
+
+
+set _OLD_VIRTUAL_PROMPT="$prompt"
+
+if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
+ set prompt = '(venv) '"$prompt"
+ setenv VIRTUAL_ENV_PROMPT '(venv) '
+endif
+
+alias pydoc python -m pydoc
+
+rehash
diff --git a/pythonBackend/venv/bin/activate.fish b/pythonBackend/venv/bin/activate.fish
new file mode 100644
index 0000000..499a16e
--- /dev/null
+++ b/pythonBackend/venv/bin/activate.fish
@@ -0,0 +1,69 @@
+# This file must be used with "source /bin/activate.fish" *from fish*
+# (https://fishshell.com/). You cannot run it directly.
+
+function deactivate -d "Exit virtual environment and return to normal shell environment"
+ # reset old environment variables
+ if test -n "$_OLD_VIRTUAL_PATH"
+ set -gx PATH $_OLD_VIRTUAL_PATH
+ set -e _OLD_VIRTUAL_PATH
+ end
+ if test -n "$_OLD_VIRTUAL_PYTHONHOME"
+ set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
+ set -e _OLD_VIRTUAL_PYTHONHOME
+ end
+
+ if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
+ set -e _OLD_FISH_PROMPT_OVERRIDE
+ # prevents error when using nested fish instances (Issue #93858)
+ if functions -q _old_fish_prompt
+ functions -e fish_prompt
+ functions -c _old_fish_prompt fish_prompt
+ functions -e _old_fish_prompt
+ end
+ end
+
+ set -e VIRTUAL_ENV
+ set -e VIRTUAL_ENV_PROMPT
+ if test "$argv[1]" != "nondestructive"
+ # Self-destruct!
+ functions -e deactivate
+ end
+end
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+set -gx VIRTUAL_ENV /home/fox/Documents/programming/Projects/JellyGlass/pythonBackend/venv
+
+set -gx _OLD_VIRTUAL_PATH $PATH
+set -gx PATH "$VIRTUAL_ENV/"bin $PATH
+
+# Unset PYTHONHOME if set.
+if set -q PYTHONHOME
+ set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
+ set -e PYTHONHOME
+end
+
+if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
+ # fish uses a function instead of an env var to generate the prompt.
+
+ # Save the current fish_prompt function as the function _old_fish_prompt.
+ functions -c fish_prompt _old_fish_prompt
+
+ # With the original prompt function renamed, we can override with our own.
+ function fish_prompt
+ # Save the return status of the last command.
+ set -l old_status $status
+
+ # Output the venv prompt; color taken from the blue of the Python logo.
+ printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal)
+
+ # Restore the return status of the previous command.
+ echo "exit $old_status" | .
+ # Output the original/"old" prompt.
+ _old_fish_prompt
+ end
+
+ set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
+ set -gx VIRTUAL_ENV_PROMPT '(venv) '
+end