fix(gui): stop quitting when the last window closes; add device and verify
Accepting the settings dialog quit the app: with a tray-resident program "last window closed" must not mean "quit", but QApplication's default made it so, and on first run the dialog is the only window. Disable quitOnLastWindowClosed. Also fix listOutputDevices hanging forever (its callbacks never signalled the PulseAudio main loop), enlarge the caption window's close button, and add capture-device selection (enumerated sinks, settings combo, --list-devices/--device) plus model integrity verification (size + sha256 in the registry, --verify-model and a "校验完整性" button). 点「确定」后程序自己退出:本程序常驻托盘,"最后一个窗口关闭"不该等于退出, 但 QApplication 默认如此,而首次启动时设置对话框正是唯一窗口。已关闭该默认 行为。同时修复 listOutputDevices 永久卡死(回调没有 signal 主循环)、放大 字幕窗关闭按钮,并新增采集设备选择与模型完整性校验(注册表含 size/sha256, 提供 --verify-model 与设置里的「校验完整性」按钮)。
This commit is contained in:
1 parent
a7a6f3e6b6
commit
0fa0bcb44a
19 files changed
+460
-55
No files matched your search
+28
-2
@@ -95,11 +95,12 @@ int runStreamFile(lc::IAsr & asr, const QString & path, const lc::StreamOptions
|
||||
return 0;
|
||||
}
|
||||
|
||||
int runCliLive(lc::IAsr & asr, const lc::StreamOptions & sop)
|
||||
int runCliLive(lc::IAsr & asr, const lc::StreamOptions & sop, const QString & device)
|
||||
{
|
||||
lc::LivePipelineOptions popt;
|
||||
popt.stream = sop;
|
||||
|
||||
popt.captureDevice = device.toStdString();
|
||||
lc::LivePipeline pipeline(asr, popt);
|
||||
pipeline.setCaptionCallback([](const std::string & committed, const std::string & tail,
|
||||
const std::string & lang) {
|
||||
@@ -140,11 +141,36 @@ int main(int argc, char ** argv)
|
||||
parser.addOption(QCommandLineOption("file", "转写一个 wav 文件后退出", "path"));
|
||||
parser.addOption(QCommandLineOption("stream-file", "以流式模式转写 wav 文件后退出", "path"));
|
||||
parser.addOption(QCommandLineOption("cli-live", "实时转写字幕,只用命令行输出(不开窗口)"));
|
||||
parser.addOption(QCommandLineOption("device", "采集源(sink 名);留空=跟随系统默认输出", "sink"));
|
||||
parser.addOption(QCommandLineOption("verify-model", "校验模型文件完整性后退出", "id"));
|
||||
parser.addOption(QCommandLineOption("list-devices", "列出可选的采集设备后退出"));
|
||||
parser.process(app);
|
||||
|
||||
const lc::AsrOptions asrOpt = lc::app::asrOptions(parser);
|
||||
lc::StreamOptions sop = lc::app::streamOptions(parser);
|
||||
|
||||
if (parser.isSet("list-devices")) {
|
||||
for (const auto & d : lc::listOutputDevices()) {
|
||||
std::printf(" %-60s %s%s\n", d.sink.c_str(), d.description.c_str(),
|
||||
d.isDefault ? " [默认]" : "");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parser.isSet("verify-model")) {
|
||||
const auto * m = lc::models::ModelStore::find(parser.value("verify-model"));
|
||||
if (!m) { std::fprintf(stderr, "注册表里没有模型: %s\n", qUtf8Printable(parser.value("verify-model"))); return 1; }
|
||||
QStringList bad;
|
||||
const bool ok = lc::models::ModelStore::verify(*m, &bad, [](int done, int total) {
|
||||
std::fprintf(stderr, "\r校验中 %d/%d", done, total);
|
||||
});
|
||||
std::fprintf(stderr, "\n");
|
||||
if (ok) { std::printf("✅ %s 校验通过\n", qUtf8Printable(m->name)); return 0; }
|
||||
std::printf("❌ %s 校验失败,损坏: %s\n", qUtf8Printable(m->name),
|
||||
qUtf8Printable(bad.join(QStringLiteral(", "))));
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string err;
|
||||
std::unique_ptr<lc::IAsr> asr = lc::app::makeAsr(parser, asrOpt, &err);
|
||||
if (!asr) {
|
||||
@@ -159,5 +185,5 @@ int main(int argc, char ** argv)
|
||||
if (parser.isSet("file")) return runFile(*asr, parser.value("file"));
|
||||
if (parser.isSet("stream-file")) return runStreamFile(*asr, parser.value("stream-file"), sop);
|
||||
|
||||
return runCliLive(*asr, sop);
|
||||
return runCliLive(*asr, sop, parser.value("device"));
|
||||
}
|
||||
Reference in new issue
Block a user