fully load local maps, and prepare bsaver map infos

This commit is contained in:
MathieuG-P
2022-11-06 01:38:31 +01:00
parent df4df8add1
commit 5e2dd10035
14 changed files with 200 additions and 24 deletions
+18
View File
@@ -0,0 +1,18 @@
import { ipcMain } from "electron";
import { LocalMapsManagerService } from "../services/maps/local-maps-manager.service";
import { UtilsService } from "../services/utils.service";
import { BSVersion } from "shared/bs-version.interface";
import { IpcRequest } from "shared/models/ipc";
import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
ipcMain.on('get-version-maps', (event, request: IpcRequest<BSVersion>) => {
const utilsService = UtilsService.getInstance();
const localMaps = LocalMapsManagerService.getInstance();
localMaps.getMaps(request.args).then(maps => {
utilsService.ipcSend<BsmLocalMap[]>(request.responceChannel, {success: true, data: maps});
}).catch(() => {
utilsService.ipcSend(request.responceChannel, {success: false});
})
});
+1
View File
@@ -8,3 +8,4 @@ import './supporters-ipcs';
import './launcher-ipcs';
import './window-manager-ipcs';
import './bs-mods-ipcs';
import './bs-maps-ipcs';
@@ -12,6 +12,11 @@ export class InstallationLocationService {
private readonly INSTALLATION_FOLDER = "BSManager";
private readonly VERSIONS_FOLDER = "BSInstances";
private readonly SHARED_CONTENT_FOLDER = "SharedContent";
private readonly SHARED_MAPS_FOLDER = "SharedMaps";
private readonly SHARED_PLAYLISTS_FOLDER = "SharedPlaylists";
private readonly STORE_INSTALLATION_PATH_KEY = "installation-folder";
@@ -36,9 +41,6 @@ export class InstallationLocationService {
this._installationDirectory = this.configService.get<string>(this.STORE_INSTALLATION_PATH_KEY) || app.getPath("documents");
}
public get installationDirectory(): string{ return path.join(this._installationDirectory, this.INSTALLATION_FOLDER); }
public get versionsDirectory(): string { return path.join(this.installationDirectory, this.VERSIONS_FOLDER); }
public setInstallationDirectory(newDir: string): Promise<string>{
const oldDir = this.installationDirectory;
const newDest = path.join(newDir, this.INSTALLATION_FOLDER);
@@ -53,7 +55,15 @@ export class InstallationLocationService {
log.error(err);
})
})
}
public get installationDirectory(): string{ return path.join(this._installationDirectory, this.INSTALLATION_FOLDER); }
public get versionsDirectory(): string { return path.join(this.installationDirectory, this.VERSIONS_FOLDER); }
public get sharedContentPath(): string { return path.join(this.installationDirectory, this.SHARED_CONTENT_FOLDER); }
public get sharedMapsPath(): string { return path.join(this.sharedContentPath, this.SHARED_MAPS_FOLDER); }
public get sharedPlaylistsPath(): string { return path.join(this.sharedContentPath, this.SHARED_PLAYLISTS_FOLDER); }
}
@@ -0,0 +1,91 @@
import path from "path";
import { BSVersion } from "shared/bs-version.interface";
import { RawMapInfoData } from "shared/models/maps";
import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
import { BSLocalVersionService } from "../bs-local-version.service";
import { InstallationLocationService } from "../installation-location.service";
import { UtilsService } from "../utils.service";
import crypto from "crypto";
export class LocalMapsManagerService {
private static instance: LocalMapsManagerService;
public static getInstance(): LocalMapsManagerService{
if(!LocalMapsManagerService.instance){ LocalMapsManagerService.instance = new LocalMapsManagerService(); }
return LocalMapsManagerService.instance;
}
private readonly LEVELS_ROOT_FOLDER = "Beat Saber_Data";
private readonly CUSTOM_LEVELS_FOLDER = "CustomLevels";
private readonly WIP_LEVELS_FOLDER = "CustomWIPLevels";
private readonly localVersion: BSLocalVersionService;
private readonly installLocation: InstallationLocationService;
private readonly utils: UtilsService;
private constructor(){
this.localVersion = BSLocalVersionService.getInstance();
this.installLocation = InstallationLocationService.getInstance();
this.utils = UtilsService.getInstance();
}
private async getMapsPath(version?: BSVersion): Promise<string>{
if(version){ return path.join(await this.localVersion.getVersionPath(version), this.LEVELS_ROOT_FOLDER); }
return this.installLocation.sharedMapsPath;
}
private async computeMapHash(mapPath: string, rawInfoString: string): Promise<string>{
const mapRawInfo = JSON.parse(rawInfoString);
let content = rawInfoString;
console.log("oui");
for(const set of mapRawInfo._difficultyBeatmapSets){
for(const diff of set._difficultyBeatmaps){
const diffFilePath = path.join(mapPath, diff._beatmapFilename);
if(!await this.utils.pathExist(diffFilePath)){ continue; }
const diffContent = (await this.utils.readFileAsync(diffFilePath)).toString()
content += diffContent;
}
}
const shasum = crypto.createHash("sha1");
shasum.update(content);
return shasum.digest("hex");
}
private async loadMapInfoFromPath(mapPath: string): Promise<BsmLocalMap>{
const infoFilePath = path.join(mapPath, "Info.dat");
if(!(await this.utils.pathExist(infoFilePath))){ return null; }
const rawInfoString = await (await (this.utils.readFileAsync(infoFilePath))).toString();
const rawInfo: RawMapInfoData = JSON.parse(rawInfoString);
const coverUrl = new URL(`file:///${path.join(mapPath, rawInfo._coverImageFilename)}`).href;
const songUrl = new URL(`file:///${path.join(mapPath, rawInfo._songFilename)}`).href;
const hash = await this.computeMapHash(mapPath, rawInfoString);
return {rawInfo, coverUrl, songUrl, hash};
}
public async getMaps(version?: BSVersion): Promise<BsmLocalMap[]>{
const levelsRoot = await this.getMapsPath(version);
const levelsFolder = path.join(levelsRoot, this.CUSTOM_LEVELS_FOLDER);
const levelsPath = (await this.utils.pathExist(levelsFolder)) ? this.utils.listDirsInDir(levelsFolder, true) : [];
const mapsInfo = await Promise.all(levelsPath.map(levelPath => this.loadMapInfoFromPath(levelPath)))
console.log(mapsInfo);
return mapsInfo.filter(info => !!info);
}
public deleteMap(version?: BSVersion){
}
}
+2 -2
View File
@@ -68,10 +68,10 @@ export class UtilsService{
});
}
public listDirsInDir(dirPath: string): string[]{
public listDirsInDir(dirPath: string, fullPath = false): string[]{
let files = readdirSync(dirPath, { withFileTypes:true});
files = files.filter(f => f.isDirectory())
return files.map(f => f.name);
return files.map(f => fullPath ? path.join(dirPath, f.name) : f.name);
}
public deleteFolder(folderPath: string): Promise<void>{
@@ -16,10 +16,6 @@ export function AvailableVersionsSlide(props: {year: string}) {
});
}, [])
//w-full max-w-full max-h-full flex items-start justify-center overflow-x-hidden overflow-y-scroll content-start scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900
//relative left-[2px] flex justify-center items-start content-start flex-wrap max-w-6xl
return (
<ol className="w-full flex items-start justify-center gap-6 shrink-0 content-start flex-wrap p-4 overflow-x-hidden overflow-y-scroll scrollbar-thin scrollbar-thumb-rounded-full scrollbar-thumb-neutral-900">
{availableVersions.map((version, index) =>
@@ -25,8 +25,8 @@ export function AvailableVersionsSlider() {
<div className="w-full h-fit max-h-full flex flex-col items-center grow min-h-0">
<TabNavBar className="mb-3" tabsText={availableYears} onTabChange={setSelectedYear}/>
<ol className="w-full min-h-0 flex transition-transform duration-300" style={{transform: `translate(${-(yearIndex * 100)}%, 0)`}}>
{ availableYears.map((year, index) =>
<AvailableVersionsSlide key={index} year={year}></AvailableVersionsSlide>
{ availableYears.map(year =>
<AvailableVersionsSlide key={year} year={year}/>
)}
</ol>
</div>
@@ -1,25 +1,35 @@
import { useState } from "react"
import { useObservable } from "renderer/hooks/use-observable.hook"
import { MapsManagerService } from "renderer/services/maps-manager.service"
import { BSVersion } from "shared/bs-version.interface"
import { TabNavBar } from "../shared/tab-nav-bar.component"
type Props = {
oneBlock?: boolean,
version?: BSVersion
}
export function MapsPlaylistsPanel({version}: Props) {
export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
const mapsManager = MapsManagerService.getInstance();
const [tabIndex, setTabIndex] = useState(0);
const maps = useObservable(mapsManager.getMaps(version));
console.log(tabIndex);
console.log(maps);
return (
<>
{!oneBlock && <TabNavBar className="mb-8 w-72" tabsText={["Maps", "Playlists"]} onTabChange={setTabIndex}/>}
<div className="w-full h-full bg-main-color-1 rounded-md shadow-black shadow-md overflow-hidden">
<TabNavBar className="!rounded-none shadow-sm" tabsText={["Maps", "Playlists"]} onTabChange={setTabIndex}/>
<div className="w-full h-full flex flex-row items-center transition-transform duration-300" style={{transform: `translate(${-(tabIndex * 100)}%, 0)`}}>
<div className="w-full h-full grow bg-red-300 shrink-0">a</div>
<div className="w-full h-full bg-green-300 shrink-0">b</div>
{oneBlock && <TabNavBar className="!rounded-none shadow-sm" tabsText={["Maps", "Playlists"]} onTabChange={setTabIndex}/>}
<div className="w-full h-full min-h-0 flex flex-row items-center transition-transform duration-300" style={{transform: `translate(${-(tabIndex * 100)}%, 0)`}}>
<div className="w-full h-full grow bg-red-300 shrink-0 overflow-y-scroll">
a
</div>
<div className="w-full h-full grow bg-green-300 shrink-0">b</div>
</div>
</div>
</>
)
}
+7
View File
@@ -0,0 +1,7 @@
export function splitIntoChunk<T = unknown>(arr: T[], chunkSize: number): T[][] {
const resArr = [];
for(let i = 0; i < arr.length; i += chunkSize){
resArr.push(arr.slice(i, i + chunkSize));
}
return resArr;
}
@@ -35,7 +35,7 @@ export function AvailableVersionsList() {
return (
<div className="relative h-full w-full flex items-center flex-col pt-2">
<Slideshow className="absolute w-full h-full top-0"></Slideshow>
<Slideshow className="absolute w-full h-full top-0"/>
<h1 className="text-gray-100 text-2xl mb-4 z-[1]">{t("pages.available-versions.title")}</h1>
<AvailableVersionsSlider/>
+2 -2
View File
@@ -5,8 +5,8 @@ export default function MapsPage() {
return (
<div className="w-full h-full flex items-center flex-col pt-2">
<Slideshow className="absolute w-full h-full top-0"/>
<h1 className="text-gray-100 text-2xl mb-4 z-[1]">Maps partagées</h1>
<div className="z-[1] w-full grow p-10">
<h1 className="text-gray-100 text-2xl z-[1]">Maps partagées</h1>
<div className="z-[1] w-full min-h-0 grow p-10 flex flex-col items-center justify-start">
<MapsPlaylistsPanel/>
</div>
</div>
@@ -0,0 +1,18 @@
import { BsvMapDetail } from "shared/models/maps";
export class BeatSaverService {
private static instance: BeatSaverService;
public static getInstance(): BeatSaverService{
if(!BeatSaverService.instance){ BeatSaverService.instance = new BeatSaverService(); }
return BeatSaverService.instance;
}
private constructor(){}
public getMapDetailsFromHashs(hashs: string[]): BsvMapDetail{
return null;
}
}
+13 -3
View File
@@ -1,4 +1,7 @@
import { Observable } from "rxjs";
import { BSVersion } from "shared/bs-version.interface";
import { BsmLocalMap } from "shared/models/maps/bsm-local-map.interface";
import { IpcService } from "./ipc.service";
export class MapsManagerService {
@@ -9,12 +12,19 @@ export class MapsManagerService {
return MapsManagerService.instance;
}
private constructor(){
private readonly ipcService: IpcService;
private constructor(){
this.ipcService = IpcService.getInstance();
}
public getMaps(version?: BSVersion): any[]{
return [];
public getMaps(version?: BSVersion): Observable<BsmLocalMap[]>{
return new Observable(obs => {
this.ipcService.send<BsmLocalMap[], BSVersion>("get-version-maps", {args: version}).then(res => {
if(!res.success){ return obs.next(null);}
obs.next(res.data);
});
});
}
public downloadMap(map: any, version?: BSVersion){
+15
View File
@@ -0,0 +1,15 @@
export { RawMapInfoData, RawMapDifficulty, RawDifficultySet } from "./raw-map.model";
export { ExportVersionMapsOption } from "./export-version-maps.model";
export {
BsvInstant,
BsvMapDetail,
BsvMapDetailMetadata,
BsvMapDifficulty,
BsvMapParitySummary,
BsvMapStats,
BsvMapTestplay,
BsvMapVersion,
BsvUserDetail
} from "./beat-saver.model";