namespace ytLive.Models; /// /// The one entitlement rule, as pure policy: what a build is allowed to emit. /// /// Frame rate is the paid boundary. Every supported resolution at 30 fps is free; /// 60 fps requires paid ownership, regardless of frame dimensions. /// /// Fail closed. Callers pass pro: false when entitlement is unknown, /// unreadable or unresolved; the free profile is the default state, never the fallback /// after an error. /// /// This is not a feature gate. Every feature, scene, overlay and the recording /// itself stay available; only the emitted frame gets smaller. Never call it for anything /// else — a cracked build buys almost nothing, which is the entire anti-piracy posture. /// public static class OutputProfile { /// Free ceiling: 30 frames per second. public const int FreeMaxFps = 30; /// /// Pre-release switch. false means the frame-rate boundary is not enforced yet, so every /// shipped tier is selectable by any build — the app is still being tested at 60 fps in /// Release, where DevBuild.IsDevelopment is false and the developer override cannot /// help. Flip to true when the paid edition ships; the rule itself already exists and its /// tests do not change. /// /// Why static readonly: a const folds the consuming || at compile /// time and leaves an unreachable operand, which is a warning. The JIT folds a readonly field /// just as well, and the build stays at zero warnings. /// public static readonly bool EntitlementEnforced = false; /// The default free tier and fallback when no shipped tier is available. public const string FreeFallbackName = "1080p30"; /// Is this tier within the free budget? Pro may use anything shipped. public static bool IsAllowed(QualityOption tier, bool pro) => pro || tier.Fps <= FreeMaxFps; /// /// The tier a session will actually encode. A permitted selection passes through /// untouched (so Pro and the free tier's own 720p30 keep their exact bitrate); a /// disallowed one resolves to the best tier the budget allows — most pixels first, then /// most frames, then the cheapest bitrate — because silently encoding something the user /// did not pick is only acceptable if it is the closest legal thing to it. /// /// What the dropdown says. /// Every tier the build offers. /// Entitlement, already resolved. Unknown must be passed as false. public static QualityOption Resolve( QualityOption requested, IEnumerable shipped, bool pro) { if (IsAllowed(requested, pro)) return requested; var candidate = shipped .Where(tier => IsAllowed(tier, pro)) .OrderByDescending(tier => tier.Width * tier.Height) .ThenByDescending(tier => tier.Fps) .ThenBy(tier => tier.Bitrate) .FirstOrDefault(); // No shipped tier fits the budget: build the floor explicitly rather than trusting // the table. A build whose tiers were all redefined must still emit a legal frame. return candidate ?? new QualityOption(FreeFallbackName, FreeMaxFps, 10.0, 1920, 1080); } }