From ec6ff1b570a522e7c095c801bf3c5332d846393d Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 14:42:18 -0400 Subject: [PATCH 01/10] Add team and stream counts to footer with improved layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added team and stream counts displayed when OBS disconnected - Added counts display alongside live status when OBS connected - Moved OFFLINE/IDLE status indicators to left column for better balance - Fixed green dot positioning to be properly next to "Connected" text - Added custom CSS for status dots since Tailwind classes weren't applying - Enhanced footer layout with better visual hierarchy 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/api/counts/route.ts | 25 ++++++++++ app/globals.css | 24 ++++++++++ components/Footer.tsx | 104 ++++++++++++++++++++++++++++++---------- 3 files changed, 127 insertions(+), 26 deletions(-) create mode 100644 app/api/counts/route.ts diff --git a/app/api/counts/route.ts b/app/api/counts/route.ts new file mode 100644 index 0000000..86a638d --- /dev/null +++ b/app/api/counts/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from 'next/server'; +import { getDatabase } from '../../../lib/database'; +import { TABLE_NAMES } from '../../../lib/constants'; +import { createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers'; + +async function getCountsHandler() { + try { + const db = await getDatabase(); + + // Get counts in parallel + const [streamsResult, teamsResult] = await Promise.all([ + db.get(`SELECT COUNT(*) as count FROM ${TABLE_NAMES.STREAMS}`), + db.get(`SELECT COUNT(*) as count FROM ${TABLE_NAMES.TEAMS}`) + ]); + + return createSuccessResponse({ + streams: streamsResult.count, + teams: teamsResult.count + }); + } catch (error) { + return createDatabaseError('fetch counts', error); + } +} + +export const GET = withErrorHandling(getCountsHandler); \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index 288b6ab..e1107df 100644 --- a/app/globals.css +++ b/app/globals.css @@ -40,6 +40,30 @@ body { line-height: 1.6; } +/* Status Indicator Dots */ +.status-dot { + width: 12px; + height: 12px; + border-radius: 50%; + display: inline-block; +} + +.status-dot.connected { + background-color: #859900; /* Solarized green */ +} + +.status-dot.disconnected { + background-color: #dc322f; /* Solarized red */ +} + +.status-dot.streaming { + background-color: #dc322f; /* Solarized red */ +} + +.status-dot.idle { + background-color: #586e75; /* Solarized base01 */ +} + /* Glass Card Component */ .glass { background: rgba(7, 54, 66, 0.4); diff --git a/components/Footer.tsx b/components/Footer.tsx index c43a131..3d4328c 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -19,8 +19,14 @@ type OBSStatus = { error?: string; }; +type Counts = { + streams: number; + teams: number; +}; + export default function Footer() { const [obsStatus, setObsStatus] = useState(null); + const [counts, setCounts] = useState(null); const [isLoading, setIsLoading] = useState(true); // Smart polling with performance monitoring and visibility detection @@ -40,9 +46,24 @@ export default function Footer() { } }; + const fetchCounts = async () => { + try { + const response = await fetch('/api/counts'); + const data = await response.json(); + // Handle both old and new API response formats + const countsData = data.success ? data.data : data; + setCounts(countsData); + } catch (error) { + console.error('Failed to fetch counts:', error); + } + }; + // Use smart polling that respects page visibility and adapts interval based on connection status const pollingInterval = obsStatus?.connected ? 15000 : 30000; // Poll faster when connected useSmartPolling(fetchOBSStatus, pollingInterval, [obsStatus?.connected]); + + // Poll counts less frequently (every 60 seconds) since they don't change as often + useSmartPolling(fetchCounts, 60000, []); if (isLoading) { return ( @@ -60,29 +81,58 @@ export default function Footer() {
{/* Connection Status */}
-
-
-
-

OBS Studio

-

- {obsStatus?.connected ? 'Connected' : 'Disconnected'} -

-
+

OBS Studio

+
+
+ + {obsStatus?.connected ? 'Connected' : 'Disconnected'} +
{obsStatus && (
{obsStatus.host}:{obsStatus.port}
{obsStatus.hasPassword &&
🔒 Authenticated
} + + {/* Streaming/Recording Status */} + {obsStatus.connected && ( +
+
+
+ {obsStatus.streaming ? 'LIVE' : 'OFFLINE'} +
+ +
+
+ {obsStatus.recording ? 'REC' : 'IDLE'} +
+
+ )}
)}
- {/* Live Status */} - {obsStatus?.connected && ( -
-

Live Status

- + {/* Live Status or Database Stats */} +
+

+ {obsStatus?.connected ? 'Live Status' : 'Database Stats'} +

+ + {/* Show counts when OBS is disconnected */} + {!obsStatus?.connected && counts && ( +
+
+ Teams: + {counts.teams} +
+
+ Streams: + {counts.streams} +
+
+ )} + + {obsStatus?.connected && (
{obsStatus.currentScene && (
@@ -98,20 +148,22 @@ export default function Footer() {
)} -
-
-
- {obsStatus.streaming ? 'LIVE' : 'OFFLINE'} -
- -
-
- {obsStatus.recording ? 'REC' : 'IDLE'} -
-
+ {/* Database counts */} + {counts && ( + <> +
+ Teams: + {counts.teams} +
+
+ Streams: + {counts.streams} +
+ + )}
-
- )} + )} +
{/* Error Message */} From 423897d1bfe3f860ee4193d02853a7b42a6bcbc4 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 14:49:55 -0400 Subject: [PATCH 02/10] Fix API response handling on main page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Handle new standardized API response format for streams endpoint - Extract data from { success: true, data: [...] } wrapper - Maintain backward compatibility with old API format - Fixes TypeError: streams.forEach is not a function 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/page.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 0e3ff66..e90a6a5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -80,8 +80,12 @@ export default function Home() { activeRes.json() ]); - setStreams(streamsData); - setActiveSources(activeData); + // Handle both old and new API response formats + const streams = streamsData.success ? streamsData.data : streamsData; + const activeSources = activeData.success ? activeData.data : activeData; + + setStreams(streams); + setActiveSources(activeSources); } catch (error) { console.error('Error fetching data:', error); showError('Failed to Load Data', 'Could not fetch streams. Please refresh the page.'); From 8c2de2c66b7d68ca5cd50142d7398686bb4c3813 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 14:53:57 -0400 Subject: [PATCH 03/10] Remove backwards compatibility for API responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clean up all backward compatibility checks for old API format - All endpoints now consistently return { success: true, data: [...] } - Simplify response handling across all components and pages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/edit/[id]/page.tsx | 3 +-- app/page.tsx | 8 ++------ app/streams/page.tsx | 5 ++--- app/teams/page.tsx | 4 +--- components/Footer.tsx | 4 +--- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/app/edit/[id]/page.tsx b/app/edit/[id]/page.tsx index 4422a66..706fc12 100644 --- a/app/edit/[id]/page.tsx +++ b/app/edit/[id]/page.tsx @@ -65,8 +65,7 @@ export default function EditStream() { team_id: streamData.team_id, }); - // Handle both old and new API response formats - const teams = teamsData.success ? teamsData.data : teamsData; + const teams = teamsData.data; // Map teams for dropdown setTeams( diff --git a/app/page.tsx b/app/page.tsx index e90a6a5..b99c179 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -80,12 +80,8 @@ export default function Home() { activeRes.json() ]); - // Handle both old and new API response formats - const streams = streamsData.success ? streamsData.data : streamsData; - const activeSources = activeData.success ? activeData.data : activeData; - - setStreams(streams); - setActiveSources(activeSources); + setStreams(streamsData.data); + setActiveSources(activeData); } catch (error) { console.error('Error fetching data:', error); showError('Failed to Load Data', 'Could not fetch streams. Please refresh the page.'); diff --git a/app/streams/page.tsx b/app/streams/page.tsx index 8520c19..2f23e7c 100644 --- a/app/streams/page.tsx +++ b/app/streams/page.tsx @@ -40,9 +40,8 @@ export default function AddStream() { const teamsData = await teamsResponse.json(); const streamsData = await streamsResponse.json(); - // Handle both old and new API response formats - const teams = teamsData.success ? teamsData.data : teamsData; - const streams = streamsData.success ? streamsData.data : streamsData; + const teams = teamsData.data; + const streams = streamsData.data; // Map the API data to the format required by the Dropdown setTeams( diff --git a/app/teams/page.tsx b/app/teams/page.tsx index 7b877e4..bb8684a 100644 --- a/app/teams/page.tsx +++ b/app/teams/page.tsx @@ -41,9 +41,7 @@ export default function Teams() { try { const res = await fetch('/api/teams'); const data = await res.json(); - // Handle both old and new API response formats - const teams = data.success ? data.data : data; - setTeams(teams); + setTeams(data.data); } catch (error) { console.error('Error fetching teams:', error); } finally { diff --git a/components/Footer.tsx b/components/Footer.tsx index 3d4328c..b3f4b2f 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -50,9 +50,7 @@ export default function Footer() { try { const response = await fetch('/api/counts'); const data = await response.json(); - // Handle both old and new API response formats - const countsData = data.success ? data.data : data; - setCounts(countsData); + setCounts(data.data); } catch (error) { console.error('Failed to fetch counts:', error); } From 5dd9707f1368fb67177e809f4046dec5c52709c1 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 15:04:08 -0400 Subject: [PATCH 04/10] Fix getActive API to use standardized response format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update /api/getActive to return { success: true, data: {...} } format - Add proper error handling with standardized error responses - Update main page to handle new response format for active sources - Remove unused variables and clean up code - Add trim() to file reads to handle whitespace properly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/api/getActive/route.ts | 57 +++++++++++++++----------------------- app/page.tsx | 2 +- 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/app/api/getActive/route.ts b/app/api/getActive/route.ts index a54b4b4..a47825b 100644 --- a/app/api/getActive/route.ts +++ b/app/api/getActive/route.ts @@ -1,7 +1,7 @@ import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; -// import config from '../../../config'; +import { createSuccessResponse, createErrorResponse, withErrorHandling } from '../../../lib/apiHelpers'; const FILE_DIRECTORY = path.resolve(process.env.FILE_DIRECTORY || './files') // Ensure directory exists @@ -10,7 +10,7 @@ if (!fs.existsSync(FILE_DIRECTORY)) { } console.log('using', FILE_DIRECTORY) -export async function GET() { +async function getActiveHandler() { try { const largePath = path.join(FILE_DIRECTORY, 'large.txt'); const leftPath = path.join(FILE_DIRECTORY, 'left.txt'); @@ -20,38 +20,27 @@ export async function GET() { const bottomLeftPath = path.join(FILE_DIRECTORY, 'bottomLeft.txt'); const bottomRightPath = path.join(FILE_DIRECTORY, 'bottomRight.txt'); - const tankPath = path.join(FILE_DIRECTORY, 'tank.txt'); - const treePath = path.join(FILE_DIRECTORY, 'tree.txt'); - const kittyPath = path.join(FILE_DIRECTORY, 'kitty.txt'); - const chickenPath = path.join(FILE_DIRECTORY, 'chicken.txt'); + const large = fs.existsSync(largePath) ? fs.readFileSync(largePath, 'utf-8').trim() : null; + const left = fs.existsSync(leftPath) ? fs.readFileSync(leftPath, 'utf-8').trim() : null; + const right = fs.existsSync(rightPath) ? fs.readFileSync(rightPath, 'utf-8').trim() : null; + const topLeft = fs.existsSync(topLeftPath) ? fs.readFileSync(topLeftPath, 'utf-8').trim() : null; + const topRight = fs.existsSync(topRightPath) ? fs.readFileSync(topRightPath, 'utf-8').trim() : null; + const bottomLeft = fs.existsSync(bottomLeftPath) ? fs.readFileSync(bottomLeftPath, 'utf-8').trim() : null; + const bottomRight = fs.existsSync(bottomRightPath) ? fs.readFileSync(bottomRightPath, 'utf-8').trim() : null; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const large = fs.existsSync(largePath) ? fs.readFileSync(largePath, 'utf-8') : null; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const left = fs.existsSync(leftPath) ? fs.readFileSync(leftPath, 'utf-8') : null; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const right = fs.existsSync(rightPath) ? fs.readFileSync(rightPath, 'utf-8') : null; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const topLeft = fs.existsSync(topLeftPath) ? fs.readFileSync(topLeftPath, 'utf-8') : null; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const topRight = fs.existsSync(topRightPath) ? fs.readFileSync(topRightPath, 'utf-8') : null; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const bottomLeft = fs.existsSync(bottomLeftPath) ? fs.readFileSync(bottomLeftPath, 'utf-8') : null; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const bottomRight = fs.existsSync(bottomRightPath) ? fs.readFileSync(bottomRightPath, 'utf-8') : null; - - const tank = fs.existsSync(tankPath) ? fs.readFileSync(tankPath, 'utf-8') : null; - const tree = fs.existsSync(treePath) ? fs.readFileSync(treePath, 'utf-8') : null; - const kitty = fs.existsSync(kittyPath) ? fs.readFileSync(kittyPath, 'utf-8') : null; - const chicken = fs.existsSync(chickenPath) ? fs.readFileSync(chickenPath, 'utf-8') : null; - - // For SaT - return NextResponse.json({ large, left, right, topLeft, topRight, bottomLeft, bottomRight }, {status: 201}) - // return NextResponse.json({ tank, tree, kitty, chicken }, {status: 201}) - } catch (error) { + return createSuccessResponse({ + large, + left, + right, + topLeft, + topRight, + bottomLeft, + bottomRight + }); + } catch (error) { console.error('Error reading active sources:', error); - return NextResponse.json({ error: 'Failed to read active sources' }, {status: 500}); - } + return createErrorResponse('Failed to read active sources', 500, 'Could not read source files', error); + } +} -} \ No newline at end of file +export const GET = withErrorHandling(getActiveHandler); \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index b99c179..d8d9eae 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -81,7 +81,7 @@ export default function Home() { ]); setStreams(streamsData.data); - setActiveSources(activeData); + setActiveSources(activeData.data); } catch (error) { console.error('Error fetching data:', error); showError('Failed to Load Data', 'Could not fetch streams. Please refresh the page.'); From f9363eac205a243e08744b7b9f27783d305c10c3 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 15:06:21 -0400 Subject: [PATCH 05/10] Fix active source detection by including team names in lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update streams API to join with teams table and return StreamWithTeam data - Modify stream lookup maps to generate proper stream group names with team prefixes - Format: {team_name}_{stream_name}_stream to match obsClient.js logic - Update type signatures throughout to support team_name and group_name fields - Now properly matches text file contents with database streams for dropdown selection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/api/streams/route.ts | 11 +++++++++-- app/page.tsx | 9 ++------- lib/performance.ts | 14 +++++++++----- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/api/streams/route.ts b/app/api/streams/route.ts index ab368e3..b52a74e 100644 --- a/app/api/streams/route.ts +++ b/app/api/streams/route.ts @@ -1,13 +1,20 @@ import { NextResponse } from 'next/server'; import { getDatabase } from '../../../lib/database'; -import { Stream } from '@/types'; +import { StreamWithTeam } from '@/types'; import { TABLE_NAMES } from '../../../lib/constants'; import { createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers'; async function getStreamsHandler() { try { const db = await getDatabase(); - const streams: Stream[] = await db.all(`SELECT * FROM ${TABLE_NAMES.STREAMS}`); + const streams: StreamWithTeam[] = await db.all(` + SELECT + s.*, + t.team_name, + t.group_name + FROM ${TABLE_NAMES.STREAMS} s + LEFT JOIN ${TABLE_NAMES.TEAMS} t ON s.team_id = t.team_id + `); return createSuccessResponse(streams); } catch (error) { return createDatabaseError('fetch streams', error); diff --git a/app/page.tsx b/app/page.tsx index d8d9eae..9b6422d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,17 +7,12 @@ import { useToast } from '@/lib/useToast'; import { ToastContainer } from '@/components/Toast'; import { useActiveSourceLookup, useDebounce, PerformanceMonitor } from '@/lib/performance'; -type Stream = { - id: number; - name: string; - obs_source_name: string; - url: string; -}; +import { StreamWithTeam } from '@/types'; type ScreenType = 'large' | 'left' | 'right' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; export default function Home() { - const [streams, setStreams] = useState([]); + const [streams, setStreams] = useState([]); const [activeSources, setActiveSources] = useState>({ large: null, left: null, diff --git a/lib/performance.ts b/lib/performance.ts index 4355393..8e71fca 100644 --- a/lib/performance.ts +++ b/lib/performance.ts @@ -38,13 +38,17 @@ export function useThrottle unknown>( } // Memoized stream lookup utilities -export function createStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string }>) { +export function createStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string; team_name?: string; group_name?: string | null }>) { const sourceToIdMap = new Map(); - const idToStreamMap = new Map(); + const idToStreamMap = new Map(); streams.forEach(stream => { // Generate stream group name to match what's written to files - const streamGroupName = `${stream.name.toLowerCase().replace(/\s+/g, '_')}_stream`; + // Format: {team_name}_{stream_name}_stream (matching obsClient.js logic) + const cleanTeamName = stream.team_name ? stream.team_name.toLowerCase().replace(/\s+/g, '_') : 'unknown'; + const cleanStreamName = stream.name.toLowerCase().replace(/\s+/g, '_'); + const streamGroupName = `${cleanTeamName}_${cleanStreamName}_stream`; + sourceToIdMap.set(streamGroupName, stream.id); idToStreamMap.set(stream.id, stream); }); @@ -53,7 +57,7 @@ export function createStreamLookupMaps(streams: Array<{ id: number; obs_source_n } // Hook version for React components -export function useStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string }>) { +export function useStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string; team_name?: string; group_name?: string | null }>) { return useMemo(() => { return createStreamLookupMaps(streams); }, [streams]); @@ -61,7 +65,7 @@ export function useStreamLookupMaps(streams: Array<{ id: number; obs_source_name // Efficient active source lookup export function useActiveSourceLookup( - streams: Array<{ id: number; obs_source_name: string; name: string }>, + streams: Array<{ id: number; obs_source_name: string; name: string; team_name?: string; group_name?: string | null }>, activeSources: Record ) { const { sourceToIdMap } = useStreamLookupMaps(streams); From b3679d6642ff7c8f732cdd23fd8eaf858f9fd791 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 15:45:02 -0400 Subject: [PATCH 06/10] Increase OBS text font size from 72 to 84 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bumped font size for better visibility of team name text overlays in OBS - Applied to both text source creation and update scenarios 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lib/obsClient.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/obsClient.js b/lib/obsClient.js index 9adfe80..049c9a7 100644 --- a/lib/obsClient.js +++ b/lib/obsClient.js @@ -323,7 +323,7 @@ async function createTextSource(sceneName, textSourceName, text) { text, font: { face: 'Arial', - size: 72, + size: 84, style: 'Bold' }, color: 0xFFFFFFFF, // White text @@ -352,7 +352,7 @@ async function createTextSource(sceneName, textSourceName, text) { text, font: { face: 'Arial', - size: 72, + size: 84, style: 'Bold' }, color: 0xFFFFFFFF, // White text From c8a7b83fc4ca996acd5992439851ea316201a420 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 15:48:10 -0400 Subject: [PATCH 07/10] Fix dropdown optimistic updates to use correct stream group names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Include team name prefix in optimistic update stream group generation - Format: {team_name}_{stream_name}_stream to match backend file writes - Prevents dropdown from reverting to wrong value after selection - Now dropdown shows correct selection immediately without requiring page refresh 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 9b6422d..2f768aa 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -93,9 +93,9 @@ export default function Home() { const handleSetActive = useCallback(async (screen: ScreenType, id: number | null) => { const selectedStream = streams.find((stream) => stream.id === id); - // Generate stream group name for optimistic updates + // Generate stream group name for optimistic updates - must match obsClient.js format const streamGroupName = selectedStream - ? `${selectedStream.name.toLowerCase().replace(/\s+/g, '_')}_stream` + ? `${selectedStream.team_name?.toLowerCase().replace(/\s+/g, '_') || 'unknown'}_${selectedStream.name.toLowerCase().replace(/\s+/g, '_')}_stream` : null; // Update local state immediately for optimistic updates From 0dbdc52fd3abf13921573cd949579e08edd70936 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 15:52:05 -0400 Subject: [PATCH 08/10] Increase OBS text font size from 84 to 96 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Further increased font size for better visibility of team name text overlays 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lib/obsClient.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/obsClient.js b/lib/obsClient.js index 049c9a7..4402708 100644 --- a/lib/obsClient.js +++ b/lib/obsClient.js @@ -323,7 +323,7 @@ async function createTextSource(sceneName, textSourceName, text) { text, font: { face: 'Arial', - size: 84, + size: 96, style: 'Bold' }, color: 0xFFFFFFFF, // White text @@ -352,7 +352,7 @@ async function createTextSource(sceneName, textSourceName, text) { text, font: { face: 'Arial', - size: 84, + size: 96, style: 'Bold' }, color: 0xFFFFFFFF, // White text From 8687d197029aa6fb5ed8f10dbf97525e547cb9b6 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 15:54:02 -0400 Subject: [PATCH 09/10] Add Resources scene to orphaned themes bypass check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added "Resources" to SYSTEM_SCENES array in verifyGroups API - Resources scene will no longer be flagged as orphaned in OBS verification - Prevents system scenes from showing up as cleanup candidates 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/api/verifyGroups/route.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/api/verifyGroups/route.ts b/app/api/verifyGroups/route.ts index 40b6df0..1ed5515 100644 --- a/app/api/verifyGroups/route.ts +++ b/app/api/verifyGroups/route.ts @@ -12,7 +12,8 @@ const SYSTEM_SCENES: string[] = [ 'Starting', 'Ending', 'Audio', - 'Movies' + 'Movies', + 'Resources' ]; interface OBSScene { From c1f1c17763670618bc7d5930e44947723bdb99d5 Mon Sep 17 00:00:00 2001 From: Decobus Date: Tue, 22 Jul 2025 15:55:55 -0400 Subject: [PATCH 10/10] Update documentation to reflect recent enhancements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated CLAUDE.md with comprehensive recent changes: - Enhanced footer with team/stream counts and status indicators - Fixed active source detection and dropdown optimization - OBS text size improvements (96pt font) - API standardization and performance optimizations - System scene protection (Resources added to bypass list) - Improved error handling and user experience - Updated README.md with key feature additions: - Enhanced footer functionality - Performance optimizations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 17 ++++++++++++++--- README.md | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index faca2d8..ad09d70 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -205,6 +205,7 @@ See [OBS Setup Guide](./docs/OBS_SETUP.md) for detailed configuration instructio - **Audio Control**: Browser sources created with "Control Audio via OBS" enabled and auto-muted - **Visual Feedback**: Clear "View Stream" links with proper contrast for accessibility - **Team Association**: Streams organized under teams with proper naming conventions +- **Active Source Detection**: Properly reads current active sources from text files on page load and navigation ### Team & Group Management - **UUID-based Tracking**: Robust OBS group synchronization using scene UUIDs @@ -213,12 +214,13 @@ See [OBS Setup Guide](./docs/OBS_SETUP.md) for detailed configuration instructio - Shared team text source - All associated stream scenes and sources - All browser sources with team prefix -- **Sync Verification**: Real-time verification of database-OBS group synchronization +- **Sync Verification**: Real-time verification of database-OBS group synchronization with system scene bypass - **Conflict Resolution**: UI actions to resolve sync issues (missing groups, name changes) - **Visual Indicators**: Clear status indicators for group linking and sync problems - 🆔 "Linked by UUID" - Group tracked by reliable UUID - 📝 "Name changed in OBS" - Group renamed in OBS, database needs update - ⚠️ "Not found in OBS" - Group in database but missing from OBS +- **System Scene Protection**: Infrastructure scenes (1-Screen, 2-Screen, 4-Screen, Starting, Ending, Audio, Movies, Resources) excluded from orphaned cleanup ### User Experience Improvements - **Toast Notifications**: Real-time feedback for all operations (success/error/info) @@ -227,13 +229,22 @@ See [OBS Setup Guide](./docs/OBS_SETUP.md) for detailed configuration instructio - **Responsive Design**: Mobile-friendly interface with glass morphism styling - **Loading States**: Clear indicators during API operations - **Error Recovery**: Graceful error handling with user-friendly messages +- **Enhanced Footer**: Real-time team/stream counts, OBS connection status with visual indicators +- **Optimistic Updates**: Immediate UI feedback with proper stream group name matching + +### OBS Integration Improvements +- **Text Size**: Team name overlays use 96pt font for better visibility +- **Color Display**: Fixed background color display (#002b4b) using proper ABGR format +- **Standardized APIs**: All endpoints use consistent `{ success: true, data: [...] }` response format +- **Performance Optimization**: Reduced code duplication and improved API response handling ### Developer Experience - **Type Safety**: Comprehensive TypeScript definitions throughout -- **API Documentation**: Well-documented endpoints with clear parameter validation +- **API Standardization**: Consistent response formats across all endpoints with proper error handling - **Migration Scripts**: Database migration tools for schema updates - **Security**: Input validation, sanitization, and secure API design -- **Testing**: Comprehensive error handling and edge case management +- **Performance Monitoring**: Smart polling with visibility detection and performance tracking +- **Code Optimization**: Eliminated redundancies and consolidated common patterns ## Known Issues diff --git a/README.md b/README.md index f83b714..f375aa9 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ A professional [Next.js](https://nextjs.org) web application for managing live s - **Professional Broadcasting**: Audio routing, scene management, and live status indicators - **Dual Integration**: WebSocket API + text file monitoring for maximum compatibility - **UUID-based Tracking**: Robust OBS group synchronization with rename-safe tracking +- **Enhanced Footer**: Real-time team/stream counts and OBS connection status +- **Optimized Performance**: Reduced code duplication and standardized API responses ## Quick Start