[feature-107] Can now open .bplist file with BSManager

This commit is contained in:
MathieuG-P
2024-02-03 16:03:17 +01:00
parent df057cf82e
commit 778b4f6ee6
5 changed files with 100 additions and 25 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

+1 -1
View File
@@ -100,7 +100,7 @@
"owner": "Zagrios"
},
"fileAssociations": [
{ "ext": "bplist", "description": "Beat Saber Playlist", "icon": "./assets/favicon.ico", "role": "Viewer" }
{ "ext": "bplist", "description": "Beat Saber Playlist (BSManager)", "icon": "./assets/bsm_file.ico", "role": "Viewer" }
]
},
"repository": {
+27 -13
View File
@@ -21,6 +21,7 @@ import { BSLauncherService } from "./services/bs-launcher/bs-launcher.service";
import { IpcRequest } from "shared/models/ipc";
import { LivShortcut } from "./services/liv/liv-shortcut.service";
import { SteamLauncherService } from "./services/bs-launcher/steam-launcher.service";
import { FileAssociationService } from "./services/file-association.service";
const isDebug = process.env.NODE_ENV === "development" || process.env.DEBUG_PROD === "true";
@@ -69,19 +70,29 @@ const initServicesMustBeInitialized = () => {
BSLauncherService.getInstance();
}
const findDeepLinkInArgs = (args: string[]): string => {
return args.find(arg => DeepLinkService.getInstance().isDeepLink(arg));
}
const findAssociatedFileInArgs = (args: string[]): string => {
return args.find(arg => FileAssociationService.getInstance().isFileAssociated(arg));
}
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on("second-instance", (_, argv) => {
const deepLink = argv.find(arg => DeepLinkService.getInstance().isDeepLink(arg));
const deepLink = findDeepLinkInArgs(argv);
const associatedFile = findAssociatedFileInArgs(argv);
if (!deepLink) {
return;
if (deepLink) {
DeepLinkService.getInstance().dispatchLinkOpened(deepLink);
} else if (associatedFile) {
FileAssociationService.getInstance().handleFileAssociation(associatedFile);
}
DeepLinkService.getInstance().dispatchLinkOpened(deepLink);
});
app.on("window-all-closed", () => {
@@ -90,25 +101,28 @@ if (!gotTheLock) {
})
app.whenReady().then(() => {
app.setAppUserModelId(APP_NAME);
initServicesMustBeInitialized();
const deepLink = process.argv.find(arg => DeepLinkService.getInstance().isDeepLink(arg));
if (!deepLink) {
createWindow();
} else {
const deepLink = findDeepLinkInArgs(process.argv);
const associatedFile = findAssociatedFileInArgs(process.argv);
if (deepLink) {
DeepLinkService.getInstance().dispatchLinkOpened(deepLink);
} else if (associatedFile) {
FileAssociationService.getInstance().handleFileAssociation(associatedFile);
} else {
createWindow();
}
SteamLauncherService.getInstance().restoreSteamVR();
// Log renderer errors
ipcMain.on("log-error", (_, args: IpcRequest<unknown>) => {
log.error(args?.args);
});
}).catch(log.error);
}
@@ -12,7 +12,7 @@ import { readFileSync } from "fs";
import { BeatSaverService } from "../thrid-party/beat-saver/beat-saver.service";
import { copy, copyFile, pathExists, realpath } from "fs-extra";
import { Progression, ensureFolderExist, pathExist } from "../../helpers/fs.helpers";
import { IpcService } from "../ipc.service";
import { FileAssociationService } from "../file-association.service";
export class LocalPlaylistsManagerService {
private static instance: LocalPlaylistsManagerService;
@@ -33,18 +33,18 @@ export class LocalPlaylistsManagerService {
private readonly maps: LocalMapsManagerService;
private readonly request: RequestService;
private readonly deepLink: DeepLinkService;
private readonly fileAssociation: FileAssociationService;
private readonly windows: WindowManagerService;
private readonly bsaver: BeatSaverService;
private readonly ipc: IpcService;
private constructor() {
this.maps = LocalMapsManagerService.getInstance();
this.versions = BSLocalVersionService.getInstance();
this.request = RequestService.getInstance();
this.deepLink = DeepLinkService.getInstance();
this.fileAssociation = FileAssociationService.getInstance();
this.windows = WindowManagerService.getInstance();
this.bsaver = BeatSaverService.getInstance();
this.ipc = IpcService.getInstance();
this.deepLink.addLinkOpenedListener(this.DEEP_LINKS.BeatSaver, link => {
log.info("DEEP-LINK RECEIVED FROM", this.DEEP_LINKS.BeatSaver, link);
@@ -52,11 +52,16 @@ export class LocalPlaylistsManagerService {
const bplistUrl = url.host === "playlist" ? url.pathname.replace("/", "") : "";
this.openOneClickDownloadPlaylistWindow(bplistUrl);
});
this.fileAssociation.registerFileAssociation(".bplist", filePath => {
log.info("FILE ASSOCIATION RECEIVED", filePath);
this.openOneClickDownloadPlaylistWindow(filePath);
});
}
private async getPlaylistsFolder(version?: BSVersion) {
if (!version) {
throw "Playlists are not available to be linked yet";
throw new Error("Playlists are not available to be linked yet");
}
const versionFolder = await this.versions.getVersionPath(version);
@@ -79,14 +84,14 @@ export class LocalPlaylistsManagerService {
}
return lastValueFrom(this.request.downloadFile(bslistSource, destFile)).then(res => res.data);
}
private async readPlaylistFile(path: string): Promise<BPList> {
if (!(await pathExist(path))) {
throw `bplist file not exist at ${path}`;
private async readPlaylistFile(filePath: string): Promise<BPList> {
if (!(await pathExist(filePath))) {
throw new Error(`bplist file not exist at ${filePath}`);
}
const rawContent = readFileSync(path).toString();
const rawContent = readFileSync(filePath).toString();
return JSON.parse(rawContent);
}
@@ -102,8 +107,8 @@ export class LocalPlaylistsManagerService {
const bpListFilePath = await this.installBPListFile(bpListUrl, version);
const bpList = await this.readPlaylistFile(bpListFilePath);
const progress: Progression<DownloadPlaylistProgressionData> = {
const progress: Progression<DownloadPlaylistProgressionData> = {
total: bpList.songs.length,
current: 0,
data: {
@@ -0,0 +1,56 @@
import { pathExistsSync } from "fs-extra";
import path from "path";
import log from "electron-log";
export class FileAssociationService {
private static instance: FileAssociationService;
public static getInstance(): FileAssociationService {
if (!FileAssociationService.instance) {
FileAssociationService.instance = new FileAssociationService();
}
return FileAssociationService.instance;
}
private readonly listeners = new Map<ExtKey, Listerner[]>();
private constructor() {}
private getAbsolutePath(filePath: string) {
return path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath);
}
public registerFileAssociation(ext: ExtKey, fn: Listerner) {
if (!this.listeners.has(ext)) {
this.listeners.set(ext, [] as Listerner[]);
}
this.listeners.get(ext).push(fn);
}
public isFileAssociated(filePath: string): boolean {
const fileExt = path.extname(filePath) as ExtKey;
return this.listeners.has(fileExt);
}
public handleFileAssociation(filePath: string) {
const absolutePath = this.getAbsolutePath(filePath);
if(!pathExistsSync(absolutePath)) {
log.error(`[FileAssociationService] File not found: ${absolutePath}`);
return;
}
const fileExt = path.extname(absolutePath) as ExtKey;
const listeners = this.listeners.get(fileExt);
if (!listeners) {
log.error(`[FileAssociationService] No listeners for file: ${absolutePath}`);
return;
}
listeners.forEach(listener => listener(this.getAbsolutePath(filePath)));
}
}
type ExtKey = `.${string}`;
type Listerner = (filePath: string) => void;