[feat-994] Add support for alternative mod repos (#995)

* [feat] Add support for alternative mod repos

* Fix lint errors

* Simplify code + Fix potential issues

---------

Co-authored-by: Zagrios <40181755+Zagrios@users.noreply.github.com>
This commit is contained in:
frto027
2026-01-26 23:06:44 +08:00
committed by GitHub
parent 49ef8203c8
commit 57ca031e70
22 changed files with 260 additions and 8 deletions
+14
View File
@@ -39,3 +39,17 @@ ipc.on("bs-mods.beatmods-up", (_, reply) => {
const beatMods = BeatModsApiService.getInstance();
reply(from(beatMods.isUp()));
})
ipc.on("bs-mods.mod-repo.get-repo-list", (_, reply) => {
const beatMods = BeatModsApiService.getInstance();
reply(from(beatMods.getModRepoList()));
})
ipc.on("bs-mods.mod-repo.get-name", (_, reply) => {
const beatMods = BeatModsApiService.getInstance();
reply(from(beatMods.getSelectedModRepoAsync()));
})
ipc.on("bs-mods.mod-repo.select-name", (args, reply) => {
const beatMods = BeatModsApiService.getInstance();
reply(from(beatMods.selectModRepo(args)));
})
@@ -3,18 +3,47 @@ import { BbmFullMod, BbmMod, BbmModVersion, BbmPlatform } from "../../../shared/
import { RequestService } from "../request.service";
import { BsStore } from "../../../shared/models/bs-store.enum";
import log from "electron-log"
import { StaticConfigurationService } from "../static-configuration.service";
import { ModRepo } from "shared/models/mods/repo.model";
export class BeatModsApiService {
private static instance: BeatModsApiService;
private readonly staticConfig: StaticConfigurationService;
private readonly requestService: RequestService;
public readonly MODS_REPO_URL = "https://beatmods.com";
private readonly MODS_REPO_API_URL = `${this.MODS_REPO_URL}/api`;
private static readonly MOD_REPO_LIST:ModRepo[] = [
{
id: "beatmods",
mods_repo_url: "https://beatmods.com",
mods_repo_api_url: "https://beatmods.com/api",
display_name: "BeatMods",
website: "https://beatmods.com"
},
{
id: "beatsabercn",
mods_repo_url: "https://beatmods.bsaber.cn",
mods_repo_api_url: "https://beatmods.bsaber.cn/api",
display_name: "CN中文镜像源",
website: "https://beatmods.bsaber.cn/front/mods"
}
];
private selectedModRepo: ModRepo = BeatModsApiService.MOD_REPO_LIST.find(repo => repo.default) || BeatModsApiService.MOD_REPO_LIST[0];
public getSelectedModRepo(): ModRepo {
return this.selectedModRepo;
}
private readonly versionModsCache = new Map<string, BbmFullMod[]>();
private readonly modsHashCache = new Map<string, BbmModVersion>();
private resetCache(){
this.versionModsCache.clear();
this.modsHashCache.clear();
}
public static getInstance(): BeatModsApiService {
if (!BeatModsApiService.instance) {
BeatModsApiService.instance = new BeatModsApiService();
@@ -23,23 +52,49 @@ export class BeatModsApiService {
}
private constructor() {
this.staticConfig = StaticConfigurationService.getInstance();
this.requestService = RequestService.getInstance();
const repoId = this.staticConfig.get("selected-mod-repo");
if(repoId){
this.selectedModRepo = BeatModsApiService.MOD_REPO_LIST.find(repo => repo.id === repoId) || BeatModsApiService.MOD_REPO_LIST[0];
}
}
public async isUp(): Promise<boolean> {
try {
// The data in status can be dropped
await this.requestService.getJSON<{}>(`${this.MODS_REPO_API_URL}/status`);
await this.requestService.getJSON<{}>(`${this.getSelectedModRepo().mods_repo_api_url}/status`);
return true;
} catch (error) {
log.error("Could not connect to beatmods", error);
log.error(`Could not connect to ${this.selectedModRepo.mods_repo_api_url}`, error);
return false;
}
}
private getVersionModsUrl(version: BSVersion): string {
const platform: BbmPlatform = version.oculus || version.metadata?.store === BsStore.OCULUS ? BbmPlatform.OculusPC : BbmPlatform.SteamPC;
return `${this.MODS_REPO_API_URL}/mods?status=verified&gameVersion=${version.BSVersion}&gameName=BeatSaber&platform=${platform}`;
return `${this.getSelectedModRepo().mods_repo_api_url}/mods?status=verified&gameVersion=${version.BSVersion}&gameName=BeatSaber&platform=${platform}`;
}
public async getModRepoList():Promise<ModRepo[]>{
return BeatModsApiService.MOD_REPO_LIST;
}
public async getSelectedModRepoAsync():Promise<ModRepo>{
return this.getSelectedModRepo();
}
public async selectModRepo(repoId:string): Promise<boolean>{
const selectedRepo = BeatModsApiService.MOD_REPO_LIST.find(repo => repo.id === repoId);
if(!selectedRepo){
return false;
}
this.selectedModRepo = selectedRepo;
this.staticConfig.set("selected-mod-repo", repoId);
this.resetCache()
return true;
}
private updateModsHashCache(mods: BbmModVersion[]): void {
@@ -76,7 +131,7 @@ export class BeatModsApiService {
}
return this.requestService.getJSON<{ modVersions: BbmModVersion[] }>(
`${this.MODS_REPO_API_URL}/hashlookup?hash=${hash}`
`${this.getSelectedModRepo().mods_repo_api_url}/hashlookup?hash=${hash}`
).then(({ data }) => {
this.updateModsHashCache(data?.modVersions ?? []);
return data?.modVersions?.at(0);
@@ -128,7 +128,7 @@ export class BsModsManagerService {
}
private async downloadZip(zipUrl: string): Promise<BsmZipExtractor> {
zipUrl = new URL(zipUrl, this.beatModsApi.MODS_REPO_URL).href;
zipUrl = new URL(zipUrl, this.beatModsApi.getSelectedModRepo().mods_repo_url).href;
log.info("Download mod zip", zipUrl);
@@ -92,6 +92,7 @@ export interface StaticConfigKeyValues {
"use-system-proxy": boolean;
"last-version-launched": BSVersion;
"auto-update": AutoUpdate;
"selected-mod-repo": string;
// Linux Specific static configs
"proton-folder": string;