Add comprehensive studio mode support and stream organization

- Implement studio mode transition workflow with Go Live buttons
- Add collapsible team grouping for better stream organization
- Include source locking functionality for newly created streams
- Enhance footer status indicators with improved visual styling
- Create triggerTransition API endpoint for studio mode operations
- Add CollapsibleGroup component for expandable content sections

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Decobus 2025-07-25 21:29:23 -04:00
parent 07028b0792
commit 3bad71cb26
8 changed files with 603 additions and 116 deletions

View file

@ -363,7 +363,7 @@ async function createTextSource(sceneName, textSourceName, text) {
}
}
async function createStreamGroup(groupName, streamName, teamName, url) {
async function createStreamGroup(groupName, streamName, teamName, url, lockSources = true) {
try {
const obsClient = await getOBSClient();
@ -443,14 +443,48 @@ async function createStreamGroup(groupName, streamName, teamName, url) {
} catch (muteError) {
console.error(`Failed to mute browser source audio for "${sourceName}":`, muteError.message);
}
// Lock the newly created browser source if requested
if (lockSources) {
try {
// Get the scene items to find the browser source's ID
const { sceneItems } = await obsClient.call('GetSceneItemList', { sceneName: streamGroupName });
const browserItem = sceneItems.find(item => item.sourceName === sourceName);
if (browserItem) {
await obsClient.call('SetSceneItemLocked', {
sceneName: streamGroupName,
sceneItemId: browserItem.sceneItemId,
sceneItemLocked: true
});
console.log(`Locked browser source "${sourceName}" in nested scene`);
}
} catch (lockError) {
console.error(`Failed to lock browser source "${sourceName}":`, lockError.message);
}
}
} else {
// Add existing source to nested scene
await obsClient.call('CreateSceneItem', {
const { sceneItemId } = await obsClient.call('CreateSceneItem', {
sceneName: streamGroupName,
sourceName: sourceName
});
console.log(`Added existing browser source "${sourceName}" to nested scene`);
// Lock the scene item if requested
if (lockSources) {
try {
await obsClient.call('SetSceneItemLocked', {
sceneName: streamGroupName,
sceneItemId: sceneItemId,
sceneItemLocked: true
});
console.log(`Locked browser source "${sourceName}" in nested scene`);
} catch (lockError) {
console.error(`Failed to lock browser source "${sourceName}":`, lockError.message);
}
}
// Ensure existing browser source has audio control enabled and correct URL
try {
await obsClient.call('SetInputSettings', {
@ -482,21 +516,49 @@ async function createStreamGroup(groupName, streamName, teamName, url) {
const colorSourceName = `${textSourceName}_bg`;
try {
await obsClient.call('CreateSceneItem', {
const { sceneItemId: colorItemId } = await obsClient.call('CreateSceneItem', {
sceneName: streamGroupName,
sourceName: colorSourceName
});
console.log(`Added color source background "${colorSourceName}" to nested scene`);
// Lock the color source if requested
if (lockSources) {
try {
await obsClient.call('SetSceneItemLocked', {
sceneName: streamGroupName,
sceneItemId: colorItemId,
sceneItemLocked: true
});
console.log(`Locked color source background "${colorSourceName}"`);
} catch (lockError) {
console.error(`Failed to lock color source:`, lockError.message);
}
}
} catch (error) {
console.log('Color source background might already be in nested scene');
}
try {
await obsClient.call('CreateSceneItem', {
const { sceneItemId: textItemId } = await obsClient.call('CreateSceneItem', {
sceneName: streamGroupName,
sourceName: textSourceName
});
console.log(`Added text source "${textSourceName}" to nested scene`);
// Lock the text source if requested
if (lockSources) {
try {
await obsClient.call('SetSceneItemLocked', {
sceneName: streamGroupName,
sceneItemId: textItemId,
sceneItemLocked: true
});
console.log(`Locked text source "${textSourceName}"`);
} catch (lockError) {
console.error(`Failed to lock text source:`, lockError.message);
}
}
} catch (error) {
console.log('Text source might already be in nested scene');
}