diff --git a/.env.example b/.env.example index 0ce2d1b..cd7faa7 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ APP_NAME=Nyone APP_ENV=local APP_KEY= APP_DEBUG=true -APP_URL=http://localhost +APP_URL=https://nyone.net APP_LOCALE=en APP_FALLBACK_LOCALE=en @@ -64,8 +64,8 @@ AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" -STREAMING_RTMP_INGEST_URL=rtmp://localhost:1935 -STREAMING_HLS_PUBLIC_URL=http://localhost:8888 +STREAMING_RTMP_INGEST_URL=rtmp://nyone.net:19935 +STREAMING_HLS_PUBLIC_URL=https://nyone-hls.net STREAMING_VOD_RETENTION_DAYS=30 STREAMING_RECORDINGS_PATH=storage/app/private/recordings MEDIAMTX_SHARED_SECRET=change-this-secret diff --git a/README.md b/README.md index 0db60f1..8298afe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ # Nyone -Nyone is a Laravel/Inertia live streaming platform scaffold. The first implementation supports one creator channel per user, OBS publishing through MediaMTX RTMP, HLS playback, channel follows, live chat, creator-selected VOD recording, and basic admin moderation. +Nyone is a Laravel/Inertia live streaming platform. The current implementation supports one creator channel per user, OBS publishing through MediaMTX RTMP, HLS playback, channel follows, live chat, creator-selected VOD recording, and basic admin moderation. + +## Stack + +- Laravel 13, Fortify auth, Inertia. +- React 19, TypeScript, Tailwind CSS 4, Radix UI primitives. +- MediaMTX for RTMP ingest, HLS playback, stream auth hooks, lifecycle hooks, and recordings. +- PostgreSQL is the production target; SQLite is fine for local development. ## Local Setup @@ -14,11 +21,45 @@ npm run dev php artisan serve ``` -Configure MediaMTX from `deploy/mediamtx.yml.example`, replacing `change-this-secret` with `MEDIAMTX_SHARED_SECRET`. +For local development, override these values in `.env` if you are not using the production domains: + +```env +APP_URL=http://127.0.0.1:8000 +STREAMING_RTMP_INGEST_URL=rtmp://127.0.0.1:19935 +STREAMING_HLS_PUBLIC_URL=http://127.0.0.1:19988 +``` + +## Production Values + +The project config is aligned to these production-style domains: + +```env +APP_NAME=Nyone +APP_URL=https://nyone.net +STREAMING_RTMP_INGEST_URL=rtmp://nyone.net:19935 +STREAMING_HLS_PUBLIC_URL=https://nyone-hls.net +MEDIAMTX_SHARED_SECRET=change-this-secret +``` + +Configure MediaMTX from `deploy/mediamtx.yml`, replacing `change-this-secret` with the same value as `MEDIAMTX_SHARED_SECRET`. Creators use: -- Server: `STREAMING_RTMP_INGEST_URL` -- Stream key: `?token=` +- OBS server: `STREAMING_RTMP_INGEST_URL` +- OBS stream key: `?token=` -Viewer playback uses `STREAMING_HLS_PUBLIC_URL//index.m3u8`. +Viewer playback uses: + +```text +STREAMING_HLS_PUBLIC_URL//index.m3u8 +``` + +## Verification + +```bash +php artisan test +npm run format:check +npm run lint:check +npm run types:check +npm run build +``` diff --git a/composer.json b/composer.json index 961ebf3..83f82bd 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,16 @@ { "$schema": "https://getcomposer.org/schema.json", - "name": "laravel/react-starter-kit", + "name": "nyone/streaming-platform", "type": "project", - "description": "The skeleton application for the Laravel framework.", + "description": "Nyone live streaming platform built with Laravel, Inertia React, and MediaMTX.", "keywords": [ + "nyone", + "streaming", + "live-streaming", + "mediamtx", + "inertia", "laravel", - "framework" + "react" ], "license": "MIT", "require": { @@ -103,4 +108,4 @@ } }, "minimum-stability": "stable" -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index d4ed697..ab5138d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "88c7fcd5b77421cdc2de93c2514309dc", + "content-hash": "1d4e9648391a072a961dcab64a9324f9", "packages": [ { "name": "bacon/bacon-qr-code", diff --git a/config/app.php b/config/app.php index 440992f..64fe929 100644 --- a/config/app.php +++ b/config/app.php @@ -13,7 +13,7 @@ return [ | */ - 'name' => env('APP_NAME', 'Laravel'), + 'name' => env('APP_NAME', 'Nyone'), /* |-------------------------------------------------------------------------- @@ -52,7 +52,7 @@ return [ | */ - 'url' => env('APP_URL', 'http://localhost'), + 'url' => env('APP_URL', 'https://nyone.net'), /* |-------------------------------------------------------------------------- diff --git a/config/cache.php b/config/cache.php index c68acdf..5d8637b 100644 --- a/config/cache.php +++ b/config/cache.php @@ -112,7 +112,7 @@ return [ | */ - 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), + 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'nyone')).'-cache-'), /* |-------------------------------------------------------------------------- diff --git a/config/database.php b/config/database.php index 64709ce..1f63fd7 100644 --- a/config/database.php +++ b/config/database.php @@ -149,7 +149,7 @@ return [ 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), + 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'nyone')).'-database-'), 'persistent' => env('REDIS_PERSISTENT', false), ], diff --git a/config/filesystems.php b/config/filesystems.php index 37d8fca..dd0c9f7 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -41,7 +41,7 @@ return [ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage', + 'url' => rtrim(env('APP_URL', 'https://nyone.net'), '/').'/storage', 'visibility' => 'public', 'throw' => false, 'report' => false, diff --git a/config/logging.php b/config/logging.php index b09cb25..f1c0f7d 100644 --- a/config/logging.php +++ b/config/logging.php @@ -76,7 +76,7 @@ return [ 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), - 'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Laravel')), + 'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Nyone')), 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 'level' => env('LOG_LEVEL', 'critical'), 'replace_placeholders' => true, diff --git a/config/mail.php b/config/mail.php index e32e88d..a200818 100644 --- a/config/mail.php +++ b/config/mail.php @@ -46,7 +46,7 @@ return [ 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), 'timeout' => null, - 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'https://nyone.net'), PHP_URL_HOST)), ], 'ses' => [ @@ -112,7 +112,7 @@ return [ 'from' => [ 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), - 'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')), + 'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Nyone')), ], ]; diff --git a/config/session.php b/config/session.php index e0c0eac..ea23db4 100644 --- a/config/session.php +++ b/config/session.php @@ -129,7 +129,7 @@ return [ 'cookie' => env( 'SESSION_COOKIE', - Str::slug((string) env('APP_NAME', 'laravel')).'-session', + Str::slug((string) env('APP_NAME', 'nyone')).'-session', ), /* diff --git a/config/streaming.php b/config/streaming.php index 66950f7..70a291b 100644 --- a/config/streaming.php +++ b/config/streaming.php @@ -1,8 +1,8 @@ env('STREAMING_RTMP_INGEST_URL', 'rtmp://localhost:1935'), - 'hls_public_url' => env('STREAMING_HLS_PUBLIC_URL', 'http://localhost:8888'), + 'rtmp_ingest_url' => env('STREAMING_RTMP_INGEST_URL', 'rtmp://nyone.net:19935'), + 'hls_public_url' => env('STREAMING_HLS_PUBLIC_URL', 'https://nyone-hls.net'), 'mediamtx_shared_secret' => env('MEDIAMTX_SHARED_SECRET', 'local-dev-secret'), 'vod_retention_days' => (int) env('STREAMING_VOD_RETENTION_DAYS', 30), 'recordings_path' => env('STREAMING_RECORDINGS_PATH', storage_path('app/private/recordings')), diff --git a/deploy/nginx/README.md b/deploy/nginx/README.md index 3de8a53..c0a91d7 100644 --- a/deploy/nginx/README.md +++ b/deploy/nginx/README.md @@ -4,12 +4,12 @@ These configs match the current project domains: - App: `https://nyone.net` - HLS playback: `https://nyone-hls.net` -- OBS ingest: `rtmp://nyone.net:1935` +- OBS ingest: `rtmp://nyone.net:19935` MediaMTX stays private on local ports: -- RTMP ingest: `127.0.0.1:1935` -- HLS: `127.0.0.1:1988` +- RTMP ingest: `0.0.0.0:19935` directly through MediaMTX +- HLS: `127.0.0.1:19988` Install layout on Ubuntu/Debian: @@ -27,7 +27,7 @@ sudo nginx -t sudo systemctl reload nginx ``` -Make sure DNS points all three hostnames to the server: +Make sure DNS points both hostnames to the server: - `nyone.net` - `nyone-hls.net` @@ -36,7 +36,9 @@ The Laravel `.env` should use: ```env APP_URL=https://nyone.net -STREAMING_RTMP_INGEST_URL=rtmp://nyone.net:1935 +STREAMING_RTMP_INGEST_URL=rtmp://nyone.net:19935 STREAMING_HLS_PUBLIC_URL=https://nyone-hls.net MEDIAMTX_SHARED_SECRET=change-this-secret ``` + +Open TCP `19935` on the server firewall for OBS ingest. Nginx is only proxying the Laravel app and HLS HTTPS playback in this setup. diff --git a/docs/ui-ux-handoff.md b/docs/ui-ux-handoff.md index 86bad9b..776d7bb 100644 --- a/docs/ui-ux-handoff.md +++ b/docs/ui-ux-handoff.md @@ -168,11 +168,6 @@ MediaMTX current files: - `deploy/mediamtx.yml`: currently points auth/hooks to `https://nyone.net`. - `deploy/mediamtx.home.yml`: points auth/hooks to `https://nyone.net`. -Known mismatch: - -- `.env` currently points the app at `nyone.net`, while `deploy/mediamtx.yml` points MediaMTX hooks at `nyone.net`. -- Before live testing, decide which domain is active and align `.env`, `deploy/mediamtx.yml`, and Nginx config. - MediaMTX ports currently configured: - RTMP: `:19935` @@ -212,4 +207,3 @@ Last known successful verification from previous implementation: ```text Read AGENTS.md and docs/ui-ux-handoff.md. Focus only on improving Nyone's UI/UX. Start with the channel watch page and creator studio, keep backend changes minimal, and verify with tests/typecheck/build. ``` - diff --git a/package.json b/package.json index 5143e08..8a5ec69 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,7 @@ { "$schema": "https://www.schemastore.org/package.json", + "name": "nyone", + "description": "Nyone live streaming platform frontend.", "private": true, "type": "module", "scripts": { diff --git a/resources/js/app.tsx b/resources/js/app.tsx index 402353f..504acab 100644 --- a/resources/js/app.tsx +++ b/resources/js/app.tsx @@ -6,7 +6,7 @@ import AppLayout from '@/layouts/app-layout'; import AuthLayout from '@/layouts/auth-layout'; import SettingsLayout from '@/layouts/settings/layout'; -const appName = import.meta.env.VITE_APP_NAME || 'Laravel'; +const appName = import.meta.env.VITE_APP_NAME || 'Nyone'; createInertiaApp({ title: (title) => (title ? `${title} - ${appName}` : appName), diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx index 86c1070..057576f 100644 --- a/resources/js/pages/dashboard.tsx +++ b/resources/js/pages/dashboard.tsx @@ -296,7 +296,7 @@ export default function Dashboard({ event.target.value, ) } - placeholder="Building live with Laravel" + placeholder="Building Nyone live" />