Skip to content

Commit

Permalink
Merge branch 'main' into fix/store-state-typing
Browse files Browse the repository at this point in the history
  • Loading branch information
Hiroshiba authored Jan 3, 2025
2 parents 4458c77 + 8a6d2a4 commit 6a8ef0f
Show file tree
Hide file tree
Showing 57 changed files with 813 additions and 376 deletions.
7 changes: 7 additions & 0 deletions .github/actions/download-engine/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ runs:
mkdir -p $DEST
mv $TEMPDIR/tmp-extract/$TARGET/* $DEST
# 実行ファイルのパーミッションを変更
if [ "${{ runner.os }}" = "Windows" ]; then
chmod +x $DEST/run.exe
else
chmod +x $DEST/run
fi
echo "::group::ll $DEST"
ls -al $DEST
echo "::endgroup::"
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/merge_gatekeeper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
self: merge_gatekeeper
# https://github.com/upsidr/merge-gatekeeper/issues/71#issuecomment-1660607977
ref: ${{ github.event.pull_request && github.event.pull_request.head.ref || github.ref }}
ref: ${{ github.event.pull_request && github.event.pull_request.head.sha || github.ref }}
timeout: 18000 # 5 hours
3 changes: 0 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,6 @@ jobs:

- name: Setup
run: |
# run.exe
chmod +x ${{ steps.download-engine.outputs.run_path }}
# .env
sed -i -e 's|"074fc39e-678b-4c13-8916-ffca8d505d1d"|"208cf94d-43d2-4cf5-abc0-9783cac36d29"|' .env.test
sed -i -e 's|"../voicevox_engine/run.exe"|"${{ steps.download-engine.outputs.run_path }}"|' .env.test
Expand Down
2 changes: 1 addition & 1 deletion openapi.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/backend/browser/browserConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const defaultEngineId = EngineId(defaultEngine.uuid);
export async function getConfigManager() {
await configManagerLock.acquire("configManager", async () => {
if (!configManager) {
configManager = new BrowserConfigManager(isMac);
configManager = new BrowserConfigManager({ isMac });
await configManager.initialize();
}
});
Expand Down
38 changes: 20 additions & 18 deletions src/backend/common/ConfigManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
getDefaultHotkeySettings,
HotkeySettingType,
} from "@/domain/hotkeyAction";
import { isMac } from "@/helpers/platform";

const lockKey = "save";

Expand Down Expand Up @@ -300,6 +299,7 @@ export type Metadata = {
*/
export abstract class BaseConfigManager {
protected config: ConfigType | undefined;
protected isMac: boolean;

private lock = new AsyncLock();

Expand All @@ -309,7 +309,9 @@ export abstract class BaseConfigManager {

protected abstract getAppVersion(): string;

constructor(protected isMac: boolean) {}
constructor({ isMac }: { isMac: boolean }) {
this.isMac = isMac;
}

public reset() {
this.config = this.getDefaultConfig();
Expand All @@ -326,7 +328,7 @@ export abstract class BaseConfigManager {
}
}
this.config = this.migrateHotkeySettings(
getConfigSchema(this.isMac).parse(data),
getConfigSchema({ isMac: this.isMac }).parse(data),
);
this._save();
} else {
Expand Down Expand Up @@ -357,7 +359,7 @@ export abstract class BaseConfigManager {
private _save() {
void this.lock.acquire(lockKey, async () => {
await this.save({
...getConfigSchema(this.isMac).parse({
...getConfigSchema({ isMac: this.isMac }).parse({
...this.config,
}),
__internal__: {
Expand Down Expand Up @@ -392,22 +394,22 @@ export abstract class BaseConfigManager {
private migrateHotkeySettings(data: ConfigType): ConfigType {
const COMBINATION_IS_NONE = HotkeyCombination("####");
const loadedHotkeys = structuredClone(data.hotkeySettings);
const hotkeysWithoutNewCombination = getDefaultHotkeySettings(isMac).map(
(defaultHotkey) => {
const loadedHotkey = loadedHotkeys.find(
(loadedHotkey) => loadedHotkey.action === defaultHotkey.action,
);
const hotkeyWithoutCombination: HotkeySettingType = {
action: defaultHotkey.action,
combination: COMBINATION_IS_NONE,
};
return loadedHotkey ?? hotkeyWithoutCombination;
},
);
const hotkeysWithoutNewCombination = getDefaultHotkeySettings({
isMac: this.isMac,
}).map((defaultHotkey) => {
const loadedHotkey = loadedHotkeys.find(
(loadedHotkey) => loadedHotkey.action === defaultHotkey.action,
);
const hotkeyWithoutCombination: HotkeySettingType = {
action: defaultHotkey.action,
combination: COMBINATION_IS_NONE,
};
return loadedHotkey ?? hotkeyWithoutCombination;
});
const migratedHotkeys = hotkeysWithoutNewCombination.map((hotkey) => {
if (hotkey.combination === COMBINATION_IS_NONE) {
const newHotkey = ensureNotNullish(
getDefaultHotkeySettings(isMac).find(
getDefaultHotkeySettings({ isMac: this.isMac }).find(
(defaultHotkey) => defaultHotkey.action === hotkey.action,
),
);
Expand All @@ -434,6 +436,6 @@ export abstract class BaseConfigManager {
}

protected getDefaultConfig(): ConfigType {
return getConfigSchema(this.isMac).parse({});
return getConfigSchema({ isMac: this.isMac }).parse({});
}
}
2 changes: 1 addition & 1 deletion src/backend/electron/electronConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ let configManager: ElectronConfigManager | undefined;

export function getConfigManager(): ElectronConfigManager {
if (!configManager) {
configManager = new ElectronConfigManager(isMac);
configManager = new ElectronConfigManager({ isMac });
}
return configManager;
}
69 changes: 53 additions & 16 deletions src/backend/electron/engineAndVvppController.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import path from "path";
import fs from "fs";
import { ReadableStream } from "node:stream/web";
import log from "electron-log/main";
import { BrowserWindow, dialog } from "electron";
import { dialog } from "electron";

import { getConfigManager } from "./electronConfig";
import { getEngineInfoManager } from "./manager/engineInfoManager";
import { getEngineProcessManager } from "./manager/engineProcessManager";
import { getRuntimeInfoManager } from "./manager/RuntimeInfoManager";
import { getVvppManager } from "./manager/vvppManager";
import { getWindowManager } from "./manager/windowManager";
import { ProgressCallback } from "./type";
import {
EngineId,
EngineInfo,
Expand Down Expand Up @@ -45,9 +48,12 @@ export class EngineAndVvppController {
/**
* VVPPエンジンをインストールする。
*/
async installVvppEngine(vvppPath: string) {
async installVvppEngine(
vvppPath: string,
callbacks?: { onProgress?: ProgressCallback },
) {
try {
await this.vvppManager.install(vvppPath);
await this.vvppManager.install(vvppPath, callbacks);
return true;
} catch (e) {
log.error(`Failed to install ${vvppPath},`, e);
Expand All @@ -67,14 +73,13 @@ export class EngineAndVvppController {
vvppPath,
reloadNeeded,
reloadCallback,
win,
}: {
vvppPath: string;
reloadNeeded: boolean;
reloadCallback?: () => void; // 再読み込みが必要な場合のコールバック
win: BrowserWindow; // dialog表示に必要。 FIXME: dialog表示関数をDI可能にし、winを削除する
}) {
const result = dialog.showMessageBoxSync(win, {
const windowManager = getWindowManager();
const result = windowManager.showMessageBoxSync({
type: "warning",
title: "エンジン追加の確認",
message: `この操作はコンピュータに損害を与える可能性があります。エンジンの配布元が信頼できない場合は追加しないでください。`,
Expand All @@ -89,8 +94,8 @@ export class EngineAndVvppController {
await this.installVvppEngine(vvppPath);

if (reloadNeeded) {
void dialog
.showMessageBox(win, {
void windowManager
.showMessageBox({
type: "info",
title: "再読み込みが必要です",
message:
Expand Down Expand Up @@ -184,6 +189,7 @@ export class EngineAndVvppController {
async downloadAndInstallVvppEngine(
downloadDir: string,
packageInfo: PackageInfo,
callbacks: { onProgress: ProgressCallback<"download" | "install"> },
) {
if (packageInfo.packages.length === 0) {
throw new UnreachableError("No packages to download");
Expand All @@ -193,27 +199,58 @@ export class EngineAndVvppController {
const downloadedPaths: string[] = [];
try {
// ダウンロード
callbacks.onProgress({ type: "download", progress: 0 });

let totalBytes = 0;
packageInfo.packages.forEach((p) => {
totalBytes += p.size;
});

let downloadedBytes = 0;
await Promise.all(
packageInfo.packages.map(async (p) => {
const { url, name, size } = p;

log.info(`Download ${name} from ${url}, size: ${size}`);
const res = await fetch(url);
const buffer = await res.arrayBuffer();
if (failed) return; // 他のダウンロードが失敗していたら中断

const { url, name } = p;
log.info(`Download ${name} from ${url}`);

const res = await fetch(url);
if (!res.ok || res.body == null)
throw new Error(`Failed to download ${name} from ${url}`);
const downloadPath = path.join(downloadDir, name);
await fs.promises.writeFile(downloadPath, Buffer.from(buffer)); // TODO: オンメモリじゃなくする
log.info(`Downloaded ${name} to ${downloadPath}`);
const fileStream = fs.createWriteStream(downloadPath);

// ファイルに書き込む
// NOTE: なぜか型が合わないのでasを使っている
for await (const chunk of res.body as ReadableStream<Uint8Array>) {
fileStream.write(chunk);
downloadedBytes += chunk.length;
callbacks.onProgress({
type: "download",
progress: (downloadedBytes / totalBytes) * 100,
});
}

// ファイルを確実に閉じる
const { promise, resolve, reject } = Promise.withResolvers();
fileStream.on("close", resolve);
fileStream.on("error", reject);
fileStream.close();
await promise;

downloadedPaths.push(downloadPath);
log.info(`Downloaded ${name} to ${downloadPath}`);

// TODO: ハッシュチェック
}),
);

// インストール
await this.installVvppEngine(downloadedPaths[0]);
await this.installVvppEngine(downloadedPaths[0], {
onProgress: ({ progress }) => {
callbacks.onProgress({ type: "install", progress });
},
});
} catch (e) {
failed = true;
log.error(`Failed to download and install VVPP engine:`, e);
Expand Down
Loading

0 comments on commit 6a8ef0f

Please sign in to comment.