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

エンジンが使うポートが割り当てできなければ、他の空いているポートで起動してユーザーに通知する #1267

Merged
merged 24 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3adb7c1
Add: ポートが割り当て済みかどうかをまず検知
wappon28dev Mar 30, 2023
057e95f
Add: 使っているプロセスIDと名前の取得
wappon28dev Mar 30, 2023
619809b
Fix: dependencies 消し忘れ
wappon28dev Mar 30, 2023
f05a87a
Add: 空いているポートを探してエンジンに port を伝える
wappon28dev Mar 30, 2023
2f81e09
Fix: 代替ポートが見つからないときのエラーメッセージを追加
wappon28dev Mar 30, 2023
f39ef13
Add: ChangePortInfosの追加
wappon28dev Apr 2, 2023
0060e97
Add: トースト通知と代替ポート用にstateを追加
wappon28dev Apr 3, 2023
e445bfd
Add: IPCに代替ポートの情報を生やした
wappon28dev Apr 6, 2023
7b0ac15
Fix: 変なdiff削除
wappon28dev Apr 8, 2023
44a5ffc
Add: "今後この通知をしない" オプション追加
wappon28dev Apr 8, 2023
3f5e18e
Enh: コメントいろいろ追加
wappon28dev Apr 8, 2023
78b9f80
Enh: 良い書き方あった
wappon28dev Apr 8, 2023
824e411
Merge branch 'VOICEVOX:main' into fix/handle-port-50021-used
wappon28dev Apr 16, 2023
220068f
Fix: 古いコメント削除
wappon28dev Apr 16, 2023
9f78aa7
Fix: 色, CSS変数の名前
wappon28dev Apr 18, 2023
bc5a40a
コメント追加
wappon28dev Apr 23, 2023
8d711ff
TODO 追加
wappon28dev Apr 24, 2023
eed8d24
Enh: getUrlを直接実行へ
wappon28dev Apr 24, 2023
dc85487
Enh: AltPortInfo -> AltPortInfos
wappon28dev Apr 24, 2023
21a4d9a
Enh: 早期リターンの削除
wappon28dev Apr 24, 2023
6b71976
Enh: noticeAltPortInfo -> engineStartedOnAltPort
wappon28dev Apr 24, 2023
324dcbc
Add: ループバックアドレスの探索追加
wappon28dev Apr 24, 2023
2857f01
Add: 探索まわりのコメントを追加
wappon28dev Apr 24, 2023
a9ca4fa
Typo: loopBack -> loopback
wappon28dev Apr 24, 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
2 changes: 1 addition & 1 deletion src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ ipcMainHandle("GET_PRIVACY_POLICY_TEXT", () => {
return privacyPolicyText;
});

ipcMainHandle("GET_ALT_PORT_INFO", () => {
ipcMainHandle("GET_ALT_PORT_INFOS", () => {
return engineManager.altPortInfo;
});

Expand Down
9 changes: 3 additions & 6 deletions src/background/engineManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
engineIdSchema,
minimumEngineManifestSchema,
} from "@/type/preload";
import { AltPortInfo } from "@/store/type";
import { AltPortInfos } from "@/store/type";

type EngineProcessContainer = {
willQuitEngine: boolean;
Expand Down Expand Up @@ -68,7 +68,7 @@ export class EngineManager {
defaultEngineInfos: EngineInfo[];
engineProcessContainers: Record<EngineId, EngineProcessContainer>;

public altPortInfo: AltPortInfo = {};
public altPortInfo: AltPortInfos = {};

constructor({
store,
Expand Down Expand Up @@ -268,10 +268,7 @@ export class EngineManager {
};

// 代替ポートを設定
engineInfo.host = new PortManager(
engineInfoUrl.hostname,
altPort
).getUrl();
engineInfo.host = `http://${engineInfoUrl.hostname}:${altPort}`;
log.warn(
`ENGINE ${engineId}: Applied Alternative Port: ${engineInfoUrl.port} -> ${altPort}`
);
Expand Down
35 changes: 27 additions & 8 deletions src/background/portManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ export class PortManager {
constructor(private hostname: string, private port: number) {}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
portLog = (...message: any) => log.info(`PORT ${this.port}: ${message}`);
portLog = (...message: any) =>
log.info(`PORT ${this.port} (${this.hostname}): ${message}`);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
portWarn = (...message: any) => log.warn(`PORT ${this.port}: ${message}`);

public getUrl(isHttps = false): string {
return `${isHttps ? "https" : "http"}://${this.hostname}:${this.port}`;
}
portWarn = (...message: any) =>
log.warn(`PORT ${this.port} (${this.hostname}): ${message}`);

/**
* "netstat -ano" の stdout から, 指定したポートを使用しているプロセスの process id を取得する
Expand Down Expand Up @@ -59,7 +57,28 @@ export class PortManager {
}).toString();

if (isWindows) {
wappon28dev marked this conversation as resolved.
Show resolved Hide resolved
stdout = this.stdout2processId(stdout)?.toString() ?? "";
// Windows の場合は, lsof のように port と pid が 1to1 で取れないので, 3つのループバックアドレスが割り当てられているか確認
const loopbackAddr = ["127.0.0.1", "0.0.0.0", "[::1]"];

// hostname が3つループバックアドレスのどれかの場合, それぞれのループバックアドレスに対して pid を取得
if (loopbackAddr.includes(this.hostname)) {
this.portLog(
"Hostname is loopback address; Getting process id from all loopback addresses..."
);

const pid: (number | undefined)[] = [];
loopbackAddr.forEach((hostname) =>
pid.push(
// TODO: インスタンスの再定義を回避するなどのリファクタリング
new PortManager(hostname, this.port).stdout2processId(stdout)
)
);

// pid が undefined (= 割り当て可能) でないものを取得 → 1つ目を取得 → stdoutへ
stdout = pid.filter((pid) => pid !== undefined)[0]?.toString() ?? "";
} else {
stdout = this.stdout2processId(stdout)?.toString() ?? "";
}
}

if (!stdout || !stdout.length) {
Expand Down Expand Up @@ -114,7 +133,7 @@ export class PortManager {

for (let altPort = this.port + 1; altPort <= altPortMax; altPort++) {
this.portLog(`Trying whether port ${altPort} is assignable...`);
const altPid = await new PortManager(
const altPid = await new PortManager( // TODO: インスタンスの再定義を回避するなどのリファクタリング
this.hostname,
altPort
).getProcessIdFromPort();
Expand Down
2 changes: 1 addition & 1 deletion src/components/MenuBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const $q = useQuasar();
const currentVersion = ref("");
const altPorts = ref<number[]>([]);

store.dispatch("GET_ALT_PORT_INFO").then(
store.dispatch("GET_ALT_PORT_INFOS").then(
(altPortInfo) =>
// {[engineId]: {from: number, to: number}} -> to: number[]
(altPorts.value = Object.values(altPortInfo).map(({ to }) => to))
Expand Down
4 changes: 2 additions & 2 deletions src/electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ const api: Sandbox = {
return await ipcRendererInvoke("GET_PRIVACY_POLICY_TEXT");
},

getAltPortInfo: async () => {
return await ipcRendererInvoke("GET_ALT_PORT_INFO");
getAltPortInfos: async () => {
return await ipcRendererInvoke("GET_ALT_PORT_INFOS");
},

saveTempAudioFile: async ({ relativePath, buffer }) => {
Expand Down
4 changes: 2 additions & 2 deletions src/store/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export const engineStore = createPartialStore<EngineStoreTypes>({
},
},

GET_ALT_PORT_INFO: {
GET_ALT_PORT_INFOS: {
async action() {
return await window.electron.getAltPortInfo();
return await window.electron.getAltPortInfos();
},
},

Expand Down
2 changes: 1 addition & 1 deletion src/store/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const settingStoreState: SettingStoreState = {
},
confirmedTips: {
tweakableSliderByScroll: false,
noticeAltPortInfo: true,
engineStartedOnAltPort: false,
},
engineSettings: {},
};
Expand Down
6 changes: 3 additions & 3 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export type Command = {
};

export type EngineState = "STARTING" | "FAILED_STARTING" | "ERROR" | "READY";
export type AltPortInfo = Record<EngineId, { from: number; to: number }>;
export type AltPortInfos = Record<EngineId, { from: number; to: number }>; // ポートが塞がれていたときの代替ポート

export type SaveResult =
| "SUCCESS"
Expand Down Expand Up @@ -739,8 +739,8 @@ export type EngineStoreTypes = {
getter: EngineInfo[];
};

GET_ALT_PORT_INFO: {
action(): Promise<AltPortInfo>;
GET_ALT_PORT_INFOS: {
action(): Promise<AltPortInfos>;
};

SET_ENGINE_MANIFESTS: {
Expand Down
6 changes: 3 additions & 3 deletions src/type/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
EngineId,
MessageBoxReturnValue,
} from "@/type/preload";
import { AltPortInfo } from "@/store/type";
import { AltPortInfos } from "@/store/type";

/**
* invoke, handle
Expand Down Expand Up @@ -69,9 +69,9 @@ export type IpcIHData = {
return: string;
};

GET_ALT_PORT_INFO: {
GET_ALT_PORT_INFOS: {
args: [];
return: AltPortInfo;
return: AltPortInfos;
};

SHOW_AUDIO_SAVE_DIALOG: {
Expand Down
8 changes: 4 additions & 4 deletions src/type/preload.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";
import { IpcSOData } from "./ipc";
import { AltPortInfo } from "@/store/type";
import { AltPortInfos } from "@/store/type";

export const isMac =
typeof process === "undefined"
Expand Down Expand Up @@ -143,7 +143,7 @@ export interface Sandbox {
getQAndAText(): Promise<string>;
getContactText(): Promise<string>;
getPrivacyPolicyText(): Promise<string>;
getAltPortInfo(): Promise<AltPortInfo>;
getAltPortInfos(): Promise<AltPortInfos>;
saveTempAudioFile(obj: { relativePath: string; buffer: ArrayBuffer }): void;
loadTempFile(): Promise<string>;
showAudioSaveDialog(obj: {
Expand Down Expand Up @@ -496,7 +496,7 @@ export type SplitterPosition = z.infer<typeof splitterPositionSchema>;

export type ConfirmedTips = {
tweakableSliderByScroll: boolean;
noticeAltPortInfo: boolean;
engineStartedOnAltPort: boolean; // エンジンのポート変更の通知
};

export const electronStoreSchema = z
Expand Down Expand Up @@ -586,7 +586,7 @@ export const electronStoreSchema = z
confirmedTips: z
.object({
tweakableSliderByScroll: z.boolean().default(false),
noticeAltPortInfo: z.boolean().default(true),
engineStartedOnAltPort: z.boolean().default(false),
})
.passthrough()
.default({}),
Expand Down
56 changes: 29 additions & 27 deletions src/views/EditorHome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -546,33 +546,35 @@ onMounted(async () => {
isCompletedInitialStartup.value = true;

// 代替ポートをトースト通知する
if (!store.state.confirmedTips.noticeAltPortInfo) return;
const altPortInfo = await store.dispatch("GET_ALT_PORT_INFO");
for (const engineId of store.state.engineIds) {
const engineName = store.state.engineInfos[engineId].name;
const altPort = altPortInfo[engineId];

if (!altPort) return;
$q.notify({
message: `${altPort.from}番ポートが使用中であるため ${engineName} は、${altPort.to}番ポートで起動しました`,
color: "toast",
textColor: "toast-display",
icon: "compare_arrows",
timeout: 5000,
actions: [
{
label: "今後この通知をしない",
textColor: "toast-button-display",
handler: () =>
store.dispatch("SET_CONFIRMED_TIPS", {
confirmedTips: {
...store.state.confirmedTips,
noticeAltPortInfo: false,
},
}),
},
],
});
// FIXME: トーストが何度も出るようにする(altPortInfoをstateに持たせてwatchする)
if (!store.state.confirmedTips.engineStartedOnAltPort) {
const altPortInfo = await store.dispatch("GET_ALT_PORT_INFOS");
for (const engineId of store.state.engineIds) {
const engineName = store.state.engineInfos[engineId].name;
const altPort = altPortInfo[engineId];

if (!altPort) return;
$q.notify({
message: `${altPort.from}番ポートが使用中であるため ${engineName} は、${altPort.to}番ポートで起動しました`,
color: "toast",
textColor: "toast-display",
icon: "compare_arrows",
timeout: 5000,
actions: [
{
label: "今後この通知をしない",
textColor: "toast-button-display",
handler: () =>
store.dispatch("SET_CONFIRMED_TIPS", {
confirmedTips: {
...store.state.confirmedTips,
engineStartedOnAltPort: true,
},
}),
},
],
});
}
}
});

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/store/Vuex.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe("store/vuex.js test", () => {
},
confirmedTips: {
tweakableSliderByScroll: false,
noticeAltPortInfo: true,
engineStartedOnAltPort: false,
},
progress: -1,
defaultPresetKeys: {},
Expand Down Expand Up @@ -252,6 +252,6 @@ describe("store/vuex.js test", () => {
assert.propertyVal(store.state.splitterPosition, "audioInfoPaneWidth", 20);
assert.propertyVal(store.state.splitterPosition, "portraitPaneWidth", 50);
assert.equal(store.state.confirmedTips.tweakableSliderByScroll, false);
assert.equal(store.state.confirmedTips.noticeAltPortInfo, true);
assert.equal(store.state.confirmedTips.engineStartedOnAltPort, false);
});
});