From 3bd84f0ed362a1ad3659016ee997efefc1ff5082 Mon Sep 17 00:00:00 2001 From: FaulMit <48646918+FaulMit@users.noreply.github.com> Date: Mon, 20 Jul 2026 21:11:51 +0200 Subject: [PATCH] Complete Captail internal rebrand --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/ci.yml | 8 +-- .github/workflows/release.yml | 2 +- CHANGELOG.md | 8 +++ CONTRIBUTING.md | 4 +- InstantReplay.sln => Captail.sln | 2 +- README.md | 16 +++++- docs/captail-main.jpg | Bin 0 -> 17072 bytes installer/Captail.iss | 2 +- native/ObsBridge/CMakeLists.txt | 8 +-- ...loopObsBridge.cpp => CaptailObsBridge.cpp} | 0 .../ObsCaptureFixture/ObsCaptureFixture.cpp | 4 +- src/{InstantReplay => Captail}/App.xaml | 2 +- src/{InstantReplay => Captail}/App.xaml.cs | 32 ++++++------ .../AssemblyInfo.cs | 0 .../Assets/Captail.ico} | Bin .../AudioDevices.cs | 2 +- src/{InstantReplay => Captail}/Autostart.cs | 9 ++-- .../Captail.csproj} | 13 ++--- src/{InstantReplay => Captail}/Config.cs | 47 +++++++----------- .../EncoderCapabilities.cs | 2 +- .../HotkeyManager.cs | 4 +- .../Interop/CaptureInterop.cs | 4 +- .../Languages/Strings.en.xaml | 0 .../Languages/Strings.ru.xaml | 0 .../Localization.cs | 2 +- src/{InstantReplay => Captail}/Log.cs | 2 +- .../ObsLogBridge.cs | 8 +-- src/{InstantReplay => Captail}/ObsNative.cs | 2 +- .../ObsReplayEngine.cs | 16 +++--- .../OverlayNotificationWindow.xaml | 2 +- .../OverlayNotificationWindow.xaml.cs | 2 +- .../SettingsWindow.xaml | 4 +- .../SettingsWindow.xaml.cs | 14 +++--- .../Themes/Theme.xaml | 16 +++--- tools/BuildRelease.ps1 | 2 +- tools/GenerateAppIcon.ps1 | 2 +- 37 files changed, 128 insertions(+), 115 deletions(-) rename InstantReplay.sln => Captail.sln (92%) create mode 100644 docs/captail-main.jpg rename native/ObsBridge/{EverloopObsBridge.cpp => CaptailObsBridge.cpp} (100%) rename src/{InstantReplay => Captail}/App.xaml (93%) rename src/{InstantReplay => Captail}/App.xaml.cs (97%) rename src/{InstantReplay => Captail}/AssemblyInfo.cs (100%) rename src/{InstantReplay/Assets/Everloop.ico => Captail/Assets/Captail.ico} (100%) rename src/{InstantReplay => Captail}/AudioDevices.cs (96%) rename src/{InstantReplay => Captail}/Autostart.cs (73%) rename src/{InstantReplay/InstantReplay.csproj => Captail/Captail.csproj} (90%) rename src/{InstantReplay => Captail}/Config.cs (57%) rename src/{InstantReplay => Captail}/EncoderCapabilities.cs (99%) rename src/{InstantReplay => Captail}/HotkeyManager.cs (96%) rename src/{InstantReplay => Captail}/Interop/CaptureInterop.cs (96%) rename src/{InstantReplay => Captail}/Languages/Strings.en.xaml (100%) rename src/{InstantReplay => Captail}/Languages/Strings.ru.xaml (100%) rename src/{InstantReplay => Captail}/Localization.cs (98%) rename src/{InstantReplay => Captail}/Log.cs (95%) rename src/{InstantReplay => Captail}/ObsLogBridge.cs (76%) rename src/{InstantReplay => Captail}/ObsNative.cs (99%) rename src/{InstantReplay => Captail}/ObsReplayEngine.cs (98%) rename src/{InstantReplay => Captail}/OverlayNotificationWindow.xaml (97%) rename src/{InstantReplay => Captail}/OverlayNotificationWindow.xaml.cs (99%) rename src/{InstantReplay => Captail}/SettingsWindow.xaml (99%) rename src/{InstantReplay => Captail}/SettingsWindow.xaml.cs (98%) rename src/{InstantReplay => Captail}/Themes/Theme.xaml (97%) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 612b769..63ae9a1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,7 @@ Describe the change and why it is needed. ## Validation -- [ ] `dotnet build .\InstantReplay.sln -c Release` +- [ ] `dotnet build .\Captail.sln -c Release` - [ ] Real Windows UI checked when applicable - [ ] Existing replay, hotkey, tray, and recovery behavior preserved - [ ] New user-facing text added to both EN and RU resources diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b604b5e..d69b9be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: with: dotnet-version: 9.0.x cache: true - cache-dependency-path: src/InstantReplay/InstantReplay.csproj + cache-dependency-path: src/Captail/Captail.csproj - name: Cache OBS runtime uses: actions/cache@v6 @@ -43,13 +43,13 @@ jobs: - name: Build Release shell: pwsh - run: dotnet build ./InstantReplay.sln -c Release -p:ContinuousIntegrationBuild=true + run: dotnet build ./Captail.sln -c Release -p:ContinuousIntegrationBuild=true - name: Validate localization dictionaries shell: pwsh run: | - [xml]$en = Get-Content ./src/InstantReplay/Languages/Strings.en.xaml - [xml]$ru = Get-Content ./src/InstantReplay/Languages/Strings.ru.xaml + [xml]$en = Get-Content ./src/Captail/Languages/Strings.en.xaml + [xml]$ru = Get-Content ./src/Captail/Languages/Strings.ru.xaml $enKeys = @($en.ResourceDictionary.String | ForEach-Object { $_.Key }) $ruKeys = @($ru.ResourceDictionary.String | ForEach-Object { $_.Key }) $differences = @(Compare-Object $enKeys $ruKeys) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dab6b3e..a265108 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: with: dotnet-version: 9.0.x cache: true - cache-dependency-path: src/InstantReplay/InstantReplay.csproj + cache-dependency-path: src/Captail/Captail.csproj - name: Cache OBS runtime uses: actions/cache@v6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d40e8a..b079505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable user-facing changes are documented here. +## [Unreleased] + +### Changed + +- Renamed remaining solution, project, namespace, icon, and native bridge identifiers to Captail. +- Added a real interface screenshot and an OBS Replay Buffer comparison to the README. +- Standardized repository comments and diagnostic messages in English. + ## [0.1.0] - 2026-07-20 First public preview. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbd7173..058815e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ Requirements: ```powershell .\tools\AcquireObsRuntime.ps1 -dotnet build .\InstantReplay.sln -c Debug +dotnet build .\Captail.sln -c Debug ``` ## Pull requests @@ -43,6 +43,8 @@ dotnet build .\InstantReplay.sln -c Debug ## Code style - Follow existing C# and XAML conventions. +- Use English for GitHub issues, pull requests, documentation, code comments, and diagnostic messages. +- Keep Russian text only in the Russian localization dictionary. - Prefer clear behavior over abstraction. - Keep user-facing text in EN/RU localization dictionaries. - Do not present unavailable hardware functionality as enabled. diff --git a/InstantReplay.sln b/Captail.sln similarity index 92% rename from InstantReplay.sln rename to Captail.sln index 2f84d49..94217cf 100644 --- a/InstantReplay.sln +++ b/Captail.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InstantReplay", "src\InstantReplay\InstantReplay.csproj", "{FB66E8EA-3BE5-4E9C-8533-1C1B10A06151}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Captail", "src\Captail\Captail.csproj", "{FB66E8EA-3BE5-4E9C-8533-1C1B10A06151}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/README.md b/README.md index 3131c61..281653c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ Captail is designed around one rule: **Instant Replay should stay on.** A game crash, game-to-desktop switch, temporary capture failure, protected surface, or recoverable graphics-driver interruption should not silently leave you without a replay. A watchdog monitors the recording pipeline and restarts it when recovery is possible. +
+
+
nR?Q*Z%x%;89c{ajAVfIUSy|> z^-3Nta_dMic}jnErWj##Cs}XpbkJohBypHerD%Yt{peF7w?5Y?W5+Xhbl&a9h8&|@ z2kOg7uB0%6bvLpao{EQu2>|e-y{k}A->;R_cEOuATy~bWUFwik0*y{B zXF2b6qLDMURI^b@0F}y&Uzo&NYLm}s`bVY4kaZpQa`CtiTI;X`Yi=s8bWsvf-0w2+ zi~$&5+q5-$STP+6uq8_}H&!__49!ydYN>7Xkcc#T3X0|c?WD$PMDCMi978jHBa&1% zlq`YMjw5lJF#euPH5HWar!;7(DcjT>f#meHd*?P?FINfEQ4iEb-*YTuOXRMIg}s{Q z=~_rj$Un8@De(yMZi#1%4$|wuv>mrqxChMUE)~Q`RSYPj-&vqUE>f75=kJx~HaeQm z+QP!30I-`p>2Sd}F<0*lQ3v=UzTJx6+T8=t&>+gGL}7LM$Vuil?^8_7LcMpa-I!BdtU&}%);A;4EaL~VZ8kRyG;#$6AaBL zoIyQ&4dcHE{+08r5!$Q%Mesw0-0DAs;BmW!C|07?xnVfxpG00S(Nrvydb6|H#oycm z^1sk0BIm2XKVZ?|9HFbr2Us+#fbJg9eZZl{9RekB*uF^waz}(UDkm#3dG+nNlcOS+ z(3oX&PcwgM6^3dCT1*oY(F)Q@OtegvG;OQ?OxCazKR%V(D?{BA?@CX}v9zF2jVyKG zGvgYqfY-3hvxMr{KHY?az>XT{1<|?gq*46qh6>Et%ZgbL%Iqg)T$nZ4FG##RuyIQ( z5hT^Pvt}dWC245$(nL~c&co*CX8m>iptj)15JTc&s6?Bqs}_iY)V@^tX;g>58GoeH z@_ly&??{da=cvUgvIcqnIVCbBA6ZrVM*PUzJmV!&bhTJAD~0eB8Z${OT%hYk-{D>l zn=@+47X+7YV1%uH{X7|PRr|Lyzd_;8EW@$VJOKcrogt~#CZ9Y${~cu0!!olwWBa`b zni7bl{KKmOO4I9aY@*`C&))=i9*LB?Mf}^)%){v(1`t4ylNtBPzZ0h}r}HIz*NEJ# z*o+fymjc9zfkEAwSR^MiqH9FToZ-KV6ol@JFj;xv{6F-sm$#z7fb9;&v|En{6kB0D z8hW7Q!Ew6HC;EKaqL%ALmHelfr&jAUAuonxj+F+=-b%wo!oXQl#Tb$>X8Vns;zom5 zp=TT =OGcE$fkrl{;5ym-S9Hp%Q za!}<|@&Zb5(B^_`Mr!iDOQ;q_q}n}V$W~rA52=1e%YYdvTOg5(rNLqLX~v2#Vmyu( zO2ngofV3Wy{$|m9ahv!7G|i=ofm&*7e<&1T*}wT7CHPIrzjmchhsVi`N@a|q4O390 z&&@B0P(2W1GbkMQF_%{^tR5pEIf`hl!Y!y7R;^lN-(X@Jm#H?7%e2*a^RyyTB|xk_ z&sO0@sRnf>bMTm~Ktb+jlO8A}r}&RXKJ33f%l_Ua@;}XIM#ad(7VOC6m1s*CeN^gV z#3_hs(9>sgz-5C3FdB1C!A6;8T+F^5=6_z?mNoAp)pZPt>m&YXnQv@XnxZ3k30}GM zvmFa>uY~$830JW10i690Z&DnlJ5#HC1wG-4j_yb;^1`X_W-m0BZ`Gen@qxZ>MW_DY zB3>12g{eI(OvOEL3NT-8e2(SPw{n=#WO_}OWRdaqeGq)Y5NL=*?;N3yce~x1)%j&T zLm!GPu7`NWq8X7-<#;CYvk@w`a^px0F-0DVm5Mnv(Uo2IYqzucm8!75!O?jZE33$l zNSWG8c>K}5Kk7G1yWQPqlOa>*PE223owAQV*RJH#6C3|3XQ5x}WbMxplN*(t{tuYS zf1KDMfG4l)3#9->+GvIX`HA-yDx6J({*mU7rAky(3RNt&{li>e@9;B}Olz9Y)P$C` z*$aLw6!X=8*D6h%Wi75V=jFBVDc{4itg~br(-Sf`vlaKAwj`yVKihJQXO*s?)W#9M zumty+S7uYas~fsWzV>LPvdjc_q(bl%Cn}LU(TAeAcUur5S&q>$S%U$2A}RU;`hSet z8X0RD=Y9Y<#8U1uS{Cknp~qQ%@3&iHe=ZPPF@Y>NG4hW_BtgKxD (W)5O~!jM;x z58@#jVIyKj$O|fL9rIHyG_i({IKj{7R 8^h)Hoa# zPaprXcAzJB|5%4VLD>Hi>O;87gE|8ZSNUhH|CML>941i^Kzk%niXO&Z@_)Pl!({zM zMd YHtz|*(+sGLqxG60mo2>-b z@+aFp;3~u}K5cw= diff --git a/src/InstantReplay/App.xaml.cs b/src/Captail/App.xaml.cs similarity index 97% rename from src/InstantReplay/App.xaml.cs rename to src/Captail/App.xaml.cs index e16d769..2072ef9 100644 --- a/src/InstantReplay/App.xaml.cs +++ b/src/Captail/App.xaml.cs @@ -9,7 +9,7 @@ using System.Windows.Threading; using H.NotifyIcon; -namespace InstantReplay; +namespace Captail; public partial class App : Application { @@ -355,7 +355,7 @@ private async void RunGameCaptureTest(string[] args) OutputDirectory = root, }; if (!TryStartPipeline(showError: false)) - throw new InvalidOperationException("OBS Game Capture не запустился."); + throw new InvalidOperationException("OBS Game Capture did not start."); await Task.Delay(TimeSpan.FromSeconds(8)); string path = await _obs!.SaveReplayAsync(); @@ -397,11 +397,11 @@ private async void RunFaultRecoveryTest() }; StartHealthMonitor(); if (!TryStartPipeline(showError: false)) - throw new InvalidOperationException("Исходный OBS pipeline не запустился."); + throw new InvalidOperationException("The initial OBS pipeline did not start."); DateTime originalStart = _pipelineStartedUtc; await Task.Delay(TimeSpan.FromSeconds(3)); - RecoverPipeline("QA: искусственный перезапуск OBS."); + RecoverPipeline("QA: simulated OBS restart."); await Task.Delay(TimeSpan.FromSeconds(6)); bool restarted = IsReplayRunning && _pipelineStartedUtc > originalStart; string path = restarted @@ -471,8 +471,8 @@ private bool AcquireSingleInstance(bool backgroundLaunch, bool isolatedUiTest) Environment.UserName; string suffix = userId.Replace('\\', '.') + (isolatedUiTest ? ".UiOnly" : ""); - string mutexName = $@"Local\Everloop.SingleInstance.{suffix}"; - _activationPipeName = $"Everloop.Activate.{suffix}"; + string mutexName = $@"Local\Captail.SingleInstance.{suffix}"; + _activationPipeName = $"Captail.Activate.{suffix}"; _singleInstanceMutex = new Mutex( initiallyOwned: true, mutexName, @@ -503,11 +503,11 @@ private void SendActivationCommand(string command) } catch (TimeoutException) { - // Первый экземпляр ещё запускается. + // The first instance may still be starting. } catch (IOException) { - // Pipe между попытками пересоздаётся. + // The pipe is recreated between attempts. } } } @@ -550,7 +550,7 @@ private async Task ActivationServerLoopAsync(CancellationToken cancellationToken private static void TerminateLegacyInstances() { int currentId = Environment.ProcessId; - foreach (Process process in Process.GetProcessesByName("InstantReplay")) + foreach (Process process in Process.GetProcessesByName("Captail")) { using (process) { @@ -568,7 +568,7 @@ private static void TerminateLegacyInstances() catch (Exception exception) { Log.Write( - $"Не удалось закрыть старый экземпляр PID {process.Id}: " + + $"Failed to close the previous instance PID {process.Id}: " + exception.Message); } } @@ -588,7 +588,7 @@ private void BindHotkeyAtStartup() } catch (Exception exception) { - Log.Write($"Глобальный хоткей недоступен: {exception.Message}"); + Log.Write($"Global hotkey unavailable: {exception.Message}"); _pendingUiError = exception.Message; ShowOverlayNotification( "!", @@ -639,7 +639,7 @@ private bool TryStartPipeline(bool showError) if (engine is not null) _capabilities = engine.Capabilities; StopPipeline(); - Log.Write($"Запуск OBS pipeline не удался: {exception}"); + Log.Write($"OBS pipeline startup failed: {exception}"); if (showError) { ShowOverlayNotification( @@ -667,7 +667,7 @@ private void StopPipeline() } catch (Exception exception) { - Log.Write($"Остановка OBS pipeline: {exception}"); + Log.Write($"OBS pipeline shutdown failed: {exception}"); } } @@ -1119,7 +1119,7 @@ private async Task SaveReplayCoreAsync(ObsReplayEngine engine) } catch (Exception exception) { - Log.Write($"Сохранение повтора упало: {exception}"); + Log.Write($"Replay save failed: {exception}"); ShowOverlayNotification( "!", Localization.Text("L.Notify.SaveError"), @@ -1161,7 +1161,7 @@ private static string FormatDuration(int seconds) => private static Icon CreateIcon() { using Stream stream = GetResourceStream( - new Uri("Assets/Everloop.ico", UriKind.Relative)).Stream; + new Uri("Assets/Captail.ico", UriKind.Relative)).Stream; using var icon = new Icon(stream); return (Icon)icon.Clone(); } @@ -1185,7 +1185,7 @@ protected override void OnExit(ExitEventArgs e) } catch { - // Уже освобождён во время аварийного завершения. + // Already released during emergency shutdown. } _singleInstanceMutex.Dispose(); } diff --git a/src/InstantReplay/AssemblyInfo.cs b/src/Captail/AssemblyInfo.cs similarity index 100% rename from src/InstantReplay/AssemblyInfo.cs rename to src/Captail/AssemblyInfo.cs diff --git a/src/InstantReplay/Assets/Everloop.ico b/src/Captail/Assets/Captail.ico similarity index 100% rename from src/InstantReplay/Assets/Everloop.ico rename to src/Captail/Assets/Captail.ico diff --git a/src/InstantReplay/AudioDevices.cs b/src/Captail/AudioDevices.cs similarity index 96% rename from src/InstantReplay/AudioDevices.cs rename to src/Captail/AudioDevices.cs index 6e31134..63b950f 100644 --- a/src/InstantReplay/AudioDevices.cs +++ b/src/Captail/AudioDevices.cs @@ -1,6 +1,6 @@ using NAudio.CoreAudioApi; -namespace InstantReplay; +namespace Captail; internal static class AudioDevices { diff --git a/src/InstantReplay/Autostart.cs b/src/Captail/Autostart.cs similarity index 73% rename from src/InstantReplay/Autostart.cs rename to src/Captail/Autostart.cs index 62b0ce1..d9da7b7 100644 --- a/src/InstantReplay/Autostart.cs +++ b/src/Captail/Autostart.cs @@ -1,18 +1,17 @@ using Microsoft.Win32; -namespace InstantReplay; +namespace Captail; -/// Автозапуск через HKCU\...\Run — без прав администратора. +///Per-user startup through HKCU\...\Run; administrator rights are not required. public static class Autostart { private const string RunKey = @"Software\Microsoft\Windows\CurrentVersion\Run"; private const string ValueName = "Captail"; - private const string LegacyValueName = "InstantReplay"; public static bool IsEnabled() { using var key = Registry.CurrentUser.OpenSubKey(RunKey); - return HasCommand(key, ValueName) || HasCommand(key, LegacyValueName); + return HasCommand(key, ValueName); } public static void SetEnabled(bool enabled) @@ -27,12 +26,10 @@ public static void SetEnabled(bool enabled) ValueName, $"\"{executablePath}\" --background", RegistryValueKind.String); - key.DeleteValue(LegacyValueName, throwOnMissingValue: false); } else { key.DeleteValue(ValueName, throwOnMissingValue: false); - key.DeleteValue(LegacyValueName, throwOnMissingValue: false); } } diff --git a/src/InstantReplay/InstantReplay.csproj b/src/Captail/Captail.csproj similarity index 90% rename from src/InstantReplay/InstantReplay.csproj rename to src/Captail/Captail.csproj index 96a75a7..084a7d9 100644 --- a/src/InstantReplay/InstantReplay.csproj +++ b/src/Captail/Captail.csproj @@ -12,7 +12,8 @@Captail Captail Captail -Assets\Everloop.ico +Captail +Assets\Captail.ico 0.1.0 @@ -23,7 +24,7 @@- + PreserveNewest PreserveNewest @@ -51,13 +52,13 @@- - + - @@ -65,7 +66,7 @@ AfterTargets="Publish" DependsOnTargets="BuildObsBridge" Condition="'$(PublishDir)' != ''"> -diff --git a/src/InstantReplay/Config.cs b/src/Captail/Config.cs similarity index 57% rename from src/InstantReplay/Config.cs rename to src/Captail/Config.cs index 149af7a..28081ff 100644 --- a/src/InstantReplay/Config.cs +++ b/src/Captail/Config.cs @@ -2,47 +2,47 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace InstantReplay; +namespace Captail; public sealed class Config { public string Language { get; set; } = "en"; public int BufferSeconds { get; set; } = 300; - /// 0 = ограничение только по длительности. + ///0 = duration-only limit. public int MaxReplaySizeMb { get; set; } public int FrameRate { get; set; } = 60; - ///0 = адаптивный битрейт по кодеку и нагрузке. + ///0 = adaptive bitrate based on codec and load. public int BitrateMbps { get; set; } public string Hotkey { get; set; } = "Ctrl+Shift+F10"; public string ToggleReplayHotkey { get; set; } = "Ctrl+Shift+F9"; public bool ReplayEnabled { get; set; } = true; - ///"av1", "hevc" или "h264". OBS выбирает доступный encoder нужного формата. + ///"av1", "hevc", or "h264". OBS selects an available encoder for the requested format. public string Codec { get; set; } = "h264"; public int MonitorIndex { get; set; } - ///"source", "720p", "1080p", "1440p" или "2160p". + ///"source", "720p", "1080p", "1440p", or "2160p". public string RecordingResolution { get; set; } = "source"; - ///"desktop" или "game". + ///"desktop" or "game". public string CaptureSource { get; set; } = "desktop"; - ///Полный путь к EXE выбранной игры; PID определяется при запуске pipeline. + ///Full path to the selected game executable; PID is resolved when the pipeline starts. public string GameExecutablePath { get; set; } = ""; public bool CaptureSystemAudio { get; set; } = true; public int SystemAudioVolume { get; set; } = 100; - ///ID render-устройства для loopback; пусто — дефолтное устройство Windows. + ///Render-device ID used for loopback; empty selects the Windows default device. public string SystemAudioDeviceId { get; set; } = ""; public bool CaptureMicrophone { get; set; } public int MicrophoneVolume { get; set; } = 100; public int MicrophoneBoostDb { get; set; } - ///ID устройства микрофона; пусто — дефолтный микрофон Windows. + ///Microphone device ID; empty selects the Windows default microphone. public string MicrophoneDeviceId { get; set; } = ""; public int AudioBitrateKbps { get; set; } = 192; - ///"aac" для fragmented MP4 или "opus" для MKV. + ///"aac" for fragmented MP4 or "opus" for MKV. public string AudioCodec { get; set; } = "aac"; ///- /// true — звук системы и микрофон сохраняются отдельными аудиодорожками; - /// false — источники сводятся в одну дорожку. + /// true stores system audio and microphone on separate tracks; + /// false mixes both sources into one track. /// public bool SeparateAudioTracks { get; set; } @@ -51,31 +51,22 @@ public sealed class Config [JsonIgnore] public static string ConfigPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Captail", "config.json"); - [JsonIgnore] - private static string LegacyConfigPath => - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "InstantReplay", "config.json"); - public static Config Load() { - foreach (string path in new[] { ConfigPath, LegacyConfigPath }) + try { - try + if (File.Exists(ConfigPath)) { - if (!File.Exists(path)) - continue; - Config config = - JsonSerializer.Deserialize(File.ReadAllText(path)) ?? + JsonSerializer.Deserialize (File.ReadAllText(ConfigPath)) ?? new Config(); config.Language = NormalizeLanguage(config.Language); - if (!string.Equals(path, ConfigPath, StringComparison.OrdinalIgnoreCase)) - config.Save(); return config; } - catch - { - // Пробуем следующий путь; повреждённый конфиг не роняет запуск. - } + } + catch + { + // A damaged config must not prevent startup. } var defaultConfig = new Config(); diff --git a/src/InstantReplay/EncoderCapabilities.cs b/src/Captail/EncoderCapabilities.cs similarity index 99% rename from src/InstantReplay/EncoderCapabilities.cs rename to src/Captail/EncoderCapabilities.cs index 331305c..8193e1a 100644 --- a/src/InstantReplay/EncoderCapabilities.cs +++ b/src/Captail/EncoderCapabilities.cs @@ -1,4 +1,4 @@ -namespace InstantReplay; +namespace Captail; public sealed record CodecCapability( string Codec, diff --git a/src/InstantReplay/HotkeyManager.cs b/src/Captail/HotkeyManager.cs similarity index 96% rename from src/InstantReplay/HotkeyManager.cs rename to src/Captail/HotkeyManager.cs index 587b8df..7e1f2ca 100644 --- a/src/InstantReplay/HotkeyManager.cs +++ b/src/Captail/HotkeyManager.cs @@ -2,9 +2,9 @@ using System.Windows.Input; using System.Windows.Interop; -namespace InstantReplay; +namespace Captail; -/// Два глобальных хоткея: сохранение повтора и включение/выключение буфера. +///Global hotkeys for saving a replay and toggling the replay buffer. public sealed class HotkeyManager : IDisposable { [DllImport("user32.dll")] diff --git a/src/InstantReplay/Interop/CaptureInterop.cs b/src/Captail/Interop/CaptureInterop.cs similarity index 96% rename from src/InstantReplay/Interop/CaptureInterop.cs rename to src/Captail/Interop/CaptureInterop.cs index ec5132c..cc9a37e 100644 --- a/src/InstantReplay/Interop/CaptureInterop.cs +++ b/src/Captail/Interop/CaptureInterop.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; using System.Text; -namespace InstantReplay.Interop; +namespace Captail.Interop; internal static class CaptureInterop { @@ -153,7 +153,7 @@ public static bool IsProcessRunning(string executablePath) } catch { - // Защищённые системные процессы не являются выбранной игрой. + // Protected system processes cannot be the selected game. } process.Dispose(); diff --git a/src/InstantReplay/Languages/Strings.en.xaml b/src/Captail/Languages/Strings.en.xaml similarity index 100% rename from src/InstantReplay/Languages/Strings.en.xaml rename to src/Captail/Languages/Strings.en.xaml diff --git a/src/InstantReplay/Languages/Strings.ru.xaml b/src/Captail/Languages/Strings.ru.xaml similarity index 100% rename from src/InstantReplay/Languages/Strings.ru.xaml rename to src/Captail/Languages/Strings.ru.xaml diff --git a/src/InstantReplay/Localization.cs b/src/Captail/Localization.cs similarity index 98% rename from src/InstantReplay/Localization.cs rename to src/Captail/Localization.cs index b0c84e4..8ea36ca 100644 --- a/src/InstantReplay/Localization.cs +++ b/src/Captail/Localization.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Windows; -namespace InstantReplay; +namespace Captail; public static class Localization { diff --git a/src/InstantReplay/Log.cs b/src/Captail/Log.cs similarity index 95% rename from src/InstantReplay/Log.cs rename to src/Captail/Log.cs index 734e8e9..fe477c5 100644 --- a/src/InstantReplay/Log.cs +++ b/src/Captail/Log.cs @@ -1,6 +1,6 @@ using System.IO; -namespace InstantReplay; +namespace Captail; public static class Log { diff --git a/src/InstantReplay/ObsLogBridge.cs b/src/Captail/ObsLogBridge.cs similarity index 76% rename from src/InstantReplay/ObsLogBridge.cs rename to src/Captail/ObsLogBridge.cs index 9a34551..5ef1a5d 100644 --- a/src/InstantReplay/ObsLogBridge.cs +++ b/src/Captail/ObsLogBridge.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace InstantReplay; +namespace Captail; internal static class ObsLogBridge { @@ -11,18 +11,18 @@ private delegate void LogCallback( private static readonly LogCallback Callback = Write; - [DllImport("EverloopObsBridge.dll", CallingConvention = CallingConvention.Cdecl)] + [DllImport("CaptailObsBridge.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] private static extern bool everloop_install_obs_log_handler(LogCallback callback); - [DllImport("EverloopObsBridge.dll", CallingConvention = CallingConvention.Cdecl)] + [DllImport("CaptailObsBridge.dll", CallingConvention = CallingConvention.Cdecl)] private static extern void everloop_remove_obs_log_handler(); internal static bool Install() { bool installed = everloop_install_obs_log_handler(Callback); if (!installed) - Log.Write("OBS log bridge не установился."); + Log.Write("OBS log bridge could not be installed."); return installed; } diff --git a/src/InstantReplay/ObsNative.cs b/src/Captail/ObsNative.cs similarity index 99% rename from src/InstantReplay/ObsNative.cs rename to src/Captail/ObsNative.cs index 172ca30..bef1587 100644 --- a/src/InstantReplay/ObsNative.cs +++ b/src/Captail/ObsNative.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace InstantReplay; +namespace Captail; internal static class ObsNative { diff --git a/src/InstantReplay/ObsReplayEngine.cs b/src/Captail/ObsReplayEngine.cs similarity index 98% rename from src/InstantReplay/ObsReplayEngine.cs rename to src/Captail/ObsReplayEngine.cs index fb2d46c..0baf990 100644 --- a/src/InstantReplay/ObsReplayEngine.cs +++ b/src/Captail/ObsReplayEngine.cs @@ -1,8 +1,8 @@ using System.IO; using System.Runtime.InteropServices; -using InstantReplay.Interop; +using Captail.Interop; -namespace InstantReplay; +namespace Captail; public sealed class ObsReplayEngine : IDisposable { @@ -163,7 +163,7 @@ public static EncoderCapabilities ProbeCapabilities(Config config) } catch (Exception exception) { - Log.Write($"Проверка возможностей GPU не удалась: {exception}"); + Log.Write($"GPU capability detection failed: {exception}"); return EncoderCapabilities.Failed(exception.Message); } finally @@ -479,8 +479,8 @@ private void CreateSources() } else { - // WGC переживает secure/DRM-поверхности и сбросы DXGI стабильнее; - // защищённые участки становятся чёрными, сам источник не останавливается. + // WGC handles secure/DRM surfaces and DXGI resets more reliably; + // protected regions become black without stopping the source. ObsNative.obs_data_set_int(videoSettings, "method", 2); ObsNative.obs_data_set_string( videoSettings, @@ -628,8 +628,8 @@ private void CreateEncoders() if (_videoEncoder == 0) { Log.Write( - $"Кодировщик {candidate.EncoderId} отклонил профиль " + - $"{loadProfile}; пробуем следующий."); + $"Encoder {candidate.EncoderId} rejected profile " + + $"{loadProfile}; trying the next candidate."); continue; } @@ -922,7 +922,7 @@ private int AudioTrackCount() int enabled = (_config.CaptureSystemAudio ? 1 : 0) + (_config.CaptureMicrophone ? 1 : 0); if (enabled == 0) - return 1; // Replay Buffer требует аудиокодировщик; дорожка остаётся тихой. + return 1; // Replay Buffer requires an audio encoder; this track remains silent. return _config.SeparateAudioTracks && enabled > 1 ? 2 : 1; } diff --git a/src/InstantReplay/OverlayNotificationWindow.xaml b/src/Captail/OverlayNotificationWindow.xaml similarity index 97% rename from src/InstantReplay/OverlayNotificationWindow.xaml rename to src/Captail/OverlayNotificationWindow.xaml index 457c8d7..7bd6d34 100644 --- a/src/InstantReplay/OverlayNotificationWindow.xaml +++ b/src/Captail/OverlayNotificationWindow.xaml @@ -1,4 +1,4 @@ -+ @@ -155,7 +155,7 @@ - + - + - + - + - +