Compare commits

..

No commits in common. "b6937f3a4fa943ce2eeb4ae1b860dbfdfa412cb0" and "78f4d325d8548fad9b0baccb6c5136aedb198419" have entirely different histories.

2 changed files with 53 additions and 70 deletions

View file

@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { getDatabase } from '../../../lib/database'; import { getDatabase } from '../../../lib/database';
import { connectToOBS, getOBSClient, disconnectFromOBS, addSourceToSwitcher, createStreamGroup } from '../../../lib/obsClient'; import { connectToOBS, getOBSClient, disconnectFromOBS, addSourceToSwitcher, createGroupIfNotExists, addSourceToGroup, createStreamGroup } from '../../../lib/obsClient';
import { open } from 'sqlite'; import { open } from 'sqlite';
import sqlite3 from 'sqlite3'; import sqlite3 from 'sqlite3';
import path from 'path'; import path from 'path';
@ -131,7 +131,7 @@ export async function POST(request: NextRequest) {
if (!sourceExists) { if (!sourceExists) {
// Create stream group with text overlay // Create stream group with text overlay
await createStreamGroup(groupName, name, teamInfo.team_name, url); const result = await createStreamGroup(groupName, name, teamInfo.team_name, url);
// Update team with group UUID if not set // Update team with group UUID if not set
if (!teamInfo.group_uuid) { if (!teamInfo.group_uuid) {
@ -152,7 +152,7 @@ export async function POST(request: NextRequest) {
// Get the scene UUID for the group // Get the scene UUID for the group
const obsClient = await getOBSClient(); const obsClient = await getOBSClient();
const { scenes } = await obsClient.call('GetSceneList'); const { scenes } = await obsClient.call('GetSceneList');
const scene = scenes.find((s: { sceneName: string; sceneUuid: string }) => s.sceneName === groupName); const scene = scenes.find((s: any) => s.sceneName === groupName);
if (scene) { if (scene) {
await db.run( await db.run(

View file

@ -321,65 +321,68 @@ async function createStreamGroup(groupName, streamName, teamName, url) {
const sourceName = streamName.toLowerCase().replace(/\s+/g, '_') + '_twitch'; const sourceName = streamName.toLowerCase().replace(/\s+/g, '_') + '_twitch';
const textSourceName = teamName.toLowerCase().replace(/\s+/g, '_') + '_text'; const textSourceName = teamName.toLowerCase().replace(/\s+/g, '_') + '_text';
// Create a nested scene for this stream (acts as a group)
try {
await obsClient.call('CreateScene', { sceneName: streamGroupName });
console.log(`Created nested scene "${streamGroupName}" for stream grouping`);
} catch (sceneError) {
console.log(`Nested scene "${streamGroupName}" might already exist`);
}
// Create text source globally (reused across streams in the team) // Create text source globally (reused across streams in the team)
await createTextSource(groupName, textSourceName, teamName); await createTextSource(groupName, textSourceName, teamName);
// Create browser source globally // Create browser source directly in the team scene
const { inputs } = await obsClient.call('GetInputList'); const { sceneItems: teamSceneItems } = await obsClient.call('GetSceneItemList', { sceneName: groupName });
const browserSourceExists = inputs.some(input => input.inputName === sourceName); const browserSourceExists = teamSceneItems.some(item => item.sourceName === sourceName);
if (!browserSourceExists) { if (!browserSourceExists) {
await obsClient.call('CreateInput', { await obsClient.call('CreateInput', {
sceneName: streamGroupName, // Create in the nested scene sceneName: groupName,
inputName: sourceName, inputName: sourceName,
inputKind: 'browser_source', inputKind: 'browser_source',
inputSettings: { inputSettings: {
width: 1920, width: 1600,
height: 1080, height: 900,
url, url,
control_audio: true, control_audio: true,
}, },
}); });
console.log(`Created browser source "${sourceName}" in nested scene`);
} else {
// Add existing source to nested scene
await obsClient.call('CreateSceneItem', {
sceneName: streamGroupName,
sourceName: sourceName
});
} }
// Add text source to nested scene // Add text source to team scene if not already there
try { const textInTeamScene = teamSceneItems.some(item => item.sourceName === textSourceName);
if (!textInTeamScene) {
await obsClient.call('CreateSceneItem', { await obsClient.call('CreateSceneItem', {
sceneName: streamGroupName, sceneName: groupName,
sourceName: textSourceName sourceName: textSourceName
}); });
} catch (e) {
console.log('Text source might already be in nested scene');
} }
// Get the scene items in the nested scene // Get the scene items after adding sources
const { sceneItems: nestedSceneItems } = await obsClient.call('GetSceneItemList', { sceneName: streamGroupName }); const { sceneItems: updatedSceneItems } = await obsClient.call('GetSceneItemList', { sceneName: groupName });
// Find the browser source and text source items in nested scene // Find the browser source and text source items
const browserSourceItem = nestedSceneItems.find(item => item.sourceName === sourceName); const browserSourceItem = updatedSceneItems.find(item => item.sourceName === sourceName);
const textSourceItem = nestedSceneItems.find(item => item.sourceName === textSourceName); const textSourceItem = updatedSceneItems.find(item => item.sourceName === textSourceName);
// Position the sources properly in the nested scene // Create a group within the team scene containing both sources
if (browserSourceItem && textSourceItem) { if (browserSourceItem && textSourceItem) {
try { try {
// Position text overlay at top-left of the browser source // Create a group with both items
await obsClient.call('CreateGroup', {
sceneName: groupName,
groupName: streamGroupName
});
// Add both sources to the group
await obsClient.call('SetSceneItemGroup', {
sceneName: groupName,
sceneItemId: browserSourceItem.sceneItemId,
groupName: streamGroupName
});
await obsClient.call('SetSceneItemGroup', {
sceneName: groupName,
sceneItemId: textSourceItem.sceneItemId,
groupName: streamGroupName
});
// Position text overlay at top-left within the group
await obsClient.call('SetSceneItemTransform', { await obsClient.call('SetSceneItemTransform', {
sceneName: streamGroupName, // In the nested scene sceneName: groupName,
sceneItemId: textSourceItem.sceneItemId, sceneItemId: textSourceItem.sceneItemId,
sceneItemTransform: { sceneItemTransform: {
positionX: 10, positionX: 10,
@ -389,49 +392,29 @@ async function createStreamGroup(groupName, streamName, teamName, url) {
} }
}); });
console.log(`Stream sources positioned in nested scene "${streamGroupName}"`); // Lock the group items
} catch (positionError) { await obsClient.call('SetSceneItemLocked', {
console.error('Failed to position sources:', positionError.message || positionError);
}
}
// Now add the nested scene to the team scene as a group
const { sceneItems: teamSceneItems } = await obsClient.call('GetSceneItemList', { sceneName: groupName });
const nestedSceneInTeam = teamSceneItems.some(item => item.sourceName === streamGroupName);
if (!nestedSceneInTeam) {
try {
const { sceneItemId } = await obsClient.call('CreateSceneItem', {
sceneName: groupName, sceneName: groupName,
sourceName: streamGroupName, sceneItemId: browserSourceItem.sceneItemId,
sceneItemEnabled: true sceneItemLocked: true
}); });
console.log(`Added nested scene "${streamGroupName}" to team scene "${groupName}"`);
// Set bounds to 1600x900 to match the source switcher dimensions await obsClient.call('SetSceneItemLocked', {
await obsClient.call('SetSceneItemTransform', {
sceneName: groupName, sceneName: groupName,
sceneItemId: sceneItemId, sceneItemId: textSourceItem.sceneItemId,
sceneItemTransform: { sceneItemLocked: true
alignment: 5, // Center alignment
boundsAlignment: 0, // Center bounds alignment
boundsType: 'OBS_BOUNDS_SCALE_INNER', // Scale to fit inside bounds
boundsWidth: 1600,
boundsHeight: 900,
scaleX: 1.0,
scaleY: 1.0
}
}); });
console.log(`Set bounds for nested scene to 1600x900`);
} catch (e) { console.log(`Stream group "${streamGroupName}" created within team scene "${groupName}"`);
console.error('Failed to add nested scene to team scene:', e.message); } catch (groupError) {
console.log('Group creation failed, sources added individually to team scene');
} }
} }
console.log(`Stream group "${streamGroupName}" created as nested scene in team "${groupName}"`); console.log(`Stream sources added to team scene "${groupName}" with text overlay`);
return { return {
success: true, success: true,
message: 'Stream group created as nested scene', message: 'Stream group created within team scene',
streamGroupName, streamGroupName,
sourceName, sourceName,
textSourceName textSourceName