2026-08-03 06:15:05 +02:00
import { NextResponse } from "next/server" ;
2026-08-07 00:51:37 +02:00
import {
COMPOSITION_FAMILIES ,
LAYOUT_TEMPLATE_LABELS ,
LAYOUT_TEMPLATES ,
} from "@/lib/layout" ;
2026-08-03 06:15:05 +02:00
import { API_DOCS_URL } from "@/lib/plans" ;
export async function GET() {
return NextResponse . json ( {
name : "Songs2VID API" ,
version : "1.0" ,
authentication : "Authorization: Bearer <api_key>" ,
2026-08-03 06:52:00 +02:00
requirements : [ "YouTube account connected" ] ,
rateLimit : "100,000 requests per minute per account" ,
2026-08-03 07:36:08 +02:00
billing : false ,
notes :
"OSS self-hosted: full entitlements, no Stripe/credits/paywall. In-repo docs: docs/api/" ,
2026-08-08 16:32:28 +02:00
webhooks :
"Optional webhookUrl on job create — HTTPS POST JSON on item/job terminal states for n8n" ,
2026-08-03 06:15:05 +02:00
guidance : {
recommended :
2026-08-08 16:32:28 +02:00
"For most jobs (especially 5+ audio files): POST /api/v1/upload per file, then POST /api/v1/render (or /api/v1/jobs) with paths + optional webhookUrl" ,
2026-08-03 06:15:05 +02:00
batch :
"POST /api/v1/jobs/batch is for small packs only. Large multipart bodies may fail with 'failed to parse body as FormData'" ,
2026-08-08 16:32:28 +02:00
polling : "GET /api/v1/jobs/:id until status is COMPLETED, FAILED, or PARTIAL" ,
2026-08-03 06:15:05 +02:00
} ,
2026-08-07 00:51:37 +02:00
layoutTemplates : LAYOUT_TEMPLATES.map ( ( id ) = > ( {
id ,
label : LAYOUT_TEMPLATE_LABELS [ id ] ,
} ) ) ,
compositionFamilies : COMPOSITION_FAMILIES.map ( ( family ) = > ( {
id : family.id ,
label : family.label ,
defaultTemplate : family.defaultTemplate ,
variants : family.variants.map ( ( id ) = > ( {
id ,
label : LAYOUT_TEMPLATE_LABELS [ id ] ,
} ) ) ,
} ) ) ,
2026-08-03 06:15:05 +02:00
endpoints : [
{
method : "POST" ,
path : "/api/v1/upload" ,
description :
2026-08-03 06:52:00 +02:00
"Upload image, audio, PNG logo, or .ttf/.otf font (font ≤10MB)" ,
2026-08-03 06:15:05 +02:00
body : "multipart/form-data: file, type (image|audio|logo|font)" ,
} ,
2026-08-08 16:32:28 +02:00
{
method : "POST" ,
path : "/api/v1/render" ,
description :
"Alias of /api/v1/jobs — create a render job (n8n-friendly). Optional webhookUrl for callbacks." ,
body : "application/json: { imagePath, webhookUrl?, items[{ audioPath, audioFilename, metadata }] }" ,
} ,
2026-08-03 06:15:05 +02:00
{
method : "POST" ,
path : "/api/v1/jobs" ,
description :
2026-08-03 06:52:00 +02:00
"Create a video job from uploaded file paths with layouts, watermark, and per-item covers" ,
2026-08-08 16:32:28 +02:00
body : "application/json: { imagePath, webhookUrl?, items[{ audioPath, audioFilename, metadata }] }" ,
2026-08-03 06:15:05 +02:00
} ,
{
method : "POST" ,
path : "/api/v1/jobs/batch" ,
description :
"One-shot batch for small packs only; large uploads may fail FormData parsing; prefer upload + jobs" ,
body : "multipart/form-data: image, audio[] (repeatable), metadata? (JSON string)" ,
} ,
{
method : "GET" ,
path : "/api/v1/playlists" ,
2026-08-03 06:52:00 +02:00
description : "List YouTube playlists for the authenticated account" ,
2026-08-03 06:15:05 +02:00
} ,
{
method : "POST" ,
path : "/api/v1/playlists" ,
description : "Create a YouTube playlist" ,
body : "application/json: { title, description?, privacy? (public|unlisted|private) }" ,
} ,
{
method : "GET" ,
path : "/api/v1/jobs" ,
2026-08-08 16:32:28 +02:00
description : "List recent jobs (also available as GET /api/v1/render)" ,
2026-08-03 06:15:05 +02:00
query : "limit (default 20, max 100)" ,
} ,
{
method : "GET" ,
path : "/api/v1/jobs/:id" ,
2026-08-08 16:32:28 +02:00
description : "Get job status and item details (poll for n8n)" ,
2026-08-03 06:15:05 +02:00
} ,
] ,
docs : API_DOCS_URL ,
} ) ;
}