Skip to content

Commit

Permalink
Merge branch 'main' into wip-音声書き出しe2eテスト
Browse files Browse the repository at this point in the history
  • Loading branch information
Hiroshiba committed Jan 18, 2025
2 parents 6c559a6 + 14f9a3d commit c16383f
Show file tree
Hide file tree
Showing 31 changed files with 605 additions and 268 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ on:
pull_request:
branches:
- "**"
merge_group:
types: [checks_requested]
workflow_dispatch:

env:
Expand Down
7 changes: 5 additions & 2 deletions build/installer_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,12 @@ elif command -v 7zr &> /dev/null; then
elif command -v 7za &> /dev/null; then
# CentOS/Fedora
COMMAND_7Z=7za
elif command -v 7zz &> /dev/null; then
# Official 7zip
COMMAND_7Z=7zz
else
cat << 'EOS' && exit 1
[!] Command '7z', '7zr' or '7za' not found
[!] Command '7z', '7zr', '7za' or '7zz' not found
Required to extract compressed files
Expand All @@ -115,7 +118,7 @@ Or
sudo yum install p7zip
Arch Linux:
sudo pacman -S p7zip
sudo pacman -S 7zip
MacOS:
brew install p7zip
Expand Down
7 changes: 7 additions & 0 deletions public/updateInfos.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
[
{
"version": "0.22.4",
"descriptions": [
"ソング:トークエディタを開かないと保存できない問題を修正"
],
"contributors": ["sigprogramming"]
},
{
"version": "0.22.3",
"descriptions": ["キャラクター「ぞん子」を追加"],
Expand Down
3 changes: 3 additions & 0 deletions src/backend/browser/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,7 @@ export const api: Sandbox = {
reloadApp(/* obj: { isMultiEngineOffMode: boolean } */) {
throw new Error(`Not supported on Browser version: reloadApp`);
},
getPathForFile(/* file: File */) {
throw new Error(`Not supported on Browser version: getPathForFile`);
},
};
241 changes: 135 additions & 106 deletions src/backend/electron/manager/vvppManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,136 @@ export const isVvppFile = (filePath: string) => {

const lockKey = "lock-key-for-vvpp-manager";

/** VVPPファイルが分割されている場合、それらのファイルを取得する */
async function getArchiveFileParts(
vvppLikeFilePath: string,
): Promise<string[]> {
let archiveFileParts: string[];
// 名前.数値.vvpppの場合は分割されているとみなして連結する
if (vvppLikeFilePath.match(/\.[0-9]+\.vvppp$/)) {
log.log("vvpp is split, finding other parts...");
const vvpppPathGlob = vvppLikeFilePath
.replace(/\.[0-9]+\.vvppp$/, ".*.vvppp")
.replace(/\\/g, "/"); // node-globはバックスラッシュを使えないので、スラッシュに置換する
const filePaths: string[] = [];
for (const p of await glob(vvpppPathGlob)) {
if (!p.match(/\.[0-9]+\.vvppp$/)) {
continue;
}
log.log(`found ${p}`);
filePaths.push(p);
}
filePaths.sort((a, b) => {
const aMatch = a.match(/\.([0-9]+)\.vvppp$/);
const bMatch = b.match(/\.([0-9]+)\.vvppp$/);
if (aMatch == null || bMatch == null) {
throw new Error(`match is null: a=${a}, b=${b}`);
}
return parseInt(aMatch[1]) - parseInt(bMatch[1]);
});
archiveFileParts = filePaths;
} else {
log.log("Not a split file");
archiveFileParts = [vvppLikeFilePath];
}
return archiveFileParts;
}

/** 分割されているVVPPファイルを連結して返す */
async function concatenateVvppFiles(
format: "zip" | "7z",
archiveFileParts: string[],
) {
// -siオプションでの7z解凍はサポートされていないため、
// ファイルを連結した一次ファイルを作成し、それを7zで解凍する。
log.log(`Concatenating ${archiveFileParts.length} files...`);
const tmpConcatenatedFile = path.join(
app.getPath("temp"),
`vvpp-${new Date().getTime()}.${format}`,
);
log.log("Temporary file:", tmpConcatenatedFile);
await new Promise<void>((resolve, reject) => {
if (!tmpConcatenatedFile) throw new Error("tmpFile is undefined");
const inputStreams = archiveFileParts.map((f) => fs.createReadStream(f));
const outputStream = fs.createWriteStream(tmpConcatenatedFile);
new MultiStream(inputStreams)
.pipe(outputStream)
.on("close", () => {
outputStream.close();
resolve();
})
.on("error", reject);
});
log.log("Concatenated");
return tmpConcatenatedFile;
}

/** 7zでファイルを解凍する */
async function unarchive(
payload: {
archiveFile: string;
outputDir: string;
format: "zip" | "7z";
},
callbacks?: { onProgress?: ProgressCallback },
) {
const { archiveFile, outputDir, format } = payload;

const args = [
"x",
"-o" + outputDir,
archiveFile,
"-t" + format,
"-bsp1", // 進捗出力
];

let sevenZipPath = import.meta.env.VITE_7Z_BIN_NAME;
if (!sevenZipPath) {
throw new Error("7z path is not defined");
}
if (import.meta.env.PROD) {
sevenZipPath = path.join(path.dirname(app.getPath("exe")), sevenZipPath);
}
log.log("Spawning 7z:", sevenZipPath, args.join(" "));
await new Promise<void>((resolve, reject) => {
const child = spawn(sevenZipPath, args, {
stdio: ["pipe", "pipe", "pipe"],
});

child.stdout?.on("data", (data: Buffer) => {
const output = data.toString("utf-8");
log.info(`7z STDOUT: ${output}`);

// 進捗を取得
// NOTE: ` 75% 106 - pyopenjtalk\open_jtalk_dic_utf_8-1.11\sys.dic` のような出力が来る
// TODO: 出力が変わるかもしれないのでテストが必要
const progressMatch = output.match(
/ *(?<percent>\d+)% ?(?<fileCount>\d+)? ?(?<file>.*)/,
);
if (progressMatch?.groups?.percent) {
callbacks?.onProgress?.({
progress: parseInt(progressMatch.groups.percent),
});
}
});

child.stderr?.on("data", (data: Buffer) => {
log.error(`7z STDERR: ${data.toString("utf-8")}`);
});

child.on("exit", (code) => {
if (code === 0) {
callbacks?.onProgress?.({ progress: 100 });
resolve();
} else {
reject(new Error(`7z exited with code ${code}`));
}
});
// FIXME: rejectが2回呼ばれることがある
child.on("error", reject);
});
}

// # 軽い概要
//
// フォルダ名:"エンジン名+UUID"
Expand Down Expand Up @@ -117,34 +247,7 @@ export class VvppManager {
const nonce = new Date().getTime().toString();
const outputDir = path.join(this.vvppEngineDir, ".tmp", nonce);

let archiveFileParts: string[];
// 名前.数値.vvpppの場合は分割されているとみなして連結する
if (vvppLikeFilePath.match(/\.[0-9]+\.vvppp$/)) {
log.log("vvpp is split, finding other parts...");
const vvpppPathGlob = vvppLikeFilePath
.replace(/\.[0-9]+\.vvppp$/, ".*.vvppp")
.replace(/\\/g, "/"); // node-globはバックスラッシュを使えないので、スラッシュに置換する
const filePaths: string[] = [];
for (const p of await glob(vvpppPathGlob)) {
if (!p.match(/\.[0-9]+\.vvppp$/)) {
continue;
}
log.log(`found ${p}`);
filePaths.push(p);
}
filePaths.sort((a, b) => {
const aMatch = a.match(/\.([0-9]+)\.vvppp$/);
const bMatch = b.match(/\.([0-9]+)\.vvppp$/);
if (aMatch == null || bMatch == null) {
throw new Error(`match is null: a=${a}, b=${b}`);
}
return parseInt(aMatch[1]) - parseInt(bMatch[1]);
});
archiveFileParts = filePaths;
} else {
log.log("Not a split file");
archiveFileParts = [vvppLikeFilePath];
}
const archiveFileParts = await getArchiveFileParts(vvppLikeFilePath);

const format = await this.detectFileFormat(archiveFileParts[0]);
if (!format) {
Expand All @@ -157,91 +260,17 @@ export class VvppManager {
let archiveFile: string;
try {
if (archiveFileParts.length > 1) {
// -siオプションでの7z解凍はサポートされていないため、
// ファイルを連結した一次ファイルを作成し、それを7zで解凍する。
log.log(`Concatenating ${archiveFileParts.length} files...`);
tmpConcatenatedFile = path.join(
app.getPath("temp"),
`vvpp-${new Date().getTime()}.${format}`,
tmpConcatenatedFile = await concatenateVvppFiles(
format,
archiveFileParts,
);
log.log("Temporary file:", tmpConcatenatedFile);
archiveFile = tmpConcatenatedFile;
await new Promise<void>((resolve, reject) => {
if (!tmpConcatenatedFile) throw new Error("tmpFile is undefined");
const inputStreams = archiveFileParts.map((f) =>
fs.createReadStream(f),
);
const outputStream = fs.createWriteStream(tmpConcatenatedFile);
new MultiStream(inputStreams)
.pipe(outputStream)
.on("close", () => {
outputStream.close();
resolve();
})
.on("error", reject);
});
log.log("Concatenated");
} else {
archiveFile = archiveFileParts[0];
log.log("Single file, not concatenating");
}

const args = [
"x",
"-o" + outputDir,
archiveFile,
"-t" + format,
"-bsp1", // 進捗出力
];

let sevenZipPath = import.meta.env.VITE_7Z_BIN_NAME;
if (!sevenZipPath) {
throw new Error("7z path is not defined");
}
if (import.meta.env.PROD) {
sevenZipPath = path.join(
path.dirname(app.getPath("exe")),
sevenZipPath,
);
}
log.log("Spawning 7z:", sevenZipPath, args.join(" "));
await new Promise<void>((resolve, reject) => {
const child = spawn(sevenZipPath, args, {
stdio: ["pipe", "pipe", "pipe"],
});

child.stdout?.on("data", (data: Buffer) => {
const output = data.toString("utf-8");
log.info(`7z STDOUT: ${output}`);

// 進捗を取得
// NOTE: ` 75% 106 - pyopenjtalk\open_jtalk_dic_utf_8-1.11\sys.dic` のような出力が来る
// TODO: 出力が変わるかもしれないのでテストが必要
const progressMatch = output.match(
/ *(?<percent>\d+)% ?(?<fileCount>\d+)? ?(?<file>.*)/,
);
if (progressMatch?.groups?.percent) {
callbacks?.onProgress?.({
progress: parseInt(progressMatch.groups.percent),
});
}
});

child.stderr?.on("data", (data: Buffer) => {
log.error(`7z STDERR: ${data.toString("utf-8")}`);
});

child.on("exit", (code) => {
if (code === 0) {
callbacks?.onProgress?.({ progress: 100 });
resolve();
} else {
reject(new Error(`7z exited with code ${code}`));
}
});
// FIXME: rejectが2回呼ばれることがある
child.on("error", reject);
});
await unarchive({ archiveFile, outputDir, format }, callbacks);
} finally {
if (tmpConcatenatedFile) {
log.log("Removing temporary file", tmpConcatenatedFile);
Expand Down
7 changes: 6 additions & 1 deletion src/backend/electron/preload.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { contextBridge, ipcRenderer } from "electron";
import { contextBridge, ipcRenderer, webUtils } from "electron";
import type { IpcRendererInvoke } from "./ipc";
import {
ConfigType,
Expand Down Expand Up @@ -228,6 +228,11 @@ const api: Sandbox = {
reloadApp: async ({ isMultiEngineOffMode }) => {
await ipcRendererInvokeProxy.RELOAD_APP({ isMultiEngineOffMode });
},

/** webUtils.getPathForFileを呼ぶ */
getPathForFile: (file) => {
return webUtils.getPathForFile(file);
},
};

contextBridge.exposeInMainWorld(SandboxKey, api);
1 change: 1 addition & 0 deletions src/components/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ onMounted(async () => {
// プロジェクトファイルが指定されていればロード
if (typeof projectFilePath === "string" && projectFilePath !== "") {
isProjectFileLoaded.value = await store.actions.LOAD_PROJECT_FILE({
type: "path",
filePath: projectFilePath,
});
} else {
Expand Down
1 change: 1 addition & 0 deletions src/components/Base/BaseListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ defineEmits<{
border: none;
padding: vars.$padding-1 vars.$padding-2;
border-radius: vars.$radius-1;
text-align: left;
&:not(.selected):hover {
background-color: colors.$clear-hovered;
Expand Down
Loading

0 comments on commit c16383f

Please sign in to comment.