diff --git a/app/api/addStream/route.ts b/app/api/addStream/route.ts index 4bb22d6..9347106 100644 --- a/app/api/addStream/route.ts +++ b/app/api/addStream/route.ts @@ -4,7 +4,7 @@ import { connectToOBS, getOBSClient, disconnectFromOBS, addSourceToSwitcher, cre import { open } from 'sqlite'; import sqlite3 from 'sqlite3'; import path from 'path'; -import { getTableName, BASE_TABLE_NAMES } from '../../../lib/constants'; +import { getTableName, BASE_TABLE_NAMES, SOURCE_SWITCHER_NAMES } from '../../../lib/constants'; interface OBSClient { call: (method: string, params?: Record) => Promise>; @@ -18,15 +18,7 @@ inputName: string; interface GetInputListResponse { inputs: OBSInput[]; } -const screens = [ - 'ss_large', - 'ss_left', - 'ss_right', - 'ss_top_left', - 'ss_top_right', - 'ss_bottom_left', - 'ss_bottom_right', -]; +const screens = SOURCE_SWITCHER_NAMES; async function fetchTeamInfo(teamId: number) { const FILE_DIRECTORY = path.resolve(process.env.FILE_DIRECTORY || './files'); diff --git a/app/api/getTeamName/route.ts b/app/api/getTeamName/route.ts index d364306..c4c7cd9 100644 --- a/app/api/getTeamName/route.ts +++ b/app/api/getTeamName/route.ts @@ -1,38 +1,32 @@ import { NextRequest, NextResponse } from 'next/server'; import { getDatabase } from '../../../lib/database'; +import { TABLE_NAMES } from '../../../lib/constants'; +import { createErrorResponse, createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers'; + +async function getTeamNameHandler(request: NextRequest) { + // Extract the team_id from the query string + const { searchParams } = new URL(request.url); + const teamId = searchParams.get('team_id'); + + if (!teamId) { + return createErrorResponse('Missing team_id', 400, 'team_id parameter is required'); + } -export async function GET(request: NextRequest) { try { - // Extract the team_id from the query string - const { searchParams } = new URL(request.url); - const teamId = searchParams.get('team_id'); - - if (!teamId) { - return NextResponse.json( - { error: 'Missing team_id' }, - { status: 400 } - ); - } - const db = await getDatabase(); const team = await db.get( - 'SELECT team_name FROM teams_2025_spring_adr WHERE team_id = ?', + `SELECT team_name FROM ${TABLE_NAMES.TEAMS} WHERE team_id = ?`, [teamId] ); if (!team) { - return NextResponse.json( - { error: 'Team not found' }, - { status: 404 } - ); + return createErrorResponse('Team not found', 404, `No team found with ID: ${teamId}`); } - return NextResponse.json({ team_name: team.team_name }); + return createSuccessResponse({ team_name: team.team_name }); } catch (error) { - console.error('Error fetching team name:', error instanceof Error ? error.message : String(error)); - return NextResponse.json( - { error: 'Failed to fetch team name' }, - { status: 500 } - ); + return createDatabaseError('fetch team name', error); } } + +export const GET = withErrorHandling(getTeamNameHandler); diff --git a/app/api/streams/route.ts b/app/api/streams/route.ts index 000106f..ab368e3 100644 --- a/app/api/streams/route.ts +++ b/app/api/streams/route.ts @@ -2,17 +2,16 @@ import { NextResponse } from 'next/server'; import { getDatabase } from '../../../lib/database'; import { Stream } from '@/types'; import { TABLE_NAMES } from '../../../lib/constants'; +import { createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers'; -export async function GET() { -try { +async function getStreamsHandler() { + try { const db = await getDatabase(); const streams: Stream[] = await db.all(`SELECT * FROM ${TABLE_NAMES.STREAMS}`); - return NextResponse.json(streams); -} catch (error) { - console.error('Error fetching streams:', error); - return NextResponse.json( - { error: 'Failed to fetch streams' }, - { status: 500 } - ); -} + return createSuccessResponse(streams); + } catch (error) { + return createDatabaseError('fetch streams', error); + } } + +export const GET = withErrorHandling(getStreamsHandler); diff --git a/lib/constants.ts b/lib/constants.ts index c67808e..e756185 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -40,3 +40,29 @@ export const TABLE_NAMES = { TEAMS: getTableName(BASE_TABLE_NAMES.TEAMS), } as const; +// Screen position constants +export const SCREEN_POSITIONS = [ + 'large', + 'left', + 'right', + 'topLeft', + 'topRight', + 'bottomLeft', + 'bottomRight' +] as const; + +export const SOURCE_SWITCHER_NAMES = [ + 'ss_large', + 'ss_left', + 'ss_right', + 'ss_top_left', + 'ss_top_right', + 'ss_bottom_left', + 'ss_bottom_right' +] as const; + +// OBS utility functions +export function cleanObsName(name: string): string { + return name.toLowerCase().replace(/\s+/g, '_'); +} + diff --git a/lib/obsClient.js b/lib/obsClient.js index cf38c44..9adfe80 100644 --- a/lib/obsClient.js +++ b/lib/obsClient.js @@ -1,4 +1,5 @@ const { OBSWebSocket } = require('obs-websocket-js'); +const { cleanObsName, SOURCE_SWITCHER_NAMES, SCREEN_POSITIONS } = require('./constants'); let obs = null; let isConnecting = false; @@ -383,11 +384,11 @@ async function createStreamGroup(groupName, streamName, teamName, url) { // Ensure team scene exists await createGroupIfNotExists(groupName); - const cleanGroupName = groupName.toLowerCase().replace(/\s+/g, '_'); - const cleanStreamName = streamName.toLowerCase().replace(/\s+/g, '_'); + const cleanGroupName = cleanObsName(groupName); + const cleanStreamName = cleanObsName(streamName); const streamGroupName = `${cleanGroupName}_${cleanStreamName}_stream`; const sourceName = `${cleanGroupName}_${cleanStreamName}`; - const textSourceName = teamName.toLowerCase().replace(/\s+/g, '_') + '_text'; + const textSourceName = cleanObsName(teamName) + '_text'; // Create a nested scene for this stream (acts as a group) try { @@ -594,11 +595,11 @@ async function deleteStreamComponents(streamName, teamName, groupName) { try { const obsClient = await getOBSClient(); - const cleanGroupName = groupName.toLowerCase().replace(/\s+/g, '_'); - const cleanStreamName = streamName.toLowerCase().replace(/\s+/g, '_'); + const cleanGroupName = cleanObsName(groupName); + const cleanStreamName = cleanObsName(streamName); const streamGroupName = `${cleanGroupName}_${cleanStreamName}_stream`; const sourceName = `${cleanGroupName}_${cleanStreamName}`; - const textSourceName = teamName.toLowerCase().replace(/\s+/g, '_') + '_text'; + const textSourceName = cleanObsName(teamName) + '_text'; console.log(`Starting comprehensive deletion for stream "${streamName}"`); console.log(`Components to delete: scene="${streamGroupName}", source="${sourceName}"`); @@ -650,15 +651,7 @@ async function deleteStreamComponents(streamName, teamName, groupName) { } // 5. Remove from all source switchers - const screens = [ - 'ss_large', - 'ss_left', - 'ss_right', - 'ss_top_left', - 'ss_top_right', - 'ss_bottom_left', - 'ss_bottom_right' - ]; + const screens = SOURCE_SWITCHER_NAMES; for (const screen of screens) { try { @@ -723,15 +716,7 @@ async function clearTextFilesForStream(streamGroupName) { try { const FILE_DIRECTORY = path.resolve(process.env.FILE_DIRECTORY || './files'); - const screens = [ - 'large', - 'left', - 'right', - 'topLeft', - 'topRight', - 'bottomLeft', - 'bottomRight' - ]; + const screens = SCREEN_POSITIONS; let clearedFiles = []; @@ -785,7 +770,7 @@ async function deleteTeamComponents(teamName, groupName) { } // 2. Delete the team text source (shared across all team streams) - const textSourceName = teamName.toLowerCase().replace(/\s+/g, '_') + '_text'; + const textSourceName = cleanObsName(teamName) + '_text'; try { const { inputs } = await obsClient.call('GetInputList'); const textSource = inputs.find(input => input.inputName === textSourceName); @@ -801,7 +786,7 @@ async function deleteTeamComponents(teamName, groupName) { // 3. Get all scenes to check for nested stream scenes try { const { scenes } = await obsClient.call('GetSceneList'); - const cleanGroupName = (groupName || teamName).toLowerCase().replace(/\s+/g, '_'); + const cleanGroupName = cleanObsName(groupName || teamName); // Find all nested stream scenes for this team const streamScenes = scenes.filter(scene => @@ -827,7 +812,7 @@ async function deleteTeamComponents(teamName, groupName) { // 4. Remove any browser sources associated with this team try { const { inputs } = await obsClient.call('GetInputList'); - const cleanGroupName = (groupName || teamName).toLowerCase().replace(/\s+/g, '_'); + const cleanGroupName = cleanObsName(groupName || teamName); // Find all browser sources for this team const teamBrowserSources = inputs.filter(input =>