"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ensureDir = ensureDir; exports.parseDetailUrl = parseDetailUrl; exports.downloadFile = downloadFile; exports.xorDecryptBuffer = xorDecryptBuffer; exports.getCreativeAssetsDir = getCreativeAssetsDir; exports.getBuildOutputDir = getBuildOutputDir; exports.scanAssetFiles = scanAssetFiles; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const https_1 = __importDefault(require("https")); const http_1 = __importDefault(require("http")); function ensureDir(dir) { if (!fs_1.default.existsSync(dir)) { fs_1.default.mkdirSync(dir, { recursive: true }); } } /** * 从填色详情页 URL 中提取素材 ID 并拼接 zip 下载地址。 * * 输入:https://color2.jccytech.cn/app/zh/pages/detail/6a154397957ac783bac98e10 * 输出:https://color2.jccytech.cn/zips/v2/number_mini/1501/6a154397957ac783bac98e10.zip */ function parseDetailUrl(detailUrl) { try { const url = new URL(detailUrl); // 取路径最后一段作为 ID const segments = url.pathname.split("/").filter(Boolean); const id = segments[segments.length - 1]; if (!id || id.length < 20) return null; const zipUrl = `https://color2.jccytech.cn/zips/v2/number_mini/1501/${id}.zip`; return { id, zipUrl }; } catch { return null; } } /** * 下载远程文件到 Buffer */ function downloadFile(url) { return new Promise((resolve, reject) => { const client = url.startsWith("https") ? https_1.default : http_1.default; client.get(url, (res) => { if (res.statusCode !== 200) { reject(new Error(`Download failed: HTTP ${res.statusCode}`)); return; } const chunks = []; res.on("data", (chunk) => chunks.push(chunk)); res.on("end", () => resolve(Buffer.concat(chunks))); res.on("error", reject); }).on("error", reject); }); } /** * XOR 解密 zip 文件。 * * 密钥 = 文件名(不含扩展名),即素材 ID。 * 对整个文件逐字节异或解密,密钥循环使用。 */ function xorDecryptBuffer(encrypted, key) { const keyBuf = Buffer.from(key); const keyLen = keyBuf.length; const decrypted = Buffer.alloc(encrypted.length); for (let i = 0; i < encrypted.length; i++) { decrypted[i] = encrypted[i] ^ keyBuf[i % keyLen]; } return decrypted; } function getCreativeAssetsDir(storageDir, creativeId) { const dir = path_1.default.join(storageDir, "creatives", creativeId, "assets"); return dir; } function getBuildOutputDir(storageDir, creativeId, buildId) { const dir = path_1.default.join(storageDir, "creatives", creativeId, "builds", buildId); ensureDir(dir); return dir; } function scanAssetFiles(assetsDir) { const result = { config: false, page: false, map: false, special: null }; if (!fs_1.default.existsSync(assetsDir)) return result; const files = fs_1.default.readdirSync(assetsDir); for (const file of files) { const lower = file.toLowerCase(); if (lower === "config.json") result.config = true; else if (lower === "page.png" || lower === "page.jpg" || lower === "page.jpeg") result.page = true; else if (lower === "map.png") result.map = true; else if (lower.startsWith("special.")) result.special = file; } return result; } //# sourceMappingURL=storageService.js.map