add ffmpeg recorder

This commit is contained in:
辉鸭蛋
2024-12-16 08:55:08 +08:00
parent fa3e7fccb0
commit e6bac7e72c
14 changed files with 457 additions and 261 deletions

View File

@@ -56,7 +56,7 @@ public partial class MaskWindowConfig : ObservableObject
/// 显示状态指示
/// </summary>
[ObservableProperty]
private bool _showStatus = true;
private bool _showStatus = false;
/// <summary>
/// UID遮盖是否启用

View File

@@ -170,7 +170,7 @@ public class MouseKeyMonitor
private void GlobalHookMouseWheelExt(object? sender, MouseEventExtArgs e)
{
Debug.WriteLine("MouseMove: {0}; \t Location: {1};\t Delta: {2};\t System Timestamp: {3}", e.Button, e.Location, e.Delta, e.Timestamp);
// Debug.WriteLine("MouseMove: {0}; \t Location: {1};\t Delta: {2};\t System Timestamp: {3}", e.Button, e.Location, e.Delta, e.Timestamp);
GlobalKeyMouseRecord.Instance.GlobalHookMouseWheel(e);
}

View File

@@ -26,7 +26,9 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
private KeyMouseRecorder? _recorder;
private SharpAviRecorder _sharpAviRecorder;
// private SharpAviRecorder _sharpAviRecorder;
private FfmpegRecorder? _ffmpegRecorder;
private readonly Dictionary<Keys, bool> _keyDownState = [];
@@ -63,7 +65,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
SystemControl.ActivateWindow();
_logger.LogInformation("录制:{Text}", "实时任务已暂停");
_logger.LogInformation("注意:录制时遇到主界面(鼠标永远在界面中心)和其他界面(鼠标可自由移动,比如地图等)的切换,请把手离开鼠标等待录制模式切换日志");
// _logger.LogInformation("注意:录制时遇到主界面(鼠标永远在界面中心)和其他界面(鼠标可自由移动,比如地图等)的切换,请把手离开鼠标等待录制模式切换日志");
// 先实例化
_recorder = new KeyMouseRecorder();
@@ -73,8 +75,10 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
{
Directory.CreateDirectory(videoPath);
}
_sharpAviRecorder = new SharpAviRecorder( Path.Combine(videoPath, $"{DateTime.Now:yyyyMMddHH_mmssffff.avi}"),
CodecIds.MotionJpeg, 90, 0, SupportedWaveFormat.WAVE_FORMAT_44M16, false, 0);
// _sharpAviRecorder = new SharpAviRecorder( Path.Combine(videoPath, $"{DateTime.Now:yyyyMMddHH_mmssffff.avi}"),
// CodecIds.MotionJpeg, 90, 0, SupportedWaveFormat.WAVE_FORMAT_44M16, false, 0);
_ffmpegRecorder = new FfmpegRecorder();
TaskTriggerDispatcher.Instance().StopTimer();
@@ -87,7 +91,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
// _timer.Start();
_sharpAviRecorder.Start();
_ffmpegRecorder.Start();
_directInputMonitor.Start();
Status = KeyMouseRecorderStatus.Recording;
@@ -108,7 +112,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
_directInputMonitor?.Dispose();
_directInputMonitor = null;
_sharpAviRecorder.Dispose();
_ffmpegRecorder?.Stop();
// _timer.Stop();
@@ -191,10 +195,10 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
public void GlobalHookMouseMoveTo(MouseEventExtArgs e)
{
if (_isInMainUi)
{
return;
}
// if (_isInMainUi)
// {
// return;
// }
// Debug.WriteLine($"MouseMove: {e.X}, {e.Y}");
_recorder?.MouseMoveTo(e);
}
@@ -208,7 +212,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
public void GlobalHookMouseMoveBy(MouseState state)
{
// Debug.WriteLine($"MouseMoveBy: {state.X}, {state.Y}");
if (state is { X: 0, Y: 0 } || !_isInMainUi)
if (state is { X: 0, Y: 0 })
{
return;
}

View File

@@ -6,6 +6,7 @@ using Gma.System.MouseKeyHook;
using SharpDX.DirectInput;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows.Forms;
@@ -38,69 +39,72 @@ public class KeyMouseRecorder
{
var rect = TaskContext.Instance().SystemInfo.CaptureAreaRect;
// 合并鼠标移动事件
var mergedMacroEvents = new List<MacroEvent>();
MacroEvent? currentMerge = null;
foreach (var macroEvent in MacroEvents)
{
if (currentMerge == null)
{
currentMerge = macroEvent;
continue;
}
if (currentMerge.Type != macroEvent.Type)
{
mergedMacroEvents.Add(currentMerge);
currentMerge = macroEvent;
continue;
}
switch (macroEvent.Type)
{
case MacroEventType.MouseMoveTo:
// 控制合并时间片段长度
if (macroEvent.Time - currentMerge.Time > MergedEventTimeMax)
{
mergedMacroEvents.Add(currentMerge);
currentMerge = macroEvent;
break;
}
// 合并为最后一个事件的位置,避免丢步
currentMerge.MouseX = macroEvent.MouseX;
currentMerge.MouseY = macroEvent.MouseY;
break;
case MacroEventType.MouseMoveBy:
if (macroEvent.Time - currentMerge.Time > MergedEventTimeMax)
{
mergedMacroEvents.Add(currentMerge);
currentMerge = macroEvent;
break;
}
// 相对位移量相加
currentMerge.MouseX += macroEvent.MouseX;
currentMerge.MouseY += macroEvent.MouseY;
if (macroEvent.CameraOrientation != null)
{
currentMerge.CameraOrientation = macroEvent.CameraOrientation;
}
break;
default:
mergedMacroEvents.Add(currentMerge);
mergedMacroEvents.Add(macroEvent);
currentMerge = null;
break;
}
}
// var mergedMacroEvents = new List<MacroEvent>();
// MacroEvent? currentMerge = null;
// foreach (var macroEvent in MacroEvents)
// {
// if (currentMerge == null)
// {
// currentMerge = macroEvent;
// continue;
// }
// if (currentMerge.Type != macroEvent.Type)
// {
// mergedMacroEvents.Add(currentMerge);
// currentMerge = macroEvent;
// continue;
// }
// switch (macroEvent.Type)
// {
// case MacroEventType.MouseMoveTo:
// // 控制合并时间片段长度
// if (macroEvent.Time - currentMerge.Time > MergedEventTimeMax)
// {
// mergedMacroEvents.Add(currentMerge);
// currentMerge = macroEvent;
// break;
// }
// // 合并为最后一个事件的位置,避免丢步
// currentMerge.MouseX = macroEvent.MouseX;
// currentMerge.MouseY = macroEvent.MouseY;
// break;
//
// case MacroEventType.MouseMoveBy:
// if (macroEvent.Time - currentMerge.Time > MergedEventTimeMax)
// {
// mergedMacroEvents.Add(currentMerge);
// currentMerge = macroEvent;
// break;
// }
// // 相对位移量相加
// currentMerge.MouseX += macroEvent.MouseX;
// currentMerge.MouseY += macroEvent.MouseY;
// if (macroEvent.CameraOrientation != null)
// {
// currentMerge.CameraOrientation = macroEvent.CameraOrientation;
// }
// break;
//
// default:
// mergedMacroEvents.Add(currentMerge);
// mergedMacroEvents.Add(macroEvent);
// currentMerge = null;
// break;
// }
// }
KeyMouseScript keyMouseScript = new()
{
MacroEvents = mergedMacroEvents,
MacroEvents = MacroEvents,
MouseMoveByMacroEvents = MouseMoveByMacroEvents,
Info = new KeyMouseScriptInfo
{
X = rect.X,
Y = rect.Y,
Width = rect.Width,
Height = rect.Height,
RecordDpi = TaskContext.Instance().DpiScale
RecordDpi = TaskContext.Instance().DpiScale,
StartTime = $"{StartTime:yyyy-MM-dd HH:mm:ss:ffff}",
StartTimeUnixTimestamp = (StartTime - new DateTime(1970, 1, 1)).TotalNanoseconds.ToString("F0")
}
};
return JsonSerializer.Serialize(keyMouseScript, JsonOptions);
@@ -174,22 +178,21 @@ public class KeyMouseRecorder
public void MouseMoveBy(MouseState state)
{
var now = DateTime.UtcNow;
int? cao = null;
if (TaskContext.Instance().Config.RecordConfig.IsRecordCameraOrientation)
{
if ((now - LastOrientationDetection).TotalMilliseconds > 100.0)
{
LastOrientationDetection = now;
cao = (int)Math.Round(CameraOrientation.Compute(TaskControl.CaptureToRectArea().SrcMat));
}
}
MacroEvents.Add(new MacroEvent
// int? cao = null;
// if (TaskContext.Instance().Config.RecordConfig.IsRecordCameraOrientation)
// {
// if ((now - LastOrientationDetection).TotalMilliseconds > 100.0)
// {
// LastOrientationDetection = now;
// cao = (int)Math.Round(CameraOrientation.Compute(TaskControl.CaptureToRectArea().SrcMat));
// }
// }
MouseMoveByMacroEvents.Add(new MacroEvent
{
Type = MacroEventType.MouseMoveBy,
MouseX = state.X,
MouseY = state.Y,
Time = (now - StartTime).TotalMilliseconds,
CameraOrientation = cao,
});
}
}

View File

@@ -9,6 +9,7 @@ namespace BetterGenshinImpact.Core.Recorder.Model;
public class KeyMouseScript
{
public List<MacroEvent> MacroEvents { get; set; } = [];
public List<MacroEvent> MouseMoveByMacroEvents { get; set; } = [];
public KeyMouseScriptInfo? Info { get; set; }
/// <summary>

View File

@@ -24,4 +24,8 @@ public class KeyMouseScriptInfo
public int Height { get; set; }
public double RecordDpi { get; set; } = 1.0;
public string StartTime { get; set; } = string.Empty;
public string StartTimeUnixTimestamp { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,120 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.GameTask.Common;
using Microsoft.Extensions.Logging;
using Serilog.Core;
namespace BetterGenshinImpact.Core.Video;
public class FfmpegRecorder
{
// ffmpeg进程
private readonly Process _process;
// ffmpeg.exe实体文件路径
private static readonly string FfmpegPath = Global.Absolute(@"video\bin\ffmpeg.exe");
private readonly string _filePath;
private string _startTime = string.Empty;
public FfmpegRecorder()
{
if (!File.Exists(FfmpegPath))
{
throw new Exception("ffmpeg.exe不存在");
}
_filePath = Global.Absolute($@"video\{DateTime.Now:yyyyMMddHH_mmssffff}.mp4");
var processInfo = new ProcessStartInfo
{
FileName = FfmpegPath,
Arguments = $" -f gdigrab -hwaccel cuvid -show_region 1 -framerate 60 -use_wallclock_as_timestamps 1 -i title=原神 -pix_fmt yuv420p -c:v libx264 -preset ultrafast \"{_filePath}\"",
StandardInputEncoding = Encoding.UTF8,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
// StandardOutputEncoding = Encoding.UTF8,
};
_process = new Process { StartInfo = processInfo };
// _process.OutputDataReceived += (sender, args) => { Debug.WriteLine(args.Data); };
_process.ErrorDataReceived += (sender, args) =>
{
Debug.WriteLine(args.Data);
if (string.IsNullOrEmpty(_startTime))
{
if (args.Data != null && args.Data.Contains("start"))
{
string pattern = @"start:\s*(\d+\.\d+)";
Match match = Regex.Match(args.Data, pattern);
if (match.Success)
{
_startTime = match.Groups[1].Value;
TaskControl.Logger.LogInformation("ffmpeg录制: 视频起始时间戳 {Text}", _startTime);
}
}
}
};
}
/// <summary>
/// 功能: 开始录制
/// </summary>
public bool Start()
{
_process.Start();
// _process.BeginOutputReadLine();
_process.BeginErrorReadLine();
TaskControl.Logger.LogInformation("ffmpeg录制: {Text}", "已启动");
return true;
}
/// <summary>
/// 功能: 停止录制
/// </summary>
public void Stop()
{
// Kernel32.AttachConsole((uint)_process.Id);
// Kernel32.SetConsoleCtrlHandler(null, true);
// Kernel32.GenerateConsoleCtrlEvent(0, 0);
// Kernel32.FreeConsole();
// AttachConsole(_process.Id);
// SetConsoleCtrlHandler(IntPtr.Zero, true);
// GenerateConsoleCtrlEvent(0, 0);
// FreeConsole();
TaskControl.Logger.LogInformation("ffmpeg录制: {Text}", "正在停止录制,请稍后...");
_process.StandardInput.WriteLine("q");
if (!_process.WaitForExit(5000))
{
_process.Kill();
}
_process.Close();
_process.Dispose();
Thread.Sleep(3000);
if (File.Exists(_filePath))
{
// 重命名文件
var newFilePath = Global.Absolute($@"video\{_startTime}.mp4");
File.Move(_filePath, newFilePath);
TaskControl.Logger.LogInformation("ffmpeg录制: {Text}", $"录制完成");
}
else
{
TaskControl.Logger.LogError("ffmpeg录制: {Text}", "未找到结果文件,录制失败");
}
}
}

View File

@@ -59,7 +59,7 @@ namespace BetterGenshinImpact.GameTask
/// 1. 是否缓存图像
/// 2. 是否执行触发器
/// </summary>
private DispatcherCaptureModeEnum _dispatcherCacheCaptureMode = DispatcherCaptureModeEnum.NormalTrigger;
private DispatcherCaptureModeEnum _dispatcherCacheCaptureMode = DispatcherCaptureModeEnum.OnlyCacheCapture;
private static readonly object _bitmapLocker = new();
private static readonly object _triggerListLocker = new();
@@ -129,7 +129,7 @@ namespace BetterGenshinImpact.GameTask
public void Start(IntPtr hWnd, CaptureModes mode, int interval = 50)
{
// 初始化截图器
GameCapture = GameCaptureFactory.Create(mode);
// GameCapture = GameCaptureFactory.Create(mode);
// 激活窗口 保证后面能够正常获取窗口信息
SystemControl.ActivateWindow(hWnd);
@@ -137,22 +137,22 @@ namespace BetterGenshinImpact.GameTask
TaskContext.Instance().Init(hWnd);
// 初始化触发器(一定要在任务上下文初始化完毕后使用)
_triggers = GameTaskManager.LoadInitialTriggers();
_triggers = [];
// 启动截图
GameCapture.Start(hWnd,
new Dictionary<string, object>()
{
{ "useBitmapCache", TaskContext.Instance().Config.WgcUseBitmapCache },
{ "autoFixWin11BitBlt", OsVersionHelper.IsWindows11_OrGreater && TaskContext.Instance().Config.AutoFixWin11BitBlt }
}
);
// GameCapture.Start(hWnd,
// new Dictionary<string, object>()
// {
// { "useBitmapCache", TaskContext.Instance().Config.WgcUseBitmapCache },
// { "autoFixWin11BitBlt", OsVersionHelper.IsWindows11_OrGreater && TaskContext.Instance().Config.AutoFixWin11BitBlt }
// }
// );
// 捕获模式初始化配置
if (TaskContext.Instance().Config.CommonConfig.ScreenshotEnabled || TaskContext.Instance().Config.MacroConfig.CombatMacroEnabled)
{
_dispatcherCacheCaptureMode = DispatcherCaptureModeEnum.CacheCaptureWithTrigger;
}
// if (TaskContext.Instance().Config.CommonConfig.ScreenshotEnabled || TaskContext.Instance().Config.MacroConfig.CombatMacroEnabled)
// {
// _dispatcherCacheCaptureMode = DispatcherCaptureModeEnum.CacheCaptureWithTrigger;
// }
// 启动定时器
_frameIndex = 0;
@@ -211,21 +211,21 @@ namespace BetterGenshinImpact.GameTask
// 检查截图器是否初始化
var maskWindow = MaskWindow.Instance();
if (GameCapture == null || !GameCapture.IsCapturing)
{
if (!TaskContext.Instance().SystemInfo.GameProcess.HasExited)
{
_logger.LogError("截图器未初始化!");
}
else
{
_logger.LogInformation("游戏已退出BetterGI 自动停止截图器");
}
UiTaskStopTickEvent?.Invoke(sender, e);
maskWindow.Invoke(maskWindow.Hide);
return;
}
// if (GameCapture == null || !GameCapture.IsCapturing)
// {
// if (!TaskContext.Instance().SystemInfo.GameProcess.HasExited)
// {
// _logger.LogError("截图器未初始化!");
// }
// else
// {
// _logger.LogInformation("游戏已退出BetterGI 自动停止截图器");
// }
//
// UiTaskStopTickEvent?.Invoke(sender, e);
// maskWindow.Invoke(maskWindow.Hide);
// return;
// }
// 检查游戏是否在前台
var hasBackgroundTriggerToRun = false;
@@ -303,60 +303,60 @@ namespace BetterGenshinImpact.GameTask
}
// 帧序号自增 1分钟后归零(MaxFrameIndexSecond)
_frameIndex = (_frameIndex + 1) % (int)(CaptureContent.MaxFrameIndexSecond * 1000d / _timer.Interval);
if (_dispatcherCacheCaptureMode == DispatcherCaptureModeEnum.NormalTrigger
&& (_triggers == null || !_triggers.Exists(t => t.IsEnabled)))
{
// Debug.WriteLine("没有可用的触发器且不处于仅截屏状态, 不再进行截屏");
return;
}
var speedTimer = new SpeedTimer();
// 捕获游戏画面
var bitmap = GameCapture.Capture();
speedTimer.Record("截图");
if (bitmap == null)
{
_logger.LogWarning("截图失败!");
return;
}
if (IsOnlyCacheCapture(bitmap))
{
return;
}
// 循环执行所有触发器 有独占状态的触发器的时候只执行独占触发器
var content = new CaptureContent(bitmap, _frameIndex, _timer.Interval);
lock (_triggerListLocker)
{
var exclusiveTrigger = _triggers!.FirstOrDefault(t => t is { IsEnabled: true, IsExclusive: true });
if (exclusiveTrigger != null)
{
exclusiveTrigger.OnCapture(content);
speedTimer.Record(exclusiveTrigger.Name);
}
else
{
var runningTriggers = _triggers!.Where(t => t.IsEnabled);
if (hasBackgroundTriggerToRun)
{
runningTriggers = runningTriggers.Where(t => t.IsBackgroundRunning);
}
foreach (var trigger in runningTriggers)
{
trigger.OnCapture(content);
speedTimer.Record(trigger.Name);
}
}
}
speedTimer.DebugPrint();
content.Dispose();
// _frameIndex = (_frameIndex + 1) % (int)(CaptureContent.MaxFrameIndexSecond * 1000d / _timer.Interval);
//
// if (_dispatcherCacheCaptureMode == DispatcherCaptureModeEnum.NormalTrigger
// && (_triggers == null || !_triggers.Exists(t => t.IsEnabled)))
// {
// // Debug.WriteLine("没有可用的触发器且不处于仅截屏状态, 不再进行截屏");
// return;
// }
//
// var speedTimer = new SpeedTimer();
// // 捕获游戏画面
// var bitmap = GameCapture.Capture();
// speedTimer.Record("截图");
//
// if (bitmap == null)
// {
// _logger.LogWarning("截图失败!");
// return;
// }
//
// if (IsOnlyCacheCapture(bitmap))
// {
// return;
// }
//
// // 循环执行所有触发器 有独占状态的触发器的时候只执行独占触发器
// var content = new CaptureContent(bitmap, _frameIndex, _timer.Interval);
//
// lock (_triggerListLocker)
// {
// var exclusiveTrigger = _triggers!.FirstOrDefault(t => t is { IsEnabled: true, IsExclusive: true });
// if (exclusiveTrigger != null)
// {
// exclusiveTrigger.OnCapture(content);
// speedTimer.Record(exclusiveTrigger.Name);
// }
// else
// {
// var runningTriggers = _triggers!.Where(t => t.IsEnabled);
// if (hasBackgroundTriggerToRun)
// {
// runningTriggers = runningTriggers.Where(t => t.IsBackgroundRunning);
// }
//
// foreach (var trigger in runningTriggers)
// {
// trigger.OnCapture(content);
// speedTimer.Record(trigger.Name);
// }
// }
// }
//
// speedTimer.DebugPrint();
// content.Dispose();
}
finally
{

View File

@@ -58,7 +58,16 @@
<ui:SymbolIcon Symbol="Play24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<ui:NavigationViewItem Content="录制回放"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:KeyMouseRecordPage}">
<ui:NavigationViewItem.Icon>
<ui:SymbolIcon Symbol="Record24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!--
<ui:NavigationViewItem Content="实时触发"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:TriggerSettingsPage}">
@@ -74,7 +83,7 @@
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!--<ui:NavigationViewItemSeparator />-->
~1~<ui:NavigationViewItemSeparator />@1@
<ui:NavigationViewItem Content="一条龙"
NavigationCacheMode="Enabled"
@@ -90,8 +99,8 @@
</ui:NavigationViewItem.Icon>
<ui:NavigationViewItem.MenuItems>
<!--<ui:NavigationViewItemSeparator />-->
<!-- ConvertRange20 -->
~1~<ui:NavigationViewItemSeparator />@1@
~1~ ConvertRange20 @1@
<ui:NavigationViewItem Content="调度器"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:ScriptControlPage}">
@@ -99,7 +108,7 @@
<ui:SymbolIcon Symbol="DeveloperBoard24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!-- WebAsset24 | DocumentJs16 | Script16 -->
~1~ WebAsset24 | DocumentJs16 | Script16 @1@
<ui:NavigationViewItem Content="JS 脚本"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:JsListPage}">
@@ -107,7 +116,7 @@
<ui:SymbolIcon Symbol="DocumentJs16" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!-- Map24 -->
~1~ Map24 @1@
<ui:NavigationViewItem Content="地图追踪"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:MapPathingPage}">
@@ -115,7 +124,7 @@
<ui:SymbolIcon Symbol="Map24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!-- Script16 -->
~1~ Script16 @1@
<ui:NavigationViewItem Content="录制回放"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:KeyMouseRecordPage}">
@@ -132,7 +141,7 @@
<ui:NavigationViewItem.Icon>
<ui:SymbolIcon Symbol="XboxController24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
</ui:NavigationViewItem>-->
<ui:NavigationViewItem Content="快捷键"
NavigationCacheMode="Enabled"
@@ -141,13 +150,6 @@
<ui:SymbolIcon Symbol="Flash24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!--<ui:NavigationViewItem Content="通知"
NavigationCacheMode="Enabled"
TargetPageType="{x:Type pages:NotificationSettingsPage}">
<ui:NavigationViewItem.Icon>
<ui:SymbolIcon Symbol="Alert24" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>-->
</ui:NavigationView.MenuItems>
<ui:NavigationView.FooterMenuItems>
<ui:NavigationViewItem Content="设置"

View File

@@ -424,12 +424,12 @@
<ui:ToggleSwitch Margin="0,0,36,0" IsChecked="{Binding Config.CommonConfig.ExitToTray, Mode=TwoWay}" />
</ui:CardControl>
<ui:TextBlock Margin="0,0,0,8"
<!--<ui:TextBlock Margin="0,0,0,8"
FontTypography="BodyStrong"
Text="通知设置" />
Text="通知设置" />-->
<!-- Webhook -->
<ui:CardExpander Margin="0,0,0,12"
<!--<ui:CardExpander Margin="0,0,0,12"
ContentPadding="0"
Icon="{ui:SymbolIcon VirtualNetwork20}">
<ui:CardExpander.Header>
@@ -523,7 +523,7 @@
TextWrapping="Wrap" />
</Grid>
</StackPanel>
</ui:CardExpander>
</ui:CardExpander>-->
<!-- 地图 -->
<!--<ui:CardControl Margin="0,0,0,12"

View File

@@ -35,14 +35,12 @@
<ui:TextBlock Grid.Row="0"
Margin="0,0,0,8"
FontTypography="BodyStrong"
Text="键鼠录制回放功能(实验功能)" />
Text="键鼠录制回放功能" />
<ui:TextBlock Grid.Row="1"
Margin="0,0,0,8"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
建议在游戏内使用快捷键进行录制,录制完成后在调度器中使用。<Hyperlink Command="{Binding GoToKmScriptUrlCommand}" Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看键鼠录制回放功能教程
</Hyperlink>
建议在游戏内使用快捷键进行录制,
</ui:TextBlock>
<StackPanel Grid.Row="2" Orientation="Horizontal">
@@ -50,7 +48,7 @@
Content="打开脚本目录"
Icon="{ui:SymbolIcon FolderOpen24}" />
<Separator Width="10" Opacity="0" />
<ui:Button Command="{Binding OpenLocalScriptRepoCommand}" Icon="{ui:SymbolIcon Archive24}">
<!--<ui:Button Command="{Binding OpenLocalScriptRepoCommand}" Icon="{ui:SymbolIcon Archive24}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ui:TextBlock>脚本仓库</ui:TextBlock>
<ui:InfoBadge Margin="0,-8,-14,0"
@@ -61,7 +59,7 @@
Visibility="{Binding Config.ScriptConfig.ScriptRepoHintDotVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</ui:Button>
<Separator Width="10" Opacity="0" />
<Separator Width="10" Opacity="0" />-->
<ui:Button Command="{Binding StartRecordCommand}"
Content="开始录制"
Icon="{ui:SymbolIcon Record20}"

View File

@@ -26,7 +26,7 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
{
private readonly ILogger<MainWindowViewModel> _logger;
private readonly IConfigService _configService;
public string Title => $"BetterGI · 更好的原神 · {Global.Version}{(RuntimeHelper.IsDebug ? " · Dev" : string.Empty)}";
public string Title => $"BetterGI · 更好的原神 · 采集版 · {Global.Version}{(RuntimeHelper.IsDebug ? " · Dev" : string.Empty)}";
[ObservableProperty]
public bool _isVisible = true;
@@ -69,46 +69,46 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
[SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "MVVMTK0039:Async void returning method annotated with RelayCommand")]
private async void OnLoaded()
{
try
{
await Task.Run(() =>
{
try
{
var s = OcrFactory.Paddle.Ocr(new Mat(Global.Absolute(@"Assets\Model\PaddleOCR\test_ocr.png"), ImreadModes.Grayscale));
Debug.WriteLine("PaddleOcr预热结果:" + s);
}
catch (Exception e)
{
Console.WriteLine(e);
_logger.LogError("PaddleOcr预热异常解决方案https://bgi.huiyadan.com/faq.html" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
var innerException = e.InnerException;
if (innerException != null)
{
_logger.LogError("PaddleOcr预热内部异常解决方案https://bgi.huiyadan.com/faq.html" + innerException.Source + "\r\n--" + Environment.NewLine + innerException.StackTrace + "\r\n---" + Environment.NewLine + innerException.Message);
throw innerException;
}
else
{
throw;
}
}
});
}
catch (Exception e)
{
MessageBox.Warning("PaddleOcr预热失败解决方案https://bgi.huiyadan.com/faq.html" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
}
try
{
await Task.Run(GetNewestInfoAsync);
}
catch (Exception e)
{
Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
_logger.LogWarning("获取 BetterGI 最新版本信息失败");
}
// try
// {
// await Task.Run(() =>
// {
// try
// {
// var s = OcrFactory.Paddle.Ocr(new Mat(Global.Absolute(@"Assets\Model\PaddleOCR\test_ocr.png"), ImreadModes.Grayscale));
// Debug.WriteLine("PaddleOcr预热结果:" + s);
// }
// catch (Exception e)
// {
// Console.WriteLine(e);
// _logger.LogError("PaddleOcr预热异常解决方案https://bgi.huiyadan.com/faq.html" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
// var innerException = e.InnerException;
// if (innerException != null)
// {
// _logger.LogError("PaddleOcr预热内部异常解决方案https://bgi.huiyadan.com/faq.html" + innerException.Source + "\r\n--" + Environment.NewLine + innerException.StackTrace + "\r\n---" + Environment.NewLine + innerException.Message);
// throw innerException;
// }
// else
// {
// throw;
// }
// }
// });
// }
// catch (Exception e)
// {
// MessageBox.Warning("PaddleOcr预热失败解决方案https://bgi.huiyadan.com/faq.html" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
// }
//
// try
// {
// await Task.Run(GetNewestInfoAsync);
// }
// catch (Exception e)
// {
// Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
// _logger.LogWarning("获取 BetterGI 最新版本信息失败");
// }
// Win11下 BitBlt截图方式不可用需要关闭窗口优化功能
if (OsVersionHelper.IsWindows11_OrGreater && TaskContext.Instance().Config.AutoFixWin11BitBlt)
@@ -117,7 +117,7 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
}
// 更新仓库
ScriptRepoUpdater.Instance.AutoUpdate();
// ScriptRepoUpdater.Instance.AutoUpdate();
}
private async Task GetNewestInfoAsync()

View File

@@ -28,6 +28,7 @@ using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Video;
using BetterGenshinImpact.GameTask.Model.Area;
using Vanara.PInvoke;
using HotKeySettingModel = BetterGenshinImpact.Model.HotKeySettingModel;
@@ -182,20 +183,20 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
);
HotKeySettingModels.Add(systemDirectory);
var timerDirectory = new HotKeySettingModel(
"实时任务"
);
HotKeySettingModels.Add(timerDirectory);
var soloTaskDirectory = new HotKeySettingModel(
"独立任务"
);
HotKeySettingModels.Add(soloTaskDirectory);
var macroDirectory = new HotKeySettingModel(
"操控辅助"
);
HotKeySettingModels.Add(macroDirectory);
// var timerDirectory = new HotKeySettingModel(
// "实时任务"
// );
// HotKeySettingModels.Add(timerDirectory);
//
// var soloTaskDirectory = new HotKeySettingModel(
// "独立任务"
// );
// HotKeySettingModels.Add(soloTaskDirectory);
//
// var macroDirectory = new HotKeySettingModel(
// "操控辅助"
// );
// HotKeySettingModels.Add(macroDirectory);
var devDirectory = new HotKeySettingModel(
"开发者"
@@ -233,7 +234,7 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
}
));
var autoPickEnabledHotKeySettingModel = new HotKeySettingModel(
/*var autoPickEnabledHotKeySettingModel = new HotKeySettingModel(
"自动拾取开关",
nameof(Config.HotKeyConfig.AutoPickEnabledHotkey),
Config.HotKeyConfig.AutoPickEnabledHotkey,
@@ -424,7 +425,7 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
{
OnKeyDownAction = (_, _) => { OneKeyFightTask.Instance.KeyDown(); },
OnKeyUpAction = (_, _) => { OneKeyFightTask.Instance.KeyUp(); }
});
});*/
devDirectory.Children.Add(new HotKeySettingModel(
"启动/停止键鼠录制",
@@ -451,6 +452,7 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
}
));
/*
devDirectory.Children.Add(new HotKeySettingModel(
"(开发)获取当前大地图中心点位置",
nameof(Config.HotKeyConfig.RecBigMapPosHotkey),
@@ -499,6 +501,7 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
}
}
));
*/
// DEBUG
if (RuntimeHelper.IsDebug)
@@ -508,13 +511,13 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
);
HotKeySettingModels.Add(debugDirectory);
soloTaskDirectory.Children.Add(new HotKeySettingModel(
"启动/停止自动活动音游",
nameof(Config.HotKeyConfig.AutoMusicGameHotkey),
Config.HotKeyConfig.AutoMusicGameHotkey,
Config.HotKeyConfig.AutoMusicGameHotkeyType,
(_, _) => { SwitchSoloTask(_taskSettingsPageViewModel.SwitchAutoMusicGameCommand); }
));
// soloTaskDirectory.Children.Add(new HotKeySettingModel(
// "启动/停止自动活动音游",
// nameof(Config.HotKeyConfig.AutoMusicGameHotkey),
// Config.HotKeyConfig.AutoMusicGameHotkey,
// Config.HotKeyConfig.AutoMusicGameHotkeyType,
// (_, _) => { SwitchSoloTask(_taskSettingsPageViewModel.SwitchAutoMusicGameCommand); }
// ));
// HotKeySettingModels.Add(new HotKeySettingModel(
// "(测试)启动/停止自动追踪",
// nameof(Config.HotKeyConfig.AutoTrackHotkey),
@@ -544,6 +547,8 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
// // _taskSettingsPageViewModel.OnSwitchAutoTrackPath();
// }
// ));
FfmpegRecorder ffmpegRecorder = new FfmpegRecorder();
debugDirectory.Children.Add(new HotKeySettingModel(
"(测试)测试",
nameof(Config.HotKeyConfig.Test1Hotkey),
@@ -574,14 +579,8 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
// 拾取物品
// Task.Run(async () => { await new ScanPickTask().Start(new CancellationToken()); });
Simulation.SendInput.Keyboard.KeyDown(false, User32.VK.VK_LMENU);
// TaskContext.Instance().PostMessageSimulator.KeyDown(User32.VK.VK_MENU);
Thread.Sleep(500);
GameCaptureRegion.GameRegion1080PPosMove(200, 100);
Thread.Sleep(500);
// TaskContext.Instance().PostMessageSimulator.KeyUp(User32.VK.VK_MENU);
Simulation.SendInput.Keyboard.KeyUp(false, User32.VK.VK_LMENU);
ffmpegRecorder.Start();
}
));
debugDirectory.Children.Add(new HotKeySettingModel(
@@ -591,8 +590,9 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
Config.HotKeyConfig.Test2HotkeyType,
(_, _) =>
{
GoToCraftingBenchTask goToCraftingBenchTask = new GoToCraftingBenchTask();
Task.Run(async () => { await goToCraftingBenchTask.Start("璃月", new CancellationToken()); });
// GoToCraftingBenchTask goToCraftingBenchTask = new GoToCraftingBenchTask();
// Task.Run(async () => { await goToCraftingBenchTask.Start("璃月", new CancellationToken()); });
ffmpegRecorder.Stop();
}
));

View File

@@ -0,0 +1,64 @@
cd /d %~dp0
if exist dist rd /s /q dist
mkdir dist\BetterGI
@echo [prepare compiler]
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "path=%path%;%%i\MSBuild\Current\Bin;%%i\Common7\IDE"
@echo [prepare version]
cd /d ..\BetterGenshinImpact
set "script=Get-Content 'BetterGenshinImpact.csproj' | Select-String -Pattern 'AssemblyVersion\>(.*)\<\/AssemblyVersion' | ForEach-Object { $_.Matches.Groups[1].Value }"
for /f "usebackq delims=" %%i in (`powershell -NoLogo -NoProfile -Command "%script%"`) do set version=%%i
echo current version is %version%
if "%b%"=="" ( set "b=%version%" )
set "tmpfolder=%~dp0dist\BetterGI"
set "archiveFile=BetterGI_T_v%b%.7z"
set "setupFile=BetterGI_T_Setup_v%b%.exe"
echo [build app using vs2022]
cd /d %~dp0
rd /s /q ..\BetterGenshinImpact\bin\x64\Release\net8.0-windows10.0.22621.0\publish\win-x64\
cd ..\
dotnet publish -c Release -p:PublishProfile=FolderProfile
echo [pack app using 7z]
cd /d %~dp0
cd /d ..\BetterGenshinImpact\bin\x64\Release\net8.0-windows10.0.22621.0\publish\win-x64\
xcopy * "%tmpfolder%" /E /C /I /Y
cd /d %~dp0
del /f /q %tmpfolder%\*.lib
del /f /q %tmpfolder%\*ffmpeg*.dll
:: 一些训练版本不需要的依赖
del /f /q %tmpfolder%\onnxruntime*.dll
del /f /q %tmpfolder%\paddle*.dll
rd /s /q %tmpfolder%\Assets\Model
:: 添加一些配置文件开始大文件不适合放在Github
if exist "E:\HuiTask\BetterGIBuild\BetterGI_train" (
xcopy "E:\HuiTask\BetterGIBuild\BetterGI_train\*" "%tmpfolder%" /E /C /I /Y
)
:: 添加一些配置文件结束
MicaSetup.Tools\7-Zip\7z a publish.7z %tmpfolder%\ -t7z -mx=5 -mf=BCJ2 -r -y
copy /y publish.7z .\MicaSetup\Resources\Setups\publish.7z
if exist "%zipFile%" ( del /f /q "%zipfile%" )
rename publish.7z %archiveFile%
@echo [build uninst using vs2022]
msbuild MicaSetup\MicaSetup.Uninst.csproj /t:Rebuild /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=FolderProfile /restore
@echo [build setup using vs2022]
copy /y .\MicaSetup\bin\Release\net472\MicaSetup.exe .\MicaSetup\Resources\Setups\Uninst.exe
msbuild MicaSetup\MicaSetup.csproj /t:Build /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=FolderProfile /restore
@echo [finish]
del /f /q MicaSetup.exe
copy /y .\MicaSetup\bin\Release\net472\MicaSetup.exe .\
rename MicaSetup.exe %setupFile%
rd /s /q dist\BetterGI
@pause