mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2025-02-16 23:31:29 +03:00
gill 0.1.0 init release
- game installation - game starting
This commit is contained in:
parent
d4698f11fa
commit
4ca47f51ca
11 changed files with 2060 additions and 1587 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
node_modules
|
||||
dist
|
||||
package-lock.json
|
||||
package-lock.json
|
||||
wineprefix-installation.log
|
17
entry.js
17
entry.js
|
@ -1,12 +1,17 @@
|
|||
const { app, BrowserWindow } = require('electron');
|
||||
const { app, BrowserWindow, ipcMain } = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
let mainWindow;
|
||||
|
||||
ipcMain.handle('hide-window', () => mainWindow.hide());
|
||||
ipcMain.handle('show-window', () => mainWindow.show());
|
||||
|
||||
function createWindow ()
|
||||
{
|
||||
// https://www.electronjs.org/docs/latest/api/browser-window/#class-browserwindow
|
||||
const mainWindow = new BrowserWindow ({
|
||||
width: 800,
|
||||
height: 600,
|
||||
mainWindow = new BrowserWindow ({
|
||||
width: 1280,
|
||||
height: 728,
|
||||
webPreferences: {
|
||||
// Is not safety
|
||||
// Use it to have access to the node modules inside html files
|
||||
|
@ -14,8 +19,8 @@ function createWindow ()
|
|||
contextIsolation: false
|
||||
},
|
||||
icon: path.join(__dirname, 'public', 'images', 'icon64.png'),
|
||||
// autoHideMenuBar: true,
|
||||
// resizable: false
|
||||
autoHideMenuBar: true,
|
||||
resizable: false
|
||||
});
|
||||
|
||||
mainWindow.loadFile(path.join(__dirname, 'public', 'html', 'index.html'));
|
||||
|
|
23
package.json
23
package.json
|
@ -1,16 +1,8 @@
|
|||
{
|
||||
"name": "electron-blank-app",
|
||||
"version": "1.0.0",
|
||||
"description": "Electron Blank Application",
|
||||
"keywords": [
|
||||
"some",
|
||||
"keywords"
|
||||
],
|
||||
"author": {
|
||||
"name" : "your_name",
|
||||
"email" : "your_email",
|
||||
"url" : "your_site"
|
||||
},
|
||||
"name": "genshin-impact-linux-launcher",
|
||||
"version": "0.1.0",
|
||||
"description": "Genshin Impact Linux Launcher",
|
||||
"author": "Nikita Podvirnyy",
|
||||
"license": "GPL-3.0",
|
||||
"main": "entry.js",
|
||||
"scripts": {
|
||||
|
@ -20,9 +12,9 @@
|
|||
"build:linux": "npm run dev && electron-builder --linux"
|
||||
},
|
||||
"build": {
|
||||
"productName": "Electron Blank Application",
|
||||
"productName": "Genshin Impact Linux Launcher",
|
||||
"artifactName": "${productName}-${os}-${arch}-${version}.${ext}",
|
||||
"appId": "com.electron.blank-app",
|
||||
"appId": "com.krypt0nn.genshin-impact-linux-launcher",
|
||||
"directories": {
|
||||
"output": "dist"
|
||||
},
|
||||
|
@ -75,5 +67,8 @@
|
|||
"electron-builder": "^22.13.1",
|
||||
"sass": "^1.41.0",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"cash-dom": "^8.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,24 @@
|
|||
<link rel="stylesheet" href="../css/index.css">
|
||||
|
||||
<!-- JS scripts -->
|
||||
<script src="../js/test.js"></script>
|
||||
<script>require('../js/index.js');</script>
|
||||
|
||||
<title>Hello World</title>
|
||||
<title>Genshin Impact Linux Launcher</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 class="greeting"></h1>
|
||||
<div id="downloader-panel" style="display: none">
|
||||
<div id="downloader-label">
|
||||
<span id="downloaded">Downloading...</span>
|
||||
<span id="speed"></span>
|
||||
<span id="eta"></span>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar" id="downloader">
|
||||
<div class="progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="button" id="launch">Launch</button>
|
||||
</body>
|
||||
</html>
|
||||
|
|
BIN
public/images/backgrounds/en-us.png
Normal file
BIN
public/images/backgrounds/en-us.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/backgrounds/ru-ru.png
Normal file
BIN
public/images/backgrounds/ru-ru.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 MiB |
|
@ -1,11 +1,83 @@
|
|||
@mixin center ($width)
|
||||
body
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif
|
||||
|
||||
#info
|
||||
position: absolute
|
||||
|
||||
top: 196px
|
||||
left: 48px
|
||||
|
||||
width: 460px
|
||||
height: 352px
|
||||
|
||||
background-color: rgba(0, 0, 0, .65)
|
||||
|
||||
.button
|
||||
border-radius: 8px
|
||||
background-color: #ffcb0b
|
||||
border: none
|
||||
cursor: pointer
|
||||
|
||||
.button:hover:not([disabled])
|
||||
background-color: #fac60b
|
||||
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, .3)
|
||||
|
||||
.button:disabled
|
||||
background-color: rgba(0, 0, 0, .35)
|
||||
color: rgba(255, 255, 255, .7)
|
||||
|
||||
#launch
|
||||
width: 238px
|
||||
height: 64px
|
||||
|
||||
font-size: 22px
|
||||
|
||||
position: absolute
|
||||
|
||||
width: $width
|
||||
left: calc(50% - $width / 2)
|
||||
top: calc(50% - $width / 2)
|
||||
right: 128px
|
||||
bottom: 64px
|
||||
|
||||
.greeting
|
||||
@include center(200px)
|
||||
.progress-bar
|
||||
padding: 0
|
||||
|
||||
text-align: center
|
||||
background-color: rgba(0, 0, 0, .1)
|
||||
border: 1px solid rgba(0, 0, 0, .2)
|
||||
border-radius: 8px
|
||||
|
||||
.progress
|
||||
width: 0
|
||||
height: 100%
|
||||
|
||||
background-color: rgba(255, 255, 255, .7)
|
||||
border-radius: 8px
|
||||
|
||||
#downloader-panel
|
||||
user-select: none
|
||||
|
||||
#downloader
|
||||
position: absolute
|
||||
|
||||
left: 48px
|
||||
bottom: 68px
|
||||
|
||||
width: 720px
|
||||
height: 36px
|
||||
|
||||
#downloader-label
|
||||
position: absolute
|
||||
|
||||
left: 48px
|
||||
bottom: 116px
|
||||
|
||||
color: white
|
||||
font-size: 18px
|
||||
|
||||
#downloaded
|
||||
max-width: 720px
|
||||
display: inline-block
|
||||
word-break: break-all
|
||||
|
||||
#speed, #eta
|
||||
margin-left: 8px
|
||||
color: #cccccc
|
||||
font-weight: 100
|
210
src/ts/Genshinlib.ts
Normal file
210
src/ts/Genshinlib.ts
Normal file
|
@ -0,0 +1,210 @@
|
|||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
type Config = {
|
||||
lang: {
|
||||
launcher: 'en-us' | 'ru-ru',
|
||||
voice: 'en-us' | 'ru-ru'
|
||||
},
|
||||
version: string|null
|
||||
};
|
||||
|
||||
export class Genshinlib
|
||||
{
|
||||
public static readonly launcherDir: string = path.join(os.homedir(), 'genshin-impact-launcher');
|
||||
public static readonly launcherJson: string = path.join(this.launcherDir, 'launcher.json');
|
||||
|
||||
public static readonly prefixDir: string = path.join(this.launcherDir, 'game');
|
||||
public static readonly gameDir: string = path.join(this.prefixDir, 'drive_c', 'Program Files', 'Genshin Impact');
|
||||
|
||||
protected static uri: string = 'https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?key=gcStgarh&launcher_id=10';
|
||||
|
||||
public static get version(): string|null
|
||||
{
|
||||
return this.getLauncherInfo().version;
|
||||
}
|
||||
|
||||
public static get lang(): { launcher: string, voice: string }
|
||||
{
|
||||
return this.getLauncherInfo().lang;
|
||||
}
|
||||
|
||||
public static getLauncherInfo (): Config
|
||||
{
|
||||
if (!fs.existsSync(this.launcherJson))
|
||||
fs.writeFileSync(this.launcherJson, JSON.stringify({
|
||||
lang: {
|
||||
launcher: 'en-us',
|
||||
voice: 'en-us'
|
||||
},
|
||||
version: null
|
||||
}));
|
||||
|
||||
return JSON.parse(fs.readFileSync(this.launcherJson));
|
||||
}
|
||||
|
||||
public static setLauncherInfo (info: Config): Genshinlib
|
||||
{
|
||||
fs.writeFileSync(this.launcherJson, JSON.stringify(info));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static async getData (): Promise<any>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(this.uri, (response: any) => {
|
||||
let data = '';
|
||||
|
||||
response.on('data', (chunk: any) => data += chunk);
|
||||
|
||||
response.on('end', () => {
|
||||
data = JSON.parse(data);
|
||||
|
||||
// @ts-expect-error
|
||||
return data.message === 'OK' ? resolve(data.data) : reject(null);
|
||||
});
|
||||
}).on('error', (err: Error) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static getBackgroundUri (): string
|
||||
{
|
||||
return path.join(__dirname, '..', 'images', 'backgrounds', this.lang.launcher + '.png');
|
||||
}
|
||||
|
||||
public static async downloadFile (uri: string, savePath: string, progress: (current: number, total: number, difference: number) => void): Promise<void|Error>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(uri, (response: any) => {
|
||||
let length = parseInt(response.headers['content-length'], 10),
|
||||
total = 0;
|
||||
|
||||
response.on('data', (chunk: any) => {
|
||||
total += chunk.length;
|
||||
|
||||
progress(total, length, chunk.length);
|
||||
|
||||
fs.appendFileSync(savePath, chunk);
|
||||
});
|
||||
|
||||
response.on('end', () => resolve());
|
||||
}).on('error', (err: Error) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static async unzip (zipPath: string, unpackedPath: string, progress: (current: number, total: number, difference: number) => void): Promise<void|Error>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
let listenerProcess = spawn('unzip', ['-v', zipPath]),
|
||||
filesList = '';
|
||||
|
||||
listenerProcess.stdout.on('data', (data: string) => filesList += data);
|
||||
|
||||
listenerProcess.on('close', () => {
|
||||
let files = filesList.split(/\r\n|\r|\n/).slice(3, -3).map(line => {
|
||||
line = line.trim();
|
||||
|
||||
if (line.slice(-1) == '/')
|
||||
line = line.slice(0, -1);
|
||||
|
||||
let matches = /^(\d+) [a-zA-Z\:]+[ ]+(\d+)[ ]+[0-9\-]+% [0-9\-]+ [0-9\:]+ [a-f0-9]{8} (.+)/.exec(line);
|
||||
|
||||
return {
|
||||
// @ts-expect-error
|
||||
path: matches[3],
|
||||
|
||||
// @ts-expect-error
|
||||
compressedSize: parseInt(matches[2]),
|
||||
|
||||
// @ts-expect-error
|
||||
uncompressedSize: parseInt(matches[1])
|
||||
};
|
||||
});
|
||||
|
||||
let total = fs.statSync(zipPath)['size'], current = 0;
|
||||
let unpackerProcess = spawn('unzip', ['-o', zipPath, '-d', unpackedPath]);
|
||||
|
||||
unpackerProcess.stdout.on('data', (data: string) => {
|
||||
data.toString().split(/\r\n|\r|\n/).forEach(line => {
|
||||
let items = line.split(': ');
|
||||
|
||||
if (items[1] !== undefined)
|
||||
{
|
||||
items[1] = path.relative(unpackedPath, items[1].trim());
|
||||
|
||||
files.forEach(file => {
|
||||
if (file.path == items[1])
|
||||
{
|
||||
current += file.compressedSize;
|
||||
|
||||
progress(current, total, file.compressedSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
unpackerProcess.on('close', () => resolve());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// WINEPREFIX='/home/observer/genshin-impact-launcher/wineprefix' winetricks corefonts
|
||||
public static async installPrefix (path: string, progress: (output: string, current: number, total: number) => void): Promise<void>
|
||||
{
|
||||
let installationSteps = [
|
||||
'Executing w_do_call corefonts',
|
||||
'Executing load_corefonts',
|
||||
'Executing load_andale',
|
||||
'Executing load_arial',
|
||||
'Executing load_comicsans',
|
||||
'Executing load_courier',
|
||||
'Executing load_georgia',
|
||||
'Executing load_impact',
|
||||
'Executing load_times',
|
||||
'Executing load_trebuchet',
|
||||
'Executing load_verdana',
|
||||
'Executing load_webdings'
|
||||
];
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let installationProgress = 0;
|
||||
|
||||
let installerProcess = spawn('winetricks', ['corefonts'], {
|
||||
env: {
|
||||
...process.env,
|
||||
WINEPREFIX: path
|
||||
}
|
||||
});
|
||||
|
||||
installerProcess.stdout.on('data', (data: string) => {
|
||||
let str = data.toString();
|
||||
|
||||
for (let i = 0; i < installationSteps.length; ++i)
|
||||
if (str.includes(installationSteps[i]))
|
||||
{
|
||||
installationProgress = i + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
progress(str, installationProgress, installationSteps.length);
|
||||
});
|
||||
|
||||
installerProcess.on('close', () => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
public static isPrefixInstalled (prefixPath: string): boolean
|
||||
{
|
||||
return fs.existsSync(path.join(prefixPath, 'drive_c'));
|
||||
}
|
||||
}
|
266
src/ts/index.ts
Normal file
266
src/ts/index.ts
Normal file
|
@ -0,0 +1,266 @@
|
|||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { exec } = require('child_process');
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
import $ from 'cash-dom';
|
||||
|
||||
import { Genshinlib } from './Genshinlib';
|
||||
|
||||
if (!fs.existsSync(Genshinlib.prefixDir))
|
||||
fs.mkdirSync(Genshinlib.prefixDir, { recursive: true });
|
||||
|
||||
$(() => {
|
||||
if (Genshinlib.version !== null)
|
||||
document.title = 'Genshin Impact Linux Launcher - ' + Genshinlib.version;
|
||||
|
||||
$('body').css('background-image', `url(${ Genshinlib.getBackgroundUri() })`);
|
||||
|
||||
Genshinlib.getData().then(data => {
|
||||
if (Genshinlib.version != data.game.latest.version)
|
||||
$('#launch').text(Genshinlib.version === null ? 'Install' : 'Update');
|
||||
|
||||
$('#launch').on('click', async () => {
|
||||
// Creating wine prefix
|
||||
if (!Genshinlib.isPrefixInstalled(Genshinlib.prefixDir))
|
||||
{
|
||||
$('#launch').css('display', 'none');
|
||||
$('#downloader-panel').css('display', 'block');
|
||||
|
||||
await Genshinlib.installPrefix(Genshinlib.prefixDir, (output: string, current: number, total: number) => {
|
||||
output = output.trim();
|
||||
|
||||
console.log(output);
|
||||
|
||||
if (!output.includes('\n') && !output.includes('\r'))
|
||||
$('#downloaded').text(output);
|
||||
|
||||
$('#downloader .progress').css('width', `${ Math.round(current / total * 100) }%`);
|
||||
});
|
||||
|
||||
$('#launch').css('display', 'block');
|
||||
$('#downloader-panel').css('display', 'none');
|
||||
}
|
||||
|
||||
// Launching game
|
||||
if ($('#launch').text() == 'Launch')
|
||||
{
|
||||
exec(`wine "${path.join(Genshinlib.gameDir, 'GenshinImpact.exe')}"`, {
|
||||
env: {
|
||||
...process.env,
|
||||
WINEPREFIX: Genshinlib.prefixDir
|
||||
}
|
||||
}, () => {
|
||||
ipcRenderer.invoke('show-window');
|
||||
});
|
||||
|
||||
ipcRenderer.invoke('hide-window');
|
||||
}
|
||||
|
||||
// Installing game
|
||||
else
|
||||
{
|
||||
$('#launch').css('display', 'none');
|
||||
$('#downloader-panel').css('display', 'block');
|
||||
|
||||
let diff = {
|
||||
path: data.game.latest.path,
|
||||
name: `latest-${data.game.latest.version}.zip`,
|
||||
voice_packs: data.game.latest.voice_packs
|
||||
};
|
||||
|
||||
for (let i = 0; i < data.game.diffs.length; ++i)
|
||||
if (data.game.diffs[i].version == Genshinlib.version)
|
||||
{
|
||||
diff = data.game.diffs[i];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (fs.existsSync(path.join(Genshinlib.gameDir, diff.name)))
|
||||
fs.unlinkSync(path.join(Genshinlib.gameDir, diff.name));
|
||||
|
||||
let beganAt = Date.now(), prevTime = Date.now(), downloaded = 0;
|
||||
|
||||
/**
|
||||
* Downloading game
|
||||
*/
|
||||
|
||||
Genshinlib.downloadFile(diff.path, path.join(Genshinlib.launcherDir, diff.name), (current: number, total: number, difference: number) => {
|
||||
$('#downloaded').text(`Downloaded: ${ Math.round(current / total * 100) }% (${ (current / 1024 / 1024 / 1024).toFixed(2) } GB / ${ Math.round(total / 1024 / 1024 / 1024).toFixed(2) } GB)`);
|
||||
|
||||
downloaded += difference;
|
||||
|
||||
if (Date.now() - prevTime > 1000)
|
||||
{
|
||||
let eta = Math.round(total / current * (Date.now() - beganAt) / 1000); // seconds
|
||||
|
||||
let etaHours = Math.floor(eta / 3600),
|
||||
etaMinutes = Math.floor((eta - etaHours * 3600) / 60),
|
||||
etaSeconds = eta - etaHours * 3600 - etaMinutes * 60;
|
||||
|
||||
if (etaHours < 10) // @ts-expect-error
|
||||
etaHours = '0' + etaHours.toString();
|
||||
|
||||
if (etaMinutes < 10) // @ts-expect-error
|
||||
etaMinutes = '0' + etaMinutes.toString();
|
||||
|
||||
if (etaSeconds < 10) // @ts-expect-error
|
||||
etaSeconds = '0' + etaSeconds.toString();
|
||||
|
||||
$('#downloader .progress').css('width', `${ Math.round(current / total * 100) }%`);
|
||||
$('#speed').text(`${ (downloaded / (Date.now() - prevTime) * 1000 / 1024 / 1024).toFixed(2) } MB/s`);
|
||||
$('#eta').text(`ETA: ${etaHours}:${etaMinutes}:${etaSeconds}`);
|
||||
|
||||
prevTime = Date.now();
|
||||
downloaded = 0;
|
||||
}
|
||||
}).then(() => {
|
||||
/**
|
||||
* Unpacking downloaded game
|
||||
*/
|
||||
|
||||
$('#speed').text('');
|
||||
$('#eta').text('');
|
||||
|
||||
if (!fs.existsSync(Genshinlib.gameDir))
|
||||
fs.mkdirSync(Genshinlib.gameDir, { recursive: true });
|
||||
|
||||
let beganAt = Date.now(), prevTime = Date.now(), unpacked = 0;
|
||||
|
||||
Genshinlib.unzip(path.join(Genshinlib.launcherDir, diff.name), Genshinlib.gameDir, (current: number, total: number, difference: number) => {
|
||||
$('#downloaded').text(`Unpacking: ${ Math.round(current / total * 100) }% (${ (current / 1024 / 1024 / 1024).toFixed(2) } GB / ${ Math.round(total / 1024 / 1024 / 1024).toFixed(2) } GB)`);
|
||||
|
||||
unpacked += difference;
|
||||
|
||||
if (Date.now() - prevTime > 1000)
|
||||
{
|
||||
let eta = Math.round(total / current * (Date.now() - beganAt) / 1000); // seconds
|
||||
|
||||
let etaHours = Math.floor(eta / 3600),
|
||||
etaMinutes = Math.floor((eta - etaHours * 3600) / 60),
|
||||
etaSeconds = eta - etaHours * 3600 - etaMinutes * 60;
|
||||
|
||||
if (etaHours < 10) // @ts-expect-error
|
||||
etaHours = '0' + etaHours.toString();
|
||||
|
||||
if (etaMinutes < 10) // @ts-expect-error
|
||||
etaMinutes = '0' + etaMinutes.toString();
|
||||
|
||||
if (etaSeconds < 10) // @ts-expect-error
|
||||
etaSeconds = '0' + etaSeconds.toString();
|
||||
|
||||
$('#downloader .progress').css('width', `${ Math.round(current / total * 100) }%`);
|
||||
$('#speed').text(`${ (unpacked / (Date.now() - prevTime) * 1000 / 1024 / 1024).toFixed(2) } MB/s`);
|
||||
$('#eta').text(`ETA: ${etaHours}:${etaMinutes}:${etaSeconds}`);
|
||||
|
||||
prevTime = Date.now();
|
||||
unpacked = 0;
|
||||
}
|
||||
}).then(() => {
|
||||
fs.unlinkSync(path.join(Genshinlib.launcherDir, diff.name));
|
||||
|
||||
let voicePack = diff.voice_packs[1]; // en-us
|
||||
|
||||
for (let i = 0; i < diff.voice_packs.length; ++i)
|
||||
if (diff.voice_packs[i].language == Genshinlib.lang.voice)
|
||||
{
|
||||
voicePack = diff.voice_packs[i];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
let beganAt = Date.now(), prevTime = Date.now(), downloaded = 0;
|
||||
|
||||
/**
|
||||
* Downloading voice data
|
||||
*/
|
||||
|
||||
Genshinlib.downloadFile(voicePack.path, path.join(Genshinlib.launcherDir, voicePack.name), (current: number, total: number, difference: number) => {
|
||||
$('#downloaded').text(`Downloaded: ${ Math.round(current / total * 100) }% (${ (current / 1024 / 1024 / 1024).toFixed(2) } GB / ${ Math.round(total / 1024 / 1024 / 1024).toFixed(2) } GB)`);
|
||||
|
||||
downloaded += difference;
|
||||
|
||||
if (Date.now() - prevTime > 1000)
|
||||
{
|
||||
let eta = Math.round(total / current * (Date.now() - beganAt) / 1000); // seconds
|
||||
|
||||
let etaHours = Math.floor(eta / 3600),
|
||||
etaMinutes = Math.floor((eta - etaHours * 3600) / 60),
|
||||
etaSeconds = eta - etaHours * 3600 - etaMinutes * 60;
|
||||
|
||||
if (etaHours < 10) // @ts-expect-error
|
||||
etaHours = '0' + etaHours.toString();
|
||||
|
||||
if (etaMinutes < 10) // @ts-expect-error
|
||||
etaMinutes = '0' + etaMinutes.toString();
|
||||
|
||||
if (etaSeconds < 10) // @ts-expect-error
|
||||
etaSeconds = '0' + etaSeconds.toString();
|
||||
|
||||
$('#downloader .progress').css('width', `${ Math.round(current / total * 100) }%`);
|
||||
$('#speed').text(`${ (downloaded / (Date.now() - prevTime) * 1000 / 1024 / 1024).toFixed(2) } MB/s`);
|
||||
$('#eta').text(`ETA: ${etaHours}:${etaMinutes}:${etaSeconds}`);
|
||||
|
||||
prevTime = Date.now();
|
||||
downloaded = 0;
|
||||
}
|
||||
}).then(() => {
|
||||
/**
|
||||
* Unpacking downloaded game
|
||||
*/
|
||||
|
||||
$('#speed').text('');
|
||||
$('#eta').text('');
|
||||
|
||||
let beganAt = Date.now(), prevTime = Date.now(), unpacked = 0;
|
||||
|
||||
Genshinlib.unzip(path.join(Genshinlib.launcherDir, voicePack.name), Genshinlib.gameDir, (current: number, total: number, difference: number) => {
|
||||
$('#downloaded').text(`Unpacking: ${ Math.round(current / total * 100) }% (${ (current / 1024 / 1024 / 1024).toFixed(2) } GB / ${ Math.round(total / 1024 / 1024 / 1024).toFixed(2) } GB)`);
|
||||
|
||||
unpacked += difference;
|
||||
|
||||
if (Date.now() - prevTime > 1000)
|
||||
{
|
||||
let eta = Math.round(total / current * (Date.now() - beganAt) / 1000); // seconds
|
||||
|
||||
let etaHours = Math.floor(eta / 3600),
|
||||
etaMinutes = Math.floor((eta - etaHours * 3600) / 60),
|
||||
etaSeconds = eta - etaHours * 3600 - etaMinutes * 60;
|
||||
|
||||
if (etaHours < 10) // @ts-expect-error
|
||||
etaHours = '0' + etaHours.toString();
|
||||
|
||||
if (etaMinutes < 10) // @ts-expect-error
|
||||
etaMinutes = '0' + etaMinutes.toString();
|
||||
|
||||
if (etaSeconds < 10) // @ts-expect-error
|
||||
etaSeconds = '0' + etaSeconds.toString();
|
||||
|
||||
$('#downloader .progress').css('width', `${ Math.round(current / total * 100) }%`);
|
||||
$('#speed').text(`${ (unpacked / (Date.now() - prevTime) * 1000 / 1024 / 1024).toFixed(2) } MB/s`);
|
||||
$('#eta').text(`ETA: ${etaHours}:${etaMinutes}:${etaSeconds}`);
|
||||
|
||||
prevTime = Date.now();
|
||||
unpacked = 0;
|
||||
}
|
||||
}).then(() => {
|
||||
fs.unlinkSync(path.join(Genshinlib.launcherDir, voicePack.name));
|
||||
|
||||
Genshinlib.setLauncherInfo({
|
||||
...Genshinlib.getLauncherInfo(),
|
||||
version: data.game.latest.version
|
||||
});
|
||||
|
||||
$('#launch').css('display', 'block');
|
||||
$('#downloader-panel').css('display', 'none');
|
||||
|
||||
$('#launch').text('Launch');
|
||||
});
|
||||
}).catch(err => console.log(err));
|
||||
}).catch(err => console.log(err));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
// When page is fully loaded
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
// Change the text inside tags with greeting class
|
||||
// @ts-ignore
|
||||
document.querySelector('.greeting').innerText = 'Hello World';
|
||||
}, false);
|
Loading…
Add table
Reference in a new issue