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. +

+ Captail main window showing replay status, audio sources, recording format, disk space, and save hotkey +

+ > [!WARNING] > Captail `v0.1.x` is an early public preview. Core recording works, but bugs and hardware-specific problems are expected. Please report anything that does not work. @@ -27,6 +31,16 @@ Captail is designed around one rule: **Instant Replay should stay on.** A game c - **Flexible audio** — system/game audio and microphone, volume controls, microphone boost, mixed or separate tracks. - **Local and private** — no account, cloud upload, analytics, or telemetry. Replays stay on your PC. +## How Captail differs from OBS Replay Buffer + +Captail uses libobs for capture and encoding, but it is not an OBS Studio frontend. + +- Starts directly in the system tray with Windows. +- Requires no scenes, sources, or streaming configuration. +- Monitors capture health and automatically recovers the recording pipeline. +- Switches between focused game capture and desktop capture. +- Stays focused on one job: reliable instant replay. + ## Download Download the latest version from [GitHub Releases](https://github.com/FaulMit/captail/releases). @@ -147,7 +161,7 @@ Requirements: git clone https://github.com/FaulMit/captail.git cd captail .\tools\AcquireObsRuntime.ps1 -dotnet build .\InstantReplay.sln -c Release +dotnet build .\Captail.sln -c Release ``` Create local release packages: diff --git a/docs/captail-main.jpg b/docs/captail-main.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c43d7d8ea84c921bd261a578a2cb6888d700854b GIT binary patch literal 17072 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 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 @@ - + - + - + - + - +