Initial OSS scaffold from Songs2VID (pre-strip)

This commit is contained in:
Songs2VID Support
2026-08-03 06:15:05 +02:00
commit 506d56fa92
195 changed files with 25023 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
/**
* Billing / credit constants for Songs2VID hybrid pricing.
* Plan enum in DB: FREE | PREMIUM (Pro = €5/mo "starter_5eur").
*/
export const CREDIT_CURRENCY = "eur";
/** Free monthly allocation */
export const FREE_MONTHLY_CREDITS = 10;
/** Pro monthly allocation */
export const PRO_MONTHLY_CREDITS = 50;
/** Free-tier top-up: buyer picks 115 credits */
export const FREE_TOP_UP_MIN = 1;
export const FREE_TOP_UP_MAX = 15;
/** €0.25 per extra credit */
export const CREDIT_PRICE_CENTS = 25;
/** @deprecated use FREE_TOP_UP_MAX */
export const FREE_TOP_UP_CREDITS = FREE_TOP_UP_MAX;
/** @deprecated use creditPurchaseTotalCents(FREE_TOP_UP_MAX) */
export const FREE_TOP_UP_PRICE_CENTS = FREE_TOP_UP_MAX * CREDIT_PRICE_CENTS;
/** Pro subscription €5 / month */
export const PRO_PRICE_CENTS = 500;
/** Soft cap on never-expiring extra credits (Pro / legacy storage bound) */
export const EXTRA_CREDITS_MAX = 1000;
/**
* Hard cap on total accumulated credits (monthly remaining + extras).
* On renewal, unused + new monthly is trimmed to this threshold.
*/
export const MAX_CREDIT_CAP = 30;
/**
* Free plan: extras balance cannot exceed 15.
* After monthly (10) + extras (≤15) are used, upgrade to Pro is required.
*/
export const FREE_EXTRA_CREDITS_MAX = FREE_TOP_UP_MAX;
/** Stripe Tax / Managed Payments SaaS personal use */
export const STRIPE_PRODUCT_TAX_CODE = "txcd_10103000";
export const CREDIT_PURCHASE_MIN = FREE_TOP_UP_MIN;
export const CREDIT_PURCHASE_MAX = FREE_TOP_UP_MAX;
export const CREDIT_BALANCE_MAX = EXTRA_CREDITS_MAX;
/** Shown in quota UI errors so UpgradeProLink can attach a CTA */
export const PRO_UPGRADE_REQUIRED_SUFFIX = " Upload up to 50 videos with Pro!";
export function monthlyCreditsForPlan(plan: "FREE" | "PREMIUM"): number {
return plan === "PREMIUM" ? PRO_MONTHLY_CREDITS : FREE_MONTHLY_CREDITS;
}
export function freeExtraCreditsCap(): number {
return FREE_EXTRA_CREDITS_MAX;
}
export function formatEuroFromCents(cents: number): string {
const amount = cents / 100;
const formatted = amount % 1 === 0 ? String(amount) : amount.toFixed(2);
return `${formatted}`;
}
export function creditPurchaseTotalCents(credits: number): number {
return credits * CREDIT_PRICE_CENTS;
}
export function formatCreditPrice(credits = 1): string {
return formatEuroFromCents(creditPurchaseTotalCents(credits));
}
export function validateFreeTopUpAmount(
credits: number,
currentExtraBalance: number,
monthlyRemaining = 0,
): { ok: true; credits: number; amountCents: number } | { ok: false; error: string } {
if (!Number.isInteger(credits)) {
return { ok: false, error: "Credit amount must be a whole number." };
}
if (credits < FREE_TOP_UP_MIN || credits > FREE_TOP_UP_MAX) {
return {
ok: false,
error: `You can buy between ${FREE_TOP_UP_MIN} and ${FREE_TOP_UP_MAX} credits per top-up.`,
};
}
if (currentExtraBalance >= FREE_EXTRA_CREDITS_MAX) {
return {
ok: false,
error: `Free plan extras are capped at ${FREE_EXTRA_CREDITS_MAX}. Upgrade to Pro for 50 videos every month.${PRO_UPGRADE_REQUIRED_SUFFIX}`,
};
}
const freeExtraRoom = FREE_EXTRA_CREDITS_MAX - currentExtraBalance;
const totalRoom = Math.max(0, MAX_CREDIT_CAP - monthlyRemaining - currentExtraBalance);
const room = Math.min(freeExtraRoom, totalRoom);
if (room <= 0) {
return {
ok: false,
error: `Your total credit balance cannot exceed ${MAX_CREDIT_CAP}. Upgrade to Pro or use existing credits first.${PRO_UPGRADE_REQUIRED_SUFFIX}`,
};
}
if (credits > room) {
return {
ok: false,
error: `You can buy up to ${room} more credits under the ${MAX_CREDIT_CAP}-credit account cap.`,
};
}
return { ok: true, credits, amountCents: creditPurchaseTotalCents(credits) };
}