Strip Songs2VID to a payment-free self-hosted OSS core.

Remove Stripe/billing/pricing/marketing, simplify schema and entitlements for unlimited self-host use, and keep auth, encode, and YouTube upload.
This commit is contained in:
Atakan Doğan Özban
2026-08-03 06:52:00 +02:00
parent 506d56fa92
commit 6476bb42a4
108 changed files with 397 additions and 8061 deletions
+7 -58
View File
@@ -1,62 +1,11 @@
import { Plan } from "@prisma/client";
import { getPlanLimits } from "./plans";
import { hasProFeatures } from "./edition";
import {
requiresArtTrackLayoutEntitlement,
type LayoutSettings,
} from "./layout";
import {
PREMIUM_REQUIRED_CODE,
requiresCustomWatermarkEntitlement,
type WatermarkSettings,
} from "./watermark";
import type { LayoutSettings } from "./layout";
import type { WatermarkSettings } from "./watermark";
export class PremiumRequiredError extends Error {
readonly code = PREMIUM_REQUIRED_CODE;
readonly status = 403;
constructor(message: string) {
super(message);
this.name = "PremiumRequiredError";
}
}
export function assertCustomWatermarkAllowed(_settings: WatermarkSettings) {}
export function assertCustomWatermarkAllowed(plan: Plan, settings: WatermarkSettings) {
if (!requiresCustomWatermarkEntitlement(settings)) return;
if (getPlanLimits(plan).customWatermark || hasProFeatures(plan)) return;
throw new PremiumRequiredError(
"Custom watermark, typography, logo overlay, and position controls require Pro.",
);
}
export function assertArtTrackLayoutAllowed(plan: Plan, settings: LayoutSettings) {
if (!requiresArtTrackLayoutEntitlement(settings)) return;
if (getPlanLimits(plan).artTrackLayouts || hasProFeatures(plan)) return;
throw new PremiumRequiredError(
"Blurred backgrounds and art-track layout templates require Pro.",
);
}
export function assertArtTrackLayoutAllowed(_settings: LayoutSettings) {}
export function assertPerItemImagesAllowed(
plan: Plan,
sharedImagePath: string,
itemImagePaths: Array<string | null | undefined>,
) {
const unique = new Set(
itemImagePaths
.map((p) => (p && p.trim() ? p.trim() : sharedImagePath))
.filter(Boolean),
);
// More than one distinct cover in the batch → Pro
if (unique.size <= 1) return;
if (getPlanLimits(plan).perItemImages || hasProFeatures(plan)) return;
throw new PremiumRequiredError(
"Matching a unique image to each audio file requires Pro. Free plan uses one shared cover image.",
);
}
export function premiumRequiredResponse(message: string) {
return {
error: message,
code: PREMIUM_REQUIRED_CODE,
};
}
_sharedImagePath: string,
_itemImagePaths: Array<string | null | undefined>,
) {}