Renames AssemblyName to LlamaCasty before the first Steam depot — Steamworks is explicit that renaming a live exe is painful, so this had to land first, not later. Four assembly-qualified pack URIs carried the old name, not three: MainWindow (window icon), OverlayHost (logo), RenameRecordingDialog and KeepBroadcastDialog. Pack URIs resolve by assembly name at runtime, so a stale one compiles, passes every test and then silently renders nothing the first time that view opens. AssemblyIdentityTests guards both halves — the assembly name, and no source XAML still naming the legacy assembly. Packaging/Package.appxmanifest and package-msix.sh carry the exe name too. scripts/publish.sh replaces the by-hand publish: single-file win-x64, ReadyToRun, ffmpeg beside the exe, and it refuses to finish if ffmpeg, LICENSE, PRIVACY.md or THIRD-PARTY-NOTICES.txt are missing, or if a test assembly leaked in. That last guard exists because the first publish was wrong in a way no test could see: PublishSingleFile + IncludeNativeLibrariesForSelfExtract bundled the libav*.dll family into the exe — a 9-file output with no ffmpeg folder and a 182 MB binary, so the locator would have found nothing at runtime. ExcludeFromSingleFile="true" on the content keeps the folder a folder. Diffing against a control publish without the single-file flags found it; guessing at item globs wasted two attempts first (recorded in MyMistakes.md). Measured: 127 MB exe + 129 MB ffmpeg = 257 MB depot. Tests: AssemblyIdentity, StorePackaging, BuildStamp, BundledFonts all pass; app builds with 0 warnings.
160 lines
9.7 KiB
XML
160 lines
9.7 KiB
XML
<Project Sdk="Microsoft.NET.Sdk">
|
||
|
||
<PropertyGroup>
|
||
<OutputType>WinExe</OutputType>
|
||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||
<Nullable>enable</Nullable>
|
||
<ImplicitUsings>enable</ImplicitUsings>
|
||
<UseWPF>true</UseWPF>
|
||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||
<ApplicationIcon>Assets\llamacasty-icon.ico</ApplicationIcon>
|
||
<!-- The shipped exe is LlamaCasty.exe (renamed 2026-11-01, before the first Steam depot so
|
||
the store never sees the legacy name). RootNamespace stays `ytLive` — the C# namespaces
|
||
are not user-visible and renaming them would churn every file for no benefit. -->
|
||
<AssemblyName>LlamaCasty</AssemblyName>
|
||
<RootNamespace>ytLive</RootNamespace>
|
||
<!-- 64-bit Windows only (creator ruling 2026-10-09). The bundled ffmpeg is the win64 build
|
||
and ONNX Runtime ships no win-x86 native, so a 32-bit process could never stream; a
|
||
32-bit address space (2–4 GB) would also choke on 1080p frame buffers. x64 makes the
|
||
exe refuse to start on 32-bit Windows instead of failing at Go Live. -->
|
||
<PlatformTarget>x64</PlatformTarget>
|
||
</PropertyGroup>
|
||
|
||
<ItemGroup>
|
||
<Compile Remove="ytLive.Tests\**" />
|
||
<EmbeddedResource Remove="ytLive.Tests\**" />
|
||
<None Remove="ytLive.Tests\**" />
|
||
<Page Remove="ytLive.Tests\**" />
|
||
</ItemGroup>
|
||
|
||
<!-- Per-build identity stamp (2026-09-04): takes 1–6 could not be attributed to an
|
||
exact binary — render perf "fixes" were being judged against builds we could not
|
||
prove were running. A fresh GUID per build makes an incremental compile impossible
|
||
(that is the point: every exe you launch was compiled NOW), the wordmark shows the
|
||
id, and startup.log records id + build time. -->
|
||
<Target Name="GenerateBuildStamp" BeforeTargets="CoreCompile">
|
||
<PropertyGroup>
|
||
<BuildStampFile>$(IntermediateOutputPath)BuildStamp.g.cs</BuildStampFile>
|
||
<BuildStampId>$([System.Guid]::NewGuid().ToString('N').Substring(0,8))</BuildStampId>
|
||
<BuildStampTime>$([System.DateTime]::Now.ToString('yyyy-MM-dd HH:mm:ss'))</BuildStampTime>
|
||
<!-- RELEASE COUNTER (creator ruling 2026-09-04): the UI shows a sequential number that
|
||
ticks +1 per COMMIT, starting at 13 — a per-BUILD GUID changed on every launch and read
|
||
as noise (rightly). Derived from git rev-list count (stable per commit, no bookkeeping).
|
||
Baseline 241 = count at introduction, first display 13. Missing git or unparsed output
|
||
falls back to the start (build never breaks). GUID + compile time stay in startup.log,
|
||
where build-identity forensics actually live. -->
|
||
<BuildNumberBaseline>17</BuildNumberBaseline>
|
||
<BuildNumberStart>13</BuildNumberStart>
|
||
</PropertyGroup>
|
||
<Exec Command=""C:\Program Files\Git\cmd\git.exe" rev-list --count HEAD"
|
||
WorkingDirectory="$(ProjectDir)" ConsoleToMSBuild="true" IgnoreExitCode="true">
|
||
<Output TaskParameter="ConsoleOutput" PropertyName="CommitCountRaw" />
|
||
</Exec>
|
||
<PropertyGroup>
|
||
<CommitCount>$(CommitCountRaw.Trim())</CommitCount>
|
||
<BuildCounterFile>$(ProjectDir)BuildCounter.txt</BuildCounterFile>
|
||
<PriorBuildCount Condition="Exists('$(BuildCounterFile)')">$([System.IO.File]::ReadAllText('$(BuildCounterFile)').Trim())</PriorBuildCount>
|
||
<PriorBuildCount Condition="'$(PriorBuildCount)' == ''">423</PriorBuildCount>
|
||
<BuildNumber>$([MSBuild]::Add($(PriorBuildCount), 1))</BuildNumber>
|
||
</PropertyGroup>
|
||
<WriteLinesToFile File="$(BuildCounterFile)" Lines="$(BuildNumber)" Overwrite="true" WriteOnlyWhenDifferent="false" />
|
||
<PropertyGroup>
|
||
<BuildNumber Condition="! $([System.Text.RegularExpressions.Regex]::IsMatch('$(BuildNumber)', '^[0-9]+$'))">$(BuildNumberStart)</BuildNumber>
|
||
</PropertyGroup>
|
||
<ItemGroup>
|
||
<BuildStampLines Include="// <auto-generated> Per-build identity (ytLive.csproj GenerateBuildStamp)."/>
|
||
<BuildStampLines Include="// Release/Label = the sequential UI stamp (+1 per commit, started at 13)."/>
|
||
<BuildStampLines Include="// Id/BuiltLocal = per-build forensic identity, logged to startup.log only. Never edit by hand."/>
|
||
<BuildStampLines Include="namespace ytLive.Helpers%3B"/>
|
||
<BuildStampLines Include="public static class BuildStamp { public const int Release = $(BuildNumber)%3B public const string Label = "#$(BuildNumber)"%3B public const string Id = "$(BuildStampId)"%3B public const string BuiltLocal = "$(BuildStampTime)"%3B }"/>
|
||
</ItemGroup>
|
||
<WriteLinesToFile File="$(BuildStampFile)" Lines="@(BuildStampLines)" Overwrite="true" WriteOnlyWhenDifferent="false" />
|
||
<ItemGroup>
|
||
<Compile Include="$(BuildStampFile)" />
|
||
</ItemGroup>
|
||
</Target>
|
||
|
||
<ItemGroup>
|
||
<Resource Include="Assets\llamacasty-icon.png" />
|
||
<Resource Include="Assets\llamacasty-logo.jpg" />
|
||
<Resource Include="Assets\starting-background.jpg" />
|
||
<Resource Include="Assets\brb-background.jpg" />
|
||
<Resource Include="Assets\live-background.jpg" />
|
||
<Resource Include="Assets\ending-background.jpg" />
|
||
<Resource Include="Assets\chat-background.jpg" />
|
||
<Resource Include="Assets\alert-default.mp4" />
|
||
<!-- Design-standard typefaces (DESIGN.md): Inter + JetBrains Mono, OFL-1.1. -->
|
||
<Resource Include="Assets\Fonts\Inter-Regular.ttf" />
|
||
<Resource Include="Assets\Fonts\Inter-Medium.ttf" />
|
||
<Resource Include="Assets\Fonts\Inter-SemiBold.ttf" />
|
||
<Resource Include="Assets\Fonts\Inter-Bold.ttf" />
|
||
<Resource Include="Assets\Fonts\JetBrainsMono-Medium.ttf" />
|
||
<Resource Include="Assets\Fonts\JetBrainsMono-SemiBold.ttf" />
|
||
</ItemGroup>
|
||
|
||
<ItemGroup>
|
||
<None Include="THIRD-PARTY-NOTICES.txt" CopyToOutputDirectory="PreserveNewest" />
|
||
<!-- The EULA and privacy policy travel with every copy: they are what the About screen and
|
||
any store listing are checked against. -->
|
||
<None Include="LICENSE" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Always" />
|
||
<None Include="PRIVACY.md" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Always" />
|
||
</ItemGroup>
|
||
|
||
<!-- Webcam background effects (TASK 51): Google's MediaPipe Selfie Segmenter (landscape,
|
||
Apache-2.0), converted to ONNX by tools/selfie-seg/ and embedded in the assembly —
|
||
~430 KB, so it lives in git, ships inside the exe, and is never downloaded at runtime
|
||
(Store policy 10.2.2, same posture as the bundled ffmpeg). -->
|
||
<ItemGroup>
|
||
<EmbeddedResource Include="Assets\Models\selfie_segmenter_landscape.onnx" LogicalName="ytLive.Assets.Models.selfie_segmenter_landscape.onnx" />
|
||
</ItemGroup>
|
||
|
||
<!-- Bundled FFmpeg (TASK 48 item 1, Store policy 10.2.2 dynamic code inclusion).
|
||
The app used to DOWNLOAD an unsigned ffmpeg from GitHub at first run and execute
|
||
it; that 404'd on the creator's first real recording (2026-09-01) and is the one
|
||
hard certification gate. ffmpeg.exe + ffprobe.exe + the libav*.dll family now ship
|
||
in the output under a single ffmpeg/ folder — the shared build resolves its DLLs
|
||
from the exe's own directory, so they must stay siblings.
|
||
|
||
The binaries are NOT in git (135 MB). scripts/fetch-ffmpeg.sh populates ./ffmpeg
|
||
from a pinned URL, verifying SHA-256 first. CopyToPublishDirectory=Always is the
|
||
one that matters for the MSIX package. -->
|
||
<ItemGroup>
|
||
<!-- `Remove` first: the SDK's default None glob already matched ffmpeg\**, so an Include
|
||
without removing it produced DUPLICATE items — one carrying the copy metadata and one
|
||
with none. Harmless until PublishSingleFile + IncludeNativeLibrariesForSelfExtract came
|
||
along, where the bundler collapses the duplicate and the folder silently stops being
|
||
published (found 2026-11-01: a 9-file publish with no ffmpeg in it). -->
|
||
<!-- ⛔ ExcludeFromSingleFile is load-bearing. With PublishSingleFile +
|
||
IncludeNativeLibrariesForSelfExtract the bundler treats the libav*.dll family as native
|
||
libs and swallows them INTO the exe — a 182 MB exe with no ffmpeg\ folder beside it, so
|
||
the locator finds nothing and every stream fails at runtime. The metadata keeps the
|
||
folder a folder. (Found 2026-11-01 by diffing the publish output against a control
|
||
publish without the single-file flags.) -->
|
||
<None Remove="ffmpeg\**" />
|
||
<None Include="ffmpeg\**" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Always" ExcludeFromSingleFile="true" />
|
||
</ItemGroup>
|
||
|
||
<Target Name="GuardBundledFfmpeg" BeforeTargets="CoreCompile">
|
||
<Error Condition="!Exists('$(MSBuildProjectDirectory)\ffmpeg\ffmpeg.exe')"
|
||
Text="Bundled FFmpeg is missing from ffmpeg\. Run scripts/fetch-ffmpeg.sh once before building (it pins the URL and verifies SHA-256). FFmpeg must be bundled, never downloaded at runtime — that is Windows Store policy 10.2.2, and the previous download path 404'd on 2026-09-01." />
|
||
</Target>
|
||
|
||
<ItemGroup>
|
||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||
<_Parameter1>ytLive.Tests</_Parameter1>
|
||
</AssemblyAttribute>
|
||
</ItemGroup>
|
||
|
||
<ItemGroup>
|
||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.10" />
|
||
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.31.0" />
|
||
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
|
||
<PackageReference Include="NAudio.WinMM" Version="2.2.1" />
|
||
<PackageReference Include="Notification.Wpf" Version="11.0.0" />
|
||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.12" />
|
||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
|
||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
||
</ItemGroup>
|
||
|
||
</Project>
|