mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge pull request #508 from Zagrios/qol/mods-load-faster
[QOL] Mods load faster
(cherry picked from commit a6c1ba9fee)
+ init migration to BadBeatMods
This commit is contained in:
@@ -7,14 +7,11 @@ export class BeatModsApiService {
|
||||
|
||||
private readonly requestService: RequestService;
|
||||
|
||||
private readonly BEAT_MODS_VERSIONS = "https://versions.beatmods.com/versions.json";
|
||||
private readonly BEAT_MODS_ALIAS = "https://alias.beatmods.com/aliases.json";
|
||||
public readonly MODS_REPO_URL = "https://bbm.saera.gay";
|
||||
private readonly BEAT_MODS_API_URL = `${this.MODS_REPO_URL}/api`;
|
||||
|
||||
private readonly BEAT_MODS_API_URL = "https://beatmods.com/api/v1/";
|
||||
public readonly BEAT_MODS_URL = "https://beatmods.com";
|
||||
|
||||
private readonly aliasesCache = new Map<string, BSVersion[]>();
|
||||
private readonly versionModsCache = new Map<string, Mod[]>();
|
||||
private readonly modsHashCache = new Map<string, Mod>();
|
||||
|
||||
private allModsCache: Mod[];
|
||||
|
||||
@@ -29,37 +26,12 @@ export class BeatModsApiService {
|
||||
this.requestService = RequestService.getInstance();
|
||||
}
|
||||
|
||||
private convertToBeatModsMod(mod: Mod): Mod {
|
||||
|
||||
}
|
||||
|
||||
private getVersionModsUrl(version: BSVersion): string {
|
||||
return `${this.BEAT_MODS_API_URL}mod?status=approved&gameVersion=${version.BSVersion}&sort=&sortDirection=1`;
|
||||
}
|
||||
|
||||
private getAllModsUrl(): string {
|
||||
return `${this.BEAT_MODS_API_URL}mod`;
|
||||
}
|
||||
|
||||
private async getVersionAlias(): Promise<Map<string, BSVersion[]>> {
|
||||
if (this.aliasesCache.size) {
|
||||
return this.aliasesCache;
|
||||
}
|
||||
return this.requestService.getJSON<Record<string, string[]>>(this.BEAT_MODS_ALIAS).then(rawAliases => {
|
||||
Object.entries(rawAliases).forEach(([key, value]) => {
|
||||
this.aliasesCache.set(
|
||||
key,
|
||||
value.map(s => ({ BSVersion: s } as BSVersion))
|
||||
);
|
||||
});
|
||||
return this.aliasesCache;
|
||||
});
|
||||
}
|
||||
|
||||
private async getAliasOfVersion(version: BSVersion): Promise<BSVersion> {
|
||||
return this.getVersionAlias().then(aliases => {
|
||||
if (Array.from(aliases.keys()).some(k => k === version.BSVersion)) {
|
||||
return version;
|
||||
}
|
||||
const alias = Array.from(aliases.entries()).find(([key, value]) => value.find(v => v.BSVersion === version.BSVersion))?.[0];
|
||||
return { BSVersion: alias } as BSVersion;
|
||||
});
|
||||
return `${this.BEAT_MODS_API_URL}/mods?status=verified&gameVersion=${version.BSVersion}`;
|
||||
}
|
||||
|
||||
private asignDependencies(mod: Mod, mods: Mod[]): Mod {
|
||||
@@ -67,27 +39,52 @@ export class BeatModsApiService {
|
||||
return mod;
|
||||
}
|
||||
|
||||
private updateModsHashCache(mods: Mod[]): void {
|
||||
|
||||
if(!Array.isArray(mods)){
|
||||
return;
|
||||
}
|
||||
|
||||
for (const mod of mods) {
|
||||
for (const downloads of (mod.downloads ?? [])) {
|
||||
for (const hashMd5 of (downloads.hashMd5 ?? [])) {
|
||||
this.modsHashCache.set(hashMd5.hash, mod);
|
||||
}
|
||||
}
|
||||
|
||||
for (const dep of (mod.dependencies ?? [])) {
|
||||
for (const downloads of (dep.downloads ?? [])) {
|
||||
for (const hashMd5 of (downloads.hashMd5 ?? [])) {
|
||||
this.modsHashCache.set(hashMd5.hash, dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async getVersionMods(version: BSVersion): Promise<Mod[]> {
|
||||
if (this.versionModsCache.has(version.BSVersion)) {
|
||||
return this.versionModsCache.get(version.BSVersion);
|
||||
}
|
||||
|
||||
const alias = await this.getAliasOfVersion(version);
|
||||
|
||||
return this.requestService.getJSON<Mod[]>(this.getVersionModsUrl(alias)).then(mods => {
|
||||
return this.requestService.getJSON<Mod[]>(this.getVersionModsUrl(version)).then(mods => {
|
||||
mods = mods.map(mod => this.asignDependencies(mod, mods));
|
||||
this.versionModsCache.set(version.BSVersion, mods);
|
||||
|
||||
this.updateModsHashCache(mods);
|
||||
|
||||
return mods;
|
||||
});
|
||||
}
|
||||
|
||||
public async getAllMods(): Promise<Mod[]> {
|
||||
if (this.allModsCache) {
|
||||
return this.allModsCache;
|
||||
public getModByHash(hash: string): Promise<Mod> {
|
||||
if (this.modsHashCache.has(hash)) {
|
||||
return Promise.resolve(this.modsHashCache.get(hash));
|
||||
}
|
||||
return this.requestService.getJSON<Mod[]>(this.getAllModsUrl()).then(mods => {
|
||||
this.allModsCache = mods;
|
||||
return this.allModsCache;
|
||||
|
||||
return this.requestService.getJSON<Mod[]>(`${this.BEAT_MODS_API_URL}/hashlookup?hash=${hash}`).then(mods => {
|
||||
this.updateModsHashCache(mods);
|
||||
return mods.at(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,23 +49,14 @@ export class BsModsManagerService {
|
||||
}
|
||||
|
||||
private async getModFromHash(hash: string): Promise<Mod> {
|
||||
const allMods = await this.beatModsApi.getAllMods();
|
||||
return allMods.find(mod => {
|
||||
if (mod.name.toLowerCase() === "bsipa") {
|
||||
return false;
|
||||
}
|
||||
return mod.downloads.some(download => download.hashMd5.some(md5 => md5.hash === hash));
|
||||
});
|
||||
}
|
||||
|
||||
private async getIpaFromHash(hash: string): Promise<Mod> {
|
||||
const allMods = await this.beatModsApi.getAllMods();
|
||||
return allMods.find(mod => {
|
||||
if (mod.name.toLowerCase() !== "bsipa") {
|
||||
return false;
|
||||
}
|
||||
return mod.downloads.some(download => download.hashMd5.some(md5 => md5.hash === hash));
|
||||
});
|
||||
const mod = await this.beatModsApi.getModByHash(hash);
|
||||
|
||||
if(mod?.name?.toLowerCase() === "bsipa"){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
private async getModsInDir(version: BSVersion, modsDir: ModsInstallFolder): Promise<Mod[]> {
|
||||
@@ -85,7 +76,6 @@ export class BsModsManagerService {
|
||||
return undefined;
|
||||
}
|
||||
const hash = await md5File(filePath);
|
||||
|
||||
const mod = await this.getModFromHash(hash);
|
||||
|
||||
if (!mod) {
|
||||
@@ -111,7 +101,7 @@ export class BsModsManagerService {
|
||||
});
|
||||
|
||||
const mods = await Promise.all(promises);
|
||||
return mods.filter(Boolean);
|
||||
return mods.filter(Boolean);
|
||||
}
|
||||
|
||||
private async getBsipaInstalled(version: BSVersion): Promise<Mod> {
|
||||
@@ -121,19 +111,18 @@ export class BsModsManagerService {
|
||||
return undefined;
|
||||
}
|
||||
const injectorMd5 = await md5File(injectorPath);
|
||||
return this.getIpaFromHash(injectorMd5);
|
||||
return this.beatModsApi.getModByHash(injectorMd5);
|
||||
}
|
||||
|
||||
private async downloadZip(zipUrl: string): Promise<BsmZipExtractor> {
|
||||
zipUrl = path.join(this.beatModsApi.BEAT_MODS_URL, zipUrl);
|
||||
zipUrl = path.join(this.beatModsApi.MODS_REPO_URL, zipUrl);
|
||||
|
||||
log.info("Download mod zip", zipUrl);
|
||||
|
||||
const buffer = await lastValueFrom(this.requestService.downloadBuffer(zipUrl))
|
||||
.then(progress => progress.data)
|
||||
.catch(e => {
|
||||
.catch((e: Error) => {
|
||||
log.error("ZIP", "Error while downloading zip", e);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (!buffer) {
|
||||
@@ -325,7 +314,7 @@ export class BsModsManagerService {
|
||||
|
||||
public async getInstalledMods(version: BSVersion): Promise<Mod[]> {
|
||||
this.manifestMatches = [];
|
||||
await this.beatModsApi.getAllMods();
|
||||
|
||||
const bsipa = await this.getBsipaInstalled(version);
|
||||
|
||||
const pluginsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.PLUGINS), this.getModsInDir(version, ModsInstallFolder.PLUGINS_PENDING)]);
|
||||
|
||||
@@ -34,3 +34,77 @@ export interface FileHashes {
|
||||
hash: string;
|
||||
file: string;
|
||||
}
|
||||
|
||||
// BBM MOD
|
||||
|
||||
export interface BbmModVersion {
|
||||
id: number;
|
||||
modId: number;
|
||||
author: BbmUserAPIResponse;
|
||||
modVersion: string;
|
||||
platform: BbmPlatform;
|
||||
zipHash: string;
|
||||
status: BbmStatus;
|
||||
dependencies: number[];
|
||||
contentHashes: BbmContentHash[];
|
||||
supportedGameVersions: BbmGameVersion[];
|
||||
downloadCount: number;
|
||||
lastApprovedById?: number;
|
||||
lastUpdatedById?: number;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export interface BbmContentHash {
|
||||
path: string;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export enum BbmStatus {
|
||||
Private = "private",
|
||||
Removed = "removed",
|
||||
Unverified = "unverified",
|
||||
Verified = "verified",
|
||||
}
|
||||
|
||||
export enum BbmPlatform {
|
||||
SteamPC = `steampc`,
|
||||
OculusPC = `oculuspc`,
|
||||
UniversalPC = `universalpc`,
|
||||
UniversalQuest = `universalquest`,
|
||||
}
|
||||
|
||||
export interface BbmGameVersion {
|
||||
readonly id: number;
|
||||
gameName: "BeatSaber";
|
||||
version: string; // semver
|
||||
defaultVersion: boolean;
|
||||
}
|
||||
|
||||
export enum BbmCategories {
|
||||
Core = "core",
|
||||
Essential = "essential",
|
||||
Library = "library",
|
||||
Cosmetic = "cosmetic",
|
||||
PracticeTraining = "practice",
|
||||
Gameplay = "gameplay",
|
||||
StreamTools = "streamtools",
|
||||
UIEnhancements = "ui",
|
||||
Lighting = "lighting",
|
||||
TweaksTools = "tweaks",
|
||||
Multiplayer = "multiplayer",
|
||||
TextChanges = "text",
|
||||
Editor = "editor",
|
||||
Other = "other",
|
||||
}
|
||||
|
||||
export interface BbmUserAPIResponse {
|
||||
id: number;
|
||||
username: string;
|
||||
githubId: string;
|
||||
sponsorUrl: string;
|
||||
displayName: string;
|
||||
bio: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user