From c702686726abb4216522af9125a699e1b98889b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atakan=20Do=C4=9Fan=20=C3=96zban?= Date: Mon, 3 Aug 2026 07:36:08 +0200 Subject: [PATCH] Close OSS self-host gaps: in-repo API docs, legal notes, and packaging. Add MIT license and docs/api, strip SaaS status/admin remnants from robots and footer, align env/compose/README with payment-free product truth. Co-authored-by: Cursor --- .env.example | 16 +- .gitignore | 2 + DOCKERHUB_OVERVIEW.md | 8 +- LICENSE | 21 +++ README.md | 28 ++- app/api/v1/route.ts | 3 + app/layout.tsx | 2 +- app/privacy/page.tsx | 69 +++++++- app/robots.ts | 9 +- app/sitemap.ts | 18 +- app/terms/page.tsx | 44 ++++- components/ApiKeySettings.tsx | 3 +- components/LegalFooter.tsx | 13 +- components/LegalPageLayout.tsx | 3 +- docker-compose.yml | 2 + docs/README.md | 12 ++ docs/api/endpoints.md | 305 +++++++++++++++++++++++++++++++++ docs/api/overview.md | 66 +++++++ lib/legal/constants.ts | 2 +- lib/youtube/errors.ts | 5 +- package.json | 1 + 21 files changed, 594 insertions(+), 38 deletions(-) create mode 100644 LICENSE create mode 100644 docs/README.md create mode 100644 docs/api/endpoints.md create mode 100644 docs/api/overview.md diff --git a/.env.example b/.env.example index 231e562..df5334d 100644 --- a/.env.example +++ b/.env.example @@ -1,19 +1,23 @@ -# Songs2VID OSS is always self-hosted and has no billing configuration. +# Songs2VID OSS is always self-hosted and payment-free. +# Do not set Stripe, billing, credits, pricing, or S2VID_EDITION variables — they are unused. + +# --- Required --- DATABASE_URL="postgresql://songs2vid:songs2vid@localhost:5433/songs2vid" REDIS_URL="redis://localhost:6380" NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_SECRET="replace-with-a-long-random-secret" -# Optional; falls back to NEXTAUTH_SECRET. -# TOKEN_ENCRYPTION_KEY="replace-with-another-long-random-secret" GOOGLE_CLIENT_ID="your-google-client-id.apps.googleusercontent.com" GOOGLE_CLIENT_SECRET="your-google-client-secret" + +# --- Optional --- +# TOKEN_ENCRYPTION_KEY="replace-with-another-long-random-secret" # falls back to NEXTAUTH_SECRET UPLOAD_DIR="./uploads" +# FFMPEG_PATH="C:/path/to/ffmpeg.exe" # override bundled / image ffmpeg +# S2VID_PORT=3000 # host port for docker-compose.yml (default 3000) +# S2VID_IMAGE_TAG=latest # Docker Hub tag when using published image NEXT_PUBLIC_DOCS_URL="https://docs.songs2vid.com" NEXT_PUBLIC_GITEA_ISSUES_URL="https://git.atakanozban.com/Songs2VID/songs2vid/issues" NEXT_PUBLIC_GITEA_URL="https://git.atakanozban.com/Songs2VID" NEXT_PUBLIC_DOCKER_HUB_URL="https://hub.docker.com/r/atakanozban/songs2vid" - -# Optional: override the bundled ffmpeg-static binary. -# FFMPEG_PATH="C:/path/to/ffmpeg.exe" diff --git a/.gitignore b/.gitignore index 709b0ae..91b2f37 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ node_modules/ .next/ .env .env.local +*.tsbuildinfo +tsconfig.tsbuildinfo website/node_modules/ website/build/ website/.docusaurus/ diff --git a/DOCKERHUB_OVERVIEW.md b/DOCKERHUB_OVERVIEW.md index b7aaaa1..32b35e1 100644 --- a/DOCKERHUB_OVERVIEW.md +++ b/DOCKERHUB_OVERVIEW.md @@ -2,7 +2,8 @@ Payment-free, self-hosted software that turns cover art and audio into YouTube videos (FFmpeg + BullMQ). Full entitlements in every deployment — no plans, credits, Stripe, or paywall. -**Image:** `atakanozban/songs2vid:latest` +**Image:** `atakanozban/songs2vid:latest` +**License:** MIT **Docs:** https://docs.songs2vid.com **Source:** https://git.atakanozban.com/Songs2VID/songs2vid @@ -21,12 +22,12 @@ Payment-free, self-hosted software that turns cover art and audio into YouTube v ```bash docker pull atakanozban/songs2vid:latest -docker compose up -d --build +docker compose up -d ``` Open http://localhost:3000 → sign in with Google → dashboard. -No Stripe or billing environment variables are required. +No Stripe, billing, or edition environment variables are required. Watermarks are optional (no free-tier badge paywall). ## What you get @@ -46,3 +47,4 @@ Next.js 15 · PostgreSQL/Prisma · Redis/BullMQ · NextAuth (Google + YouTube) - Getting started: https://docs.songs2vid.com/docs/getting-started - Environment variables: https://docs.songs2vid.com/docs/environment - REST API: https://docs.songs2vid.com/docs/api/overview +- In-repo API notes: https://git.atakanozban.com/Songs2VID/songs2vid/src/branch/main/docs/api diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..97bb0d9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Atakan Doğan Özban + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0173570..ab17cf5 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ plans, credits, purchases, subscriptions, paywalls, or Stripe. Sign in with Google, open the dashboard, and create videos. Settings cover account, YouTube connection, and API keys only. +**License:** [MIT](./LICENSE) + ## Quick start (Docker) Published image: [`atakanozban/songs2vid:latest`](https://hub.docker.com/r/atakanozban/songs2vid) @@ -23,15 +25,16 @@ Published image: [`atakanozban/songs2vid:latest`](https://hub.docker.com/r/ataka 4. Start the stack (web + worker + Postgres + Redis): ```bash -docker compose up -d --build +docker compose up -d ``` -Or pull the published image and run Compose after pointing services at -`atakanozban/songs2vid:latest` (same env and volumes). +Compose uses the published image by default (`atakanozban/songs2vid:latest`) and can also +build from this repo (`docker compose up -d --build`). Open http://localhost:3000 → sign in → dashboard. -No Stripe keys or billing env vars are required. +No Stripe keys, billing env vars, or edition flags are required. Watermarks are optional; +OSS does not force a free-tier SaaS badge paywall. ## Features @@ -42,6 +45,12 @@ No Stripe keys or billing env vars are required. - REST API for automation (keys under **Dashboard → Settings → API key**) - Always-on self-hosted entitlements (no quota paywall) +## API documentation + +- In-repo: [docs/api/overview.md](./docs/api/overview.md) and [docs/api/endpoints.md](./docs/api/endpoints.md) +- Live site: [docs.songs2vid.com/docs/api/overview](https://docs.songs2vid.com/docs/api/overview) +- Machine-readable discovery: `GET /api/v1` (no auth; no billing routes) + ## Local development ```bash @@ -55,6 +64,17 @@ npm run dev:all - App: http://localhost:3000 - `dev:all` runs Next.js and the BullMQ worker +## Environment + +See [`.env.example`](./.env.example) for the required and optional variables. Matching guide: +[docs.songs2vid.com/docs/environment](https://docs.songs2vid.com/docs/environment). + +## Legal (self-hosted) + +In-app **Privacy** and **Terms** pages describe self-hosted responsibility: the operator of +each instance controls data and access; this software does not process payments. They are not +the paid cloud terms for [songs2vid.com](https://songs2vid.com). + ## Stack - Next.js 15, TypeScript, Tailwind diff --git a/app/api/v1/route.ts b/app/api/v1/route.ts index 6762c01..fcc0786 100644 --- a/app/api/v1/route.ts +++ b/app/api/v1/route.ts @@ -8,6 +8,9 @@ export async function GET() { authentication: "Authorization: Bearer ", requirements: ["YouTube account connected"], rateLimit: "100,000 requests per minute per account", + billing: false, + notes: + "OSS self-hosted: full entitlements, no Stripe/credits/paywall. In-repo docs: docs/api/", guidance: { recommended: "For most jobs (especially 5+ audio files): POST /api/v1/upload per file, then POST /api/v1/jobs with the returned paths", diff --git a/app/layout.tsx b/app/layout.tsx index d58fffd..284de26 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -8,7 +8,7 @@ const inter = Inter({ subsets: ["latin"] }); const SITE_NAME = "Songs2VID"; const DEFAULT_TITLE = "Songs2VID"; const DEFAULT_DESCRIPTION = - "Self-hosted audio-to-video creation and YouTube uploading."; + "Payment-free self-hosted audio-to-video creation and YouTube uploading."; export const metadata: Metadata = { title: DEFAULT_TITLE, diff --git a/app/privacy/page.tsx b/app/privacy/page.tsx index 2bed7fb..e5374ff 100644 --- a/app/privacy/page.tsx +++ b/app/privacy/page.tsx @@ -1,15 +1,76 @@ import { LegalPageLayout } from "@/components/LegalPageLayout"; +import { LEGAL_OPERATOR } from "@/lib/legal/constants"; +import { DOCS_URL, GITEA_URL } from "@/lib/plans"; export default function PrivacyPage() { return (

- Songs2VID is self-hosted software. Your operator controls the deployment, database, uploads, - logs, and Google OAuth configuration. Songs2VID does not include payment processing. + This policy describes how a self-hosted Songs2VID instance typically + handles data. Songs2VID OSS does not include payment processing, plans, credits, or + Stripe. The person or organization that operates this deployment (the "operator") + controls the server, database, uploads, logs, and Google OAuth configuration, and is + responsible for privacy compliance for their users.

+ +

1. Who is responsible

- Google and YouTube process account and upload data according to their own policies. Contact - the operator of this instance for data access or deletion requests. + For this software package as published by {LEGAL_OPERATOR.legalName}, contact:{" "} + {LEGAL_OPERATOR.email}. For data on{" "} + this running instance, contact the operator of the deployment you signed into — + not necessarily the hosted SaaS at songs2vid.com, which is a separate product with its own + policies. +

+ +

2. Data this software processes

+
    +
  • Account profile from Google sign-in (email, name, avatar)
  • +
  • YouTube OAuth tokens (encrypted at rest when configured) and channel metadata
  • +
  • Uploaded cover art, audio, optional logos/fonts, and derived video files
  • +
  • Job metadata (titles, tags, privacy, layouts, watermarks)
  • +
  • API key hashes and rate-limit counters
  • +
+ +

3. Google and YouTube

+

+ Google and YouTube process account and upload data under their own terms and policies. + Operators must configure OAuth correctly and respect{" "} + + Google API Services User Data Policy + {" "} + and{" "} + + YouTube API Services Policies + + . +

+ +

4. Your choices

+

+ Use Dashboard → Settings to export or delete your account data on this + instance, or ask the operator. You can also revoke Google access in your{" "} + + Google Account permissions + + . +

+ +

5. Documentation

+

+ Product docs: {DOCS_URL}. Source:{" "} + {GITEA_URL}.

); diff --git a/app/robots.ts b/app/robots.ts index fd6a184..2e9842e 100644 --- a/app/robots.ts +++ b/app/robots.ts @@ -1,12 +1,17 @@ import type { MetadataRoute } from "next"; +function siteUrl() { + const raw = process.env.NEXTAUTH_URL?.trim() || "http://localhost:3000"; + return raw.replace(/\/$/, ""); +} + export default function robots(): MetadataRoute.Robots { return { rules: { userAgent: "*", allow: "/", - disallow: ["/admin", "/api/admin"], + disallow: ["/api/", "/dashboard", "/jobs"], }, - sitemap: "https://songs2vid.com/sitemap.xml", + sitemap: `${siteUrl()}/sitemap.xml`, }; } diff --git a/app/sitemap.ts b/app/sitemap.ts index 796b3f3..3e8b26d 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -1,17 +1,27 @@ import type { MetadataRoute } from "next"; -const SITE_URL = "https://songs2vid.com"; +function siteUrl() { + const raw = process.env.NEXTAUTH_URL?.trim() || "http://localhost:3000"; + return raw.replace(/\/$/, ""); +} export default function sitemap(): MetadataRoute.Sitemap { + const base = siteUrl(); return [ { - url: SITE_URL, + url: base, lastModified: new Date(), - changeFrequency: "daily", + changeFrequency: "weekly", priority: 1.0, }, { - url: `${SITE_URL}/privacy`, + url: `${base}/privacy`, + lastModified: new Date(), + changeFrequency: "monthly", + priority: 0.3, + }, + { + url: `${base}/terms`, lastModified: new Date(), changeFrequency: "monthly", priority: 0.3, diff --git a/app/terms/page.tsx b/app/terms/page.tsx index 9767c77..7e60e14 100644 --- a/app/terms/page.tsx +++ b/app/terms/page.tsx @@ -1,15 +1,51 @@ import { LegalPageLayout } from "@/components/LegalPageLayout"; +import { LEGAL_OPERATOR } from "@/lib/legal/constants"; +import { DOCS_URL, GITEA_URL } from "@/lib/plans"; export default function TermsPage() { return (

- Songs2VID is provided as open-source, self-hosted software without warranty. The operator of - each instance is responsible for availability, configuration, and user access. + Songs2VID OSS is open-source, payment-free, self-hosted software. These + terms describe use of the software and of instances that run it. They are{" "} + not the paid cloud Terms of Service for songs2vid.com (a separate hosted + product with its own billing and policies).

+ +

1. Software license and warranty

- You are responsible for the media you process and upload, including compliance with - copyright law and YouTube's terms. + The source is provided under the license in the repository LICENSE file. The + software is provided without warranty of any kind. The operator of each + instance is responsible for availability, security, backups, configuration, and who may + sign in. +

+ +

2. No paid features in this edition

+

+ This edition has no Stripe integration, subscriptions, credits, pricing pages, or paywalls. + Full layout, watermark, API, and playlist features are available to signed-in users of the + instance. Any paid offering lives only on the separate hosted SaaS product. +

+ +

3. Your content and YouTube

+

+ You are solely responsible for media you upload and for compliance with copyright law and + YouTube's terms. Connecting Google/YouTube authorizes the instance to upload on your + behalf within the scopes granted. +

+ +

4. Acceptable use

+

+ Do not use the software to infringe rights, abuse YouTube or Google APIs, or circumvent + another party's security. Operators may suspend access on their instances. +

+ +

5. Contact

+

+ Package / project contact:{" "} + {LEGAL_OPERATOR.email}. Instance-specific + support: contact your operator. Docs: {DOCS_URL}. Source:{" "} + {GITEA_URL}.

); diff --git a/components/ApiKeySettings.tsx b/components/ApiKeySettings.tsx index 94c39af..8cdc100 100644 --- a/components/ApiKeySettings.tsx +++ b/components/ApiKeySettings.tsx @@ -58,7 +58,8 @@ export function ApiKeySettings({ initialStatus, initialRateLimit }: Props) { return (

- Use the REST API to upload files and create video jobs programmatically. + Use the REST API to upload files and create video jobs programmatically. There are no + billing endpoints in this self-hosted edition.

Rate limit: {initialRateLimit.limit} requests per {initialRateLimit.windowSeconds} seconds. diff --git a/components/LegalFooter.tsx b/components/LegalFooter.tsx index 93c6892..4b50ba5 100644 --- a/components/LegalFooter.tsx +++ b/components/LegalFooter.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import type { ReactNode } from "react"; +import { DOCS_URL, GITEA_URL } from "@/lib/plans"; function LegalLink({ href, @@ -33,7 +34,7 @@ export function LegalFooter() { return (

diff --git a/components/LegalPageLayout.tsx b/components/LegalPageLayout.tsx index 3b9bc50..4faeaad 100644 --- a/components/LegalPageLayout.tsx +++ b/components/LegalPageLayout.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import type { ReactNode } from "react"; import { Logo } from "@/components/Logo"; import { LEGAL_LAST_UPDATED } from "@/lib/legal/constants"; +import { DOCS_URL } from "@/lib/plans"; type Props = { title: string; @@ -45,7 +46,7 @@ export function LegalPageLayout({ title, description, children }: Props) { Terms