mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2025-03-19 22:50:09 +03:00
pull #9 implementation
This commit is contained in:
parent
9d8e07b037
commit
2a6c3d5780
8 changed files with 152 additions and 135 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
node_modules
|
||||
dist
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
wineprefix-installation.log
|
||||
public/css/index.css
|
||||
public/css/settings.css
|
||||
|
|
16
entry.js
16
entry.js
|
@ -9,6 +9,8 @@ const {
|
|||
|
||||
const path = require('path');
|
||||
|
||||
const { Genshinlib } = require('./public/js/Genshinlib');
|
||||
|
||||
let mainWindow;
|
||||
|
||||
ipcMain.handle('hide-window', () => mainWindow.hide());
|
||||
|
@ -69,10 +71,7 @@ function createWindow ()
|
|||
}
|
||||
|
||||
// Set language on start
|
||||
if(Genshinlib.getConfig().lang.launcher)
|
||||
app.commandLine.appendSwitch('lang', Genshinlib.getConfig().lang.launcher);
|
||||
else
|
||||
app.commandLine.appendSwitch('lang', 'en-us');
|
||||
app.commandLine.appendSwitch('lang', Genshinlib.lang.launcher ?? 'en-us');
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
|
@ -88,10 +87,11 @@ app.whenReady().then(() => {
|
|||
createWindow();
|
||||
});
|
||||
|
||||
// This has to be here otherwise webContents is invalid.
|
||||
ipcMain.on('changelang', (event, args) => {
|
||||
app.commandLine.appendSwitch('lang', Genshinlib.getConfig().lang.launcher);
|
||||
mainWindow.webContents.send('changelang', { 'lang': args.lang });
|
||||
// This has to be here otherwise webContents is invalid
|
||||
ipcMain.on('change-lang', (event, args) => {
|
||||
app.commandLine.appendSwitch('lang', Genshinlib.lang.launcher);
|
||||
|
||||
mainWindow.webContents.send('change-lang', { 'lang': args.lang });
|
||||
});
|
||||
|
||||
ipcMain.on('updateVP', (event, args) => {
|
||||
|
|
|
@ -18,14 +18,15 @@
|
|||
<div class="menu">
|
||||
<div class="menu-item menu-item-active" anchor="general" i18id="GeneralSettings">General</div>
|
||||
<div class="menu-item" anchor="runners" i18id="Runners">Runners</div>
|
||||
<div class="menu-item" anchor="dxvks">DXVK</div>
|
||||
<div class="menu-item" anchor="dxvks" i18id="DXVK">DXVK</div>
|
||||
</div>
|
||||
|
||||
<div class="settings">
|
||||
<div class="settings-item" id="general">
|
||||
<h2 i18id="GeneralSettings">General</h2>
|
||||
<hr>
|
||||
|
||||
<h3 i18id="Langs">Language</h3>
|
||||
|
||||
<select class="dropdown-menu" id="language-list">
|
||||
<option value="en-us">English (US)</option>
|
||||
<option value="zh-cn">中文(简化)(Chinese Simplified)</option>
|
||||
|
@ -41,20 +42,27 @@
|
|||
<option value="zh-tw">中文(繁體)(Chinese Traditional)</option>
|
||||
<option value="vi-vn">Tiếng Việt (Vietnamese)</option>
|
||||
</select>
|
||||
|
||||
<br>
|
||||
|
||||
<h3 i18id="Voice">Voice Pack</h3>
|
||||
|
||||
<select class="dropdown-menu" id="voice-list" disabled>
|
||||
<option value="en-us">English (US)</option>
|
||||
<option value="zh-cn">汉语 (Chinese)</option>
|
||||
<option value="ja-jp">日本語 (Japanese)</option>
|
||||
<option value="ko-kr">한국어 (Korean)</option>
|
||||
</select>
|
||||
|
||||
<br>
|
||||
|
||||
<h3>Discord RPC</h3>
|
||||
|
||||
<input type="checkbox" id="drpc" name="drpc">
|
||||
|
||||
<br>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<div class="settings-item" id="runners">
|
||||
<h2>Runners</h2>
|
||||
|
||||
|
|
|
@ -93,9 +93,9 @@ export class Genshinlib
|
|||
public static getDXVKs (): Promise<DXVK[]>
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(this.runnersUri)
|
||||
fetch(this.dxvksUri)
|
||||
.then(response => response.json())
|
||||
.then(runners => resolve(runners));
|
||||
.then(dxvks => resolve(dxvks));
|
||||
});
|
||||
|
||||
// return new Promise(resolve => resolve(JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'dxvks.json')))));
|
||||
|
@ -510,4 +510,4 @@ export class Genshinlib
|
|||
});
|
||||
});
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import $ from 'cash-dom';
|
||||
import i18n from './i18n';
|
||||
import { Genshinlib } from './Genshinlib';
|
||||
import { i18n } from './i18n';
|
||||
|
||||
type LauncherState =
|
||||
'patch-unavailable' |
|
||||
|
@ -18,11 +19,6 @@ export class LauncherUI
|
|||
return this._launcherState;
|
||||
}
|
||||
|
||||
public static refreshLang (langcode: string)
|
||||
{
|
||||
i18n.updatelang(langcode);
|
||||
}
|
||||
|
||||
public static setState (state: LauncherState)
|
||||
{
|
||||
$('#downloader-panel').css('display', 'none');
|
||||
|
@ -154,4 +150,9 @@ export class LauncherUI
|
|||
|
||||
$('#downloader .progress').css('width', '0');
|
||||
}
|
||||
|
||||
public static updateBackground (): void
|
||||
{
|
||||
Genshinlib.getBackgroundUri().then(uri => $('body').css('background-image', `url(${uri})`));
|
||||
}
|
||||
}
|
|
@ -1,39 +1,44 @@
|
|||
const path = require("path");
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
let loadedLanguage: any;
|
||||
|
||||
function i18n(): any {
|
||||
if(fs.existsSync(path.join(path.dirname(__dirname), 'locales', navigator.language.toLowerCase() + '.json'))) {
|
||||
loadedLanguage = JSON.parse(fs.readFileSync(path.join(path.dirname(__dirname), 'locales', navigator.language.toLowerCase() + '.json'), 'utf8'));
|
||||
export class i18n
|
||||
{
|
||||
public static loadedLanguage: any;
|
||||
|
||||
public static translate (phrase: string)
|
||||
{
|
||||
if (i18n.loadedLanguage === undefined)
|
||||
this.setLang(navigator.language);
|
||||
|
||||
return i18n.loadedLanguage[phrase] ?? phrase;
|
||||
}
|
||||
else {
|
||||
loadedLanguage = JSON.parse(fs.readFileSync(path.join(path.dirname(__dirname), 'locales', 'en.json'), 'utf8'));
|
||||
}
|
||||
};
|
||||
|
||||
i18n.prototype.translate = function(phrase: any) {
|
||||
let translation = loadedLanguage[phrase];
|
||||
public static setLang (lang: string)
|
||||
{
|
||||
lang = lang.toLowerCase();
|
||||
|
||||
if(translation === undefined) {
|
||||
translation = phrase;
|
||||
}
|
||||
// Test if the locale is the same string so if it's de-de or id-id remove -de or -id like navigator.language does.
|
||||
let samecode = new RegExp(`(${lang.replace(/-.*$/, '')}.*){2}`, 'g');
|
||||
|
||||
return translation
|
||||
}
|
||||
if (samecode.test(lang.toLowerCase()))
|
||||
lang = lang.replace(/-.*$/, '');
|
||||
|
||||
i18n.prototype.updatelang = function(newlang: string) {
|
||||
// Test if the locale is the same string so if it's de-de or id-id remove -de or -id like navigator.language does.
|
||||
let samecode = new RegExp(`(${newlang.toLowerCase().replace(/-.*$/, '')}.*){2}`, 'g');
|
||||
samecode.test(newlang.toLowerCase()) ? newlang = newlang.toLowerCase().replace(/-.*$/, '') : newlang = newlang.toLowerCase();
|
||||
if (newlang == 'ja-jp') newlang = 'ja';
|
||||
if (newlang == 'vi-vn') newlang = 'vi';
|
||||
switch (lang)
|
||||
{
|
||||
case 'ja-jp':
|
||||
lang = 'ja';
|
||||
|
||||
if(fs.existsSync(path.join(path.dirname(__dirname), 'locales', newlang + '.json'))) {
|
||||
loadedLanguage = JSON.parse(fs.readFileSync(path.join(path.dirname(__dirname), 'locales', newlang + '.json'), 'utf8'));
|
||||
}
|
||||
else {
|
||||
loadedLanguage = JSON.parse(fs.readFileSync(path.join(path.dirname(__dirname), 'locales', 'en.json'), 'utf8'));
|
||||
break;
|
||||
|
||||
case 'vi-vn':
|
||||
lang = 'vi';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
i18n.loadedLanguage = JSON.parse(fs.readFileSync(path.join(path.dirname(__dirname), 'locales',
|
||||
fs.existsSync(path.join(path.dirname(__dirname), 'locales', lang + '.json')) ?
|
||||
lang + '.json' : 'en.json'
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
export default new (i18n as any);
|
120
src/ts/index.ts
120
src/ts/index.ts
|
@ -3,10 +3,9 @@ const fs = require('fs');
|
|||
const discordrpc = require("discord-rpc");
|
||||
const { exec } = require('child_process');
|
||||
const { ipcRenderer } = require('electron');
|
||||
let rpc: any;
|
||||
|
||||
import $ from 'cash-dom';
|
||||
import i18n from './i18n';
|
||||
import { i18n } from './i18n';
|
||||
|
||||
import { Genshinlib } from './Genshinlib';
|
||||
import { LauncherUI } from './LauncherUI';
|
||||
|
@ -17,32 +16,51 @@ if (!fs.existsSync(Genshinlib.prefixDir))
|
|||
if (!fs.existsSync(Genshinlib.runnersDir))
|
||||
fs.mkdirSync(Genshinlib.runnersDir, { recursive: true });
|
||||
|
||||
if (!fs.existsSync(Genshinlib.dxvksDir))
|
||||
fs.mkdirSync(Genshinlib.dxvksDir, { recursive: true });
|
||||
|
||||
$(() => {
|
||||
if (Genshinlib.version !== null)
|
||||
document.title = 'Genshin Impact Linux Launcher - ' + Genshinlib.version;
|
||||
|
||||
if (Genshinlib.getConfig().rpc) {
|
||||
rpc = new discordrpc.Client({ transport: "ipc" });
|
||||
LauncherUI.setState('game-launch-available');
|
||||
LauncherUI.updateBackground();
|
||||
|
||||
fetch(`https://genshin.mihoyo.com/launcher/10/${Genshinlib.lang.launcher}?api_url=https%3A%2F%2Fapi-os-takumi.mihoyo.com%2Fhk4e_global&prev=false`)
|
||||
.then(res => res.text())
|
||||
.then(body => {
|
||||
$(body).find('#__layout').appendTo('#launchcontent');
|
||||
|
||||
$('#launchcontent .home__main .home-swiper-wrap').remove();
|
||||
$('#launchcontent .home__main .home-news').remove();
|
||||
});
|
||||
|
||||
ipcRenderer.on('change-lang', (event: void, data: any) => {
|
||||
LauncherUI.updateBackground();
|
||||
LauncherUI.setState(LauncherUI.launcherState);
|
||||
|
||||
i18n.setLang(data.lang);
|
||||
});
|
||||
|
||||
let rpc: any;
|
||||
|
||||
// FIXME
|
||||
if (Genshinlib.getConfig().rpc)
|
||||
{
|
||||
rpc = new discordrpc.Client({ transport: 'ipc' });
|
||||
rpc.login({ clientId: '901534333360304168' }).catch(console.error);
|
||||
|
||||
rpc.on('ready', () => {
|
||||
rpc.setActivity({
|
||||
details: `Preparing to launch`,
|
||||
largeImageKey: `launcher`,
|
||||
largeImageText: `An Anime Game Launcher`,
|
||||
details: 'Preparing to launch',
|
||||
largeImageKey: 'launcher',
|
||||
largeImageText: 'An Anime Game Launcher',
|
||||
instance: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
LauncherUI.setState('game-launch-available');
|
||||
|
||||
ipcRenderer.on('changelang', (event: void, data: any) => {
|
||||
Genshinlib.getBackgroundUri().then(uri => $('body').css('background-image', `url(${ uri })`));
|
||||
LauncherUI.refreshLang(data.lang);
|
||||
LauncherUI.setState(LauncherUI.launcherState);
|
||||
});
|
||||
|
||||
// FIXME
|
||||
ipcRenderer.on('rpcstate', (event: void, data: any) => {
|
||||
if(!rpc) {
|
||||
rpc = new discordrpc.Client({ transport: "ipc" });
|
||||
|
@ -71,6 +89,7 @@ $(() => {
|
|||
}
|
||||
});
|
||||
|
||||
// FIXME
|
||||
ipcRenderer.on('updateVP', (event: void, remotedata: any) => {
|
||||
Genshinlib.getData().then(data => {
|
||||
LauncherUI.initProgressBar();
|
||||
|
@ -122,17 +141,6 @@ $(() => {
|
|||
});
|
||||
});
|
||||
|
||||
Genshinlib.getBackgroundUri().then(uri => $('body').css('background-image', `url(${ uri })`));
|
||||
|
||||
fetch(`https://genshin.mihoyo.com/launcher/10/${Genshinlib.lang.launcher}?api_url=https%3A%2F%2Fapi-os-takumi.mihoyo.com%2Fhk4e_global&prev=false`)
|
||||
.then(res => res.text())
|
||||
.then(body => {
|
||||
$(body).find('#__layout').appendTo('#launchcontent');
|
||||
|
||||
$('#launchcontent .home__main .home-swiper-wrap').remove();
|
||||
$('#launchcontent .home__main .home-news').remove();
|
||||
});
|
||||
|
||||
Genshinlib.getData().then(data => {
|
||||
// Update available
|
||||
if (Genshinlib.version != data.game.latest.version)
|
||||
|
@ -242,38 +250,44 @@ $(() => {
|
|||
|
||||
console.log(`Wine executable: ${wineExeutable}`);
|
||||
|
||||
if (rpc)
|
||||
rpc.setActivity({
|
||||
details: `In-Game`,
|
||||
largeImageKey: `game`,
|
||||
largeImageText: `An Anime Game Launcher`,
|
||||
startTimestamp: parseInt(new Date().setDate(new Date().getDate()).toString()),
|
||||
instance: false,
|
||||
});
|
||||
|
||||
exec(`${wineExeutable} launcher.bat`, {
|
||||
cwd: Genshinlib.gameDir,
|
||||
env: {
|
||||
...process.env,
|
||||
WINEPREFIX: Genshinlib.prefixDir
|
||||
// FIXME
|
||||
if (rpc)
|
||||
{
|
||||
rpc.setActivity({
|
||||
details: `In-Game`,
|
||||
largeImageKey: `game`,
|
||||
largeImageText: `An Anime Game Launcher`,
|
||||
startTimestamp: parseInt(new Date().setDate(new Date().getDate()).toString()),
|
||||
instance: false,
|
||||
});
|
||||
}
|
||||
}, (err: any, stdout: any, stderr: any) => {
|
||||
console.log(`%c> Game closed`, 'font-size: 16px');
|
||||
|
||||
exec(`${wineExeutable} launcher.bat`, {
|
||||
cwd: Genshinlib.gameDir,
|
||||
env: {
|
||||
...process.env,
|
||||
WINEPREFIX: Genshinlib.prefixDir
|
||||
}
|
||||
}, (err: any, stdout: any, stderr: any) => {
|
||||
console.log(`%c> Game closed`, 'font-size: 16px');
|
||||
|
||||
ipcRenderer.invoke('show-window');
|
||||
|
||||
if (rpc)
|
||||
rpc.setActivity({
|
||||
details: `Preparing to launch`,
|
||||
largeImageKey: `launcher`,
|
||||
largeImageText: `An Anime Game Launcher`,
|
||||
instance: false,
|
||||
});
|
||||
// FIXME
|
||||
if (rpc)
|
||||
{
|
||||
rpc.setActivity({
|
||||
details: `Preparing to launch`,
|
||||
largeImageKey: `launcher`,
|
||||
largeImageText: `An Anime Game Launcher`,
|
||||
instance: false,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(err);
|
||||
console.log(stdout);
|
||||
console.log(stderr);
|
||||
});
|
||||
console.log(err);
|
||||
console.log(stdout);
|
||||
console.log(stderr);
|
||||
});
|
||||
|
||||
ipcRenderer.invoke('hide-window');
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@ const { ipcRenderer } = require('electron');
|
|||
const { exec } = require('child_process');
|
||||
|
||||
import $ from 'cash-dom';
|
||||
import i18n from './i18n';
|
||||
import { i18n } from './i18n';
|
||||
import { Genshinlib } from './Genshinlib';
|
||||
|
||||
$(() => {
|
||||
|
||||
$("*[i18id]").each((i, el) => {
|
||||
el.innerText = i18n.translate(el.getAttribute('i18id')?.toString());
|
||||
$('*[i18id]').each((i, element) => {
|
||||
element.innerText = i18n.translate(element.getAttribute('i18id')?.toString()!);
|
||||
});
|
||||
|
||||
$('.menu-item').on('click', (e) => {
|
||||
|
@ -27,22 +27,14 @@ $(() => {
|
|||
$(`.menu-item[anchor=${anchor}]`).addClass('menu-item-active');
|
||||
});
|
||||
|
||||
// Select the saved options in launcher.json on load.
|
||||
$(`#voice-list option[value="${Genshinlib.getConfig().lang.voice}"]`).prop('selected', true);
|
||||
// Select the saved options in launcher.json on load
|
||||
$(`#voice-list option[value="${Genshinlib.lang.voice}"]`).prop('selected', true);
|
||||
$(`#language-list option[value="${Genshinlib.lang.launcher}"]`).prop('selected', true);
|
||||
|
||||
if (Genshinlib.getConfig().rpc)
|
||||
$(`#drpc`).prop('checked', true);
|
||||
$(`#language-list option[value="${Genshinlib.getConfig().lang.launcher}"]`).prop('selected', true);
|
||||
|
||||
$('#drpc').on('change', (e) => {
|
||||
if ($("#drpc").is(':checked'))
|
||||
{
|
||||
ipcRenderer.send('rpcstate', {});
|
||||
}
|
||||
else
|
||||
{
|
||||
ipcRenderer.send('rpcstate', {});
|
||||
}
|
||||
})
|
||||
$('#drpc').on('change', () => ipcRenderer.send('rpcstate', {}));
|
||||
|
||||
$('#voice-list').on('change', (e) => {
|
||||
let activeVP = Genshinlib.getConfig().lang.voice;
|
||||
|
@ -61,25 +53,24 @@ $(() => {
|
|||
$(`#voice-list option[value="${activeVP}"]`).removeProp('selected');
|
||||
$(`#voice-list option[value="${e.target.value}"]`).prop('selected', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('VP can\' be changed to the already set language');
|
||||
}
|
||||
|
||||
else console.log('VP can\' be changed to the already set language');
|
||||
});
|
||||
|
||||
$('#language-list').on('change', (e) => {
|
||||
let activeLNG = Genshinlib.getConfig().lang.launcher;
|
||||
let activeLang = Genshinlib.lang.launcher;
|
||||
|
||||
if (activeLNG != e.target.value)
|
||||
if (activeLang != e.target.value)
|
||||
{
|
||||
Genshinlib.updateConfig({
|
||||
lang: {
|
||||
launcher: e.target.value,
|
||||
voice: Genshinlib.getConfig().lang.voice
|
||||
voice: Genshinlib.lang.voice
|
||||
}
|
||||
});
|
||||
|
||||
// This is required as the file name changes on the API but since we don't call the API before checking if the time is null or expired we set time to null here.
|
||||
// This is required as the file name changes on the API but since we don't call the API before checking
|
||||
// if the time is null or expired we set time to null here.
|
||||
Genshinlib.updateConfig({
|
||||
background: {
|
||||
time: null,
|
||||
|
@ -88,19 +79,16 @@ $(() => {
|
|||
});
|
||||
|
||||
// Send language updates
|
||||
i18n.updatelang(e.target.value);
|
||||
ipcRenderer.send('changelang', { 'lang': e.target.value });
|
||||
$("*[i18id]").each((i, el) => {
|
||||
el.innerText = i18n.translate(el.getAttribute('i18id')?.toString());
|
||||
i18n.setLang(e.target.value);
|
||||
ipcRenderer.send('change-lang', { 'lang': e.target.value });
|
||||
|
||||
$('*[i18id]').each((i, element) => {
|
||||
element.innerText = i18n.translate(element.getAttribute('i18id')?.toString()!);
|
||||
});
|
||||
|
||||
$(`#language-list option[value="${activeLNG}"]`).removeProp('selected');
|
||||
$(`#language-list option[value="${activeLang}"]`).removeProp('selected');
|
||||
$(`#language-list option[value="${e.target.value}"]`).prop('selected', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('New language can\' be changed to the already set language');
|
||||
}
|
||||
});
|
||||
|
||||
let activeRunner = Genshinlib.getConfig().runner;
|
||||
|
|
Loading…
Add table
Reference in a new issue