防撤回软件提交

This commit is contained in:
wangxinfei
2025-05-19 17:39:49 +08:00
parent 8020113d3b
commit 7bf0e11589
404 changed files with 69545 additions and 0 deletions
@@ -0,0 +1,857 @@
const semver = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
const validateAndParse = (version) => {
if (typeof version !== "string") {
throw new TypeError("Invalid argument expected string");
}
const match = version.match(semver);
if (!match) {
throw new Error(`Invalid argument not valid semver ('${version}' received)`);
}
match.shift();
return match;
};
const isWildcard = (s) => s === "*" || s === "x" || s === "X";
const tryParse = (v) => {
const n = parseInt(v, 10);
return isNaN(n) ? v : n;
};
const forceType = (a, b) => typeof a !== typeof b ? [String(a), String(b)] : [a, b];
const compareStrings = (a, b) => {
if (isWildcard(a) || isWildcard(b))
return 0;
const [ap, bp] = forceType(tryParse(a), tryParse(b));
if (ap > bp)
return 1;
if (ap < bp)
return -1;
return 0;
};
const compareSegments = (a, b) => {
for (let i = 0; i < Math.max(a.length, b.length); i++) {
const r = compareStrings(a[i] || "0", b[i] || "0");
if (r !== 0)
return r;
}
return 0;
};
const compareVersions = (v1, v2) => {
const n1 = validateAndParse(v1);
const n2 = validateAndParse(v2);
const p1 = n1.pop();
const p2 = n2.pop();
const r = compareSegments(n1, n2);
if (r !== 0)
return r;
if (p1 && p2) {
return compareSegments(p1.split("."), p2.split("."));
} else if (p1 || p2) {
return p1 ? -1 : 1;
}
return 0;
};
const compare = (v1, v2, operator) => {
assertValidOperator(operator);
const res = compareVersions(v1, v2);
return operatorResMap[operator].includes(res);
};
const operatorResMap = {
">": [1],
">=": [0, 1],
"=": [0],
"<=": [-1, 0],
"<": [-1],
"!=": [-1, 1]
};
const allowedOperators = Object.keys(operatorResMap);
const assertValidOperator = (op) => {
if (typeof op !== "string") {
throw new TypeError(`Invalid operator type, expected string but got ${typeof op}`);
}
if (allowedOperators.indexOf(op) === -1) {
throw new Error(`Invalid operator, expected one of ${allowedOperators.join("|")}`);
}
};
class Node {
value;
next;
constructor(value) {
this.value = value;
}
}
class Queue {
#head;
#tail;
#size;
constructor() {
this.clear();
}
enqueue(value) {
const node = new Node(value);
if (this.#head) {
this.#tail.next = node;
this.#tail = node;
} else {
this.#head = node;
this.#tail = node;
}
this.#size++;
}
dequeue() {
const current = this.#head;
if (!current) {
return;
}
this.#head = this.#head.next;
this.#size--;
return current.value;
}
clear() {
this.#head = void 0;
this.#tail = void 0;
this.#size = 0;
}
get size() {
return this.#size;
}
*[Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
}
const AsyncResource = {
bind(fn, _type, thisArg) {
return fn.bind(thisArg);
}
};
function pLimit(concurrency) {
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
}
const queue = new Queue();
let activeCount = 0;
const next = () => {
activeCount--;
if (queue.size > 0) {
queue.dequeue()();
}
};
const run = async (function_, resolve, arguments_) => {
activeCount++;
const result = (async () => function_(...arguments_))();
resolve(result);
try {
await result;
} catch {
}
next();
};
const enqueue = (function_, resolve, arguments_) => {
queue.enqueue(
AsyncResource.bind(run.bind(void 0, function_, resolve, arguments_))
);
(async () => {
await Promise.resolve();
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()();
}
})();
};
const generator = (function_, ...arguments_) => new Promise((resolve) => {
enqueue(function_, resolve, arguments_);
});
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount
},
pendingCount: {
get: () => queue.size
},
clearQueue: {
value() {
queue.clear();
}
}
});
return generator;
}
const hostReg = /^https?:\/\/[^/]+/;
const originMirrors = ["https://mirror.ghproxy.com/", "https://ghproxy.net/", "https://github.moeyy.xyz/"];
const thisSlug = "list-viewer";
let config;
async function initConfig() {
const defaultConfig = {
inactivePlugins: [],
debug: false,
useMirror: true,
mirrors: {
downloadUrl: ["https://cdn.jsdelivr.net/gh"]
// rawUrl: []
}
};
config = await LiteLoader.api.config.get(thisSlug, defaultConfig);
const save = debounce((obj) => {
LiteLoader.api.config.set(thisSlug, obj);
}, 1e3);
config = deepWatch(config, () => {
save(config);
});
}
function getRandomItem(arr) {
return arr ? arr[Math.floor(Math.random() * arr.length)] : void 0;
}
function useMirror(url2, mirror, removeHost = true) {
if (/\/gh$/.test(mirror) && mirror.includes("jsdelivr")) {
return mirror + url2.replace(hostReg, "").replace("/raw/", "@").replace(/@v(?!v)/, "@vv");
} else {
return mirror + (removeHost ? url2.replace(hostReg, "") : url2);
}
}
function localFetch(path, plugin = "list-viewer") {
return fetch(`local:///${LiteLoader.plugins[plugin].path.plugin.replace(":\\", "://").replaceAll("\\", "/")}/${path.startsWith("/") ? path.slice(1) : path}`);
}
function fetchWithTimeout(url2, options, timeout = 3e3) {
config.debug && console.log("fetchWithTimeout", url2);
return new Promise((resolve, reject) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
reject(new Error("请求超时"));
}, timeout);
fetch(url2, { ...options, signal: controller.signal }).then((response) => {
clearTimeout(timeoutId);
resolve(response);
}).catch((error) => {
clearTimeout(timeoutId);
reject(error);
});
});
}
function debounce(func, delay) {
let timeoutId;
return function(...args) {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
func(args);
}, delay);
};
}
function deepWatch(obj, callback) {
const observer = new Proxy(obj, {
set(target, key, value, receiver) {
const oldValue = target[key];
if (oldValue !== value) {
callback();
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
deepWatch(value, callback);
}
}
return Reflect.set(target, key, value, receiver);
}
});
return observer;
}
const listUrl = {
repo: "LiteLoaderQQNT/Plugin-List",
branch: "v4",
file: "plugins.json"
};
const defaultIcon = "local://root/src/setting/static/default.png";
const domParser = new DOMParser();
const typeMap = {
extension: "扩展",
theme: "主题",
framework: "框架"
};
let pluginList;
let currentItem;
let currentManifest;
let showDialog;
let filterInput;
function onSettingWindowCreated(view) {
initConfig();
localFetch("/assets/view.html").then((e) => e.text()).then(async (res) => {
const doms = domParser.parseFromString(res, "text/html");
filterInput = doms.querySelector("#list-search");
const refreshBtn = doms.querySelector(".refresh-btn");
const totalEl = doms.querySelector(".total-text");
const dialogInstall = doms.querySelector(".list-dialog-install");
const dialogInstallClose = doms.querySelector(".list-dialog-install-btn-close");
let resFunc;
dialogInstallClose.addEventListener("click", () => {
dialogInstall.close();
resFunc();
});
const useRepo = doms.querySelector(".list-dialog-btn-repo");
useRepo.addEventListener("click", () => {
dialogInstall.close();
resFunc(false);
});
const useRelease = doms.querySelector(".list-dialog-btn-release");
useRelease.addEventListener("click", () => {
dialogInstall.close();
resFunc(true);
});
let dialogResolve;
const dialog = doms.querySelector(".list-dialog");
const dialogClose = doms.querySelector(".list-dialog-btn-close");
dialogClose.addEventListener("click", () => {
dialog.close();
dialogResolve();
});
const dialogTitle = doms.querySelector(".list-dialog-title");
const dialogContent = doms.querySelector(".list-dialog-content");
const dialogConfirm = doms.querySelector(".list-dialog-btn-confirm");
dialogConfirm.addEventListener("click", () => {
dialog.close();
dialogResolve(true);
});
const dialogCancel = doms.querySelector(".list-dialog-btn-cancel");
dialogCancel.addEventListener("click", () => {
dialog.close();
dialogResolve(false);
});
showDialog = (option) => {
let dialogInput;
dialogTitle.innerText = option.title;
if (option.type === "confirm" || option.type === "message") {
if (option.message) {
dialogContent.innerText = option.message;
}
if (option.content) {
if (typeof option.content === "string") {
dialogContent.innerHTML = option.content;
} else {
dialogContent.replaceChildren(option.content);
}
}
if (option.type === "message") {
dialogCancel.style.display = "none";
}
} else if (option.type === "prompt") {
dialogInput = option.textarea ? document.createElement("textarea") : document.createElement("input");
dialogInput.placeholder = option.placeholder ?? "请输入内容";
dialogInput.value = option.default ?? "";
dialogInput.style.width = "100%";
dialogInput.style.background = "var(--background_02)";
dialogInput.style.color = "var(--bg_white_light)";
dialogInput.style.marginTop = "10px";
dialogContent.replaceChildren(option.message || "", dialogInput);
}
dialogConfirm.innerText = option.confirm || "确定";
dialogCancel.innerText = option.cancel || "取消";
return new Promise((resolve) => {
dialog.showModal();
dialogResolve = (bool) => {
if (option.type === "prompt") {
resolve(bool ? dialogInput.value : "");
} else {
resolve(bool);
}
};
});
};
const mirrorSwitch = doms.querySelector(".mirror-switch");
mirrorSwitch.toggleAttribute("is-active", config.useMirror);
mirrorSwitch.onclick = () => {
const isActive = mirrorSwitch.hasAttribute("is-active");
mirrorSwitch.toggleAttribute("is-active", !isActive);
config.useMirror = !isActive;
};
const mirrorAddBtn = doms.querySelector(".mirror-add-btn");
mirrorAddBtn.onclick = () => {
showDialog({
title: "添加镜像",
type: "prompt",
placeholder: "请输入镜像地址,每行一个",
message: `请输入镜像地址,每行一个,如果代理方式是完整url在地址后面,比如
https://mirror/https://github.com/xx/xx,则需要写
https://mirror/https://github.com/
如果代理方式是 path在地址后面,比如
https://mirror/xx/xx,则需要写https://mirror
jsdelivr镜像直接按默认那个写就行
内置三个镜像'https://mirror.ghproxy.com', 'https://ghproxy.net', 'https://github.moeyy.xyz'
使用时默认优先使用第一个,如果没有响应才会使用其他镜像`,
textarea: true,
default: config.mirrors.downloadUrl.join("\n")
}).then((res2) => {
if (res2) {
config.mirrors.downloadUrl = res2.split("\n");
}
});
};
doms.body.childNodes.forEach((dom) => {
view.appendChild(dom);
});
const showInstallDialog = () => new Promise((resolve) => {
dialogInstall.showModal();
resFunc = resolve;
});
createItemComponent(await localFetch("/assets/list-item.html").then((e) => e.text()), showInstallDialog);
const pluginListDom = view.querySelector("#plugin-list");
const getList1 = (noCache = false) => {
refreshBtn.setAttribute("is-disabled", "");
getList(noCache).then(async (list) => {
pluginList = list;
totalEl.innerText = list.length.toString();
const promArr = [];
const limit = pLimit(3);
list.forEach((plugin, i) => {
const dom = document.createElement("plugin-item");
pluginListDom.appendChild(dom);
promArr.push(
limit(async () => {
const manifest = await getManifest(plugin, noCache);
dom.dataset.index = i + "";
config.debug && console.log(plugin, manifest);
updateElProp(dom, manifest, plugin.repo);
})
);
});
return Promise.all(promArr);
}).finally(() => {
refreshBtn.removeAttribute("is-disabled");
});
};
refreshBtn.addEventListener("click", () => {
pluginListDom.replaceChildren();
getList1(true);
});
getList1();
}).catch(console.error);
}
function createItemComponent(innerHtml, showInstallDialog) {
class PluginListClass extends HTMLElement {
titleEl;
descriptionEl;
versionEl;
authorsEl;
manipulateEl;
iconEl;
updateBtnEl;
installBtnEl;
uninstallBtnEl;
detailBtnEl;
retryBtnEl;
typeEl;
dependenciesItemsEl;
platformsEl;
manifest = null;
#initPromise;
#initPromiseResolve;
#initialized = false;
constructor() {
super();
const shadow = this.attachShadow({ mode: "open" });
shadow.innerHTML = innerHtml;
this.#initPromise = new Promise((resolve) => {
this.#initPromiseResolve = resolve;
if (this.#initialized)
resolve();
});
}
connectedCallback() {
this.titleEl = this.shadowRoot.querySelector(".title");
this.descriptionEl = this.shadowRoot.querySelector(".description");
this.versionEl = this.shadowRoot.querySelector(".version");
this.authorsEl = this.shadowRoot.querySelector(".authors");
this.manipulateEl = this.shadowRoot.querySelector(".manipulate");
this.iconEl = this.shadowRoot.querySelector(".icon");
this.typeEl = this.shadowRoot.querySelector(".type");
this.dependenciesItemsEl = this.shadowRoot.querySelector(".dependencies>.items");
this.platformsEl = this.shadowRoot.querySelector(".platforms");
this.updateBtnEl = this.shadowRoot.querySelector(".update");
const installEvent = async (update = false) => {
currentItem = pluginList[Number(this.dataset.index)];
currentManifest = this.manifest;
showInstallDialog().then((res) => {
if (res !== void 0) {
if (update) {
this.updateBtnEl.setAttribute("is-disabled", "");
this.updateBtnEl.innerText = "安装中...";
} else {
this.installBtnEl.setAttribute("is-disabled", "");
this.installBtnEl.innerText = "安装中...";
}
install(res).then((res2) => {
if (res2.success) {
this.dataset.installed = "1";
this.dataset.inactive = "1";
if (update)
delete this.dataset.update;
config.inactivePlugins.push(this.manifest.slug);
this.updateOpenDirEvent();
} else {
showDialog({ title: "安装失败", message: res2.message, type: "message" });
}
}).catch((e) => {
showDialog({ title: "安装失败", message: e.message, type: "message" });
}).finally(() => {
if (update) {
this.updateBtnEl.removeAttribute("is-disabled");
this.updateBtnEl.innerText = "更新";
} else {
this.installBtnEl.removeAttribute("is-disabled");
this.installBtnEl.innerText = "安装";
}
});
}
});
};
this.updateBtnEl.addEventListener("click", () => installEvent(true));
this.installBtnEl = this.shadowRoot.querySelector(".install");
this.installBtnEl.addEventListener("click", () => installEvent());
this.uninstallBtnEl = this.shadowRoot.querySelector(".uninstall");
this.uninstallBtnEl.addEventListener("click", async () => {
config.debug && console.log("uninstall", this.manifest.name);
currentItem = pluginList[Number(this.dataset.index)];
currentManifest = this.manifest;
showDialog({ title: "卸载", message: `确定要卸载插件 ${this.manifest.name} 吗?`, type: "confirm" }).then((e) => {
if (e) {
this.uninstallBtnEl.innerText = "卸载中...";
this.uninstallBtnEl.setAttribute("is-disabled", "");
uninstall().then((res) => {
if (res.success) {
this.uninstallBtnEl.innerText = "卸载";
this.uninstallBtnEl.removeAttribute("is-disabled");
delete this.dataset.installed;
if (config.inactivePlugins.includes(this.manifest.slug)) {
this.dataset.inactive = "0";
config.inactivePlugins = config.inactivePlugins.filter((e2) => e2 !== this.manifest.slug);
}
this.updateOpenDirEvent();
} else {
showDialog({ title: "卸载失败", message: res.message, type: "message" });
}
});
}
});
});
this.retryBtnEl = this.shadowRoot.querySelector(".retry");
this.retryBtnEl.addEventListener("click", async () => {
this.retryBtnEl.innerText = "重试中...";
this.retryBtnEl.setAttribute("is-disabled", "");
const manifest = await getManifest(pluginList[Number(this.dataset.index)]);
this.manifest = manifest;
updateElProp(this, manifest, this.dataset.failed);
this.retryBtnEl.innerText = "重试";
this.retryBtnEl.removeAttribute("is-disabled");
});
this.detailBtnEl = this.shadowRoot.querySelector(".detail");
this.detailBtnEl.addEventListener("click", async () => {
LiteLoader.api.openExternal(`https://github.com/${pluginList[Number(this.dataset.index)].repo}/tree/${pluginList[Number(this.dataset.index)].branch}`);
});
filterInput.addEventListener("input", () => this.updateHidden());
this.updateHidden();
this.updateOpenDirEvent();
this.#initialized = true;
this.#initPromiseResolve?.();
}
static get observedAttributes() {
return ["data-name", "data-version", "data-description", "data-authors", "data-icon", "data-failed", "data-type", "data-dependencies", "data-platforms"];
}
attributeChangedCallback(name, _oldValue, newValue) {
this.#initPromise.then(() => {
try {
switch (name) {
case "data-failed":
if (newValue) {
this.titleEl.innerText = newValue;
this.titleEl.style.color = "red";
this.descriptionEl.innerText = "获取失败";
} else {
this.titleEl.style.removeProperty("color");
}
break;
case "data-name":
this.titleEl.innerText = newValue || "插件名";
this.titleEl.title = newValue || "";
break;
case "data-version":
this.versionEl.innerText = newValue || "版本";
this.versionEl.title = newValue || "";
break;
case "data-description":
this.descriptionEl.innerText = newValue || "插件描述";
this.descriptionEl.title = newValue || "";
break;
case "data-authors": {
const arr = newValue === "1" ? this.manifest.authors : [];
this.authorsEl.append(
...arr.map((author) => {
const a = document.createElement("a");
a.title = author.link;
a.innerText = author.name;
a.onclick = () => LiteLoader.api.openExternal(author.link);
return a;
}).reduce((p, v, i) => {
p[i * 2] = v;
if (i)
p[i * 2 - 1] = " | ";
return p;
}, [])
);
break;
}
case "data-type":
this.typeEl.innerText = typeMap[newValue + ""] || "unknown";
break;
case "data-platforms":
this.platformsEl.title = this.platformsEl.innerText = newValue || "";
break;
case "data-dependencies": {
const arr = newValue === "1" ? this.manifest.dependencies : [];
this.dependenciesItemsEl.append(
...arr.map((e) => {
const a = document.createElement("a");
a.title = e;
a.innerText = e;
a.onclick = () => {
const item = document.getElementById(`item-${e}`);
if (item) {
item.scrollIntoView?.();
item.classList.add("highlight-item");
setTimeout(() => {
item.classList.remove("highlight-item");
}, 2e3);
}
};
return a;
}).reduce((p, v, i) => {
p[i * 2] = v;
if (i)
p[i * 2 - 1] = " | ";
return p;
}, [])
);
break;
}
case "data-icon": {
const [src, src1] = (newValue || "").split(",");
this.iconEl.src = src || defaultIcon;
let num = 0;
this.iconEl.addEventListener("error", () => {
if (src1 && num < 3) {
const iconPath = this.manifest.icon.replace(/^\.?\//, "");
switch (num) {
case 0:
this.iconEl.src = src.replace(iconPath, `src/${iconPath}`);
break;
case 1:
this.iconEl.src = src1;
break;
case 2:
this.iconEl.src = src1.replace(iconPath, `src/${iconPath}`);
break;
}
} else {
this.iconEl.src = defaultIcon;
}
num++;
});
break;
}
default:
break;
}
} catch (error) {
console.error(this.dataset.name || this.dataset.failed, error);
}
});
}
updateOpenDirEvent() {
if (this.manifest && LiteLoader.plugins[this.manifest.slug] && this.dataset.installed === "1") {
this.titleEl.title = "点击打开插件所在目录";
this.titleEl.addEventListener("click", () => {
LiteLoader.api.openPath(LiteLoader.plugins[this.manifest.slug].path.plugin);
});
this.titleEl.style.cursor = "pointer";
}
}
/**
* 过滤用,判断是否应该隐藏
*/
updateHidden() {
try {
const authors = this.dataset.authors === "1" ? this.manifest.authors : [];
const str = (this.dataset.name || "") + (this.dataset.version || "") + (this.dataset.description || "") + (this.dataset.version || "") + authors.map((e) => e.name).join("");
if (!filterInput.value || str.toLowerCase().includes(filterInput.value.toLowerCase())) {
this.hidden = false;
} else {
this.hidden = true;
}
} catch (error) {
console.error(this.manifest?.slug || this.dataset.name, error);
}
}
}
customElements.define("plugin-item", PluginListClass);
return new PluginListClass();
}
function updateElProp(el, manifest, repo) {
if (manifest) {
el.id = `item-${manifest.slug}`;
el.dataset.name = manifest.name;
el.manifest = manifest;
el.updateOpenDirEvent();
el.dataset.description = manifest.description;
el.dataset.lower4 = Number(manifest.manifest_version) >= 4 ? "" : "1";
el.dataset.authors = manifest.authors ? "1" : "";
el.dataset.platforms = manifest.platform.join("|");
el.dataset.installed = LiteLoader.plugins[manifest.slug] ? "1" : "";
el.dataset.slug = manifest.slug;
el.dataset.icon = getIconUrls(pluginList[Number(el.dataset.index)], manifest).toString();
el.dataset.defaultIcon = defaultIcon;
el.dataset.type = manifest.type;
el.dataset.dependencies = manifest.dependencies ? "1" : "";
delete el.dataset.failed;
if (LiteLoader.plugins[manifest.slug]) {
el.dataset.version = LiteLoader.plugins[manifest.slug].manifest.version;
config.debug && console.log(manifest.slug, LiteLoader.plugins[manifest.slug], manifest);
el.dataset.update = compare(manifest.version, LiteLoader.plugins[manifest.slug]?.manifest?.version ?? manifest.version, ">") ? "1" : "";
el.shadowRoot.querySelector(".newer-version").innerText = `-> ${manifest.version}`;
} else {
el.dataset.version = manifest.version;
}
if (config.inactivePlugins.includes(manifest.slug)) {
if (!LiteLoader.plugins[manifest.slug]) {
el.dataset.inactive = "1";
} else {
config.inactivePlugins = config.inactivePlugins.filter((v) => v !== manifest.slug);
}
}
} else {
el.dataset.failed = repo;
}
}
async function getList(noCache = false, again = false) {
let url;
if (config.useMirror) {
const m = getGithubMirror(!again);
url = useMirror(`https://github.com/${listUrl.repo}/raw/${listUrl.branch}/${listUrl.file}`, m || getRandomItem(originMirrors), !!m);
} else {
url = `https://github.com/${listUrl.repo}/raw/${listUrl.branch}/${listUrl.file}`;
}
return await fetchWithTimeout(url, {
cache: noCache ? "no-cache" : "default"
}).then((res) => res.status === 200 ? res.json() : null).catch((err) => {
if (again) {
console.error(`getList ${url}`, err);
return null;
} else {
console.warn(`getList ${url}`, err);
return getList(noCache, true);
}
});
}
async function getManifest(item, noCache = false, again = false) {
let url;
let m = getGithubMirror(!again);
if (config.useMirror) {
url = useMirror(`https://github.com/${item.repo}/raw/${item.branch}/manifest.json`, m || getRandomItem(originMirrors), !!m);
} else {
url = `https://github.com/${item.repo}/raw/${item.branch}/manifest.json`;
}
return await fetchWithTimeout(url, {
cache: noCache ? "no-cache" : "default"
}).then((res) => {
if (res.status === 200) {
return res.json();
} else {
m = getGithubMirror(!again);
if (config.useMirror) {
url = useMirror(`https://github.com/${item.repo}/raw/${item.branch}/package.json`, m || getRandomItem(originMirrors), !!m);
} else {
url = `https://github.com/${item.repo}/raw/${item.branch}/package.json`;
}
return fetchWithTimeout(url, {
cache: noCache ? "no-cache" : "default"
}).then(async (res1) => {
if (res1.status === 200) {
const pkg = await res1.json();
const obj = pkg.liteloader_manifest;
if (obj) {
obj.version = pkg.version;
obj.description = pkg.description;
obj.authors = typeof pkg.author === "string" ? [{ name: pkg.author, link: `https://github.com/${pkg.author}` }] : [pkg.author];
return obj;
}
}
return null;
});
}
}).catch((err) => {
if (again) {
console.error(`getManifest ${url}`, err);
return null;
} else {
console.warn(`getManifest ${url}`, err);
return getManifest(item, noCache, true);
}
});
}
async function install(release = false) {
let url;
config.debug && console.log("install", currentItem);
if (release) {
url = await getLatestReleaseUrl(currentItem);
if (!url) {
return Promise.resolve({
success: false,
message: "获取release包失败"
});
}
} else {
url = getArchiveUrl(currentItem);
}
if (config.useMirror) {
const m = getGithubMirror();
return ListViewer.getPkg(currentManifest.slug, useMirror(url, m || getRandomItem(originMirrors), !!m));
} else {
return ListViewer.getPkg(currentManifest.slug, url);
}
}
function getIconUrls(item, manifest) {
if (manifest.icon) {
const iconPath = manifest.icon.replace(/^\.?\//, "");
const m = getGithubMirror(true);
const m1 = getGithubMirror();
if (config.useMirror) {
return [
useMirror(`https://github.com/${item.repo}/raw/${item.branch}/${iconPath}`, m || getRandomItem(originMirrors), !!m),
useMirror(`https://github.com/${item.repo}/raw/${item.branch}/${iconPath}`, m1 || getRandomItem(originMirrors), !!m1)
];
} else {
return [`https://github.com/${item.repo}/raw/${item.branch}/${iconPath}`];
}
}
return [];
}
function uninstall() {
return ListViewer.removePkg(currentManifest.slug);
}
function getGithubMirror(first = false) {
if (first) {
return config.mirrors.downloadUrl[0];
}
return getRandomItem(config.mirrors.downloadUrl.splice(1));
}
function getArchiveUrl(item) {
return `https://github.com/${item.repo}/archive/refs/heads/${item.branch}.zip`;
}
async function getLatestReleaseUrl(item) {
const url = `https://api.github.com/repos/${item.repo}/releases/latest`;
const body = await fetchWithTimeout(url).then((e) => e.json()).catch((err) => {
throw new Error(`${err.message}
${url}`);
});
const zipFile = body.assets.find((asset) => asset.name.endsWith(".zip"));
return zipFile.browser_download_url;
}
export {
onSettingWindowCreated
};