mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
advencement on oneclick playlists
This commit is contained in:
+2
-1
@@ -33,7 +33,8 @@ module.exports = {
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-useless-escape": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off"
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"no-async-promise-executor": "off"
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { IpcRequest } from "shared/models/ipc";
|
||||
import { LocalPlaylistsManagerService } from "../services/additional-content/local-playlists-manager.service";
|
||||
import { UtilsService } from "../services/utils.service";
|
||||
|
||||
ipcMain.on("one-click-install-playlist", async (event, request: IpcRequest<string>) => {
|
||||
|
||||
console.log("OUI");
|
||||
|
||||
const utils = UtilsService.getInstance();
|
||||
const playlists = LocalPlaylistsManagerService.getInstance();
|
||||
|
||||
console.log(request.args);
|
||||
|
||||
playlists.oneClickInstallPlaylist(request.args).then(() => {
|
||||
console.log("C FINI");
|
||||
utils.ipcSend(request.responceChannel, {success: true});
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
utils.ipcSend(request.responceChannel, {success: false, error: e});
|
||||
});
|
||||
});
|
||||
@@ -8,4 +8,5 @@ import './launcher-ipcs';
|
||||
import './window-manager-ipcs';
|
||||
import './bs-mods-ipcs';
|
||||
import './bs-maps-ipcs';
|
||||
import './beat-saver-ipcs'
|
||||
import './beat-saver-ipcs';
|
||||
import './bs-playlist-ipcs';
|
||||
@@ -61,7 +61,7 @@ export class LocalMapsManagerService {
|
||||
});
|
||||
}
|
||||
|
||||
private async getMapsFolderPath(version?: BSVersion): Promise<string>{
|
||||
public async getMapsFolderPath(version?: BSVersion): Promise<string>{
|
||||
if(version){ return path.join(await this.localVersion.getVersionPath(version), this.LEVELS_ROOT_FOLDER, this.CUSTOM_LEVELS_FOLDER); }
|
||||
const sharedMapsPath = path.join(this.installLocation.sharedMapsPath, this.CUSTOM_LEVELS_FOLDER);
|
||||
if(!(await this.utils.pathExist(sharedMapsPath))){
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import path from "path";
|
||||
import { BehaviorSubject, Observable } from "rxjs";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { BsvPlaylist, BsvPlaylistPage } from "shared/models/maps/beat-saver.model";
|
||||
import { BSLocalVersionService } from "../bs-local-version.service";
|
||||
import { DeepLinkService } from "../deep-link.service";
|
||||
import { RequestService } from "../request.service";
|
||||
@@ -12,8 +11,11 @@ import { isValidUrl } from "../../helpers/url.helpers";
|
||||
import { ipcMain } from "electron";
|
||||
import { WindowManagerService } from "../window-manager.service";
|
||||
import { IpcRequest } from "shared/models/ipc";
|
||||
import { BPList } from "shared/models/playlists/playlist.interface";
|
||||
import { readFileSync } from "fs";
|
||||
import { BPList, DownloadPlaylistProgression } from "shared/models/playlists/playlist.interface";
|
||||
import { copyFileSync, readFileSync } from "fs";
|
||||
import { BeatSaverService } from "../thrid-party/beat-saver/beat-saver.service";
|
||||
import { copySync } from "fs-extra";
|
||||
import { timer } from "rxjs";
|
||||
|
||||
export class LocalPlaylistsManagerService {
|
||||
|
||||
@@ -36,6 +38,7 @@ export class LocalPlaylistsManagerService {
|
||||
private readonly request: RequestService;
|
||||
private readonly deepLink: DeepLinkService;
|
||||
private readonly windows: WindowManagerService;
|
||||
private readonly bsaver: BeatSaverService;
|
||||
|
||||
private constructor(){
|
||||
this.maps = LocalMapsManagerService.getInstance();
|
||||
@@ -44,6 +47,7 @@ export class LocalPlaylistsManagerService {
|
||||
this.request = RequestService.getInstance();
|
||||
this.deepLink = DeepLinkService.getInstance();
|
||||
this.windows = WindowManagerService.getInstance();
|
||||
this.bsaver = BeatSaverService.getInstance();
|
||||
|
||||
this.deepLink.addLinkOpenedListener(this.DEEP_LINKS.BeatSaver, link => {
|
||||
|
||||
@@ -69,29 +73,54 @@ export class LocalPlaylistsManagerService {
|
||||
return splited[idIndex + 1];
|
||||
}
|
||||
|
||||
private async downloadPlaylistFile(playlist: BsvPlaylist, version: BSVersion): Promise<string>{
|
||||
private async getPlaylistsFolder(version?: BSVersion){
|
||||
|
||||
if(!version){ throw "Playlists are not available to be linked yet" }
|
||||
|
||||
const versionFolder = await this.versions.getVersionPath(version);
|
||||
const bpListUrl = playlist.downloadURL;
|
||||
|
||||
const bsListDest = path.join(versionFolder, this.PLAYLISTS_FOLDER, path.basename(bpListUrl));
|
||||
const folder = path.join(versionFolder, this.PLAYLISTS_FOLDER);
|
||||
|
||||
return this.request.downloadFile(bpListUrl, bsListDest);
|
||||
await this.utils.createFolderIfNotExist(folder)
|
||||
|
||||
return folder;
|
||||
|
||||
}
|
||||
|
||||
private async installBPListFile(bpListUrlOrPath: string, version: BSVersion): Promise<string>{
|
||||
|
||||
const playlistFolder = await this.getPlaylistsFolder(version);
|
||||
|
||||
const bpListDest = path.join(playlistFolder, path.basename(bpListUrlOrPath));
|
||||
|
||||
console.log("INSTALL BPLIST", bpListUrlOrPath, playlistFolder);
|
||||
|
||||
if(this.utils.pathExist(bpListUrlOrPath)){
|
||||
console.log("PATH EXIST", bpListUrlOrPath);
|
||||
console.log("COPY", bpListUrlOrPath, bpListDest);
|
||||
copyFileSync(bpListUrlOrPath, bpListDest);
|
||||
}
|
||||
else{
|
||||
await this.request.downloadFile(bpListUrlOrPath, bpListDest);
|
||||
}
|
||||
|
||||
return bpListDest;
|
||||
|
||||
}
|
||||
|
||||
private async readPlaylistFile(path: string): Promise<BPList>{
|
||||
|
||||
if(!this.utils.pathExist(path)){ return null; }
|
||||
if(!this.utils.pathExist(path)){ throw `bplist file not exist at ${path}`; }
|
||||
|
||||
const content = readFileSync(path).toJSON();
|
||||
const rawContent = readFileSync(path).toString();
|
||||
|
||||
return JSON.parse(rawContent);
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
private openOneClickDownloadPlaylistWindow(downloadUrl: string): void{
|
||||
|
||||
// TODO make once
|
||||
ipcMain.on("one-click-playlist-info", async (event, req: IpcRequest<void>) => {
|
||||
this.utils.ipcSend(req.responceChannel, {success: true, data: {bpListUrl: downloadUrl, id: this.getPlaylistIdFromDownloadUrl(downloadUrl)}});
|
||||
});
|
||||
@@ -100,30 +129,98 @@ export class LocalPlaylistsManagerService {
|
||||
|
||||
}
|
||||
|
||||
public downloadPlaylist(playlistPage: BsvPlaylistPage, version: BSVersion): Observable<string[]>{
|
||||
public downloadPlaylist(bpListUrl: string, version: BSVersion): Observable<DownloadPlaylistProgression>{
|
||||
|
||||
const res = new BehaviorSubject<string[]>([]);
|
||||
console.log("DOWNLOAD PLAYLIST");
|
||||
|
||||
const res = new BehaviorSubject<DownloadPlaylistProgression>({progression: 0, current: null, downloadedMaps: [], mapsPath: [], bpListPath: ""});
|
||||
|
||||
const sub = res.subscribe(process => {
|
||||
console.log("PROGRESS", process);
|
||||
this.utils.ipcSend("download-playlist-progress", {success: true, data: process});
|
||||
}, err => {
|
||||
this.utils.ipcSend("download-playlist-progress", {success: false, error: err});
|
||||
});
|
||||
|
||||
const observer = async () => {
|
||||
try{
|
||||
const bpListPath = await this.downloadPlaylistFile(playlistPage.playlist, version);
|
||||
|
||||
for(const map of playlistPage.maps){
|
||||
const mapPath = await this.maps.downloadMap(map.map, version);
|
||||
res.next([...res.value, mapPath]);
|
||||
try{
|
||||
|
||||
const bpListPath = await this.installBPListFile(bpListUrl, version);
|
||||
|
||||
res.next({...res.value, bpListPath});
|
||||
|
||||
const bpList = await this.readPlaylistFile(bpListPath);
|
||||
|
||||
for(const song of bpList.songs){
|
||||
|
||||
if(!song.key){ continue; }
|
||||
|
||||
const map = await this.bsaver.getMapDetailsById(song.key);
|
||||
|
||||
res.next({
|
||||
...res.value,
|
||||
current: map,
|
||||
progression: ((res.value.downloadedMaps.length + .5) / bpList.songs.length) * 100
|
||||
});
|
||||
|
||||
const mapPath = await this.maps.downloadMap(map, version);
|
||||
|
||||
const progression = ((res.value.downloadedMaps.length + 1) / bpList.songs.length) * 100;
|
||||
|
||||
res.next({
|
||||
...res.value,
|
||||
downloadedMaps: [...res.value.downloadedMaps, map],
|
||||
mapsPath: [...res.value.mapsPath, mapPath],
|
||||
progression
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
res.next([...res.value, bpListPath]);
|
||||
res.complete();
|
||||
}
|
||||
catch(e){
|
||||
res.error(e);
|
||||
}
|
||||
|
||||
res.complete();
|
||||
|
||||
}
|
||||
|
||||
observer();
|
||||
observer().finally(() => sub.unsubscribe());
|
||||
return res.asObservable();
|
||||
|
||||
}
|
||||
|
||||
public async oneClickInstallPlaylist(bpListUrl: string): Promise<void>{
|
||||
|
||||
const versions = await this.versions.getInstalledVersions();
|
||||
|
||||
await timer(2);
|
||||
|
||||
const firstVersion = versions.shift();
|
||||
const fistVersionLinked = await this.maps.versionIsLinked(firstVersion);
|
||||
|
||||
const {bpListPath, mapsPath} = await this.downloadPlaylist(bpListUrl, firstVersion).toPromise();
|
||||
|
||||
for(const version of versions){
|
||||
|
||||
await this.installBPListFile(bpListPath, version);
|
||||
|
||||
const versionIsLinked = await this.maps.versionIsLinked(version);
|
||||
|
||||
if(fistVersionLinked && versionIsLinked){ continue; }
|
||||
|
||||
for(const mapPath of mapsPath){
|
||||
const versionMapsFolder = await this.maps.getMapsFolderPath(version);
|
||||
const mapDest = path.join(versionMapsFolder, path.basename(mapPath));
|
||||
|
||||
console.log("COPY MAP", mapPath, mapDest);
|
||||
|
||||
copySync(mapPath, mapDest, {overwrite: true});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -98,8 +98,10 @@ export class UtilsService{
|
||||
|
||||
public ipcSend<T = any>(channel: string, response: IpcResponse<T>): void{
|
||||
try {
|
||||
console.log(channel, ...Array.from(this.windows.keys()), response);
|
||||
Array.from(this.windows.values()).forEach(window => window.webContents.send(channel, response));
|
||||
} catch (error) {
|
||||
console.log("ERREUR", error);
|
||||
log.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ export class WindowManagerService{
|
||||
public openWindow(windowType: AppWindow, options?: BrowserWindowConstructorOptions): Promise<BrowserWindow>{
|
||||
const window = new BrowserWindow({...this.appWindowsOptions[windowType], ...this.baseWindowOption, ...options});
|
||||
|
||||
console.log({...this.appWindowsOptions[windowType], ...this.baseWindowOption, ...options});
|
||||
|
||||
const promise = window.loadURL(resolveHtmlPath(windowType));
|
||||
window.removeMenu();
|
||||
window.setMenu(null);
|
||||
|
||||
@@ -34,10 +34,17 @@ export class IpcService {
|
||||
}
|
||||
|
||||
public watch<T>(channel: string): Observable<IpcResponse<T>>{
|
||||
console.log("watch", channel);
|
||||
if(this.channelObservables.has(channel)){ return this.channelObservables.get(channel) as Observable<IpcResponse<T>>; }
|
||||
console.log("allo");
|
||||
|
||||
window.electron.ipcRenderer.on(channel, (res: IpcResponse<T>) => {
|
||||
console.log("oui", res);
|
||||
});
|
||||
|
||||
const obs = new Observable<IpcResponse<T>>(observer => {
|
||||
window.electron.ipcRenderer.on(channel, res => {
|
||||
window.electron.ipcRenderer.on(channel, (res: IpcResponse<T>) => {
|
||||
console.log("oui", res);
|
||||
observer.next(res);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { map, multicast, tap } from "rxjs/operators";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { DownloadPlaylistProgression } from "shared/models/playlists/playlist.interface";
|
||||
import { IpcService } from "./ipc.service";
|
||||
import { ProgressBarService } from "./progress-bar.service";
|
||||
import { ProgressionInterface } from "shared/models/progress-bar";
|
||||
|
||||
export class PlaylistDownloaderService{
|
||||
|
||||
private static instance: PlaylistDownloaderService;
|
||||
|
||||
public static getInstance(): PlaylistDownloaderService{
|
||||
if(!PlaylistDownloaderService.instance){ PlaylistDownloaderService.instance = new PlaylistDownloaderService(); }
|
||||
return PlaylistDownloaderService.instance;
|
||||
}
|
||||
|
||||
private readonly progressWatcher$ = new Subject<DownloadPlaylistProgression>();
|
||||
|
||||
private readonly progress: ProgressBarService;
|
||||
private readonly ipc: IpcService;
|
||||
|
||||
private constructor(){
|
||||
this.progress = ProgressBarService.getInstance();
|
||||
this.ipc = IpcService.getInstance();
|
||||
|
||||
this.ipc.watch<DownloadPlaylistProgression>("download-playlist-progress").pipe(map(val => val.data)).subscribe(progress => {
|
||||
this.progressWatcher$.next(progress);
|
||||
})
|
||||
}
|
||||
|
||||
private get downloadProgression$(): Observable<ProgressionInterface>{
|
||||
return this.progressWatcher$.pipe(map(value => {
|
||||
return {progression: value?.progression ?? 0, label: value?.current?.name ?? ""};
|
||||
}));
|
||||
}
|
||||
|
||||
public oneClickInstallPlaylist(bpListUrl: string): Observable<DownloadPlaylistProgression>{
|
||||
|
||||
if(!this.progress.require()){ return null; }
|
||||
|
||||
const res = new Subject<DownloadPlaylistProgression>();
|
||||
|
||||
this.progress.show(this.downloadProgression$, true);
|
||||
|
||||
const sub = this.progressWatcher$.subscribe(progress => {console.log("progress"); res.next(progress)})
|
||||
|
||||
this.ipc.send<void, string>("one-click-install-playlist", {args: bpListUrl}).then(oneClickRes => {
|
||||
sub.unsubscribe();
|
||||
if(!oneClickRes.success){
|
||||
return res.error(oneClickRes.error);
|
||||
}
|
||||
res.complete();
|
||||
})
|
||||
|
||||
return res.asObservable();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { BsmProgressBar } from "renderer/components/progress-bar/bsm-progress-ba
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import TitleBar from "renderer/components/title-bar/title-bar.component";
|
||||
import { IpcService } from "renderer/services/ipc.service"
|
||||
import { PlaylistDownloaderService } from "renderer/services/playlist-downloader.service";
|
||||
import { ProgressBarService } from "renderer/services/progress-bar.service";
|
||||
import { ThemeService } from "renderer/services/theme.service";
|
||||
import { BeatSaverService } from "renderer/services/thrird-partys/beat-saver.service";
|
||||
@@ -15,6 +16,7 @@ export default function OneClickDownloadPlaylist() {
|
||||
const bSaver = BeatSaverService.getInstance();
|
||||
const progress = ProgressBarService.getInstance();
|
||||
const themeService = ThemeService.getInstance();
|
||||
const playlistDownloader = PlaylistDownloaderService.getInstance();
|
||||
|
||||
const [playlist, setPlaylist] = useState<BsvPlaylist>(null);
|
||||
|
||||
@@ -28,18 +30,25 @@ export default function OneClickDownloadPlaylist() {
|
||||
else { document.documentElement.classList.remove('dark'); }
|
||||
});
|
||||
|
||||
progress.open();
|
||||
|
||||
ipc.send<{bpListUrl: string, id: string}>("one-click-playlist-info").then(res => {
|
||||
const promise = new Promise(async (resolve, reject) => {
|
||||
|
||||
console.log(res);
|
||||
|
||||
const playlistId = res.data.id;
|
||||
const downloadUrl = res.data.bpListUrl;
|
||||
const infos = await ipc.send<{bpListUrl: string, id: string}>("one-click-playlist-info");
|
||||
|
||||
bSaver.getPlaylistDetailsById(playlistId).then(playlist => setPlaylist(() => playlist));
|
||||
if(!infos.success){ reject(infos.error); }
|
||||
|
||||
bSaver.getPlaylistDetailsById(infos.data.id).then(details => setPlaylist(details));
|
||||
|
||||
const res = await playlistDownloader.oneClickInstallPlaylist(infos.data.bpListUrl).toPromise();
|
||||
|
||||
console.log("OMG C FINI");
|
||||
|
||||
resolve(res);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { BsvMapDetail } from "../maps"
|
||||
|
||||
export interface BPList {
|
||||
playlistTitle: string,
|
||||
playlistAuthor: string,
|
||||
@@ -12,4 +14,12 @@ export interface PlaylistSong {
|
||||
hash: string,
|
||||
songName: string,
|
||||
uploader?: string
|
||||
}
|
||||
|
||||
export interface DownloadPlaylistProgression {
|
||||
mapsPath: string[],
|
||||
downloadedMaps: BsvMapDetail[],
|
||||
bpListPath: string,
|
||||
current: BsvMapDetail,
|
||||
progression: number
|
||||
}
|
||||
Reference in New Issue
Block a user