mirror of
https://github.com/danieladov/jellyfin-plugin-skin-manager.git
synced 2026-01-18 16:37:31 +01:00
389 lines
14 KiB
JavaScript
389 lines
14 KiB
JavaScript
class MainController {
|
|
constructor(jsonUrl) {
|
|
this.jsonUrl = jsonUrl;
|
|
this.skins = [];
|
|
this.currentSkin = null;
|
|
this.selectElement = document.getElementById("cssOptions");
|
|
this.descriptionElement = document.getElementById("description");
|
|
this.optionsElement = document.getElementById("options");
|
|
this.setSkinButton = document.getElementById("setSkin");
|
|
this.configController = new ConfigController();
|
|
}
|
|
|
|
async init() {
|
|
try {
|
|
const [json, currentSkin] = await Promise.all([
|
|
this.fetchJson(),
|
|
this.loadCurrentSkinFromHistory()
|
|
]);
|
|
this.loadSkins(json);
|
|
this.injectCurrentSkin(currentSkin);
|
|
this.populateSelect();
|
|
this.initEventListeners();
|
|
} catch (error) {
|
|
console.error("Error cargando las skins:", error);
|
|
}
|
|
}
|
|
|
|
async fetchJson() {
|
|
const response = await fetch(this.jsonUrl);
|
|
if (!response.ok) throw new Error(`Error HTTP: ${response.status}`);
|
|
return await response.json();
|
|
}
|
|
|
|
async loadCurrentSkinFromHistory() {
|
|
try {
|
|
const history = await this.configController.loadHistorySkins();
|
|
if (!Array.isArray(history) || history.length === 0) {
|
|
return null;
|
|
}
|
|
const latest = history[0];
|
|
return this.cloneAsCurrentSkin(latest);
|
|
} catch (error) {
|
|
console.warn("No se pudo cargar la skin actual:", error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
loadSkins(json) {
|
|
this.skins = json.skins.map(s => new Skin(s));
|
|
}
|
|
|
|
injectCurrentSkin(currentSkin) {
|
|
if (!currentSkin) {
|
|
return;
|
|
}
|
|
this.skins.unshift(currentSkin);
|
|
}
|
|
|
|
cloneAsCurrentSkin(skin) {
|
|
if (!skin) {
|
|
return null;
|
|
}
|
|
|
|
const plainSkin = JSON.parse(JSON.stringify(skin));
|
|
const baseName = this.extractSkinBaseName(plainSkin.name);
|
|
plainSkin.name = `Current Skin - ${baseName}`;
|
|
plainSkin.description = plainSkin.description || "Skin currently applied with your saved settings.";
|
|
|
|
if (Array.isArray(plainSkin.categories)) {
|
|
plainSkin.categories = plainSkin.categories.filter(cat => cat && cat.name !== "Custom CSS");
|
|
}
|
|
|
|
return new Skin(plainSkin);
|
|
}
|
|
|
|
extractSkinBaseName(name) {
|
|
if (!name) {
|
|
return "unknown";
|
|
}
|
|
const match = String(name).match(/-\s*(.+)$/);
|
|
if (match && match[1]) {
|
|
return match[1].trim();
|
|
}
|
|
return String(name).trim();
|
|
}
|
|
|
|
populateSelect() {
|
|
this.selectElement.innerHTML = ""; // Limpiar por si acaso
|
|
this.skins.forEach((skin, index) => {
|
|
const option = document.createElement("option");
|
|
option.value = index;
|
|
option.textContent = skin.name;
|
|
this.selectElement.appendChild(option);
|
|
});
|
|
|
|
// Mostrar la primera skin por defecto si existe
|
|
if (this.skins.length > 0) {
|
|
this.selectElement.value = 0;
|
|
this.currentSkin = this.skins[0];
|
|
this.showSkin();
|
|
}
|
|
}
|
|
|
|
showSkin() {
|
|
this.optionsElement.innerHTML = this.currentSkin.generateHTML();
|
|
this.currentSkin.attachEventListeners();
|
|
}
|
|
|
|
updateSkinInfo(index) {
|
|
const skin = this.skins[index];
|
|
if (!skin) return;
|
|
|
|
// Actualizar descripción
|
|
this.descriptionElement.textContent = `Skin: ${skin.name}`;
|
|
|
|
// Actualizar categorías
|
|
this.optionsElement.innerHTML = "";
|
|
skin.categories.forEach(cat => {
|
|
const div = document.createElement("div");
|
|
div.textContent = cat;
|
|
this.optionsElement.appendChild(div);
|
|
});
|
|
|
|
// Aplicar el CSS
|
|
this.applySkin(skin.css);
|
|
}
|
|
|
|
|
|
|
|
changeSkin() {
|
|
const selectedIndex = parseInt(this.selectElement.value, 10);
|
|
if (Number.isNaN(selectedIndex) || !this.skins[selectedIndex]) {
|
|
return;
|
|
}
|
|
this.currentSkin = this.skins[selectedIndex];
|
|
this.showSkin();
|
|
console.log(`Skin changed to: ${this.currentSkin.name}`);
|
|
}
|
|
|
|
async applyCurrentSkin() {
|
|
if (!this.currentSkin) return;
|
|
|
|
const css = this.currentSkin.generateCSS();
|
|
const appliedSkinName = this.currentSkin.name;
|
|
|
|
try {
|
|
const serverConfig = await ApiClient.getServerConfiguration();
|
|
await ApiClient.updateServerConfiguration(serverConfig);
|
|
|
|
const brandingConfig = await ApiClient.getNamedConfiguration("branding");
|
|
const existingCss = brandingConfig && typeof brandingConfig.CustomCss === "string"
|
|
? brandingConfig.CustomCss
|
|
: "";
|
|
|
|
if (existingCss && !this.configController.isManagedCss(existingCss)) {
|
|
await this.configController.saveUserCss(existingCss);
|
|
}
|
|
|
|
brandingConfig.CustomCss = css;
|
|
await ApiClient.updateNamedConfiguration("branding", brandingConfig);
|
|
Dashboard.processServerConfigurationUpdateResult();
|
|
|
|
// Save the skin to history after a successful update
|
|
await this.configController.saveSkin(this.currentSkin);
|
|
await this.configController.setSelectedSkin(appliedSkinName);
|
|
|
|
window.location.reload(true);
|
|
} catch (error) {
|
|
console.error("Error applying skin:", error);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
initEventListeners() {
|
|
this.setSkinButton.addEventListener('click', () => {
|
|
if (this.currentSkin) {
|
|
this.applyCurrentSkin();
|
|
}
|
|
});
|
|
|
|
this.selectElement.addEventListener('change', () => {
|
|
this.changeSkin();
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
async initDebug() {
|
|
try {
|
|
const json = `{
|
|
"skins": [
|
|
{
|
|
"name": "Finimalism",
|
|
"description": "A modern, customizable skin for Jellyfin.",
|
|
"css":"",
|
|
"categories": [
|
|
{
|
|
"name": "Default",
|
|
"controls": [
|
|
{
|
|
"type": "select",
|
|
"label": "Mode",
|
|
"description": "Select light or dark mode",
|
|
"id": "mode",
|
|
"default": "@import url('https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@latest/finimalism10.11.css');",
|
|
"css": "%value%",
|
|
"options": [
|
|
{
|
|
"label": "Colour",
|
|
"value": "@import url('https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@latest/finimalism10.11.css');"
|
|
},
|
|
{
|
|
"label": "Black",
|
|
"value": "@import url('https://cdn.jsdelivr.net/gh/tedhinklater/finimalism@latest/finimalism10.11-black.css');"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
|
|
},
|
|
{
|
|
"name": "JellySkin",
|
|
"description": "A modern, customizable skin for Jellyfin.",
|
|
"css": "@import url('https://cdn.jsdelivr.net/npm/jellyskin@latest/dist/main.css');",
|
|
"previews": [
|
|
{
|
|
"name": "Login Page",
|
|
"url": "https://raw.githubusercontent.com/danieladov/jellyfin-plugin-skin-manager/master/src/img/Default/1.png"
|
|
},
|
|
{
|
|
"name": "Home/Index Page",
|
|
"url": "https://raw.githubusercontent.com/danieladov/jellyfin-plugin-skin-manager/master/src/img/Default/2.png"
|
|
},
|
|
{
|
|
"name": "Library Page",
|
|
"url": "https://raw.githubusercontent.com/danieladov/jellyfin-plugin-skin-manager/master/src/img/Default/3.png"
|
|
},
|
|
{
|
|
"name": "Title page",
|
|
"url": "https://raw.githubusercontent.com/danieladov/jellyfin-plugin-skin-manager/master/src/img/Default/4.png"
|
|
}
|
|
],
|
|
"categories": [
|
|
{
|
|
"name": "Default",
|
|
"controls": [
|
|
{
|
|
"type": "fontPicker",
|
|
"label": "Base Font",
|
|
"description": "Select the base font for the skin",
|
|
"id": "baseFont",
|
|
"default": "Arial, sans-serif",
|
|
"css": ":root { --base-font: %value%; }"
|
|
},
|
|
{
|
|
"type": "color",
|
|
"label": "Background Color",
|
|
"description": "Set the background color of the skin",
|
|
"id": "bgColor",
|
|
"default": "#ffffff",
|
|
"css": ":root { --bg-color: %value%; }"
|
|
},
|
|
{
|
|
"type": "color",
|
|
"label": "Text Color",
|
|
"id": "textColor",
|
|
"default": "#000000",
|
|
"css": ":root { --text-color: %value%; }"
|
|
},
|
|
{
|
|
"type": "slider",
|
|
"label": "Font Size",
|
|
"id": "fontSize",
|
|
"min": 10,
|
|
"max": 30,
|
|
"default": 16,
|
|
"css": ":root { --font-size: %value%px; }"
|
|
},
|
|
{
|
|
"type": "checkbox",
|
|
"label": "Dark Mode",
|
|
"description": "Enable dark mode",
|
|
"id": "checkbox",
|
|
"default": true,
|
|
"css": ":root { --dark-mode: %value%; }"
|
|
},
|
|
{
|
|
"type": "number",
|
|
"label": "Border Radius",
|
|
"description": "Set the border radius",
|
|
"id": "borderRadius",
|
|
"min": 0,
|
|
"max": 50,
|
|
"default": 0,
|
|
"css": ":root { --border-radius: %value%px; }"
|
|
},
|
|
{
|
|
"type": "select",
|
|
"label": "Font Family",
|
|
"description": "Select the font family",
|
|
"id": "fontFamily",
|
|
"default": "Arial",
|
|
"css": ":root { --font-family: %value%; }",
|
|
"options": [
|
|
{
|
|
"label": "Arial",
|
|
"value": "Arial"
|
|
},
|
|
{
|
|
"label": "Verdana",
|
|
"value": "Verdana"
|
|
},
|
|
{
|
|
"label": "Georgia",
|
|
"value": "Georgia"
|
|
},
|
|
{
|
|
"label": "Times New Roman",
|
|
"value": "Times New Roman"
|
|
},
|
|
{
|
|
"label": "Trebuchet MS",
|
|
"value": "Trebuchet MS"
|
|
},
|
|
{
|
|
"label": "Arial Black",
|
|
"value": "Arial Black"
|
|
},
|
|
{
|
|
"label": "Impact",
|
|
"value": "Impact"
|
|
},
|
|
{
|
|
"label": "Comic Sans MS",
|
|
"value": "Comic Sans MS"
|
|
},
|
|
{
|
|
"label": "Courier New",
|
|
"value": "Courier New"
|
|
},
|
|
{
|
|
"label": "Lucida Console",
|
|
"value": "Lucida Console"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{ "name": "DarkSkin",
|
|
"description": "A sleek dark theme for Jellyfin.",
|
|
"css": "@import url('https://cdn.jsdelivr.net/npm/jellyskin@latest/dist/dark.css');",
|
|
"previews": [],
|
|
"categories": [
|
|
{
|
|
"name": "Dark Mode",
|
|
"controls": [
|
|
{
|
|
"type": "color",
|
|
"label": "Background Color",
|
|
"description": "Set the background color of the skin",
|
|
"id": "bgColor",
|
|
"default": "#121212",
|
|
"css": ":root { --bg-color: %value%; }"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}`;
|
|
const jsonObj = JSON.parse(json);
|
|
this.loadSkins(jsonObj);
|
|
const currentSkin = await this.loadCurrentSkinFromHistory();
|
|
this.injectCurrentSkin(currentSkin);
|
|
this.populateSelect();
|
|
this.initEventListeners();
|
|
} catch (error) {
|
|
console.error("Error cargando las skins:", error);
|
|
}
|
|
|
|
}
|
|
}
|