Stream
- Date
- Author
NINikki Sophie 🌸- Stack
- Next.js, TypeScript, Tailwind CSS, shadcn/ui, PostgreSQL, Prisma, Better Auth
- Tags
- Front-EndBack-EndFull-StackWebsiteSelf-HostedStreaming
Pictures
Overview
One form that sets my stream's title, category and tags on every platform at once. Before it, going live meant opening Twitch's dashboard and YouTube Studio and typing the same title twice. Twitch and YouTube work now, with Kick next. It's self-hosted and made for one person: the first visit creates the account, and nobody can create another after that.
Features
- Everywhere first, then each platform: the form starts with what every platform shares, the title, tags and language, followed by a section for each platform with only its own settings. One save sends each section to its platform.
- Twitch: the category searched on Twitch with its box art, tags as chips checked against Twitch's rules while typing, content labels and branded content.
- YouTube: the category, the description, who can watch, whether it's made for kids, and the thumbnail, which goes up as soon as it's picked.
- Results per platform: if YouTube turns something away, Twitch's change still goes through, and the form says which one failed and why.
- Strict sign-in, since it holds the keys to my channels: email verification, two-step sign-in by an authenticator app or emailed codes with backup codes, passkeys offered in the browser's autofill, a list of signed-in devices, and rate limits kept in the database.

How it works
One account, enforced in one place. Better Auth has several ways to create a user. Instead of guarding each of them, a database hook runs before any user is created and refuses if one already exists, so none of them can get around it.
Platforms connected through the same sign-in. Twitch and YouTube are linked to the account with OAuth, asking only for what's needed to change a stream (channel:manage:broadcast on Twitch, youtube.force-ssl on Google). Their tokens are encrypted in the database.
Two very different APIs. Twitch takes a PATCH with only what changed. YouTube takes a PUT and clears whatever is left out, so Stream reads the video first and sends back everything it doesn't change. On YouTube it also has to work out which stream to change: the live broadcast, or else the next one that's bound to a stream key.
Rules from both platforms. Tags follow the stricter of the two, which is Twitch: at most 10, up to 25 characters each, letters and numbers only with no spaces, and no duplicates. The rules show under the field with a count, so a tag isn't turned away before you know why. Tags read back from YouTube that Twitch wouldn't take are left out.
Stack
Next.js (App Router, server actions), React, TypeScript, Better Auth, Prisma on PostgreSQL, the Twitch Helix and YouTube Data APIs, Nodemailer, Tailwind CSS and shadcn/ui, Bun, Docker.


