From 2df642cf5263887decd36962d534134989e31e26 Mon Sep 17 00:00:00 2001 From: Scott Scoble Date: Fri, 15 May 2026 21:48:43 -0700 Subject: [PATCH] feat: add Lessons page with API and sidebar nav entry (#5) - Add LessonsController with GET /Plugins/Lessons/List endpoint - Add LessonStore DI registration in ServiceRegistrator - Add lessonsPage.html embedded resource with grouped lesson listing - Implement IHasWebPages in Plugin for sidebar nav entry - Add ImplicitUsings/Nullable to csproj for net9.0 compatibility - Add Microsoft.AspNetCore.App FrameworkReference for MVC - Fix ServiceRegistrator to match 10.10.x IPluginServiceRegistrator signature --- plugin/Api/LessonsController.cs | 34 ++++++ plugin/Configuration/lessonsPage.html | 152 ++++++++++++++++++++++++++ plugin/Jellyfin.Plugin.Lessons.csproj | 10 ++ plugin/Plugin.cs | 22 +++- plugin/ServiceRegistrator.cs | 8 +- 5 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 plugin/Api/LessonsController.cs create mode 100644 plugin/Configuration/lessonsPage.html diff --git a/plugin/Api/LessonsController.cs b/plugin/Api/LessonsController.cs new file mode 100644 index 0000000..aad1b18 --- /dev/null +++ b/plugin/Api/LessonsController.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Mvc; +using Jellyfin.Plugin.Lessons.Data; + +namespace Jellyfin.Plugin.Lessons.Api; + +[ApiController] +[Route("Plugins/Lessons")] +public class LessonsController : ControllerBase +{ + private readonly LessonStore _store; + + public LessonsController(LessonStore store) + { + _store = store; + } + + [HttpGet("List")] + public IActionResult List() + { + var lessons = _store.GetAllLessons(); + + var summaries = lessons.Select(l => new + { + id = l.Id, + title = l.Title, + grade = l.Grade, + subject = l.Subject, + lessonDate = l.LessonDate, + description = l.Description + }); + + return Ok(summaries); + } +} diff --git a/plugin/Configuration/lessonsPage.html b/plugin/Configuration/lessonsPage.html new file mode 100644 index 0000000..30034e4 --- /dev/null +++ b/plugin/Configuration/lessonsPage.html @@ -0,0 +1,152 @@ + + + + + + Lessons + + + +
+

📚 Lessons

+
Loading lessons…
+
+ + + + diff --git a/plugin/Jellyfin.Plugin.Lessons.csproj b/plugin/Jellyfin.Plugin.Lessons.csproj index faba00e..5682323 100644 --- a/plugin/Jellyfin.Plugin.Lessons.csproj +++ b/plugin/Jellyfin.Plugin.Lessons.csproj @@ -3,6 +3,8 @@ net9.0 JellyfinLessons jellyfin-lessons + enable + enable @@ -16,4 +18,12 @@ runtime + + + + + + + + diff --git a/plugin/Plugin.cs b/plugin/Plugin.cs index 59f6f42..52c4269 100644 --- a/plugin/Plugin.cs +++ b/plugin/Plugin.cs @@ -1,12 +1,14 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; using Jellyfin.Plugin.Lessons.Configuration; namespace Jellyfin.Plugin.Lessons; -public class Plugin : BasePlugin +public class Plugin : BasePlugin, IHasWebPages { public Plugin(IApplicationPaths appPaths, IXmlSerializer xmlSerializer) : base(appPaths, xmlSerializer) @@ -18,4 +20,20 @@ public class Plugin : BasePlugin public override Guid Id => Guid.Parse("a1b2c3d4-e5f6-7890-abcd-ef1234567890"); public override string Description => "Homeschool lesson plans with embedded media"; + + public IEnumerable GetPages() + { + return new[] + { + new PluginPageInfo + { + Name = Name, + DisplayName = "Lessons", + EmbeddedResourcePath = GetType().Namespace + ".Configuration.lessonsPage.html", + EnableInMainMenu = true, + MenuSection = "plugins", + MenuIcon = "book" + } + }; + } } diff --git a/plugin/ServiceRegistrator.cs b/plugin/ServiceRegistrator.cs index cae7cc3..378341e 100644 --- a/plugin/ServiceRegistrator.cs +++ b/plugin/ServiceRegistrator.cs @@ -1,11 +1,15 @@ -using MediaBrowser.Common.Plugins; +using Jellyfin.Plugin.Lessons.Data; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Plugins; using Microsoft.Extensions.DependencyInjection; namespace Jellyfin.Plugin.Lessons; public class ServiceRegistrator : IPluginServiceRegistrator { - public void RegisterServices(IServiceCollection services) + public void RegisterServices(IServiceCollection services, IServerApplicationHost appHost) { + services.AddSingleton(sp => + new LessonStore("~/.local/share/jellyfin-lessons/lessons.db")); } }