diff --git a/src/main/ipcs/bs-maps-ipcs.ts b/src/main/ipcs/bs-maps-ipcs.ts index ae435007..4b18294b 100644 --- a/src/main/ipcs/bs-maps-ipcs.ts +++ b/src/main/ipcs/bs-maps-ipcs.ts @@ -61,6 +61,17 @@ ipcMain.on("delete-maps", async (event, request: IpcRequest<{version: BSVersion, }); }); +ipcMain.on("export-maps", async (event, request: IpcRequest<{version: BSVersion, maps: BsmLocalMap[], outPath: string}>) => { + const utils = UtilsService.getInstance(); + const maps = LocalMapsManagerService.getInstance(); + + maps.exportMaps(request.args.version, request.args.maps, request.args.outPath).then(() => { + utils.ipcSend(request.responceChannel, {success: true}); + }).catch(err => { + utils.ipcSend(request.responceChannel, {success: false, error: err}); + }); +}); + ipcMain.on("download-map", async (event, request: IpcRequest<{map: BsvMapDetail, version: BSVersion}>) => { const utils = UtilsService.getInstance(); const maps = LocalMapsManagerService.getInstance(); diff --git a/src/main/ipcs/index.ts b/src/main/ipcs/index.ts index 7712adff..7a2d2395 100644 --- a/src/main/ipcs/index.ts +++ b/src/main/ipcs/index.ts @@ -3,7 +3,6 @@ import './os-controls-ipcs'; import './bs-launcher-ipcs'; import './bs-version-ipcs'; import './bs-uninstall-ipcs'; -import './map-ipcs'; import './supporters-ipcs'; import './launcher-ipcs'; import './window-manager-ipcs'; diff --git a/src/main/ipcs/map-ipcs.ts b/src/main/ipcs/map-ipcs.ts deleted file mode 100644 index 19ea6ff8..00000000 --- a/src/main/ipcs/map-ipcs.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ipcMain} from 'electron'; -import { MapService } from '../services/map.service'; -import { UtilsService } from '../services/utils.service'; -import { IpcRequest } from 'shared/models/ipc'; -import { ExportVersionMapsOption } from 'shared/models/maps/export-version-maps.model'; - -ipcMain.on("map.export-version", (event, request: IpcRequest) => { - const utils = UtilsService.getInstance(); - const mapService = MapService.getInstance(); - mapService.exportVersionMaps(request.args.version, request.args.path).then(() => { - utils.ipcSend(request.responceChannel, {success: true}); - }).catch(() => utils.ipcSend(request.responceChannel, {success: false, error: {title: "", msg: ""}})); -}); \ No newline at end of file diff --git a/src/main/services/map.service.ts b/src/main/services/map.service.ts deleted file mode 100644 index 596321c8..00000000 --- a/src/main/services/map.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import path from "path"; -import { BSVersion } from "shared/bs-version.interface"; -import { BSLocalVersionService } from "./bs-local-version.service"; -import archiver from "archiver"; -import { createWriteStream } from "fs"; -import log from "electron-log"; - -export class MapService{ - - private readonly MAP_PATH = path.join("Beat Saber_Data", "CustomLevels"); - - private static instance: MapService; - - private readonly localVersionService: BSLocalVersionService; - - public static getInstance(): MapService{ - if(!MapService.instance){ MapService.instance = new MapService(); } - return MapService.instance; - } - - private constructor(){ - this.localVersionService = BSLocalVersionService.getInstance(); - } - - private getMapsPath(versionPath: string): string{ - return path.join(versionPath, this.MAP_PATH); - } - - public async exportVersionMaps(version: BSVersion, outputZip: string): Promise{ - const versionPath = await this.localVersionService.getVersionPath(version); - const mapsPath = this.getMapsPath(versionPath); - - const output = createWriteStream(outputZip); - const archive = archiver("zip", {zlib: {level: 9}}); - - const promise = new Promise((resolve, reject) => { - archive.pipe(output); - archive.directory(mapsPath, false); - archive.on("error", reject); - output.on("close", resolve); - archive.finalize() - }); - - return promise; - } - -} \ No newline at end of file diff --git a/src/main/services/maps/local-maps-manager.service.ts b/src/main/services/maps/local-maps-manager.service.ts index e7ea724f..f10f9f5f 100644 --- a/src/main/services/maps/local-maps-manager.service.ts +++ b/src/main/services/maps/local-maps-manager.service.ts @@ -6,11 +6,12 @@ import { BSLocalVersionService } from "../bs-local-version.service"; import { InstallationLocationService } from "../installation-location.service"; import { UtilsService } from "../utils.service"; import crypto from "crypto"; -import { lstatSync, symlinkSync, unlinkSync, readdirSync } from "fs"; +import { lstatSync, symlinkSync, unlinkSync, readdirSync, createWriteStream } from "fs"; import { copySync } from "fs-extra"; import StreamZip from "node-stream-zip"; import { RequestService } from "../request.service"; import sanitize from "sanitize-filename"; +import archiver from "archiver"; export class LocalMapsManagerService { @@ -90,6 +91,28 @@ export class LocalMapsManagerService { return {zip, zipPath}; } + private async getAbsoluteFolderOfMaps(maps: BsmLocalMap[], version: BSVersion): Promise{ + + const mapsFolder = await this.getMapsFolderPath(version); + + const res: string[] = []; + const mapHashs = maps.map(map => map.hash); + + const mapsFolders = readdirSync(mapsFolder, {withFileTypes: true}); + + for(const content of mapsFolders){ + if(!content.isDirectory()){ continue; } + const mapFolderPath = path.join(mapsFolder, content.name); + const { hash } = await this.loadMapInfoFromPath(mapFolderPath); + if(mapHashs.includes(hash)){ + res.push(mapFolderPath); + } + } + + return res; + + } + public async getMaps(version?: BSVersion): Promise{ const levelsFolder = await this.getMapsFolderPath(version); @@ -146,18 +169,13 @@ export class LocalMapsManagerService { public async deleteMaps(maps: BsmLocalMap[], verion?: BSVersion){ - const mapsFolder = await this.getMapsFolderPath(verion); + const mapsFolders = await this.getAbsoluteFolderOfMaps(maps, verion); + const mapsHashsToDelete = maps.map(map => map.hash); - const mapsHashsToDelete = maps.map(m => m.hash); - - const mapsFolders = readdirSync(mapsFolder, {withFileTypes: true}); - - for(const content of mapsFolders){ - if(!content.isDirectory()){ continue; } - const mapFolderPath = path.join(mapsFolder, content.name); - const { hash } = await this.loadMapInfoFromPath(mapFolderPath); + for(const folder of mapsFolders){ + const { hash } = await this.loadMapInfoFromPath(folder); if(mapsHashsToDelete.includes(hash)){ - await this.utils.deleteFolder(mapFolderPath); + await this.utils.deleteFolder(folder); } } @@ -187,6 +205,34 @@ export class LocalMapsManagerService { unlinkSync(zipPath); } + public async exportMaps(version: BSVersion, maps: BsmLocalMap[], outPath: string){ + + const output = createWriteStream(outPath); + const archive = archiver("zip", {zlib: {level: 9}}); + + archive.pipe(output); + archive.on("error", (e) => {throw e}); + + if(!maps || maps.length === 0){ + + const mapsFolder = await this.getMapsFolderPath(version); + archive.directory(mapsFolder, false); + + } + else{ + + const mapsFolders = await this.getAbsoluteFolderOfMaps(maps, version); + + for(const folder of mapsFolders){ + archive.directory(folder, path.basename(folder)); + } + + } + + await archive.finalize(); + + } + } \ No newline at end of file diff --git a/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx b/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx index 9879559c..e8643986 100644 --- a/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx +++ b/src/renderer/components/maps-mangement-components/local-maps-list-panel.component.tsx @@ -32,7 +32,7 @@ export const LocalMapsListPanel = forwardRef(({version, className, filter, searc mapsManager.deleteMaps(mapsToDelete).finally(loadMaps); }, exportMaps(){ - console.log("TODO EXPORT MAPS"); + mapsManager.exportMaps(version, selectedMaps) } }), [selectedMaps, maps]); diff --git a/src/renderer/pages/version-viewer.component.tsx b/src/renderer/pages/version-viewer.component.tsx index d4a36ea7..596a46cb 100644 --- a/src/renderer/pages/version-viewer.component.tsx +++ b/src/renderer/pages/version-viewer.component.tsx @@ -11,7 +11,6 @@ import { ModalExitCode, ModalService } from '../services/modale.service'; import DefautVersionImage from "../../../assets/images/default-version-img.jpg"; import { BsDownloaderService } from 'renderer/services/bs-downloader.service'; import { IpcService } from 'renderer/services/ipc.service'; -import { MapService } from 'renderer/services/maps.service'; import { LaunchSlide } from 'renderer/components/version-viewer/slides/launch/launch-slide.component'; import { ModsSlide } from 'renderer/components/version-viewer/slides/mods/mods-slide.component'; import { UninstallModal } from 'renderer/components/modal/modal-types/uninstall-modal.component'; @@ -24,7 +23,6 @@ export function VersionViewer() { const modalService = ModalService.getInsance(); const bsDownloaderService = BsDownloaderService.getInstance(); const ipcService = IpcService.getInstance(); - const mapService = MapService.getInstance(); const {state} = useLocation() as {state: BSVersion}; const navigate = useNavigate(); @@ -32,7 +30,6 @@ export function VersionViewer() { const navigateToVersion = (version: BSVersion) => navigate(`/bs-version/${version.BSVersion}`, {state: version}); const openFolder = () => ipcService.sendLazy("bs-version.open-folder", {args: state}); - const exportMaps = () => mapService.exportVersionMaps(state); const verifyFiles = () => bsDownloaderService.download(state, true); const uninstall = async () => { @@ -71,7 +68,7 @@ export function VersionViewer() {
- +
@@ -79,7 +76,6 @@ export function VersionViewer() { = new Subject(); private readonly lastUnlinkedVersion$: Subject = new Subject(); @@ -32,6 +35,7 @@ export class MapsManagerService { this.bsaver = BeatSaverService.getInstance(); this.modal = ModalService.getInsance(); this.progressBar = ProgressBarService.getInstance(); + this.notifications = NotificationService.getInstance(); } public getMaps(version?: BSVersion, withDetails = true): Observable{ @@ -130,6 +134,30 @@ export class MapsManagerService { return res.success; } + public async exportMaps(version: BSVersion, maps?: BsmLocalMap[]): Promise{ + if(!this.progressBar.require()){ return; } + + const resFile = await this.ipcService.send("save-file", {args: { + filename: `${version.BSVersion} Maps`, + filters: [{name: "zip", extensions: ["zip"]}] + }}); + + if(!resFile.success){ return; } + + this.progressBar.showFake(.008); + + const resExport = await this.ipcService.send("export-maps", {args: {version, maps, outPath: resFile.data}}); + if(!resExport.success && resExport.error.title){ + const {title, msg} = resExport.error; + this.notifications.notifyError({title, desc: msg}); + } + else { + this.notifications.notifySuccess({title: "Export terminé 🎉", duration: 3000}); + } + this.progressBar.complete(); + this.progressBar.hide(true); + } + public get versionLinked$(): Observable{ return this.lastLinkedVersion$.asObservable(); } diff --git a/src/renderer/services/maps.service.ts b/src/renderer/services/maps.service.ts deleted file mode 100644 index 8048e36d..00000000 --- a/src/renderer/services/maps.service.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { BSVersion } from "shared/bs-version.interface"; -import { OpenSaveDialogOption } from "shared/models/ipc"; -import { ExportVersionMapsOption } from "shared/models/maps/export-version-maps.model"; -import { IpcService } from "./ipc.service"; -import { NotificationService } from "./notification.service"; -import { ProgressBarService } from "./progress-bar.service"; - -export class MapService { - - private static instance: MapService; - - private readonly ipcService: IpcService; - private readonly progressService: ProgressBarService; - private readonly notificationService: NotificationService; - - public static getInstance(): MapService{ - if(!MapService.instance){ MapService.instance = new MapService(); } - return MapService.instance; - } - - private constructor(){ - this.ipcService = IpcService.getInstance(); - this.notificationService = NotificationService.getInstance(); - this.progressService = ProgressBarService.getInstance(); - } - - public async exportVersionMaps(version: BSVersion): Promise{ - if(!this.progressService.require()){ return; } - - const resFile = await this.ipcService.send("save-file", {args: { - filename: `${version.BSVersion} Maps`, - filters: [{name: "zip", extensions: ["zip"]}] - }}); - - if(!resFile.success){ return; } - - this.progressService.showFake(.008); - - const resExport = await this.ipcService.send("map.export-version", {args: {version, path: resFile.data}}); - if(!resExport.success && resExport.error.title){ - const {title, msg} = resExport.error; - this.notificationService.notifyError({title, desc: msg}); - } - else { - this.notificationService.notifySuccess({title: "Export terminé 🎉", duration: 3000}); - } - this.progressService.complete(); - this.progressService.hide(true); - } - -} \ No newline at end of file diff --git a/src/shared/models/maps/export-version-maps.model.ts b/src/shared/models/maps/export-version-maps.model.ts deleted file mode 100644 index 521359b2..00000000 --- a/src/shared/models/maps/export-version-maps.model.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { BSVersion } from "shared/bs-version.interface"; - -export interface ExportVersionMapsOption {version: BSVersion, path: string}; \ No newline at end of file diff --git a/src/shared/models/maps/index.ts b/src/shared/models/maps/index.ts index c126f53c..568a0457 100644 --- a/src/shared/models/maps/index.ts +++ b/src/shared/models/maps/index.ts @@ -1,7 +1,5 @@ export { RawMapInfoData, RawMapDifficulty, RawDifficultySet } from "./raw-map.model"; -export { ExportVersionMapsOption } from "./export-version-maps.model"; - export { BsvInstant, BsvMapDetail,