Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change: ブラウザ版のConfigをBaseConfigManagerベースに #1629

Merged
merged 33 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
60f4a14
(add/browser-config)
sevenc-nanashi Oct 28, 2023
ffa2729
Change: ブラウザのコンフィグをConfigManagerベースに
sevenc-nanashi Oct 28, 2023
994a656
Change: store名を変更
sevenc-nanashi Oct 28, 2023
f5a74e4
Merge: origin/main -> add/browser-config
sevenc-nanashi Oct 29, 2023
ff3f473
Add: チェックを追加
sevenc-nanashi Oct 31, 2023
2318674
Add: tmateを追加
sevenc-nanashi Oct 31, 2023
3464c5b
Merge: origin/main -> add/browser-config
sevenc-nanashi Nov 4, 2023
10e8af5
Delete: tmateを削除
sevenc-nanashi Nov 4, 2023
d09df6f
Change: 調整結果.spec.tsに限定
sevenc-nanashi Nov 4, 2023
fdffc37
Change: --を追加
sevenc-nanashi Nov 4, 2023
4d9ea03
Add: console.logを追加
sevenc-nanashi Nov 4, 2023
fc763cc
Change: fail-fast: false
sevenc-nanashi Nov 4, 2023
16d27d0
Fix: CIが落ちてるのを修正
sevenc-nanashi Nov 4, 2023
e880771
Change: リトライしないように
sevenc-nanashi Nov 4, 2023
00a6acb
Change: タイムアウトを延長
sevenc-nanashi Nov 4, 2023
08b59d1
Change: もう少し待つ
sevenc-nanashi Nov 4, 2023
690efaa
Add: consoleのログを追加
sevenc-nanashi Nov 4, 2023
8ceed61
Change: retriesを復活
sevenc-nanashi Nov 4, 2023
7cf4411
Change: テストする物を限定
sevenc-nanashi Nov 4, 2023
7b32a2d
Change: dotfiles持ち込み
sevenc-nanashi Nov 4, 2023
45293ee
Change: engineInfo周りをenvから持ってくるように
sevenc-nanashi Nov 5, 2023
cb2db2b
Delete: 指定を削除
sevenc-nanashi Nov 5, 2023
af1055d
Add: tmate復活
sevenc-nanashi Nov 5, 2023
b7c7d47
Fix: 動いて欲しい(祈りを捧げながら)
sevenc-nanashi Nov 5, 2023
8d6cc72
Delete: consoleのハンドラを削除
sevenc-nanashi Nov 5, 2023
48e898f
Code: FIXMEを追加
sevenc-nanashi Nov 5, 2023
a0695f8
Fix: import.meta.envを使うように
sevenc-nanashi Nov 5, 2023
50bec9a
Refactor: lockで書き直し
sevenc-nanashi Nov 7, 2023
31d07cf
Code: 固定値であることを追記
sevenc-nanashi Nov 9, 2023
4628907
Change: protectedに
sevenc-nanashi Nov 10, 2023
57e6eb1
Add: Mutexを追加
sevenc-nanashi Nov 11, 2023
4d2f0b3
Code: コメントを追加
sevenc-nanashi Nov 11, 2023
1c0b3db
Fix: initialize忘れ
sevenc-nanashi Nov 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
e2e-test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
include:
Expand Down Expand Up @@ -78,9 +79,10 @@ jobs:
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
sed -i -e 's|"executionArgs": \[\],|"executionArgs": ["--port=50021"],|' .env.test
cp .env.test .env
sed -i -e 's|"../voicevox_engine/run.exe"|"${{ steps.download-engine.outputs.run_path }}"|' .env
sed -i -e 's|"executionArgs": \[\],|"executionArgs": ["--port=50021"],|' .env
Comment on lines +82 to -83
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あ、このあたりも必要な変更だった感じでしょうか 👀

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

色々試してたときのやつですね。問題自体はここには無かったのでいらないっちゃ要らないはず。

Copy link
Member

@Hiroshiba Hiroshiba Nov 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほどです!
エンジンIDの変更が意図的かどうかパッとわからないので、やったほうが良さそうであればエンジンリポジトリと同様に環境変数へ、なくても良さそうであればこの行を消すはどうでしょう?

.env.testを書き換えるかはどちらでも良さそう。
(どっちかというと.envを自由に書き換えるほうがローカル変数っぽみがあって直感的だとは思います。.env.testは元からあるのでグローバルっぽみ。)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Viteは.envより.env.testの方が優先度が高いので、.env.env.testは同期しておきたいんですよね

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あ、たしかに!!気をつけないといけない仕様ですね・・・。
(だとしたらexecutionArgsが元の.env.testの空っぽのに上書きされてそうですけど、なんで今まで動いてたんだろう)

.env.test直接書き換えで良い気がしました!


- name: Run npm run test:browser-e2e
run: |
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ if (isElectron) {
const config: PlaywrightTestConfig = {
testDir: "./tests/e2e",
/* Maximum time one test can run for. */
timeout: 120 * 1000,
timeout: 60 * 1000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
Expand Down
1 change: 1 addition & 0 deletions src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ async function launchEngines() {
// エンジンの追加と削除を反映させるためEngineInfoとAltPortInfoを再生成する。
engineManager.initializeEngineInfosAndAltPortInfo();

// TODO: デフォルトエンジンの処理をConfigManagerに移してブラウザ版と共通化する
const engineInfos = engineManager.fetchEngineInfos();
const engineSettings = configManager.get("engineSettings");
for (const engineInfo of engineInfos) {
Expand Down
8 changes: 4 additions & 4 deletions src/background/electronConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import { BaseConfigManager, Metadata } from "@/shared/ConfigManager";
import { ConfigType } from "@/type/preload";

export class ElectronConfigManager extends BaseConfigManager {
getAppVersion() {
protected getAppVersion() {
return app.getVersion();
}

public async exists() {
protected async exists() {
return await fs.promises
.stat(this.configPath)
.then(() => true)
.catch(() => false);
}

public async load(): Promise<Record<string, unknown> & Metadata> {
protected async load(): Promise<Record<string, unknown> & Metadata> {
return JSON.parse(await fs.promises.readFile(this.configPath, "utf-8"));
}

public async save(config: ConfigType) {
protected async save(config: ConfigType & Metadata) {
await fs.promises.writeFile(
this.configPath,
JSON.stringify(config, undefined, 2)
Expand Down
15 changes: 2 additions & 13 deletions src/background/engineManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import shlex from "shlex";
import { app, dialog } from "electron"; // FIXME: ここでelectronをimportするのは良くない

import log from "electron-log/main";
import { z } from "zod";
import {
findAltPort,
getPidFromPort,
Expand All @@ -21,8 +20,8 @@ import {
EngineDirValidationResult,
MinimumEngineManifest,
EngineId,
engineIdSchema,
minimumEngineManifestSchema,
envEngineInfoSchema,
} from "@/type/preload";
import { AltPortInfos } from "@/store/type";
import { BaseConfigManager } from "@/shared/ConfigManager";
Expand All @@ -40,17 +39,7 @@ function createDefaultEngineInfos(defaultEngineDir: string): EngineInfo[] {
const defaultEngineInfosEnv =
import.meta.env.VITE_DEFAULT_ENGINE_INFOS ?? "[]";

const envSchema = z
.object({
uuid: engineIdSchema,
host: z.string(),
name: z.string(),
executionEnabled: z.boolean(),
executionFilePath: z.string(),
executionArgs: z.array(z.string()),
path: z.string().optional(),
})
.array();
const envSchema = envEngineInfoSchema.array();
const engines = envSchema.parse(JSON.parse(defaultEngineInfosEnv));

return engines.map((engineInfo) => {
Expand Down
15 changes: 6 additions & 9 deletions src/browser/contract.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { EngineInfo, EngineId } from "@/type/preload";
import { EngineInfo, envEngineInfoSchema } from "@/type/preload";

const baseEngineInfo = envEngineInfoSchema
.array()
.parse(JSON.parse(import.meta.env.VITE_DEFAULT_ENGINE_INFOS))[0];

export const defaultEngine: EngineInfo = {
uuid: EngineId("074fc39e-678b-4c13-8916-ffca8d505d1d"),
host: "http://127.0.0.1:50021",
name: "VOICEVOX Engine",
path: undefined,
executionEnabled: false,
executionFilePath: "",
executionArgs: [],
...baseEngineInfo,
type: "default",
};

export const directoryHandleStoreKey = "directoryHandle";
6 changes: 3 additions & 3 deletions src/browser/fileImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const showWritableDirectoryPicker = async (): Promise<
export const showOpenDirectoryDialogImpl: typeof window[typeof SandboxKey]["showOpenDirectoryDialog"] =
async () => {
const _directoryHandler = await showWritableDirectoryPicker();
if (_directoryHandler === undefined) {
if (_directoryHandler == undefined) {
return undefined;
}

Expand All @@ -83,15 +83,15 @@ const getDirectoryHandleFromDirectoryPath = async (
): Promise<FileSystemDirectoryHandle> => {
const maybeHandle = directoryHandleMap.get(maybeDirectoryPathKey);

if (maybeHandle !== undefined) {
if (maybeHandle != undefined) {
return maybeHandle;
} else {
// NOTE: fixedDirectoryの場合こっちに落ちる場合がある
const maybeFixedDirectory = await fetchStoredDirectoryHandle(
maybeDirectoryPathKey
);

if (maybeFixedDirectory === undefined) {
if (maybeFixedDirectory == undefined) {
throw new Error(
`フォルダへのアクセス許可がありません。アクセスしようとしたフォルダ名: ${maybeDirectoryPathKey}`
);
Expand Down
13 changes: 8 additions & 5 deletions src/browser/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
showOpenDirectoryDialogImpl,
writeFileImpl,
} from "./fileImpl";
import { getSettingEntry, setSettingEntry } from "./storeImpl";
import { getConfigManager } from "./storeImpl";

import { IpcSOData } from "@/type/ipc";
import {
Expand Down Expand Up @@ -285,11 +285,14 @@ export const api: Sandbox = {
// NOTE: 何もしなくて良さそう
return Promise.resolve();
},
getSetting(key) {
return getSettingEntry(key);
async getSetting(key) {
const configManager = await getConfigManager();
return configManager.get(key);
},
setSetting(key, newValue) {
return setSettingEntry(key, newValue).then(() => getSettingEntry(key));
async setSetting(key, newValue) {
const configManager = await getConfigManager();
configManager.set(key, newValue);
return newValue;
},
async setEngineSetting(engineId: EngineId, engineSetting: EngineSetting) {
const engineSettings = (await this.getSetting(
Expand Down
141 changes: 82 additions & 59 deletions src/browser/storeImpl.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
import AsyncLock from "async-lock";
import { defaultEngine, directoryHandleStoreKey } from "./contract";

import {
configSchema,
ConfigType,
EngineId,
engineSettingSchema,
} from "@/type/preload";
import { BaseConfigManager, Metadata } from "@/shared/ConfigManager";
import { ConfigType, EngineId, engineSettingSchema } from "@/type/preload";

const dbName = `${import.meta.env.VITE_APP_NAME}-web`;
const settingStoreKey = "electronStore";
// FIXME: DBのschemaを変更したら、dbVersionを上げる
// TODO: 気づけるようにしたい
const dbVersion = 1;
const settingStoreKey = "config";
const dbVersion = 1; // 固定値。configのmigrationには使用していない。
// NOTE: settingを複数持つことはないと仮定して、keyを固定してしまう
export const entryKey = "value";
const entryKey = "value";

let configManager: BrowserConfigManager | undefined;

const configManagerLock = new AsyncLock();
const defaultEngineId = EngineId(defaultEngine.uuid);

export async function getConfigManager() {
await configManagerLock.acquire("configManager", async () => {
if (!configManager) {
configManager = new BrowserConfigManager();
await configManager.initialize();
}
});

if (!configManager) {
throw new Error("configManager is undefined");
}

return configManager;
}

const waitRequest = (request: IDBRequest) =>
new Promise<void>((resolve, reject) => {
request.onsuccess = () => {
resolve();
};
request.onerror = () => {
reject(request.error);
};
});

export const openDB = () =>
new Promise<IDBDatabase>((resolve, reject) => {
Expand All @@ -29,20 +54,15 @@ export const openDB = () =>
if (ev.oldVersion === 0) {
// Initialize
const db = request.result;
const baseSchema = configSchema.parse({});

const defaultVoicevoxEngineId = EngineId(defaultEngine.uuid);
baseSchema.engineSettings = {
[defaultVoicevoxEngineId]: engineSettingSchema.parse({}),
};
db.createObjectStore(settingStoreKey).add(baseSchema, entryKey);
db.createObjectStore(settingStoreKey);

// NOTE: fixedExportDirectoryを使用してファイルの書き出しをする際、
// audio.tsの現在の実装では、ディレクトリを選択するモーダルを表示しないようになっている
// ディレクトリへの書き出し権限の要求は、モーダルの表示かディレクトリを指定したファイルの書き出しの時のみで、
// directoryHandleがないと権限の要求が出来ないため、directoryHandleを永続化しておく
db.createObjectStore(directoryHandleStoreKey);
} else if (ev.newVersion !== null && ev.newVersion > ev.oldVersion) {
} else if (ev.newVersion != null && ev.newVersion > ev.oldVersion) {
// TODO: migrate
/* eslint-disable no-console */ // logger みたいなパッケージに切り出して、それに依存する形でもいいかも
console.error(
Expand All @@ -53,49 +73,52 @@ export const openDB = () =>
};
});

export const setSettingEntry = async <Key extends keyof ConfigType>(
key: Key,
newValue: ConfigType[Key]
) => {
const db = await openDB();

// TODO: Schemaに合っているか保存時にvalidationしたい
return new Promise((resolve, reject) => {
const transaction = db.transaction(settingStoreKey, "readwrite");
const store = transaction.objectStore(settingStoreKey);
const getRequest = store.get(entryKey);
getRequest.onsuccess = () => {
const baseSchema = configSchema.parse(getRequest.result);
baseSchema[key] = newValue;
const validatedSchema = configSchema.parse(baseSchema);
const putRequest = store.put(validatedSchema, entryKey);
putRequest.onsuccess = () => {
resolve(putRequest.result);
};
putRequest.onerror = () => {
reject(putRequest.error);
};
};
getRequest.onerror = () => {
reject(getRequest.error);
};
});
};
class BrowserConfigManager extends BaseConfigManager {
protected getAppVersion() {
return import.meta.env.VITE_APP_VERSION;
}
protected async exists() {
const db = await openDB();

export const getSettingEntry = async <Key extends keyof ConfigType>(
key: Key
): Promise<ConfigType[Key]> => {
const db = await openDB();
try {
const transaction = db.transaction(settingStoreKey, "readonly");
const store = transaction.objectStore(settingStoreKey);
const request = store.get(entryKey);
await waitRequest(request);
const result = request.result;
return result != undefined;
} catch (e) {
return false;
}
}
protected async load(): Promise<Record<string, unknown> & Metadata> {
const db = await openDB();

return new Promise((resolve, reject) => {
const transaction = db.transaction(settingStoreKey, "readonly");
const store = transaction.objectStore(settingStoreKey);
const request = store.get(entryKey);
request.onsuccess = () => {
resolve(request.result[key]);
};
request.onerror = () => {
reject(request.error);
};
});
};
await waitRequest(request);
const result = request.result;
if (result == undefined) {
throw new Error("設定ファイルが見つかりません");
}
return JSON.parse(result);
}

protected async save(data: ConfigType & Metadata) {
const db = await openDB();

const transaction = db.transaction(settingStoreKey, "readwrite");
const store = transaction.objectStore(settingStoreKey);
const request = store.put(JSON.stringify(data), entryKey);
await waitRequest(request);
}

protected getDefaultConfig() {
const baseConfig = super.getDefaultConfig();
baseConfig.engineSettings[defaultEngineId] ??= engineSettingSchema.parse(
{}
);
return baseConfig;
}
}
16 changes: 10 additions & 6 deletions src/components/SettingDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -1087,21 +1087,25 @@ const changeShowAddAudioItemButton = async (
const canSetAudioOutputDevice = computed(() => {
return !!HTMLAudioElement.prototype.setSinkId;
});
const currentAudioOutputDeviceComputed = computed<{
key: string;
label: string;
} | null>({
const currentAudioOutputDeviceComputed = computed<
| {
key: string;
label: string;
}
| undefined
>({
get: () => {
// 再生デバイスが見つからなかったらデフォルト値に戻す
// FIXME: watchなどにしてgetter内で操作しないようにする
const device = availableAudioOutputDevices.value?.find(
(device) => device.key === store.state.savingSetting.audioOutputDevice
);
if (device) {
return device;
} else {
} else if (store.state.savingSetting.audioOutputDevice !== "default") {
handleSavingSettingChange("audioOutputDevice", "default");
return null;
}
return undefined;
},
set: (device) => {
if (device) {
Expand Down
Loading