mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
286 lines
9.7 KiB
JavaScript
286 lines
9.7 KiB
JavaScript
export class ResourceManager
|
|
{
|
|
constructor()
|
|
{
|
|
this.data = Resource.all;
|
|
this.skippedImport = [];
|
|
this.attributes = {};
|
|
this.styleManager = new StyleManager(this);
|
|
const styleMethods = Object.getOwnPropertyNames(StyleManager.prototype).filter(it => it !== "constructor");
|
|
for (const key of styleMethods)
|
|
{
|
|
this[key] = function (...params)
|
|
{
|
|
this.styleManager[key](...params);
|
|
};
|
|
}
|
|
this.setupColors();
|
|
}
|
|
setupColors()
|
|
{
|
|
this.color = new ColorProcessor(settings.customStyleColor);
|
|
settings.foreground = this.color.foreground;
|
|
settings.blueImageFilter = this.color.blueImageFilter;
|
|
settings.pinkImageFilter = this.color.pinkImageFilter;
|
|
settings.brightness = this.color.brightness;
|
|
settings.filterInvert = this.color.filterInvert;
|
|
|
|
const hexToRgba = input => this.color.rgbToString(this.color.hexToRgba(input));
|
|
let styles = [];
|
|
styles.push("--theme-color:" + settings.customStyleColor);
|
|
for (let opacity = 10; opacity <= 90; opacity += 10)
|
|
{
|
|
const color = this.color.hexToRgba(settings.customStyleColor);
|
|
color.a = opacity / 100;
|
|
styles.push(`--theme-color-${opacity}:` + this.color.rgbToString(color));
|
|
}
|
|
styles.push("--foreground-color:" + settings.foreground);
|
|
styles.push("--foreground-color-b:" + hexToRgba(settings.foreground + "b"));
|
|
styles.push("--foreground-color-d:" + hexToRgba(settings.foreground + "d"));
|
|
styles.push("--blue-image-filter:" + settings.blueImageFilter);
|
|
styles.push("--pink-image-filter:" + settings.pinkImageFilter);
|
|
styles.push("--brightness:" + settings.brightness);
|
|
styles.push("--invert-filter:" + settings.filterInvert);
|
|
styles.push("--blur-background-opacity:" + settings.blurBackgroundOpacity);
|
|
styles.push("--custom-control-background-opacity:" + settings.customControlBackgroundOpacity);
|
|
this.applyStyleFromText(`<style id="bilibili-evolved-variables">html{${styles.join(";")}}</style>`);
|
|
}
|
|
resolveComponentName(componentName)
|
|
{
|
|
const keyword = "/" + componentName.replace("./", "") + ".min.js";
|
|
for (const [name, value] of Object.entries(Resource.all))
|
|
{
|
|
if (value.url.endsWith(keyword))
|
|
{
|
|
return name;
|
|
}
|
|
}
|
|
return componentName;
|
|
}
|
|
resolveComponent(componentName)
|
|
{
|
|
const resource = Resource.all[this.resolveComponentName(componentName)];
|
|
if (!resource)
|
|
{
|
|
this.skippedImport.push(componentName);
|
|
}
|
|
return resource;
|
|
}
|
|
importAsync(componentName)
|
|
{
|
|
return new Promise(resolve =>
|
|
{
|
|
const resource = this.resolveComponent(componentName);
|
|
if (!resource)
|
|
{
|
|
resolve(unsafeWindow.bilibiliEvolved);
|
|
}
|
|
if (!resource.downloaded)
|
|
{
|
|
this.fetchByKey(resource.key).then(() => resolve(this.import(componentName)));
|
|
}
|
|
else
|
|
{
|
|
resolve(this.import(componentName));
|
|
}
|
|
});
|
|
}
|
|
import(componentName)
|
|
{
|
|
const resource = this.resolveComponent(componentName);
|
|
if (!resource)
|
|
{
|
|
return unsafeWindow.bilibiliEvolved;
|
|
}
|
|
if (resource.type.name === "html" || resource.type.name === "style")
|
|
{
|
|
if (!resource.downloaded)
|
|
{
|
|
console.error(`Import failed: component "${componentName}" is not loaded.`);
|
|
return null;
|
|
}
|
|
return resource.text;
|
|
}
|
|
else
|
|
{
|
|
const attribute = this.attributes[this.resolveComponentName(componentName)];
|
|
if (attribute === undefined)
|
|
{
|
|
console.error(`Import failed: component "${componentName}" is not loaded.`);
|
|
return null;
|
|
}
|
|
return attribute.export;
|
|
}
|
|
}
|
|
async fetchByKey(key)
|
|
{
|
|
const resource = Resource.all[key];
|
|
if (!resource)
|
|
{
|
|
return null;
|
|
}
|
|
const text = await resource.download().catch(reason =>
|
|
{
|
|
console.error(`Download error, XHR status: ${reason}`);
|
|
let toastMessage = `无法下载组件<span>${Resource.all[key].displayName}</span>`;
|
|
if (settings.toastInternalError)
|
|
{
|
|
toastMessage += "\n" + reason;
|
|
}
|
|
Toast.error(toastMessage, "错误");
|
|
});
|
|
await Promise.all(resource.dependencies
|
|
.filter(it => it.type.name === "style")
|
|
.map(it => this.styleManager.fetchStyleByKey(it.key)));
|
|
await Promise.all(resource.dependencies
|
|
.filter(it => it.type.name === "script")
|
|
.map(it => this.fetchByKey(it.key)));
|
|
this.applyComponent(key, text);
|
|
}
|
|
async fetch()
|
|
{
|
|
const isCacheValid = this.validateCache();
|
|
let loadingToast = null;
|
|
if (settings.toast === true)
|
|
{
|
|
await this.fetchByKey("toast");
|
|
unsafeWindow.bilibiliEvolved.Toast = Toast = this.attributes.toast.export.Toast || this.attributes.toast.export;
|
|
if (!isCacheValid && settings.useCache)
|
|
{
|
|
loadingToast = Toast.info(/*html*/`<div class="loading"></div>正在初始化脚本`, "初始化");
|
|
}
|
|
}
|
|
const promises = [];
|
|
for (const key in settings)
|
|
{
|
|
if (settings[key] === true && key !== "toast")
|
|
{
|
|
const promise = this.fetchByKey(key);
|
|
if (promise)
|
|
{
|
|
promises.push(promise);
|
|
}
|
|
}
|
|
}
|
|
await Promise.all(promises);
|
|
saveSettings(settings);
|
|
if (loadingToast)
|
|
{
|
|
loadingToast.dismiss();
|
|
}
|
|
await this.applyDropdownOptions();
|
|
this.applyWidgets();
|
|
}
|
|
applyComponent(key, text)
|
|
{
|
|
const func = eval(text);
|
|
if (func)
|
|
{
|
|
try
|
|
{
|
|
const attribute = func(settings, this) || {};
|
|
this.attributes[key] = attribute;
|
|
}
|
|
catch (error)
|
|
{
|
|
console.error(`Failed to apply feature "${key}": ${error}`);
|
|
let toastMessage = `加载组件<span>${Resource.all[key].displayName}</span>失败`;
|
|
if (settings.toastInternalError)
|
|
{
|
|
toastMessage += "\n" + error;
|
|
}
|
|
Toast.error(toastMessage, "错误");
|
|
}
|
|
}
|
|
}
|
|
async applyWidget(info)
|
|
{
|
|
let condition = true;
|
|
if (typeof info.condition === "function")
|
|
{
|
|
condition = info.condition();
|
|
if (condition instanceof Promise)
|
|
{
|
|
condition = await condition.catch(() => { return false; });
|
|
}
|
|
}
|
|
if (condition === true)
|
|
{
|
|
if (info.content)
|
|
{
|
|
document.querySelector(".widgets-container").insertAdjacentHTML("beforeend", info.content);
|
|
}
|
|
if (info.success)
|
|
{
|
|
info.success();
|
|
}
|
|
}
|
|
}
|
|
async applyWidgets()
|
|
{
|
|
await Promise.all(Object.values(this.attributes)
|
|
.filter(it => it.widget)
|
|
.map(it => this.applyWidget(it.widget))
|
|
);
|
|
}
|
|
async applyDropdownOptions()
|
|
{
|
|
async function applyDropdownOption(info)
|
|
{
|
|
if (Array.isArray(info))
|
|
{
|
|
await Promise.all(info.map(applyDropdownOption));
|
|
}
|
|
else
|
|
{
|
|
const dropdownInput = await SpinQuery.select(`.gui-settings-dropdown input[key=${info.key}]`);
|
|
const dropdown = dropdownInput.parentElement;
|
|
const list = dropdown.querySelector("ul");
|
|
const input = dropdown.querySelector("input");
|
|
info.items.forEach(itemHtml =>
|
|
{
|
|
list.insertAdjacentHTML("beforeend", `<li>${itemHtml}</li>`);
|
|
});
|
|
list.querySelectorAll("li").forEach(li => li.addEventListener("click", () =>
|
|
{
|
|
input.value = li.innerText;
|
|
raiseEvent(input, "input");
|
|
raiseEvent(input, "change");
|
|
}));
|
|
}
|
|
}
|
|
const manifests = Object.values(Resource.manifest).filter(it => it.dropdown).map(it => it.dropdown);
|
|
Object.values(Resource.all).filter(it => it.dropdown).map(it => it.dropdown).forEach(it =>
|
|
{
|
|
if (!manifests.some(m => m.key === it.key))
|
|
{
|
|
manifests.push(it);
|
|
}
|
|
});
|
|
await Promise.all(manifests.map(it => applyDropdownOption(it)));
|
|
}
|
|
validateCache()
|
|
{
|
|
if (typeof offlineData !== "undefined") // offline version always has cache
|
|
{
|
|
return true;
|
|
}
|
|
if (Object.getOwnPropertyNames(settings.cache).length === 0) // has no cache
|
|
{
|
|
return false;
|
|
}
|
|
if (settings.cache.version === undefined) // Has newly downloaded cache
|
|
{
|
|
settings.cache.version = settings.currentVersion;
|
|
saveSettings(settings);
|
|
return true;
|
|
}
|
|
if (settings.cache.version !== settings.currentVersion) // Has old version cache
|
|
{
|
|
settings.cache = {};
|
|
saveSettings(settings);
|
|
return false;
|
|
}
|
|
return true; // Has cache
|
|
}
|
|
} |