diff --git a/Jellyfin.Plugin.Seasonals/Api/SeasonalsController.cs b/Jellyfin.Plugin.Seasonals/Api/SeasonalsController.cs index 4d4e7ea..df1343f 100644 --- a/Jellyfin.Plugin.Seasonals/Api/SeasonalsController.cs +++ b/Jellyfin.Plugin.Seasonals/Api/SeasonalsController.cs @@ -6,10 +6,17 @@ using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Plugin.Seasonals.Api; +/// +/// Controller for serving seasonal resources and configuration. +/// [ApiController] [Route("Seasonals")] public class SeasonalsController : ControllerBase { + /// + /// Gets the current plugin configuration. + /// + /// The configuration object. [HttpGet("Config")] [Produces("application/json")] public ActionResult GetConfig() @@ -22,11 +29,16 @@ public class SeasonalsController : ControllerBase }; } + /// + /// Serves embedded resources. + /// + /// The path to the resource. + /// The resource file. [HttpGet("Resources/{*path}")] public ActionResult GetResource(string path) { // Sanitize path - if (string.IsNullOrWhiteSpace(path) || path.Contains("..")) + if (string.IsNullOrWhiteSpace(path) || path.Contains("..", StringComparison.Ordinal)) { return BadRequest(); } @@ -47,7 +59,7 @@ public class SeasonalsController : ControllerBase return File(stream, contentType); } - private string GetContentType(string path) + private static string GetContentType(string path) { if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase)) return "application/javascript"; if (path.EndsWith(".css", StringComparison.OrdinalIgnoreCase)) return "text/css"; diff --git a/Jellyfin.Plugin.Seasonals/Configuration/configPage.html b/Jellyfin.Plugin.Seasonals/Configuration/configPage.html index e975058..d6be848 100644 --- a/Jellyfin.Plugin.Seasonals/Configuration/configPage.html +++ b/Jellyfin.Plugin.Seasonals/Configuration/configPage.html @@ -37,6 +37,7 @@ +
Please reload the page (F5) after saving for changes to take effect.
diff --git a/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj b/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj index bd712cf..be06b21 100644 --- a/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj +++ b/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj @@ -13,6 +13,7 @@ CodeDevMLH CodeDevMLH Jellyfin Seasonals Plugin + 1.0.0.0 https://github.com/CodeDevMLH/jellyfin-plugin-seasonals https://github.com/CodeDevMLH/jellyfin-plugin-seasonals @@ -26,12 +27,6 @@ - - - - - - diff --git a/Jellyfin.Plugin.Seasonals/ScriptInjector.cs b/Jellyfin.Plugin.Seasonals/ScriptInjector.cs index f935a52..02feb5f 100644 --- a/Jellyfin.Plugin.Seasonals/ScriptInjector.cs +++ b/Jellyfin.Plugin.Seasonals/ScriptInjector.cs @@ -1,24 +1,35 @@ -using System; +using System; using System.IO; -using System.Linq; +using System.Reflection; using Microsoft.Extensions.Logging; using MediaBrowser.Common.Configuration; namespace Jellyfin.Plugin.Seasonals; +/// +/// Handles the injection of the Seasonals script into the Jellyfin web interface. +/// public class ScriptInjector { private readonly IApplicationPaths _appPaths; private readonly ILogger _logger; - private const string ScriptTag = ""; + private const string ScriptTag = ""; private const string Marker = ""; + /// + /// Initializes a new instance of the class. + /// + /// The application paths. + /// The logger. public ScriptInjector(IApplicationPaths appPaths, ILogger logger) { _appPaths = appPaths; _logger = logger; } + /// + /// Injects the script tag into index.html if it's not already present. + /// public void Inject() { try @@ -38,14 +49,15 @@ public class ScriptInjector } var content = File.ReadAllText(indexPath); - if (content.Contains(ScriptTag)) + if (content.Contains(ScriptTag, StringComparison.Ordinal)) { _logger.LogInformation("Seasonals script already injected."); return; } - var newContent = content.Replace(Marker, $"{ScriptTag}\n{Marker}"); - if (newContent == content) + // Insert before the closing body tag + var newContent = content.Replace(Marker, $"{ScriptTag}\n{Marker}", StringComparison.Ordinal); + if (string.Equals(newContent, content, StringComparison.Ordinal)) { _logger.LogWarning("Could not find closing body tag in index.html. Script injection skipped."); return; @@ -60,20 +72,35 @@ public class ScriptInjector } } + /// + /// Removes the script tag from index.html. + /// public void Remove() { try { var webPath = GetWebPath(); - if (string.IsNullOrEmpty(webPath)) return; + if (string.IsNullOrEmpty(webPath)) + { + return; + } var indexPath = Path.Combine(webPath, "index.html"); - if (!File.Exists(indexPath)) return; + if (!File.Exists(indexPath)) + { + return; + } var content = File.ReadAllText(indexPath); - if (!content.Contains(ScriptTag)) return; + if (!content.Contains(ScriptTag, StringComparison.Ordinal)) + { + return; + } - var newContent = content.Replace(ScriptTag, "").Replace($"{ScriptTag}\n", ""); + // Try to remove with newline first, then just the tag to ensure clean removal + var newContent = content.Replace($"{ScriptTag}\n", "", StringComparison.Ordinal) + .Replace(ScriptTag, "", StringComparison.Ordinal); + File.WriteAllText(indexPath, newContent); _logger.LogInformation("Successfully removed Seasonals script from index.html."); } @@ -83,37 +110,14 @@ public class ScriptInjector } } + /// + /// Retrieves the path to the Jellyfin web interface directory. + /// + /// The path to the web directory, or null if not found. private string? GetWebPath() { - // Try to find the web path using IApplicationPaths - // Note: The property name might vary depending on the Jellyfin version, - // but usually it's accessible via the configuration or standard paths. - // For this plugin context, we'll try to rely on the standard path structure if IApplicationPaths doesn't expose it directly - // or if we need to infer it. - - // In many Jellyfin versions, appPaths.WebPath is the property. - // However, since we are in a plugin, we might need to use reflection if the interface doesn't expose it in the reference assembly, - // or just assume it's there. - - // Let's try to use the property if it exists. - // If the compilation fails, we will adjust. - // For now, we assume 'WebPath' is available on IApplicationPaths implementation - // but it might not be on the interface in the nuget package. - - // Workaround: Check known locations if property is missing or use reflection. - - // Attempt 1: Reflection to get WebPath property (safest if interface varies) - var prop = _appPaths.GetType().GetProperty("WebPath"); - if (prop != null) - { - return prop.GetValue(_appPaths) as string; - } - - // Attempt 2: Guess based on ProgramDataPath (common in Windows) - // Usually /jellyfin/web or /jellyfin-web - - // Let's try to look relative to the application executable if possible, but that's hard from a plugin. - - return null; + // Use reflection to access WebPath property to ensure compatibility across different Jellyfin versions + var prop = _appPaths.GetType().GetProperty("WebPath", BindingFlags.Instance | BindingFlags.Public); + return prop?.GetValue(_appPaths) as string; } } diff --git a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.deps.json b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.deps.json index 43ebd58..ab7efa6 100644 --- a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.deps.json +++ b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.deps.json @@ -9,10 +9,7 @@ "Jellyfin.Plugin.Seasonals/1.0.0.0": { "dependencies": { "Jellyfin.Controller": "10.11.0", - "Jellyfin.Model": "10.11.0", - "SerilogAnalyzer": "0.15.0", - "SmartAnalyzers.MultithreadingAnalyzer": "1.1.31", - "StyleCop.Analyzers": "1.2.0-beta.556" + "Jellyfin.Model": "10.11.0" }, "runtime": { "Jellyfin.Plugin.Seasonals.dll": {} @@ -166,14 +163,6 @@ } }, "Polly.Core/8.6.4": {}, - "SerilogAnalyzer/0.15.0": {}, - "SmartAnalyzers.MultithreadingAnalyzer/1.1.31": {}, - "StyleCop.Analyzers/1.2.0-beta.556": { - "dependencies": { - "StyleCop.Analyzers.Unstable": "1.2.0.556" - } - }, - "StyleCop.Analyzers.Unstable/1.2.0.556": {}, "System.Globalization/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0", @@ -421,34 +410,6 @@ "path": "polly.core/8.6.4", "hashPath": "polly.core.8.6.4.nupkg.sha512" }, - "SerilogAnalyzer/0.15.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-sVpwfls4MfNnwIXLSGCgaUnV+c9kgJ8ia6GsyRcpd4Vs3gLogSDtSYBYrre2K2u/PNMo8GgG09RehwVnze70Tw==", - "path": "seriloganalyzer/0.15.0", - "hashPath": "seriloganalyzer.0.15.0.nupkg.sha512" - }, - "SmartAnalyzers.MultithreadingAnalyzer/1.1.31": { - "type": "package", - "serviceable": true, - "sha512": "sha512-2f2k7bbhDd132ArglCKpzKoWBcp3uzbIFcb4aosnlqIKlfYKDE2HevBVRNVa+LkWFnjXFFWs47Bo96fu8iS//Q==", - "path": "smartanalyzers.multithreadinganalyzer/1.1.31", - "hashPath": "smartanalyzers.multithreadinganalyzer.1.1.31.nupkg.sha512" - }, - "StyleCop.Analyzers/1.2.0-beta.556": { - "type": "package", - "serviceable": true, - "sha512": "sha512-llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==", - "path": "stylecop.analyzers/1.2.0-beta.556", - "hashPath": "stylecop.analyzers.1.2.0-beta.556.nupkg.sha512" - }, - "StyleCop.Analyzers.Unstable/1.2.0.556": { - "type": "package", - "serviceable": true, - "sha512": "sha512-zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ==", - "path": "stylecop.analyzers.unstable/1.2.0.556", - "hashPath": "stylecop.analyzers.unstable.1.2.0.556.nupkg.sha512" - }, "System.Globalization/4.3.0": { "type": "package", "serviceable": true, diff --git a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.dll b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.dll index b612770..d1ae1f5 100644 Binary files a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.dll and b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.dll differ diff --git a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.pdb b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.pdb index 79f7d2e..3847bb2 100644 Binary files a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.pdb and b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.pdb differ diff --git a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.xml b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.xml index 57f9a64..f1dae18 100644 --- a/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.xml +++ b/Jellyfin.Plugin.Seasonals/bin/Release/net9.0/Jellyfin.Plugin.Seasonals.xml @@ -4,6 +4,24 @@ Jellyfin.Plugin.Seasonals + + + Controller for serving seasonal resources and configuration. + + + + + Gets the current plugin configuration. + + The configuration object. + + + + Serves embedded resources. + + The path to the resource. + The resource file. + Plugin configuration. @@ -51,5 +69,33 @@ + + + Handles the injection of the Seasonals script into the Jellyfin web interface. + + + + + Initializes a new instance of the class. + + The application paths. + The logger. + + + + Injects the script tag into index.html if it's not already present. + + + + + Removes the script tag from index.html. + + + + + Retrieves the path to the Jellyfin web interface directory. + + The path to the web directory, or null if not found. + diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md new file mode 100644 index 0000000..11dbfd3 --- /dev/null +++ b/RELEASE_GUIDE.md @@ -0,0 +1,59 @@ +# Release & Update Guide + +Diese Anleitung beschreibt die Schritte, die notwendig sind, um eine neue Version des **Seasonals** Plugins zu veröffentlichen. + +## 1. Version erhöhen + +Bevor du baust, musst du die Versionsnummer in den folgenden Dateien aktualisieren (z.B. von `1.0.0.0` auf `1.0.1.0`): + +1. **`Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj`** + Suche nach `...` und ändere die Nummer. + +2. **`build.yaml`** + Ändere den Wert bei `version: "..."`. + +3. **`manifest.json`** + Füge einen neuen Eintrag oben in die `versions`-Liste ein (oder bearbeite den vorhandenen, wenn es noch kein Release gab). + * `version`: Deine neue Nummer. + * `changelog`: Was hat sich geändert? + * `timestamp`: Das aktuelle Datum (wird später aktualisiert). + * `checksum`: (wird nach dem Build aktualisiert). + +## 2. Plugin bauen und packen + +Führe den folgenden Befehl im Terminal (PowerShell) im Hauptverzeichnis aus. Dieser Befehl baut das Projekt, erstellt das ZIP-Archiv und berechnet direkt die Checksumme (Hash). + +```powershell +dotnet publish Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj --configuration Release --output bin/Publish; Compress-Archive -Path bin/Publish/* -DestinationPath bin/Publish/Jellyfin.Plugin.Seasonals.zip -Force; $hash = (Get-FileHash -Algorithm MD5 bin/Publish/Jellyfin.Plugin.Seasonals.zip).Hash.ToLower(); $time = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); Write-Output "`n----------------------------------------"; Write-Output "NEUE CHECKSUMME (MD5): $hash"; Write-Output "ZEITSTEMPEL: $time"; Write-Output "----------------------------------------`n" +``` + +## 3. Manifest aktualisieren + +Nachdem der Befehl durchgelaufen ist, siehst du am Ende eine Ausgabe wie: + +```text +---------------------------------------- +NEUE CHECKSUMME (MD5): ef8654666ffeae9695e660944f644ad3 +ZEITSTEMPEL: 2025-12-15T12:34:56Z +---------------------------------------- +``` + +1. Kopiere die **Checksumme**. +2. Öffne `manifest.json`. +3. Füge die Checksumme bei deinem Versionseintrag unter `"checksum"` ein. +4. Kopiere den **Zeitstempel** und füge ihn unter `"timestamp"` ein. +5. Stelle sicher, dass die `sourceUrl` korrekt auf dein Repository zeigt. + +## 4. Veröffentlichen + +1. Lade die Datei `bin/Publish/Jellyfin.Plugin.Seasonals.zip` irgendwo hoch (z.B. GitHub Releases). +2. Stelle sicher, dass die `sourceUrl` im `manifest.json` auf diesen Download zeigt (oder auf das Repo, je nachdem wie Jellyfin das handhabt - meistens ist `sourceUrl` der Link zum ZIP). + * *Hinweis:* Wenn du das Plugin über ein Repo hostest, muss die URL im Manifest direkt auf die ZIP-Datei zeigen. + +## Zusammenfassung der Dateien + +| Datei | Zweck | Änderung nötig? | +| :--- | :--- | :--- | +| `Jellyfin.Plugin.Seasonals.csproj` | Definiert die DLL-Version | **Ja** | +| `build.yaml` | Build-Konfiguration | **Ja** | +| `manifest.json` | Plugin-Liste für Jellyfin | **Ja** (Version, Hash, Zeit) | diff --git a/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json b/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json index 43ebd58..ab7efa6 100644 --- a/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json +++ b/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json @@ -9,10 +9,7 @@ "Jellyfin.Plugin.Seasonals/1.0.0.0": { "dependencies": { "Jellyfin.Controller": "10.11.0", - "Jellyfin.Model": "10.11.0", - "SerilogAnalyzer": "0.15.0", - "SmartAnalyzers.MultithreadingAnalyzer": "1.1.31", - "StyleCop.Analyzers": "1.2.0-beta.556" + "Jellyfin.Model": "10.11.0" }, "runtime": { "Jellyfin.Plugin.Seasonals.dll": {} @@ -166,14 +163,6 @@ } }, "Polly.Core/8.6.4": {}, - "SerilogAnalyzer/0.15.0": {}, - "SmartAnalyzers.MultithreadingAnalyzer/1.1.31": {}, - "StyleCop.Analyzers/1.2.0-beta.556": { - "dependencies": { - "StyleCop.Analyzers.Unstable": "1.2.0.556" - } - }, - "StyleCop.Analyzers.Unstable/1.2.0.556": {}, "System.Globalization/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0", @@ -421,34 +410,6 @@ "path": "polly.core/8.6.4", "hashPath": "polly.core.8.6.4.nupkg.sha512" }, - "SerilogAnalyzer/0.15.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-sVpwfls4MfNnwIXLSGCgaUnV+c9kgJ8ia6GsyRcpd4Vs3gLogSDtSYBYrre2K2u/PNMo8GgG09RehwVnze70Tw==", - "path": "seriloganalyzer/0.15.0", - "hashPath": "seriloganalyzer.0.15.0.nupkg.sha512" - }, - "SmartAnalyzers.MultithreadingAnalyzer/1.1.31": { - "type": "package", - "serviceable": true, - "sha512": "sha512-2f2k7bbhDd132ArglCKpzKoWBcp3uzbIFcb4aosnlqIKlfYKDE2HevBVRNVa+LkWFnjXFFWs47Bo96fu8iS//Q==", - "path": "smartanalyzers.multithreadinganalyzer/1.1.31", - "hashPath": "smartanalyzers.multithreadinganalyzer.1.1.31.nupkg.sha512" - }, - "StyleCop.Analyzers/1.2.0-beta.556": { - "type": "package", - "serviceable": true, - "sha512": "sha512-llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==", - "path": "stylecop.analyzers/1.2.0-beta.556", - "hashPath": "stylecop.analyzers.1.2.0-beta.556.nupkg.sha512" - }, - "StyleCop.Analyzers.Unstable/1.2.0.556": { - "type": "package", - "serviceable": true, - "sha512": "sha512-zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ==", - "path": "stylecop.analyzers.unstable/1.2.0.556", - "hashPath": "stylecop.analyzers.unstable.1.2.0.556.nupkg.sha512" - }, "System.Globalization/4.3.0": { "type": "package", "serviceable": true, diff --git a/bin/Publish/Jellyfin.Plugin.Seasonals.dll b/bin/Publish/Jellyfin.Plugin.Seasonals.dll index b612770..d1ae1f5 100644 Binary files a/bin/Publish/Jellyfin.Plugin.Seasonals.dll and b/bin/Publish/Jellyfin.Plugin.Seasonals.dll differ diff --git a/bin/Publish/Jellyfin.Plugin.Seasonals.pdb b/bin/Publish/Jellyfin.Plugin.Seasonals.pdb index 79f7d2e..3847bb2 100644 Binary files a/bin/Publish/Jellyfin.Plugin.Seasonals.pdb and b/bin/Publish/Jellyfin.Plugin.Seasonals.pdb differ diff --git a/bin/Publish/Jellyfin.Plugin.Seasonals.xml b/bin/Publish/Jellyfin.Plugin.Seasonals.xml index 57f9a64..f1dae18 100644 --- a/bin/Publish/Jellyfin.Plugin.Seasonals.xml +++ b/bin/Publish/Jellyfin.Plugin.Seasonals.xml @@ -4,6 +4,24 @@ Jellyfin.Plugin.Seasonals + + + Controller for serving seasonal resources and configuration. + + + + + Gets the current plugin configuration. + + The configuration object. + + + + Serves embedded resources. + + The path to the resource. + The resource file. + Plugin configuration. @@ -51,5 +69,33 @@ + + + Handles the injection of the Seasonals script into the Jellyfin web interface. + + + + + Initializes a new instance of the class. + + The application paths. + The logger. + + + + Injects the script tag into index.html if it's not already present. + + + + + Removes the script tag from index.html. + + + + + Retrieves the path to the Jellyfin web interface directory. + + The path to the web directory, or null if not found. + diff --git a/bin/Publish/Jellyfin.Plugin.Seasonals.zip b/bin/Publish/Jellyfin.Plugin.Seasonals.zip index 6a5a363..c8ba0c7 100644 Binary files a/bin/Publish/Jellyfin.Plugin.Seasonals.zip and b/bin/Publish/Jellyfin.Plugin.Seasonals.zip differ diff --git a/manifest.json b/manifest.json index 4498b41..d0e62de 100644 --- a/manifest.json +++ b/manifest.json @@ -13,8 +13,8 @@ "changelog": "Initial release", "targetAbi": "10.9.0.0", "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/raw/branch/main/bin/Publish/Jellyfin.Plugin.Seasonals.zip", - "checksum": "f7e8c352385768c118d1577d09dcff7e", - "timestamp": "2025-12-14T22:50:50Z" + "checksum": "87990236bd9337eda22757423d896e22", + "timestamp": "2025-12-14T23:50:27Z" } ] }