API improvements

- added `start` events for `Archive` and `Downloader` streams
- fixed `Downloader.download` file size calculation
- `Downloader.fileFromUri` is now public
- fixed `Runners.get` output type
- added `Runners.download` method
This commit is contained in:
Observer KRypt0n_ 2021-12-21 20:00:19 +02:00
parent a1d1d9a3ca
commit 9dc5b47983
No known key found for this signature in database
GPG key ID: DC5D4EC1303465DA
4 changed files with 246 additions and 11 deletions

View file

@ -5,6 +5,7 @@ import Window from '../ts/neutralino/Window';
import Downloader from '../ts/Downloader';
import Archive from '../ts/Archive';
import Configs from '../ts/Configs';
import Runners from '../ts/Runners';
const app = Vue.createApp({
data: () => ({
@ -18,6 +19,21 @@ const app = Vue.createApp({
mounted: () => {
Window.current.show();
/*Downloader.download('https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.20-GE-1/wine-lutris-ge-6.20-1-x86_64.tar.xz', '123.tar.xz').then((stream) => {
stream.start(() => console.log('Downloading started'));
stream.finish(() => console.log('Downloading finished'));
stream.progress((current, total) => console.log(`${Math.round(current / total * 100)}%`));
});*/
/*Runners.download('wine-lutris-ge-6.20-1-x86_64').then((stream) => {
stream.downloadStart(() => console.log('Downloading started'));
stream.downloadFinish(() => console.log('Downloading finished'));
stream.unpackStart(() => console.log('Unpacking started'));
stream.unpackFinish(() => console.log('Unpacking finished'));
});*/
/*Archive.unpack('Audio_English(US)_2.3.0.zip', 'tmp').then((stream) => {
stream.progress((current, total) => {
console.log(`${Math.round(current / total * 100)}%`);

View file

@ -18,10 +18,12 @@ class Stream
protected archive: ArchiveInfo;
protected onStart?: () => void;
protected onProgress?: (current: number, total: number, difference: number) => void;
protected onFinish?: () => void;
protected onError?: () => void;
protected started: boolean = false;
protected finished: boolean = false;
protected throwedError: boolean = false;
@ -33,6 +35,10 @@ class Stream
{
this.path = path;
this.unpackDir = unpackDir;
this.started = true;
if (this.onStart)
this.onStart();
Archive.getInfo(path).then((info) => {
if (info === null)
@ -102,6 +108,19 @@ class Stream
});
}
/**
* Specify event that will be called when the unpacking will be started
*
* @param callback
*/
public start(callback: () => void)
{
this.onStart = callback;
if (this.started)
callback();
}
/**
* Specify event that will be called every [this.progressInterval] ms during archive unpacking
*

View file

@ -9,15 +9,21 @@ class Stream
protected total: number;
protected previous: number = 0;
protected onStart?: () => void;
protected onProgress?: (current: number, total: number, difference: number) => void;
protected onFinish?: () => void;
protected started: boolean = false;
protected finished: boolean = false;
public constructor(uri: string, output: string, total: number)
{
this.uri = uri;
this.total = total;
this.started = true;
if (this.onStart)
this.onStart();
// @ts-expect-error
Neutralino.os.execCommand(`curl -s -L -N -o "${output}" "${uri}"`, {
@ -51,6 +57,19 @@ class Stream
setTimeout(updateProgress, this.progressInterval);
}
/**
* Specify event that will be called when the downloading will be started
*
* @param callback
*/
public start(callback: () => void)
{
this.onStart = callback;
if (this.started)
callback();
}
/**
* Specify event that will be called every [this.progressInterval] ms during the file downloading
*
@ -96,13 +115,16 @@ export default class Downloader
else statsRaw = statsRaw.stdOut;
const length = parseInt(/content-length: ([\d]+)/i.exec(statsRaw)[1]);
let length = 0;
for (const match of statsRaw.matchAll(/content-length: ([\d]+)/gi))
length = match[1];
resolve(new Stream(uri, output ?? this.fileFromUri(uri), length));
});
}
protected static fileFromUri(uri: string): string
public static fileFromUri(uri: string): string
{
const file = uri.split('/').pop().split('#')[0].split('?')[0];

View file

@ -1,9 +1,168 @@
import type {
import {
Runner,
RunnerFamily
} from './types/Runners';
import constants from './Constants';
import Constants from './Constants';
import Downloader from './Downloader';
import Archive from './Archive';
class Stream
{
/**
* The interval in ms between progress event calls
*/
public downloadProgressInterval: number = 200;
/**
* The interval in ms between progress event calls
*/
public unpackProgressInterval: number = 500;
protected onDownloadStart?: () => void;
protected onUnpackStart?: () => void;
protected onDownloadProgress?: (current: number, total: number, difference: number) => void;
protected onUnpackProgress?: (current: number, total: number, difference: number) => void;
protected onDownloadFinish?: () => void;
protected onUnpackFinish?: () => void;
protected downloadStarted: boolean = false;
protected unpackStarted: boolean = false;
protected downloadFinished: boolean = false;
protected unpackFinished: boolean = false;
public constructor(runner: Runner)
{
Constants.paths.launcher.then((launcherDir) => {
const archivePath = `${launcherDir}/${Downloader.fileFromUri(runner.uri)}`;
// Download archive
Downloader.download(runner.uri, archivePath).then((stream) => {
stream.progressInterval = this.downloadProgressInterval;
stream.start(() => {
this.downloadStarted = true;
if (this.onDownloadStart)
this.onDownloadStart();
});
stream.progress((current, total, difference) => {
if (this.onDownloadProgress)
this.onDownloadProgress(current, total, difference);
});
stream.finish(() => {
this.downloadFinished = true;
if (this.onDownloadFinish)
this.onDownloadFinish();
// And then unpack it
Constants.paths.runners.then((runners) => {
Archive.unpack(archivePath, runners).then((stream) => {
stream.progressInterval = this.unpackProgressInterval;
stream.start(() => {
this.unpackStarted = true;
if (this.onUnpackStart)
this.onUnpackStart();
});
stream.progress((current, total, difference) => {
if (this.onUnpackProgress)
this.onUnpackProgress(current, total, difference);
});
stream.finish(() => {
this.unpackFinished = true;
if (this.onUnpackFinish)
this.onUnpackFinish();
});
});
});
});
});
});
}
/**
* Specify event that will be called after the runner will begin downloading
*
* @param callback
*/
public downloadStart(callback: () => void)
{
this.onDownloadStart = callback;
if (this.downloadStarted)
callback();
}
/**
* Specify event that will be called after the runner will begin unpacking
*
* @param callback
*/
public unpackStart(callback: () => void)
{
this.onUnpackStart = callback;
if (this.unpackStarted)
callback();
}
/**
* Specify event that will be called every [this.downloadProgressInterval] ms during the runner downloading
*
* @param callback
*/
public downloadProgress(callback: (current: number, total: number, difference: number) => void)
{
this.onDownloadProgress = callback;
}
/**
* Specify event that will be called every [this.unpackProgressInterval] ms during the runner unpacking
*
* @param callback
*/
public unpackProgress(callback: (current: number, total: number, difference: number) => void)
{
this.onUnpackProgress = callback;
}
/**
* Specify event that will be called after the runner will be downloaded
*
* @param callback
*/
public downloadFinish(callback: () => void)
{
this.onDownloadFinish = callback;
if (this.downloadFinished)
callback();
}
/**
* Specify event that will be called after the runner will be unpacked
*
* @param callback
*/
public unpackFinish(callback: () => void)
{
this.onUnpackFinish = callback;
if (this.unpackFinished)
callback();
}
}
class Runners
{
@ -12,17 +171,17 @@ class Runners
*
* @returns Promise<Runner[]>
*/
public static get(): Promise<Runner[]>
public static get(): Promise<RunnerFamily[]>
{
return new Promise((resolve) => {
constants.paths.runners.then(async (runnersDir: string) => {
Constants.paths.runners.then(async (runnersDir: string) => {
// @ts-expect-error
let list: RunnerFamily[] = JSON.parse(await Neutralino.filesystem.readFile(`${constants.dirs.app}/public/runners.json`));
let list: RunnerFamily[] = JSON.parse(await Neutralino.filesystem.readFile(`${Constants.paths.app}/public/runners.json`));
// @ts-expect-error
const installed: { entry: string, type: string }[] = await Neutralino.filesystem.readDirectory(runnersDir);
let runners = [];
let runners: RunnerFamily[] = [];
list.forEach((family) => {
let newFamily: RunnerFamily = {
@ -51,16 +210,35 @@ class Runners
});
}
public static download(runner: Runner|Runner['name']): Promise<boolean>
public static download(runner: Runner|Runner['name']): Promise<null|Stream>
{
return new Promise((resolve) => {
return new Promise(async (resolve) => {
// If we provided runner property as a name of the runner
// then we should find this runner and call this method from it
if (typeof runner == 'string')
{
let foundRunner = null;
(await this.get()).forEach((family) => {
family.runners.forEach((familyRunner) => {
if (familyRunner.name == runner)
foundRunner = familyRunner;
});
});
resolve(foundRunner === null ? null : new Stream(foundRunner));
}
// Otherwise we can use runner.uri and so on to download runner
else resolve(new Stream(runner));
});
}
}
export default Runners;
export { Stream };
export type {
Runner,
RunnerFamily