From f369b0d339d17ef4f0373201a8c4287571613b48 Mon Sep 17 00:00:00 2001 From: Starsky16 <198834854@qq.com> Date: Mon, 31 Aug 2026 21:49:56 +0800 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E9=BA=A6?= =?UTF-8?q?=E5=85=8B=E9=A3=8E=E9=87=87=E6=A0=B7=E5=B9=B6=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=98=88=E5=80=BC=E6=8F=90=E9=86=92=EF=BC=8C=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E5=81=A5=E5=A3=AE=E6=80=A7=E4=B8=8E=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改部分: - Services 新增 AudioPeakMeter(麦克风峰值采样:实时计量优先 + 多重回退采样 + 结果缓存 + 信号量并发保护)、DecibelCalculator(分贝换算纯函数)、DecibelNotificationProvider(ClassIsland 超阈值提醒通知提供方); - Plugin.cs 改为注册共享 AudioPeakMeter 单例与通知提供方,移除原设备枚举采样逻辑; - 组件与设置控件去掉各自重复的 WaveIn/Wasapi 采样实现,统一复用共享采样服务,并修正 Avalonia INotifyPropertyChanged 事件转发;校准流程改用显示刻度参考; - 设置模型新增阈值提醒配置(启用开关、阈值、提示文字、冷却分钟数); - 新增单元测试工程(覆盖 DecibelCalculator 纯函数)与 ClassIsland 集成冒烟测试 workflow;调整 Build/Test workflow;.gitignore 忽略 cipx/ 打包产物;删除多余占位文件并更新 README。 修改逻辑: - 原采样代码在组件与设置控件中重复且高频刷新会反复打开音频设备,易并发抢占、开销大、设备不提供实时计量时数值长期为 0; - 现将采样收敛为单一共享服务:快速路径仅读 AudioMeterInformation,验证过支持实时计量的设备在静音时直接返回真实 0;不支持时才做短时录音回退采样,结果缓存、会话受信号量保护,避免 UI 高频刷新触发耗时操作; - 补齐超阈值提醒:超过阈值时组件显示提示并经 ClassIsland 通知系统弹出提醒,冷却期内不重复触发;换算逻辑抽为与设备无关的纯函数便于测试。 由 VS Code 的 Cline 使用 DeepSeek V4 Flash 生成。 --- .github/workflows/Build.yml | 16 +- .github/workflows/IntegrationTest.yml | 77 ++++ .github/workflows/Test.yml | 34 +- .gitignore | 2 + ClassIsland.Desktop/PluginLoadContext.cs | 0 .../DecibelComponentSettingsControl.axaml | 47 ++- .../DecibelComponentSettingsControl.axaml.cs | 261 ++++--------- Controls/Components/DecibelComponent.axaml | 60 +-- Controls/Components/DecibelComponent.axaml.cs | 245 +++++-------- Decibel_Monitor.csproj | 7 + .../DecibelComponentSettings.cs | 24 +- Plugin.cs | 34 +- README.md | 23 +- Services/AudioPeakMeter.cs | 344 ++++++++++++++++++ Services/DecibelCalculator.cs | 62 ++++ Services/DecibelNotificationProvider.cs | 61 ++++ .../DecibelCalculatorTests.cs | 89 +++++ .../Decibel_Monitor.Tests.csproj | 26 ++ 18 files changed, 959 insertions(+), 453 deletions(-) create mode 100644 .github/workflows/IntegrationTest.yml delete mode 100644 ClassIsland.Desktop/PluginLoadContext.cs create mode 100644 Services/AudioPeakMeter.cs create mode 100644 Services/DecibelCalculator.cs create mode 100644 Services/DecibelNotificationProvider.cs create mode 100644 Tests/Decibel_Monitor.Tests/DecibelCalculatorTests.cs create mode 100644 Tests/Decibel_Monitor.Tests/Decibel_Monitor.Tests.csproj diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index 860ad12..e54f542 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -4,8 +4,7 @@ on: push: workflow_dispatch: pull_request: - types: [closed] - + jobs: build_app: runs-on: windows-latest @@ -13,7 +12,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Install .NET Core uses: actions/setup-dotnet@v4 with: @@ -26,13 +25,16 @@ jobs: key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} restore-keys: | ${{ runner.os }}-nuget- - + - name: Build run: pwsh -ep bypass ./tools/publish.ps1 + - name: Test + run: dotnet test Tests/Decibel_Monitor.Tests/Decibel_Monitor.Tests.csproj -c Release + - name: Md5Checksum run: pwsh -ep bypass ./tools/generate-md5.ps1 cipx/ - + - name: Upload APP to release uses: ncipollo/release-action@v1 if: ${{ startsWith(github.ref, 'refs/tags/') }} @@ -40,9 +42,9 @@ jobs: artifacts: "./cipx/*.cipx" draft: true generateReleaseNotes: true - body: ./cipx/checksums.md + bodyFile: ./cipx/checksums.md token: ${{ secrets.GITHUB_TOKEN }} - + - name: Upload artifact uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/IntegrationTest.yml b/.github/workflows/IntegrationTest.yml new file mode 100644 index 0000000..f643295 --- /dev/null +++ b/.github/workflows/IntegrationTest.yml @@ -0,0 +1,77 @@ +name: Integration Test + +# 集成冒烟测试:用官方 Release 版 ClassIsland 启动并加载本插件, +# 验证插件入口 Initialize 能完整执行(注册组件/提醒提供方无异常)。 +# 运行时功能(麦克风采样、UI 交互)在无头 CI 环境中无法验证。 + +on: + push: + workflow_dispatch: + +jobs: + integration-test: + runs-on: windows-latest + name: Launch in ClassIsland + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET 8 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + + - name: Publish plugin + shell: pwsh + run: dotnet publish Decibel_Monitor.csproj -c Release -o "$env:RUNNER_TEMP\plugin" + + - name: Download & extract ClassIsland (official release 2.1.0.1) + shell: pwsh + run: | + $zip = Join-Path $env:RUNNER_TEMP "classisland.zip" + $dest = Join-Path $env:RUNNER_TEMP "classisland" + Invoke-WebRequest -Uri "https://github.com/ClassIsland/ClassIsland/releases/download/2.1.0.1/ClassIsland_app_windows_x64_full_folder.zip" -OutFile $zip + Expand-Archive -Path $zip -DestinationPath $dest -Force + $exe = Get-ChildItem -Path $dest -Recurse -Filter "ClassIsland.Desktop.exe" | Select-Object -First 1 + if (-not $exe) { throw "未在解压目录中找到 ClassIsland.Desktop.exe" } + Write-Host "ClassIsland.Desktop.exe: $($exe.FullName)" + Add-Content -Path $env:GITHUB_ENV -Value "CI_CLASSISLAND_EXE=$($exe.FullName)" + + - name: Launch ClassIsland with plugin & verify load marker + shell: pwsh + env: + DECIBEL_MONITOR_CI_MARKER: "1" + run: | + $exe = $env:CI_CLASSISLAND_EXE + $exeDir = Split-Path $exe + # folder 打包:AppPackageRoot = exeDir/.., AppRoot = AppPackageRoot/data, + # 插件配置目录 = AppRoot/Config/Plugins/ + $appRoot = (Resolve-Path (Join-Path $exeDir "..")).Path + $marker = Join-Path $appRoot "data\Config\Plugins\Decibel_Monitor\loaded.marker" + $pluginDir = Join-Path $env:RUNNER_TEMP "plugin" + Write-Host "插件目录: $pluginDir" + Write-Host "期望标记文件: $marker" + + $proc = Start-Process -FilePath $exe -ArgumentList @("-epp", $pluginDir, "--skip-oobe", "--quiet") -PassThru -WindowStyle Hidden + try { + $deadline = (Get-Date).AddSeconds(150) + $found = $false + while ((Get-Date) -lt $deadline) { + if (Test-Path $marker) { $found = $true; break } + if ($proc.HasExited) { + Write-Host "ClassIsland 进程已退出, ExitCode=$($proc.ExitCode)" + break + } + Start-Sleep -Seconds 5 + } + if (-not $found) { + Write-Host "ClassIsland 进程是否存活: $(-not $proc.HasExited)" + Write-Host "AppRoot 下内容:" + if (Test-Path $appRoot) { Get-ChildItem $appRoot -Recurse -Depth 3 | Select-Object -ExpandProperty FullName | Out-String | Write-Host } + throw "插件未能在 ClassIsland 中成功加载(标记文件不存在: $marker)" + } + Write-Host "✅ 插件加载成功,标记文件: $marker" + Get-Content $marker + } finally { + if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue } + } diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 73b380f..91f8057 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -4,16 +4,15 @@ on: push: workflow_dispatch: pull_request: - types: [closed] - + jobs: - build_app: + test: runs-on: windows-latest - name: Build + name: Test steps: - name: Checkout uses: actions/checkout@v4 - + - name: Install .NET Core uses: actions/setup-dotnet@v4 with: @@ -26,26 +25,9 @@ jobs: key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} restore-keys: | ${{ runner.os }}-nuget- - - - name: Build - run: pwsh -ep bypass ./tools/publish.ps1 - - name: Checksum - run: pwsh -ep bypass ./tools/generate-md5.ps1 cipx/ + - name: Build main project + run: dotnet build Decibel_Monitor.csproj -c Release - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - path: | - ./cipx/*.cipx - ./cipx/checksums.md - name: Decibel_Monitor-Actions - - - name: Upload APP to release - uses: ncipollo/release-action@v1 - if: ${{ startsWith(github.ref, 'refs/tags/') }} - with: - artifacts: "./cipx/*.cipx" - draft: true - bodyFile: ./cipx/checksums.md - token: ${{ secrets.GITHUB_TOKEN }} + - name: Run unit tests + run: dotnet test Tests/Decibel_Monitor.Tests/Decibel_Monitor.Tests.csproj -c Release diff --git a/.gitignore b/.gitignore index 9491a2f..a388ece 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ x86/ bld/ [Bb]in/ [Oo]bj/ +# ClassIsland 插件打包产物 +cipx/ [Oo]ut/ [Ll]og/ [Ll]ogs/ diff --git a/ClassIsland.Desktop/PluginLoadContext.cs b/ClassIsland.Desktop/PluginLoadContext.cs deleted file mode 100644 index e69de29..0000000 diff --git a/Controls/ComponentSettings/DecibelComponentSettingsControl.axaml b/Controls/ComponentSettings/DecibelComponentSettingsControl.axaml index 6e15a48..72ffc13 100644 --- a/Controls/ComponentSettings/DecibelComponentSettingsControl.axaml +++ b/Controls/ComponentSettings/DecibelComponentSettingsControl.axaml @@ -1,17 +1,3 @@ - - + - +