mirror of
https://github.com/MasterAcnolo/Freedom-Loader.git
synced 2026-07-29 18:25:47 +02:00
Fix: Display custom these with a basic order
This commit is contained in:
@@ -7,6 +7,8 @@ const MAX_IMAGE_SIZE = 10 * 1024 * 1024;
|
|||||||
const ALLOWED_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".avif"];
|
const ALLOWED_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".avif"];
|
||||||
const ALLOWED_IMAGE_NAMES = ["cover", "background"];
|
const ALLOWED_IMAGE_NAMES = ["cover", "background"];
|
||||||
|
|
||||||
|
const THEME_ORDER = ["dark", "light", "neon", "fnatic", "chirac", "drift", "nf", "songbird", "spicyfire"];
|
||||||
|
|
||||||
const REQUIRED_KEYS = [
|
const REQUIRED_KEYS = [
|
||||||
["meta", "name"],
|
["meta", "name"],
|
||||||
["meta", "author"],
|
["meta", "author"],
|
||||||
@@ -31,6 +33,97 @@ function validateThemeJson(json) {
|
|||||||
return { valid: true };
|
return { valid: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildThemeObject(themeId, themeJson, imageData) {
|
||||||
|
return {
|
||||||
|
id: themeId.toLowerCase(),
|
||||||
|
name: themeJson.meta.name || themeId,
|
||||||
|
author: themeJson.meta.author || "Unknown",
|
||||||
|
version: themeJson.meta.version,
|
||||||
|
subtitle: themeJson.meta.subtitle || "",
|
||||||
|
style: themeJson.style,
|
||||||
|
image: imageData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractImage(buffer, filename) {
|
||||||
|
if (buffer.length > MAX_IMAGE_SIZE) {
|
||||||
|
logger.warn(`Image too large, ignoring`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const ext = path.extname(filename).toLowerCase().replace(".", "");
|
||||||
|
const mime = ext === "jpg" || ext === "jpeg" ? "jpeg" : ext;
|
||||||
|
return `data:image/${mime};base64,${buffer.toString("base64")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadThemeFromZip(zipPath, themeId) {
|
||||||
|
try {
|
||||||
|
const zipBuffer = fs.readFileSync(zipPath);
|
||||||
|
const zip = await JSZip.loadAsync(zipBuffer);
|
||||||
|
|
||||||
|
const jsonFile = Object.keys(zip.files).find(f => f.endsWith(".theme.json"));
|
||||||
|
if (!jsonFile) { logger.warn(`Theme ${themeId}: no .theme.json, skipping`); return null; }
|
||||||
|
|
||||||
|
let themeJson;
|
||||||
|
try { themeJson = JSON.parse(await zip.files[jsonFile].async("string")); }
|
||||||
|
catch { logger.warn(`Theme ${themeId}: invalid JSON, skipping`); return null; }
|
||||||
|
|
||||||
|
const validation = validateThemeJson(themeJson);
|
||||||
|
if (!validation.valid) { logger.warn(`Theme ${themeId}: ${validation.reason}, skipping`); return null; }
|
||||||
|
|
||||||
|
let imageData = null;
|
||||||
|
const imageFile = Object.keys(zip.files).find(f => {
|
||||||
|
const ext = path.extname(f).toLowerCase();
|
||||||
|
const name = path.basename(f, ext).toLowerCase();
|
||||||
|
return ALLOWED_IMAGE_NAMES.includes(name) && ALLOWED_IMAGE_EXTENSIONS.includes(ext);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (imageFile) {
|
||||||
|
const buf = await zip.files[imageFile].async("nodebuffer");
|
||||||
|
imageData = extractImage(buf, imageFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Theme loaded (zip): ${themeId}`);
|
||||||
|
return buildThemeObject(themeId, themeJson, imageData);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`Theme ${themeId}: failed to load zip — ${err.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadThemeFromFolder(folderPath, themeId) {
|
||||||
|
try {
|
||||||
|
const jsonFile = fs.readdirSync(folderPath).find(f => f.endsWith(".theme.json"));
|
||||||
|
if (!jsonFile) { logger.warn(`Theme ${themeId}: no .theme.json, skipping`); return null; }
|
||||||
|
|
||||||
|
let themeJson;
|
||||||
|
try { themeJson = JSON.parse(fs.readFileSync(path.join(folderPath, jsonFile), "utf-8")); }
|
||||||
|
catch { logger.warn(`Theme ${themeId}: invalid JSON, skipping`); return null; }
|
||||||
|
|
||||||
|
const validation = validateThemeJson(themeJson);
|
||||||
|
if (!validation.valid) { logger.warn(`Theme ${themeId}: ${validation.reason}, skipping`); return null; }
|
||||||
|
|
||||||
|
let imageData = null;
|
||||||
|
const imageFile = fs.readdirSync(folderPath).find(f => {
|
||||||
|
const ext = path.extname(f).toLowerCase();
|
||||||
|
const name = path.basename(f, ext).toLowerCase();
|
||||||
|
return ALLOWED_IMAGE_NAMES.includes(name) && ALLOWED_IMAGE_EXTENSIONS.includes(ext);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (imageFile) {
|
||||||
|
const buf = fs.readFileSync(path.join(folderPath, imageFile));
|
||||||
|
imageData = extractImage(buf, imageFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Theme loaded (folder): ${themeId}`);
|
||||||
|
return buildThemeObject(themeId, themeJson, imageData);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`Theme ${themeId}: failed to load folder — ${err.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let cachedThemes = null;
|
let cachedThemes = null;
|
||||||
let themeFolderPath = null;
|
let themeFolderPath = null;
|
||||||
|
|
||||||
@@ -52,14 +145,12 @@ async function loadThemesFromFolder() {
|
|||||||
const filePath = path.join(themeFolderPath, file);
|
const filePath = path.join(themeFolderPath, file);
|
||||||
const themeId = path.basename(file, path.extname(file));
|
const themeId = path.basename(file, path.extname(file));
|
||||||
|
|
||||||
// ZIP
|
|
||||||
if (file.endsWith(".zip")) {
|
if (file.endsWith(".zip")) {
|
||||||
const theme = await loadThemeFromZip(filePath, themeId);
|
const theme = await loadThemeFromZip(filePath, themeId);
|
||||||
if (theme) themes.push(theme);
|
if (theme) themes.push(theme);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dossier décompressé
|
|
||||||
if (fs.statSync(filePath).isDirectory()) {
|
if (fs.statSync(filePath).isDirectory()) {
|
||||||
const theme = await loadThemeFromFolder(filePath, themeId);
|
const theme = await loadThemeFromFolder(filePath, themeId);
|
||||||
if (theme) themes.push(theme);
|
if (theme) themes.push(theme);
|
||||||
@@ -70,117 +161,17 @@ async function loadThemesFromFolder() {
|
|||||||
return themes;
|
return themes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadThemeFromZip(zipPath, themeId) {
|
function getThemeOrder(id) {
|
||||||
try {
|
const index = THEME_ORDER.indexOf(id);
|
||||||
const zipBuffer = fs.readFileSync(zipPath);
|
return index === -1 ? Infinity : index;
|
||||||
const zip = await JSZip.loadAsync(zipBuffer);
|
|
||||||
|
|
||||||
const jsonFile = Object.keys(zip.files).find(f => f.endsWith(".theme.json"));
|
|
||||||
if (!jsonFile) {
|
|
||||||
logger.warn(`Theme ${themeId}: no .theme.json found, skipping`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonContent = await zip.files[jsonFile].async("string");
|
|
||||||
let themeJson;
|
|
||||||
try { themeJson = JSON.parse(jsonContent); }
|
|
||||||
catch { logger.warn(`Theme ${themeId}: invalid JSON, skipping`); return null; }
|
|
||||||
|
|
||||||
const validation = validateThemeJson(themeJson);
|
|
||||||
if (!validation.valid) {
|
|
||||||
logger.warn(`Theme ${themeId}: ${validation.reason}, skipping`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let imageData = null;
|
|
||||||
const imageFile = Object.keys(zip.files).find(f => {
|
|
||||||
const ext = path.extname(f).toLowerCase();
|
|
||||||
const name = path.basename(f, ext).toLowerCase();
|
|
||||||
return ALLOWED_IMAGE_NAMES.includes(name) && ALLOWED_IMAGE_EXTENSIONS.includes(ext);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (imageFile) {
|
|
||||||
const imageBuffer = await zip.files[imageFile].async("nodebuffer");
|
|
||||||
if (imageBuffer.length > MAX_IMAGE_SIZE) {
|
|
||||||
logger.warn(`Theme ${themeId}: image too large, ignoring image`);
|
|
||||||
} else {
|
|
||||||
const ext = path.extname(imageFile).toLowerCase().replace(".", "");
|
|
||||||
const mime = ext === "jpg" || ext === "jpeg" ? "jpeg" : ext;
|
|
||||||
imageData = `data:image/${mime};base64,${imageBuffer.toString("base64")}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`Theme loaded (zip): ${themeId}`);
|
|
||||||
return buildThemeObject(themeId, themeJson, imageData);
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn(`Theme ${themeId}: failed to load zip — ${err.message}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadThemeFromFolder(folderPath, themeId) {
|
|
||||||
try {
|
|
||||||
const jsonFile = fs.readdirSync(folderPath).find(f => f.endsWith(".theme.json"));
|
|
||||||
if (!jsonFile) {
|
|
||||||
logger.warn(`Theme ${themeId}: no .theme.json found, skipping`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonContent = fs.readFileSync(path.join(folderPath, jsonFile), "utf-8");
|
|
||||||
let themeJson;
|
|
||||||
try { themeJson = JSON.parse(jsonContent); }
|
|
||||||
catch { logger.warn(`Theme ${themeId}: invalid JSON, skipping`); return null; }
|
|
||||||
|
|
||||||
const validation = validateThemeJson(themeJson);
|
|
||||||
if (!validation.valid) {
|
|
||||||
logger.warn(`Theme ${themeId}: ${validation.reason}, skipping`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let imageData = null;
|
|
||||||
const imageFile = fs.readdirSync(folderPath).find(f => {
|
|
||||||
const ext = path.extname(f).toLowerCase();
|
|
||||||
const name = path.basename(f, ext).toLowerCase();
|
|
||||||
return ALLOWED_IMAGE_NAMES.includes(name) && ALLOWED_IMAGE_EXTENSIONS.includes(ext);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (imageFile) {
|
|
||||||
const imageBuffer = fs.readFileSync(path.join(folderPath, imageFile));
|
|
||||||
if (imageBuffer.length > MAX_IMAGE_SIZE) {
|
|
||||||
logger.warn(`Theme ${themeId}: image too large, ignoring image`);
|
|
||||||
} else {
|
|
||||||
const ext = path.extname(imageFile).toLowerCase().replace(".", "");
|
|
||||||
const mime = ext === "jpg" || ext === "jpeg" ? "jpeg" : ext;
|
|
||||||
imageData = `data:image/${mime};base64,${imageBuffer.toString("base64")}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`Theme loaded (folder): ${themeId}`);
|
|
||||||
return buildThemeObject(themeId, themeJson, imageData);
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn(`Theme ${themeId}: failed to load folder — ${err.message}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildThemeObject(themeId, themeJson, imageData) {
|
|
||||||
return {
|
|
||||||
id: themeId,
|
|
||||||
name: themeJson.meta.name || themeId,
|
|
||||||
author: themeJson.meta.author || "Unknown",
|
|
||||||
version: themeJson.meta.version,
|
|
||||||
subtitle: themeJson.meta.subtitle || "",
|
|
||||||
style: themeJson.style,
|
|
||||||
image: imageData,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initThemes(folderPath) {
|
async function initThemes(folderPath) {
|
||||||
setThemeFolderPath(folderPath);
|
setThemeFolderPath(folderPath);
|
||||||
cachedThemes = await loadThemesFromFolder();
|
const themes = await loadThemesFromFolder();
|
||||||
logger.info(`${cachedThemes.length} theme(s) loaded`);
|
logger.info(`Themes before sort: ${themes.map(t => t.id).join(", ")}`);
|
||||||
|
cachedThemes = themes.sort((a, b) => getThemeOrder(a.id) - getThemeOrder(b.id));
|
||||||
|
logger.info(`Themes after sort: ${cachedThemes.map(t => t.id).join(", ")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThemes() {
|
function getThemes() {
|
||||||
|
|||||||
Reference in New Issue
Block a user