Updated project identity/configs to match Nyone
This commit is contained in:
@@ -2,7 +2,7 @@ APP_NAME=Nyone
|
|||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost
|
APP_URL=https://nyone.net
|
||||||
|
|
||||||
APP_LOCALE=en
|
APP_LOCALE=en
|
||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=en
|
||||||
@@ -64,8 +64,8 @@ AWS_USE_PATH_STYLE_ENDPOINT=false
|
|||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
|
|
||||||
STREAMING_RTMP_INGEST_URL=rtmp://localhost:1935
|
STREAMING_RTMP_INGEST_URL=rtmp://nyone.net:19935
|
||||||
STREAMING_HLS_PUBLIC_URL=http://localhost:8888
|
STREAMING_HLS_PUBLIC_URL=https://nyone-hls.net
|
||||||
STREAMING_VOD_RETENTION_DAYS=30
|
STREAMING_VOD_RETENTION_DAYS=30
|
||||||
STREAMING_RECORDINGS_PATH=storage/app/private/recordings
|
STREAMING_RECORDINGS_PATH=storage/app/private/recordings
|
||||||
MEDIAMTX_SHARED_SECRET=change-this-secret
|
MEDIAMTX_SHARED_SECRET=change-this-secret
|
||||||
|
|||||||
51
README.md
51
README.md
@@ -1,6 +1,13 @@
|
|||||||
# Nyone
|
# 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
|
## Local Setup
|
||||||
|
|
||||||
@@ -14,11 +21,45 @@ npm run dev
|
|||||||
php artisan serve
|
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:
|
Creators use:
|
||||||
|
|
||||||
- Server: `STREAMING_RTMP_INGEST_URL`
|
- OBS server: `STREAMING_RTMP_INGEST_URL`
|
||||||
- Stream key: `<channel-slug>?token=<generated-stream-key>`
|
- OBS stream key: `<channel-slug>?token=<generated-stream-key>`
|
||||||
|
|
||||||
Viewer playback uses `STREAMING_HLS_PUBLIC_URL/<channel-slug>/index.m3u8`.
|
Viewer playback uses:
|
||||||
|
|
||||||
|
```text
|
||||||
|
STREAMING_HLS_PUBLIC_URL/<channel-slug>/index.m3u8
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php artisan test
|
||||||
|
npm run format:check
|
||||||
|
npm run lint:check
|
||||||
|
npm run types:check
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://getcomposer.org/schema.json",
|
"$schema": "https://getcomposer.org/schema.json",
|
||||||
"name": "laravel/react-starter-kit",
|
"name": "nyone/streaming-platform",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"description": "The skeleton application for the Laravel framework.",
|
"description": "Nyone live streaming platform built with Laravel, Inertia React, and MediaMTX.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
"nyone",
|
||||||
|
"streaming",
|
||||||
|
"live-streaming",
|
||||||
|
"mediamtx",
|
||||||
|
"inertia",
|
||||||
"laravel",
|
"laravel",
|
||||||
"framework"
|
"react"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
|
|||||||
2
composer.lock
generated
2
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "88c7fcd5b77421cdc2de93c2514309dc",
|
"content-hash": "1d4e9648391a072a961dcab64a9324f9",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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-'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ return [
|
|||||||
|
|
||||||
'options' => [
|
'options' => [
|
||||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
'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),
|
'persistent' => env('REDIS_PERSISTENT', false),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ return [
|
|||||||
'public' => [
|
'public' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('app/public'),
|
'root' => storage_path('app/public'),
|
||||||
'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage',
|
'url' => rtrim(env('APP_URL', 'https://nyone.net'), '/').'/storage',
|
||||||
'visibility' => 'public',
|
'visibility' => 'public',
|
||||||
'throw' => false,
|
'throw' => false,
|
||||||
'report' => false,
|
'report' => false,
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ return [
|
|||||||
'slack' => [
|
'slack' => [
|
||||||
'driver' => 'slack',
|
'driver' => 'slack',
|
||||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
'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:'),
|
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||||
'level' => env('LOG_LEVEL', 'critical'),
|
'level' => env('LOG_LEVEL', 'critical'),
|
||||||
'replace_placeholders' => true,
|
'replace_placeholders' => true,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ return [
|
|||||||
'username' => env('MAIL_USERNAME'),
|
'username' => env('MAIL_USERNAME'),
|
||||||
'password' => env('MAIL_PASSWORD'),
|
'password' => env('MAIL_PASSWORD'),
|
||||||
'timeout' => null,
|
'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' => [
|
'ses' => [
|
||||||
@@ -112,7 +112,7 @@ return [
|
|||||||
|
|
||||||
'from' => [
|
'from' => [
|
||||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
'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')),
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ return [
|
|||||||
|
|
||||||
'cookie' => env(
|
'cookie' => env(
|
||||||
'SESSION_COOKIE',
|
'SESSION_COOKIE',
|
||||||
Str::slug((string) env('APP_NAME', 'laravel')).'-session',
|
Str::slug((string) env('APP_NAME', 'nyone')).'-session',
|
||||||
),
|
),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'rtmp_ingest_url' => env('STREAMING_RTMP_INGEST_URL', 'rtmp://localhost:1935'),
|
'rtmp_ingest_url' => env('STREAMING_RTMP_INGEST_URL', 'rtmp://nyone.net:19935'),
|
||||||
'hls_public_url' => env('STREAMING_HLS_PUBLIC_URL', 'http://localhost:8888'),
|
'hls_public_url' => env('STREAMING_HLS_PUBLIC_URL', 'https://nyone-hls.net'),
|
||||||
'mediamtx_shared_secret' => env('MEDIAMTX_SHARED_SECRET', 'local-dev-secret'),
|
'mediamtx_shared_secret' => env('MEDIAMTX_SHARED_SECRET', 'local-dev-secret'),
|
||||||
'vod_retention_days' => (int) env('STREAMING_VOD_RETENTION_DAYS', 30),
|
'vod_retention_days' => (int) env('STREAMING_VOD_RETENTION_DAYS', 30),
|
||||||
'recordings_path' => env('STREAMING_RECORDINGS_PATH', storage_path('app/private/recordings')),
|
'recordings_path' => env('STREAMING_RECORDINGS_PATH', storage_path('app/private/recordings')),
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ These configs match the current project domains:
|
|||||||
|
|
||||||
- App: `https://nyone.net`
|
- App: `https://nyone.net`
|
||||||
- HLS playback: `https://nyone-hls.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:
|
MediaMTX stays private on local ports:
|
||||||
|
|
||||||
- RTMP ingest: `127.0.0.1:1935`
|
- RTMP ingest: `0.0.0.0:19935` directly through MediaMTX
|
||||||
- HLS: `127.0.0.1:1988`
|
- HLS: `127.0.0.1:19988`
|
||||||
|
|
||||||
Install layout on Ubuntu/Debian:
|
Install layout on Ubuntu/Debian:
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ sudo nginx -t
|
|||||||
sudo systemctl reload nginx
|
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.net`
|
||||||
- `nyone-hls.net`
|
- `nyone-hls.net`
|
||||||
@@ -36,7 +36,9 @@ The Laravel `.env` should use:
|
|||||||
|
|
||||||
```env
|
```env
|
||||||
APP_URL=https://nyone.net
|
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
|
STREAMING_HLS_PUBLIC_URL=https://nyone-hls.net
|
||||||
MEDIAMTX_SHARED_SECRET=change-this-secret
|
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.
|
||||||
|
|||||||
@@ -168,11 +168,6 @@ MediaMTX current files:
|
|||||||
- `deploy/mediamtx.yml`: currently points auth/hooks to `https://nyone.net`.
|
- `deploy/mediamtx.yml`: currently points auth/hooks to `https://nyone.net`.
|
||||||
- `deploy/mediamtx.home.yml`: 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:
|
MediaMTX ports currently configured:
|
||||||
|
|
||||||
- RTMP: `:19935`
|
- RTMP: `:19935`
|
||||||
@@ -212,4 +207,3 @@ Last known successful verification from previous implementation:
|
|||||||
```text
|
```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.
|
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.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://www.schemastore.org/package.json",
|
"$schema": "https://www.schemastore.org/package.json",
|
||||||
|
"name": "nyone",
|
||||||
|
"description": "Nyone live streaming platform frontend.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import AppLayout from '@/layouts/app-layout';
|
|||||||
import AuthLayout from '@/layouts/auth-layout';
|
import AuthLayout from '@/layouts/auth-layout';
|
||||||
import SettingsLayout from '@/layouts/settings/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({
|
createInertiaApp({
|
||||||
title: (title) => (title ? `${title} - ${appName}` : appName),
|
title: (title) => (title ? `${title} - ${appName}` : appName),
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ export default function Dashboard({
|
|||||||
event.target.value,
|
event.target.value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
placeholder="Building live with Laravel"
|
placeholder="Building Nyone live"
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<label className="mt-6 flex h-9 items-center gap-2 rounded-md border px-3 text-sm">
|
<label className="mt-6 flex h-9 items-center gap-2 rounded-md border px-3 text-sm">
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
@viteReactRefresh
|
@viteReactRefresh
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.tsx', "resources/js/pages/{$page['component']}.tsx"])
|
@vite(['resources/css/app.css', 'resources/js/app.tsx', "resources/js/pages/{$page['component']}.tsx"])
|
||||||
<x-inertia::head>
|
<x-inertia::head>
|
||||||
<title>{{ config('app.name', 'Laravel') }}</title>
|
<title>{{ config('app.name', 'Nyone') }}</title>
|
||||||
</x-inertia::head>
|
</x-inertia::head>
|
||||||
</head>
|
</head>
|
||||||
<body class="font-sans antialiased">
|
<body class="font-sans antialiased">
|
||||||
|
|||||||
Reference in New Issue
Block a user