advencement on oneclick playlists

This commit is contained in:
MathieuG-P
2022-12-22 00:57:06 +01:00
parent 16aa65afed
commit d556350eef
11 changed files with 240 additions and 34 deletions
+2 -1
View File
@@ -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,
+22
View File
@@ -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});
});
});
+2 -1
View File
@@ -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});
}
}
}
}
+2
View File
@@ -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);
+8 -1
View File
@@ -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
}