From 2c341e8ceaa698f17e880d67375cfabb7388ba4d Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Fri, 26 Dec 2025 10:18:40 -0800 Subject: [PATCH] Add index page with URL generator and remove passphrase from display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created a beautiful landing page with random room/passphrase generation and updated security model for read-only access. New Files: - server/php/index.html: Landing page with URL generator Features: - Random room name generation (e.g., "swift-phoenix-1234") - Random passphrase generation (16 chars, URL-safe) - Copy-to-clipboard functionality - Responsive design with gradient header - Step-by-step usage instructions - FAQ section Security Model Changes: - WRITE (send transcriptions): Requires room + passphrase - READ (view display): Only requires room name Updated Files: - server.php: * handleStream(): Passphrase optional (read-only) * handleList(): Passphrase optional (read-only) * Added roomExists() helper function - display.php: * Removed passphrase from URL parameters * Removed passphrase from SSE connection * Removed passphrase from list endpoint Benefits: - Display URL is safer (no passphrase in OBS browser source) - Simpler setup (only room name needed for viewing) - Better security model (write-protected, read-open) - Anyone with room name can watch, only authorized can send Example URLs: - Client: server.php (with room + passphrase in app settings) - Display: display.php?room=swift-phoenix-1234&fade=10×tamps=true 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- server/php/display.php | 5 +- server/php/index.html | 360 +++++++++++++++++++++++++++++++++++++++++ server/php/server.php | 37 +++-- 3 files changed, 387 insertions(+), 15 deletions(-) create mode 100644 server/php/index.html diff --git a/server/php/display.php b/server/php/display.php index 92ba676..ce63225 100644 --- a/server/php/display.php +++ b/server/php/display.php @@ -71,7 +71,6 @@ // Get URL parameters const urlParams = new URLSearchParams(window.location.search); const room = urlParams.get('room') || 'default'; - const passphrase = urlParams.get('passphrase') || ''; const fadeAfter = parseInt(urlParams.get('fade') || '10'); const showTimestamps = urlParams.get('timestamps') !== 'false'; @@ -96,7 +95,7 @@ // Connect to Server-Sent Events function connect() { - const url = `server.php?action=stream&room=${encodeURIComponent(room)}&passphrase=${encodeURIComponent(passphrase)}`; + const url = `server.php?action=stream&room=${encodeURIComponent(room)}`; const eventSource = new EventSource(url); eventSource.onopen = () => { @@ -162,7 +161,7 @@ // Load recent transcriptions on startup async function loadRecent() { try { - const url = `server.php?action=list&room=${encodeURIComponent(room)}&passphrase=${encodeURIComponent(passphrase)}`; + const url = `server.php?action=list&room=${encodeURIComponent(room)}`; const response = await fetch(url); const data = await response.json(); diff --git a/server/php/index.html b/server/php/index.html new file mode 100644 index 0000000..64fcb7c --- /dev/null +++ b/server/php/index.html @@ -0,0 +1,360 @@ + + + + + + Multi-User Transcription Server + + + +
+
+

🎙️ Multi-User Transcription Server

+

Merge captions from multiple streamers into a single OBS display

+
+ +
+ +
+

What is this?

+

This server allows multiple streamers using the Local Transcription app to merge their real-time captions into a single stream. Perfect for collaborative streams, podcasts, or gaming sessions with multiple commentators.

+ +
+
+

🔒 Secure

+

Room-based isolation with passphrase authentication

+
+
+

🎨 Colorful

+

Each user gets a unique color (supports 20+ users)

+
+
+

⚡ Real-time

+

Low-latency streaming via Server-Sent Events

+
+
+

🌐 Universal

+

Works on any standard PHP hosting

+
+
+
+ + +
+

Get Started

+

Click the button below to generate a unique room with random credentials:

+ +
+ + +
+
+

📱 For Desktop App Users

+
Room Name:
+
-
+ + +
Passphrase:
+
-
+ + +
Server URL:
+
-
+ +
+ +
+

📺 For OBS Browser Source

+
Display URL:
+
-
+ + +
+ Note: The display URL does not contain the passphrase for security. Only users with the passphrase can send transcriptions. +
+
+
+
+
+ + +
+

How to Use

+
+
+

Generate Room Credentials

+

Click "Generate New Room" above to create a unique room with a random name and passphrase. Share these with your streaming team.

+
+
+

Configure Desktop App

+

In the Local Transcription app, go to Settings → Server Sync and enter:

+
    +
  • Enable Server Sync: ✓
  • +
  • Server URL: (from above)
  • +
  • Room Name: (from above)
  • +
  • Passphrase: (from above)
  • +
+
+
+

Add to OBS

+

In OBS, add a Browser source and paste the Display URL. Set width to 1920 and height to your preference (e.g., 200-400px).

+
+
+

Start Streaming!

+

All team members start transcription in their apps. Captions from everyone appear merged in OBS with different colors per person.

+
+
+
+ + +
+

Frequently Asked Questions

+ +

How many users can join one room?

+

Technically unlimited, but we've tested up to 20 users successfully. Each user gets a unique color.

+ +

Is my passphrase secure?

+

Yes! Passphrases are hashed using PHP's password_hash() function. They're never stored in plain text.

+ +

How long does a room stay active?

+

Rooms are automatically cleaned up after 2 hours of inactivity to save server resources.

+ +

Can I use custom room names?

+

Yes! You can use any room name you want instead of the randomly generated one. Just make sure all team members use the exact same name.

+
+
+
+ + + + diff --git a/server/php/server.php b/server/php/server.php index d048dfa..941361d 100644 --- a/server/php/server.php +++ b/server/php/server.php @@ -83,18 +83,26 @@ function handleSend() { /** * Handle streaming transcriptions via Server-Sent Events + * Note: Passphrase is optional for streaming (read-only access) */ function handleStream() { // Get parameters $room = sanitize($_GET['room'] ?? ''); - $passphrase = $_GET['passphrase'] ?? ''; - if (empty($room) || empty($passphrase)) { - sendError('Missing room or passphrase', 400); + if (empty($room)) { + sendError('Missing room name', 400); } - if (!verifyPassphrase($room, $passphrase)) { - sendError('Invalid passphrase', 401); + // Passphrase is optional for streaming (read-only) + // If room doesn't exist yet, return empty stream + if (!roomExists($room)) { + // Return empty stream - room doesn't exist yet + header('Content-Type: text/event-stream'); + header('Cache-Control: no-cache'); + header('X-Accel-Buffering: no'); + echo ": waiting for room to be created\n\n"; + flush(); + exit(); } // Set SSE headers @@ -136,19 +144,17 @@ function handleStream() { /** * Handle listing recent transcriptions + * Note: Passphrase is optional for listing (read-only access) */ function handleList() { $room = sanitize($_GET['room'] ?? ''); - $passphrase = $_GET['passphrase'] ?? ''; - if (empty($room) || empty($passphrase)) { - sendError('Missing room or passphrase', 400); - } - - if (!verifyPassphrase($room, $passphrase)) { - sendError('Invalid passphrase', 401); + if (empty($room)) { + sendError('Missing room name', 400); } + // Passphrase is optional for read-only access + // If room doesn't exist, return empty array $transcriptions = getTranscriptions($room); sendJson(['transcriptions' => $transcriptions]); } @@ -235,6 +241,13 @@ function getRoomFile($room) { return STORAGE_DIR . '/room_' . md5($room) . '.json'; } +/** + * Check if room exists + */ +function roomExists($room) { + return file_exists(getRoomFile($room)); +} + /** * Cleanup old sessions */