妈妈再也不用担心我找不到字幕了
win11自带了一个实时字幕,识别效果还不错
于是我就有了个想法,抓取文本并,通过网络本地调用,然后实时加载到在线视频上。
说干就干,一开始我尝试使用node,flutter都失败了。
于是我就只能被迫使用了我从未用过的C#
万万没想到,我的第一个C#程序不是helloworld
using System;
using System.Collections.Generic;
using System.Windows.Automation;
class Program
{
static void Main(string[] args)
{
string windowTitle;
if (args.Length == 0)
{
windowTitle = "实时辅助字幕";
Console.WriteLine("Usage: uiget.exe \"Window Title\"");
//return;
}
else
{
windowTitle = args[0];
}
// 查找具有特定标题的窗口
AutomationElement window = FindWindowByTitle(windowTitle);
if (window != null)
{
List<string> texts = new List<string>();
WalkAutomationTreeAndCollectTexts(window, texts);
Console.WriteLine(texts[0]);
}
else
{
Console.WriteLine("");
}
}
static AutomationElement FindWindowByTitle(string title)
{
var condition = new PropertyCondition(AutomationElement.NameProperty, title);
return AutomationElement.RootElement.FindFirst(TreeScope.Children, condition);
}
static void WalkAutomationTree(AutomationElement rootElement)
{
var condition = Condition.TrueCondition; // 查找所有子节点
var treeWalker = new TreeWalker(condition);
WalkAutomationTree(treeWalker, rootElement, 0);
}
// 递归遍历UI自动化树
static void WalkAutomationTree(TreeWalker walker, AutomationElement element, int indent)
{
string indentString = new string(' ', indent);
// 尝试使用ObjectModel获取文本
string elementText = GetTextFromElement(element);
Console.WriteLine($"{indentString}{element.Current.ControlType.ProgrammaticName} - Name: {element.Current.Name}, Text: {elementText}");
AutomationElement child = walker.GetFirstChild(element);
while (child != null)
{
WalkAutomationTree(walker, child, indent + 2); // 增加缩进
child = walker.GetNextSibling(child); // 移动到下一个兄弟元素
}
}
// 从元素获取文本的尝试
static string GetTextFromElement(AutomationElement element)
{
try
{
object patternObj;
if (element.TryGetCurrentPattern(TextPattern.Pattern, out patternObj))
{
var textPattern = (TextPattern)patternObj;
return textPattern.DocumentRange.GetText(-1).TrimEnd('\r'); // 获取所有文本并删除尾随的回车符
}
else if (element.TryGetCurrentPattern(ValuePattern.Pattern, out patternObj))
{
var valuePattern = (ValuePattern)patternObj;
return valuePattern.Current.Value;
}
else
{
// 没有找到TextPattern或ValuePattern
return string.Empty;
}
}
catch (ElementNotAvailableException)
{
return string.Empty;
}
}
static void WalkAutomationTreeAndCollectTexts(AutomationElement element, List<string> texts)
{
var treeWalker = TreeWalker.RawViewWalker;
CollectTextsRecursively(treeWalker, element, texts);
}
static void CollectTextsRecursively(TreeWalker walker, AutomationElement element, List<string> texts)
{
string elementText = GetTextFromElement(element);
if (!string.IsNullOrEmpty(elementText))
{
texts.Add(elementText);
}
AutomationElement child = walker.GetFirstChild(element);
while (child != null)
{
CollectTextsRecursively(walker, child, texts);
child = walker.GetNextSibling(child);
}
}
}
然后再使用我熟悉的nodejs调用
const { exec } = require('child_process');
const iconv = require('iconv-lite');
function getUITextByTitle(title, callback) {
exec(`uiget.exe "${title}"`, { encoding: 'buffer' }, (error, stdoutBuffer, stderrBuffer) => {
console.log('当前Node.js默认编码:', Buffer.from('').constructor.name);
if (error) {
return callback(error, null);
}
if (stderrBuffer.length) {
// 注意:这里假设标准错误流(stderr)也是 GB2312 编码
return callback(new Error(iconv.decode(stderrBuffer, 'GB2312')), null);
}
try {
// 使用iconv-lite将Buffer从GB2312转码为UTF-8字符串
const stdoutText = iconv.decode(stdoutBuffer, 'GB2312');
callback(null, stdoutText);
} catch (parseError) {
callback(parseError, null);
}
});
}
大功告成,之后就是封装为route,用express代理一下就好了