diff --git a/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj b/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj index a42bea4..59efc6e 100644 --- a/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj +++ b/Jellyfin.Plugin.Seasonals/Jellyfin.Plugin.Seasonals.csproj @@ -12,13 +12,14 @@ Jellyfin Seasonals Plugin CodeDevMLH - 1.1.0.0 + 1.2.0.0 https://github.com/CodeDevMLH/jellyfin-plugin-seasonals + diff --git a/Jellyfin.Plugin.Seasonals/Plugin.cs b/Jellyfin.Plugin.Seasonals/Plugin.cs index 47601b6..ad1af94 100644 --- a/Jellyfin.Plugin.Seasonals/Plugin.cs +++ b/Jellyfin.Plugin.Seasonals/Plugin.cs @@ -1,12 +1,17 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; using Jellyfin.Plugin.Seasonals.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Jellyfin.Plugin.Seasonals; @@ -28,7 +33,10 @@ public class Plugin : BasePlugin, IHasWebPages { Instance = this; _scriptInjector = new ScriptInjector(applicationPaths, loggerFactory.CreateLogger()); - _scriptInjector.Inject(); + if (!_scriptInjector.Inject()) + { + TryRegisterFallback(loggerFactory.CreateLogger("FileTransformationFallback")); + } } /// @@ -42,16 +50,99 @@ public class Plugin : BasePlugin, IHasWebPages /// public static Plugin? Instance { get; private set; } + /// + /// Callback method for FileTransformation plugin. + /// + /// The payload containing the file contents. + /// The modified file contents. + public static string TransformIndexHtml(JObject payload) + { + // CRITICAL: Always return original content if something fails or is null + string? originalContents = payload?["contents"]?.ToString(); + + if (string.IsNullOrEmpty(originalContents)) + { + return originalContents ?? string.Empty; + } + + try + { + var html = originalContents; + const string inject = "\n x.Assemblies) + .FirstOrDefault(x => x.FullName?.Contains(".FileTransformation") ?? false); + + if (assembly == null) + { + logger.LogWarning("FileTransformation plugin not found. Fallback injection skipped."); + return; + } + + var type = assembly.GetType("Jellyfin.Plugin.FileTransformation.PluginInterface"); + if (type == null) + { + logger.LogWarning("Jellyfin.Plugin.FileTransformation.PluginInterface not found."); + return; + } + + var method = type.GetMethod("RegisterTransformation"); + if (method == null) + { + logger.LogWarning("RegisterTransformation method not found."); + return; + } + + // Create JObject payload directly using Newtonsoft.Json + var payload = new JObject + { + { "id", Id.ToString() }, + { "fileNamePattern", "index.html" }, + { "callbackAssembly", this.GetType().Assembly.FullName }, + { "callbackClass", this.GetType().FullName }, + { "callbackMethod", nameof(TransformIndexHtml) } + }; + + // Invoke RegisterTransformation with the JObject payload + method.Invoke(null, new object[] { payload }); + logger.LogInformation("Successfully registered fallback transformation via FileTransformation plugin."); + } + catch (Exception ex) + { + logger.LogError(ex, "Error attempting to register fallback transformation."); + } + } + /// public IEnumerable GetPages() { - return - [ + return new[] + { new PluginPageInfo { Name = Name, EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.configPage.html", GetType().Namespace) } - ]; + }; } } diff --git a/Jellyfin.Plugin.Seasonals/ScriptInjector.cs b/Jellyfin.Plugin.Seasonals/ScriptInjector.cs index d84532f..6432b26 100644 --- a/Jellyfin.Plugin.Seasonals/ScriptInjector.cs +++ b/Jellyfin.Plugin.Seasonals/ScriptInjector.cs @@ -30,7 +30,8 @@ public class ScriptInjector /// /// Injects the script tag into index.html if it's not already present. /// - public void Inject() + /// True if injection was successful or already present, false otherwise. + public bool Inject() { try { @@ -38,21 +39,21 @@ public class ScriptInjector if (string.IsNullOrEmpty(webPath)) { _logger.LogWarning("Could not find Jellyfin web path. Script injection skipped."); - return; + return false; } var indexPath = Path.Combine(webPath, "index.html"); if (!File.Exists(indexPath)) { _logger.LogWarning("index.html not found at {Path}. Script injection skipped.", indexPath); - return; + return false; } var content = File.ReadAllText(indexPath); if (content.Contains(ScriptTag, StringComparison.Ordinal)) { _logger.LogInformation("Seasonals script already injected."); - return; + return true; } // Insert before the closing body tag @@ -60,19 +61,22 @@ public class ScriptInjector if (string.Equals(newContent, content, StringComparison.Ordinal)) { _logger.LogWarning("Could not find closing body tag in index.html. Script injection skipped."); - return; + return false; } File.WriteAllText(indexPath, newContent); _logger.LogInformation("Successfully injected Seasonals script into index.html."); + return true; } catch (UnauthorizedAccessException) { _logger.LogWarning("Permission denied when attempting to inject script into index.html. Automatic injection failed. Please ensure the Jellyfin web directory is writable by the process, or manually add the script tag: {ScriptTag}", ScriptTag); + return false; } catch (Exception ex) { _logger.LogError(ex, "Error injecting Seasonals script."); + return false; } } diff --git a/Jellyfin.Plugin.Seasonals/Web/seasonals.js b/Jellyfin.Plugin.Seasonals/Web/seasonals.js index d232124..4904d47 100644 --- a/Jellyfin.Plugin.Seasonals/Web/seasonals.js +++ b/Jellyfin.Plugin.Seasonals/Web/seasonals.js @@ -162,6 +162,7 @@ async function initializeTheme() { automateThemeSelection = config.automateSeasonSelection; defaultTheme = config.selectedSeason; window.SeasonalsPluginConfig = config; + console.log('Seasonals Config loaded:', config); } else { console.error('Failed to fetch Seasonals config'); } @@ -170,7 +171,7 @@ async function initializeTheme() { } let currentTheme; - if (!automateThemeSelection) { + if (automateThemeSelection === false) { currentTheme = defaultTheme; } else { currentTheme = determineCurrentTheme(); @@ -178,7 +179,7 @@ async function initializeTheme() { console.log(`Selected theme: ${currentTheme}`); - if (currentTheme === 'none') { + if (!currentTheme || currentTheme === 'none') { console.log('No theme selected.'); removeSelf(); return; @@ -200,8 +201,9 @@ async function initializeTheme() { removeSelf(); } - -//document.addEventListener('DOMContentLoaded', initializeTheme); -document.addEventListener('DOMContentLoaded', () => { +// Ensure DOM is ready before initializing +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeTheme); +} else { initializeTheme(); -}); \ No newline at end of file +} \ No newline at end of file diff --git a/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json b/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json index 7ff4bb9..1b094db 100644 --- a/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json +++ b/bin/Publish/Jellyfin.Plugin.Seasonals.deps.json @@ -6,10 +6,11 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v9.0": { - "Jellyfin.Plugin.Seasonals/1.1.0.0": { + "Jellyfin.Plugin.Seasonals/1.2.0.0": { "dependencies": { "Jellyfin.Controller": "10.11.0", - "Jellyfin.Model": "10.11.0" + "Jellyfin.Model": "10.11.0", + "Newtonsoft.Json": "13.0.4" }, "runtime": { "Jellyfin.Plugin.Seasonals.dll": {} @@ -326,6 +327,14 @@ } } }, + "Newtonsoft.Json/13.0.4": { + "runtime": { + "lib/net6.0/Newtonsoft.Json.dll": { + "assemblyVersion": "13.0.0.0", + "fileVersion": "13.0.4.30916" + } + } + }, "Polly/8.6.4": { "dependencies": { "Polly.Core": "8.6.4" @@ -363,7 +372,7 @@ } }, "libraries": { - "Jellyfin.Plugin.Seasonals/1.1.0.0": { + "Jellyfin.Plugin.Seasonals/1.2.0.0": { "type": "project", "serviceable": false, "sha512": "" @@ -578,6 +587,13 @@ "path": "nebml/1.1.0.5", "hashPath": "nebml.1.1.0.5.nupkg.sha512" }, + "Newtonsoft.Json/13.0.4": { + "type": "package", + "serviceable": true, + "sha512": "sha512-pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==", + "path": "newtonsoft.json/13.0.4", + "hashPath": "newtonsoft.json.13.0.4.nupkg.sha512" + }, "Polly/8.6.4": { "type": "package", "serviceable": true, diff --git a/bin/Publish/Jellyfin.Plugin.Seasonals.dll b/bin/Publish/Jellyfin.Plugin.Seasonals.dll index 4e4d4f0..cac93a6 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 e1474af..610ca72 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.zip b/bin/Publish/Jellyfin.Plugin.Seasonals.zip index 6893df3..9a906ca 100644 Binary files a/bin/Publish/Jellyfin.Plugin.Seasonals.zip and b/bin/Publish/Jellyfin.Plugin.Seasonals.zip differ diff --git a/build.yaml b/build.yaml index a19222e..64e5252 100644 --- a/build.yaml +++ b/build.yaml @@ -1,7 +1,7 @@ --- name: "Seasonals" guid: "ef1e863f-cbb0-4e47-9f23-f0cbb1826ad4" -version: "1.1.0.0" +version: "1.2.0.0" targetAbi: "10.11.0.0" framework: "net9.0" overview: "Seasonal effects for Jellyfin" diff --git a/manifest.json b/manifest.json index 1f057ec..2348215 100644 --- a/manifest.json +++ b/manifest.json @@ -8,6 +8,14 @@ "category": "General", "imageUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/raw/branch/main/logo.png", "versions": [ + { + "version": "1.2.0.0", + "changelog": "Bug fixing: Fix path, fix injection issue, added File Transformator as fallback if direct injection is blocked due to permissions.", + "targetAbi": "10.11.0.0", + "sourceUrl": "https://git.mahom03-spacecloud.de/CodeDevMLH/Jellyfin-Seasonals-Plugin/releases/download/v1.2.0.0/Jellyfin.Plugin.Seasonals.zip", + "checksum": "be2e93364396b6e0e02368d5a7db53bc", + "timestamp": "2025-12-17T15:32:08Z" + }, { "version": "1.1.1.0", "changelog": "Bug fixing: Added Advanced Configuration UI for customizing individual seasonal effects.",