57 lines
2.7 KiB
C#
57 lines
2.7 KiB
C#
namespace ytLive.Models;
|
|
|
|
/// <summary>
|
|
/// The one entitlement rule, as pure policy: what a build is allowed to emit.
|
|
///
|
|
/// <para><b>Frame rate is the paid boundary.</b> Every supported resolution at 30 fps is free;
|
|
/// 60 fps requires paid ownership, regardless of frame dimensions.</para>
|
|
///
|
|
/// <para><b>Fail closed.</b> Callers pass <c>pro: false</c> when entitlement is unknown,
|
|
/// unreadable or unresolved; the free profile is the default state, never the fallback
|
|
/// after an error.</para>
|
|
///
|
|
/// <para><b>This is not a feature gate.</b> 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.</para>
|
|
/// </summary>
|
|
public static class OutputProfile
|
|
{
|
|
/// <summary>Free ceiling: 30 frames per second.</summary>
|
|
public const int FreeMaxFps = 30;
|
|
|
|
/// <summary>The default free tier and fallback when no shipped tier is available.</summary>
|
|
public const string FreeFallbackName = "1080p30";
|
|
|
|
/// <summary>Is this tier within the free budget? Pro may use anything shipped.</summary>
|
|
public static bool IsAllowed(QualityOption tier, bool pro)
|
|
=> pro
|
|
|| tier.Fps <= FreeMaxFps;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="requested">What the dropdown says.</param>
|
|
/// <param name="shipped">Every tier the build offers.</param>
|
|
/// <param name="pro">Entitlement, already resolved. Unknown must be passed as false.</param>
|
|
public static QualityOption Resolve(
|
|
QualityOption requested, IEnumerable<QualityOption> 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, 8.0, 1920, 1080);
|
|
}
|
|
}
|