mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c264684ec6 | |||
| aa32f4e2f4 | |||
| bfafe0e4b1 | |||
| a8c36a5e7c | |||
| de3284922a | |||
| d018ac5c9a | |||
| e69983d6a7 | |||
| 2a8f4feea8 | |||
| b08aa4c1d9 | |||
| c0e169cead | |||
| 99f087a89d | |||
| e0fd024314 | |||
| 599e04a96f | |||
| 77b838187c | |||
| 0ce2b9493a | |||
| 8124577728 | |||
| 2e5929c74c | |||
| a4908ef850 | |||
| cf0fa2b787 |
@@ -541,5 +541,13 @@
|
||||
"ReleaseImg": "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/clans/32055887/9c3d476c03e4a2ac72ca5df016b7c94d242b41cb.png",
|
||||
"ReleaseDate": "1683644991",
|
||||
"year": "2023"
|
||||
},
|
||||
{
|
||||
"BSVersion": "1.30.0",
|
||||
"BSManifest": "7304715857122259127",
|
||||
"ReleaseURL": "https://steamcommunity.com/games/620980/announcements/detail/3725085394628677636",
|
||||
"ReleaseImg": "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/clans/32055887/cd56688ad716b544af14a61b2fbace9088478887.png",
|
||||
"ReleaseDate": "1684965390",
|
||||
"year": "2023"
|
||||
}
|
||||
]
|
||||
@@ -6,5 +6,7 @@
|
||||
{"username": "Carbon", "type": "gold"},
|
||||
{"username": "Naysy"},
|
||||
{"username": "Falmil"},
|
||||
{"username": "K1Lc4m"}
|
||||
{"username": "K1Lc4m"},
|
||||
{"username": "Adam"},
|
||||
{"username": "Cabalia"}
|
||||
]
|
||||
@@ -472,7 +472,8 @@
|
||||
},
|
||||
"password": {
|
||||
"label": "Passwort",
|
||||
"placeholder": "Gebe dein Passwort ein"
|
||||
"placeholder": "Gebe dein Passwort ein",
|
||||
"max-length-warning": "Das Passwort überschreitet 64 Zeichen! Wenn das Passwort ungültig ist, versuchen Sie nur die ersten 64 Zeichen einzugeben."
|
||||
},
|
||||
"stay": {
|
||||
"label": "Verbunden bleiben"
|
||||
|
||||
@@ -472,7 +472,8 @@
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"placeholder": "Enter your password"
|
||||
"placeholder": "Enter your password",
|
||||
"max-length-warning": "The password exceeds 64 characters! If the password is invalid, try to put its first 64 characters."
|
||||
},
|
||||
"stay": {
|
||||
"label": "Stay connected"
|
||||
|
||||
@@ -471,7 +471,8 @@
|
||||
},
|
||||
"password": {
|
||||
"label": "Contraseña",
|
||||
"placeholder": "Introduzca su contraseña"
|
||||
"placeholder": "Introduzca su contraseña",
|
||||
"max-length-warning": "¡La contraseña supera los 64 caracteres! Si la contraseña es inválida, intenta ingresar solo sus primeros 64 caracteres."
|
||||
},
|
||||
"stay": {
|
||||
"label": "Manténgase conectado"
|
||||
|
||||
@@ -471,7 +471,8 @@
|
||||
},
|
||||
"password": {
|
||||
"label": "Mot de passe",
|
||||
"placeholder": "Entre ton mot de passe"
|
||||
"placeholder": "Entre ton mot de passe",
|
||||
"max-length-warning": "Le mot de passe dépasse 64 caractères ! Si le mot de passe est invalide, tente d'entrer seulement ses 64 premiers caractères."
|
||||
},
|
||||
"stay": {
|
||||
"label": "Rester connecté"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bs-manager",
|
||||
"version": "1.2.0-alpha.1",
|
||||
"version": "1.2.0-alpha.3",
|
||||
"description": "A foundation for scalable desktop apps",
|
||||
"main": "./dist/main/main.js",
|
||||
"author": {
|
||||
|
||||
@@ -90,6 +90,8 @@ export class BSInstallerService{
|
||||
|
||||
public async downloadBsVersion(downloadInfos: DownloadInfo): Promise<DownloadEvent>{
|
||||
|
||||
// TODO : Can be a lot improved by using ipcV2 and Observable
|
||||
|
||||
if(this.downloadProcess && this.downloadProcess.connected){ throw "AlreadyDownloading"; }
|
||||
const {bsVersion} = downloadInfos;
|
||||
if(!bsVersion){ return {type: "[Error]"}; }
|
||||
|
||||
@@ -30,7 +30,7 @@ export class BSVersionLibService{
|
||||
}
|
||||
|
||||
private getRemoteVersions(): Promise<BSVersion[]>{
|
||||
return this.requestService.get<BSVersion[]>(this.REMOTE_BS_VERSIONS_URL);
|
||||
return this.requestService.getJSON<BSVersion[]>(this.REMOTE_BS_VERSIONS_URL);
|
||||
}
|
||||
|
||||
private async getLocalVersions(): Promise<BSVersion[]>{
|
||||
|
||||
@@ -38,7 +38,7 @@ export class BeatModsApiService {
|
||||
|
||||
private async getVersionAlias(): Promise<Map<string, BSVersion[]>>{
|
||||
if(this.aliasesCache.size){ return this.aliasesCache; }
|
||||
return this.requestService.get<Record<string, string[]>>(this.BEAT_MODS_ALIAS).then(rawAliases => {
|
||||
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)));
|
||||
});
|
||||
@@ -64,7 +64,7 @@ export class BeatModsApiService {
|
||||
|
||||
const alias = await this.getAliasOfVersion(version);
|
||||
|
||||
return this.requestService.get<Mod[]>(this.getVersionModsUrl(alias)).then(mods => {
|
||||
return this.requestService.getJSON<Mod[]>(this.getVersionModsUrl(alias)).then(mods => {
|
||||
mods = mods.map(mod => this.asignDependencies(mod, mods));
|
||||
this.versionModsCache.set(version.BSVersion, mods);
|
||||
return mods;
|
||||
@@ -73,16 +73,10 @@ export class BeatModsApiService {
|
||||
|
||||
public async getAllMods(): Promise<Mod[]>{
|
||||
if(!!this.allModsCache){ return this.allModsCache; }
|
||||
return this.requestService.get<Mod[]>(this.getAllModsUrl()).then(mods => {
|
||||
return this.requestService.getJSON<Mod[]>(this.getAllModsUrl()).then(mods => {
|
||||
this.allModsCache = mods;
|
||||
return this.allModsCache;
|
||||
});
|
||||
}
|
||||
|
||||
public loadAllMods(){ return this.getAllMods(); }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -48,7 +48,7 @@ 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" || mod.status === "declined"){ return false; }
|
||||
if(mod.name.toLowerCase() === "bsipa"){ return false; }
|
||||
return mod.downloads.some(download => download.hashMd5.some(md5 => md5.hash === hash));
|
||||
})
|
||||
}
|
||||
@@ -245,7 +245,7 @@ export class BsModsManagerService {
|
||||
|
||||
public async getInstalledMods(version: BSVersion): Promise<Mod[]>{
|
||||
this.manifestMatches = [];
|
||||
await this.beatModsApi.loadAllMods();
|
||||
await this.beatModsApi.getAllMods();
|
||||
const bsipa = await this.getBsipaInstalled(version);
|
||||
return Promise.all([
|
||||
this.getModsInDir(version, ModsInstallFolder.PLUGINS_PENDING),
|
||||
@@ -253,11 +253,16 @@ export class BsModsManagerService {
|
||||
this.getModsInDir(version, ModsInstallFolder.PLUGINS),
|
||||
this.getModsInDir(version, ModsInstallFolder.LIBS)
|
||||
]).then(dirMods => {
|
||||
const res = [];
|
||||
if(!!bsipa){ res.push(bsipa); }
|
||||
const installedMods = Array.from(new Map<string, Mod>(dirMods.flat().map(m => [m.name, m])).values());
|
||||
res.push(...installedMods);
|
||||
return res;
|
||||
const modsDict = new Map<string, Mod>();
|
||||
|
||||
if(!!bsipa){ modsDict.set(bsipa.name, bsipa); }
|
||||
|
||||
for(const mod of dirMods.flat()){
|
||||
if(modsDict.has(mod.name)){ continue; }
|
||||
modsDict.set(mod.name, mod);
|
||||
}
|
||||
|
||||
return Array.from(modsDict.values());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { RequestOptions, get } from "https";
|
||||
import { get } from "https";
|
||||
import { createWriteStream, unlink } from "fs";
|
||||
import { Progression, unlinkPath } from "main/helpers/fs.helpers";
|
||||
import { Observable, buffer, shareReplay, tap } from "rxjs";
|
||||
import { Progression } from "main/helpers/fs.helpers";
|
||||
import { Observable, shareReplay, tap } from "rxjs";
|
||||
import log from "electron-log";
|
||||
import fetch, { RequestInfo, RequestInit } from "node-fetch";
|
||||
|
||||
export class RequestService {
|
||||
|
||||
@@ -15,18 +16,20 @@ export class RequestService {
|
||||
|
||||
private constructor(){}
|
||||
|
||||
public get<T = any>(options: string|RequestOptions): Promise<T>{
|
||||
public async getJSON<T = unknown>(url: RequestInfo, options?: RequestInit): Promise<T>{
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let body = ''
|
||||
get(options, (res) => {
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => {
|
||||
resolve(JSON.parse(body));
|
||||
});
|
||||
res.on('error', (err) => reject(err))
|
||||
}).on("error", err => reject(err));
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status} ${response}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
catch (err) {
|
||||
log.error(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
public downloadFile(url: string, dest: string): Observable<Progression<string>>{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { writeFileSync } from "fs";
|
||||
import { readJSON } from "fs-extra";
|
||||
import { get } from "https";
|
||||
import isOnline from "is-online";
|
||||
import path from "path";
|
||||
import { Supporter } from "shared/models/supporters/supporter.interface";
|
||||
import { UtilsService } from "./utils.service";
|
||||
import { RequestService } from "./request.service";
|
||||
import { allSettled } from "../../shared/helpers/promise.helpers";
|
||||
|
||||
export class SupportersService {
|
||||
|
||||
@@ -16,6 +16,8 @@ export class SupportersService {
|
||||
private cache: Supporter[];
|
||||
|
||||
private readonly utilsService: UtilsService;
|
||||
private readonly requestService: RequestService;
|
||||
|
||||
|
||||
public static getInstance(): SupportersService{
|
||||
if(!SupportersService.instance){ SupportersService.instance = new SupportersService(); }
|
||||
@@ -24,6 +26,7 @@ export class SupportersService {
|
||||
|
||||
private constructor(){
|
||||
this.utilsService = UtilsService.getInstance();
|
||||
this.requestService = RequestService.getInstance();
|
||||
}
|
||||
|
||||
private async updateLocalSupporters(supporters: Supporter[]): Promise<void>{
|
||||
@@ -31,15 +34,8 @@ export class SupportersService {
|
||||
writeFileSync(patreonsPath, JSON.stringify(supporters, null, "\t"), {encoding: 'utf-8', flag: 'w'});
|
||||
}
|
||||
|
||||
private async getRemoteSupporters(): Promise<Supporter[]>{
|
||||
return new Promise<Supporter[]>((resolve, reject) => {
|
||||
let body = ''
|
||||
get(this.PATREONS_URL, (res) => {
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => resolve(JSON.parse(body)));
|
||||
res.on('error', () => reject(null))
|
||||
})
|
||||
})
|
||||
private getRemoteSupporters(): Promise<Supporter[]>{
|
||||
return this.requestService.getJSON(this.PATREONS_URL);
|
||||
}
|
||||
|
||||
private async getLocalSupporters(): Promise<Supporter[]>{
|
||||
@@ -50,11 +46,9 @@ export class SupportersService {
|
||||
private async loadSupporters(): Promise<Supporter[]>{
|
||||
if(!!this.cache && this.cache.length){ return this.cache; }
|
||||
|
||||
const isOnlineRes = await isOnline({timeout: 1500});
|
||||
|
||||
const [localVersions, remoteVersions] = await Promise.all([
|
||||
this.getLocalSupporters(), (isOnlineRes && this.getRemoteSupporters())
|
||||
]);
|
||||
const [localVersions, remoteVersions] = await allSettled([
|
||||
this.getLocalSupporters(), this.getRemoteSupporters()
|
||||
], {keepStructure: true});
|
||||
|
||||
const supporters = remoteVersions?.length ? remoteVersions : localVersions;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export const LoginModal: ModalComponent<{username: string, password: string, sta
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={(e) => {e.preventDefault(); loggin();}}>
|
||||
<form className="max-w-xs" onSubmit={(e) => {e.preventDefault(); loggin();}}>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.steam-login.title")}</h1>
|
||||
<BsmImage className="mx-auto h-20" image={BeatImpatient} />
|
||||
|
||||
@@ -33,9 +33,12 @@ export const LoginModal: ModalComponent<{username: string, password: string, sta
|
||||
<label className="block font-bold cursor-pointer tracking-wide text-gray-800 dark:text-gray-200" htmlFor="username">{t("modals.steam-login.inputs.username.label")}</label>
|
||||
<input className="w-full bg-light-main-color-1 dark:bg-main-color-1 px-1 py-[2px] rounded-md outline-none" onChange={e => setUsername(e.target.value)} value={username} type="text" name="username" id="username" placeholder={t("modals.steam-login.inputs.username.placeholder")}/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<div className="mb-2 flex flex-col w-full">
|
||||
<label className="block font-bold cursor-pointer tracking-wide text-gray-800 dark:text-gray-200" htmlFor="password">{t("modals.steam-login.inputs.password.label")}</label>
|
||||
<input className="w-full bg-light-main-color-1 dark:bg-main-color-1 px-1 py-[2px] rounded-md outline-none" onChange={e => setPassword(e.target.value)} value={password} type="password" name="password" id="password" placeholder={t("modals.steam-login.inputs.password.placeholder")}/>
|
||||
{password?.length > 64 && (
|
||||
<span className="text-orange-700 dark:text-orange-400 text-xs whitespace-normal max-w-full w-full overflow-hidden">{t("modals.steam-login.inputs.password.max-length-warning")}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span onClick={whyCredentials} className="underline my-4 block cursor-pointer">{t("modals.steam-login.why-credentials")}</span>
|
||||
|
||||
@@ -102,7 +102,8 @@ function modelItem<T = unknown>(props: Props<T>) {
|
||||
<GlowEffect visible={props.selected || hovered}/>
|
||||
<div className="absolute top-0 left-0 w-full h-full rounded-lg overflow-hidden blur-none bg-black shadow-sm shadow-black">
|
||||
<div ref={ref} className="contents">
|
||||
<BsmImage className={`absolute top-0 left-0 w-full h-full object-cover ${props.type === MSModelType.Avatar ? "object-top" : ""}`} image={thumbnailUrl} placeholder={defaultImage} loading="lazy"/>
|
||||
<BsmImage className={`absolute top-0 left-0 w-full h-full object-cover brightness-50 scale-[200%] blur-md`} image={thumbnailUrl} placeholder={defaultImage} loading="lazy"/>
|
||||
<BsmImage className={`absolute top-0 left-1/2 -translate-x-1/2 max-w-[20rem] w-full h-full object-cover`} image={thumbnailUrl} placeholder={defaultImage} loading="lazy"/>
|
||||
<div className="absolute top-0 right-0 h-full w-0 flex flex-col items-end gap-1 pt-1.5 pr-1.5">
|
||||
{!props.isDownloading ? (
|
||||
actionButtons().map((button, index) => (
|
||||
|
||||
Reference in New Issue
Block a user