import { Plan } from "@prisma/client"; import { getResolution, RESOLUTIONS } from "./constants"; import { isSelfHostedEdition } from "./edition"; export type PlanLimits = { monthlyQuota: number; maxBatchSize: number; maxResolutionHeight: number; maxFileSizeBytes: number; watermarkOptional: boolean; customWatermark: boolean; perItemImages: boolean; artTrackLayouts: boolean; allowedAudioExtensions: readonly string[]; id3TagSupport: boolean; }; /** Unlimited self-hosted limits — used for every plan in this OSS build. */ export const SELFHOSTED_LIMITS: PlanLimits = { monthlyQuota: 1_000_000, maxBatchSize: 1_000, maxResolutionHeight: Math.max(...RESOLUTIONS.map((r) => r.height)), maxFileSizeBytes: 500 * 1024 * 1024, watermarkOptional: true, customWatermark: true, perItemImages: true, artTrackLayouts: true, allowedAudioExtensions: [".mp3", ".wav", ".flac"], id3TagSupport: true, }; /** Kept for type compatibility; OSS always returns SELFHOSTED_LIMITS. */ export const PLAN_LIMITS: Record = { FREE: { ...SELFHOSTED_LIMITS }, PREMIUM: { ...SELFHOSTED_LIMITS }, }; export const SALES_EMAIL = "songs2vid@atakanozban.com"; export const SUPPORT_EMAIL = "songs2vid@atakanozban.com"; export const QUOTA_REQUEST_EMAIL = "songs2vid@atakanozban.com"; export const UPGRADE_URL = "https://songs2vid.com"; export const WEBSITE_URL = "https://songs2vid.com"; export const GITEA_ISSUES_URL = process.env.NEXT_PUBLIC_GITEA_ISSUES_URL ?? "https://git.atakanozban.com/Songs2VID/songs2vid/issues"; export const GITEA_URL = process.env.NEXT_PUBLIC_GITEA_URL ?? "https://git.atakanozban.com/Songs2VID/songs2vid"; export const DOCKER_HUB_URL = process.env.NEXT_PUBLIC_DOCKER_HUB_URL ?? "https://hub.docker.com/r/atakanozban/songs2vid"; /** Public docs (Docusaurus). Override with NEXT_PUBLIC_DOCS_URL if needed. */ export const DOCS_URL = ( process.env.NEXT_PUBLIC_DOCS_URL?.trim() || "https://docs.songs2vid.com" ).replace(/\/$/, ""); export const API_DOCS_URL = `${DOCS_URL}/docs/api/overview`; export function getPlanLimits(_plan?: Plan): PlanLimits { void isSelfHostedEdition(); return SELFHOSTED_LIMITS; } export function getResolutionsForPlan(plan: Plan) { const maxHeight = getPlanLimits(plan).maxResolutionHeight; return RESOLUTIONS.filter((r) => r.height <= maxHeight); } export function isResolutionAllowedForPlan(resolution: string, plan: Plan): boolean { const res = getResolution(resolution); if (!res) return false; return res.height <= getPlanLimits(plan).maxResolutionHeight; } export function isAudioExtensionAllowed(filename: string, plan: Plan): boolean { const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase(); return getPlanLimits(plan).allowedAudioExtensions.includes(ext); } export function getNextMonthlyQuotaReset(from: Date = new Date()): Date { return new Date(from.getFullYear(), from.getMonth() + 1, 1, 0, 0, 0, 0); }