{ "pageTitle": "VDO.Ninja OBS Control Dock", "mainHeading": "VDO.Ninja OBS Control", "languageSwitcher": { "label": "Language:" }, "collapsible": { "clickToExpand": "Click to expand", "clickToCollapse": "Click to collapse" }, "customCss": { "title": "Custom CSS", "label": "Enter your custom CSS here:", "description": "This CSS will be applied to the sources." }, "obsConnection": { "title": "OBS WebSocket Connection", "websocketUrlLabel": "WebSocket URL:", "passwordLabel": "Password:", "cameraPrefixLabel": "General Camera prefix:", "reactionPrefixLabel": "Reaction prefix: VDO.", "highlightPrefixLabel": "Highlight prefix: VDO.", "reactionPrefixDynamicLabel": "Reaction prefix: {{prefix}}{{separator}}{{subPrefix}}", "highlightPrefixDynamicLabel": "Highlight prefix: {{prefix}}{{separator}}{{subPrefix}}", "connectButton": "Connect", "disconnectButton": "Disconnect", "statusDisconnected": "Status: Disconnected", "statusConnected": "Status: Connected", "statusConnecting": "Status: Connecting...", "statusError": "Status: Error", "statusErrorUrlMissing": "Status: Error - URL missing", "statusErrorTimeout": "Status: Error - Connection timed out", "statusErrorCameraPrefixMissing": "Status: Error - Camera Prefix Missing" }, "vdoNinjaSettings": { "title": "VDO.Ninja Settings", "baseUrlLabel": "VDO.Ninja Base URL:", "baseUrlPlaceholder": "https://vdo.ninja", "roomNameLabel": "Room Name:", "roomNamePlaceholder": "e.g., MyNinjaRoom", "passwordLabel": "Password:", "passwordPlaceholder": "Room or &password", "streamIdsLabel": "Stream IDs:", "streamIdsPlaceholder": "streamId1,streamId2", "roomOrStreamIdsNeeded": "Room Name or Stream ID(s) needed", "connectButton": "Connect", "disconnectButton": "Disconnect", "cancelButton": "Cancel", "statusDisconnected": "Status: Disconnected", "statusConnected": "Status: Connected", "statusConnecting": "Status: Connecting...", "statusConnectionFailed": "Status: Connection Failed", "statusConnectionLost": "Status: Connection Lost" }, "streamIdMappings": { "title": "Stream ID Mappings", "addNewMappingButton": "Add New Mapping", "ruleTitle": "Stream Mapping Rule", "streamIdPlaceholder": "Stream ID", "streamIdTooltip": "VDO.Ninja Stream ID", "labelPlaceholder": "Label (optional)", "labelTooltip": "VDO.Ninja Stream Label", "matchTypeTooltip": "How to match stream", "matchType": { "idOnly": "ID Only", "labelOnly": "Label Only", "bothRequired": "Both Required", "eitherMatch": "Either Match" }, "targetSceneTooltip": "Target OBS Scene", "selectSceneOption": "Select a scene...", "removeRuleTooltip": "Remove this mapping rule", "cloneToMainSceneLabel": "Clone to main scene", "switchToSceneOnAddLabel": "Switch to scene on add", "ruleDescription": "Define how incoming streams are routed to OBS scenes." }, "obsTargetSettings": { "title": "OBS Target Settings", "sourceCreationScenesLabel": "Scenes for Source Creation:", "sourceCreationScenesDesc": "Select one or more scenes. The first will be the primary, others for copying.", "loadingScenes": "Loading scenes...", "noScenesFound": "No scenes found", "refetchScenesButton": "Re-Fetch Scenes", "screenShareSettings": { "title": "Screen Sharing Settings:", "widthLabel": "Width:", "heightLabel": "Height:", "resolutionNote": "This resolution will be used for screen sharing sources in scenes with a 'Reaction' layout." }, "autoAddSourcesLabel": "Auto-add new streams as sources", "autoRemoveSourcesLabel": "Auto-remove sources on disconnect", "newSourceSizing": { "label": "Default Source Sizing:", "defaultSizeOption": "Default (1920x1080 at 0,0)", "bestFitOption": "Best Fit (Preserve Aspect)", "stretchToFillOption": "Stretch to Fill Screen", "overrideNote": "Specific layouts defined in the \"Layouts\" section will override these for those scenes." }, "codec": { "label": "Codec:", "noneOption": "none", "learnMoreTitle": "Learn more about codec options" } }, "sceneLayouts": { "title": "Scene Layouts", "addNewLayoutButton": "Add New Layout", "description": "Define automatic layouts for specific OBS scenes. These will override the default source sizing for the configured scenes.", "sceneLabel": "Scene:", "selectSceneOption": "Select Scene...", "layoutLabel": "Layout:", "selectLayoutOption": "Select Layout...", "layoutTypes": { "grid": "Grid", "reaction": "Reaction", "highlight": "Highlight" }, "noActiveLayouts": "No active layouts", "removeLayoutTooltip": "Remove this layout configuration", "sceneAlreadyConfiguredTooltip": "(Already in use)", "sceneAlreadyConfiguredAlert": "Scene '{{sceneName}}' is already in use by another layout configuration.", "controls": { "margin": "Margin", "spacing": "Spacing", "offsetX": "X Offset", "offsetY": "Y Offset", "gridSplitScreenTwoCameras": "Split screen for 2 cameras (Grid)", "distributeCameras": "Distribute cameras (Reaction/Highlight)" } }, "activeStreams": { "title": "Active Streams", "noActiveStreams": "No active streams", "streamIdLabel": "ID: {{id}}", "labelLabel": "Label: {{label}}", "targetSceneLabelText": "Target Scene: {{sceneName}}", "notSet": "Not Set", "defaultSceneTag": "default", "mappedSceneTag": "mapped", "addedToObs": "✓ Added to OBS", "notInObs": "✗ Not in OBS", "buttons": { "addToObs": "Add to OBS", "removeFromObs": "Remove from OBS", "highlight": "Highlight", "unhighlight": "Unhighlight", "screenShare": "Screen Share", "stopScreenShare": "Stop Screen Share" } }, "log": { "title": "Log" }, "vdoNinja": { "defaultStreamLabel": "Stream {{id}}" }, "logMessages": { "settingsSaved": "Settings saved.", "settingsLoaded": "Settings loaded from localStorage.", "customCssChangedWillApplyToSource": "Custom CSS input changed. It will be applied directly to OBS source settings on next creation/update.", "errorLoadingSettings": "Error loading settings from localStorage: {{message}}. Using defaults.", "noSavedSettings": "No saved settings found. Using default values.", "appInitialized": "VDO.Ninja OBS Control Dock Initialized. Welcome!", "jsShaLoaded": "jsSHA library loaded successfully (fallback for Web Crypto).", "errorLoadingJsSha": "Error: Failed to load jsSHA library. OBS authentication might fail if Web Crypto is also unavailable.", "loadedStreamMappings": "Loaded {{count}} stream mappings.", "errorLoadingStreamMappings": "Error loading stream mappings from localStorage: {{message}}", "warningStreamMappingElementsNotFound": "Warning: Could not find all expected elements in a stream mapping UI div.", "loadedLayoutConfigs": "Loaded {{count}} scene layout configurations.", "errorLoadingLayoutConfigs": "Error loading scene layout configurations: {{message}}", "savedLayoutConfigs": "Saved {{count}} scene layout configurations.", "errorSavingLayoutsDuplicateScene": "Error saving layouts: Scene '{{sceneName}}' is configured multiple times. Please ensure each scene has only one layout.", "layoutSceneNotFound": "Saved scene '{{sceneName}}' for layout not found in current OBS scenes.", "errorHidingSource": "Error hiding source item {{sourceName}} in {{sceneName}}: {{message}}", "sceneAlreadyConfiguredError": "Scene '{{sceneName}}' is already configured for another layout. Reverting selection.", "vdoNinja": { "disconnected": "Disconnected from VDO.Ninja.", "alreadyConnected": "Already connected to VDO.Ninja.", "errorRoomOrStreamIdNeeded": "VDO.Ninja Error: Room Name or Stream ID(s) must be provided.", "connectionTimeout": "VDO.Ninja connection timed out. No activity received from iframe.", "roomOrStreamIdNeededForConnect": "VDO.Ninja: Room Name or specific Stream ID(s) must be provided to connect.", "streamIdsEmptyAfterTrim": "VDO.Ninja: Stream IDs provided but were empty after trimming.", "initializingIframe": "Initializing VDO.Ninja iframe with URL: {{url}}", "invalidBaseUrl": "Invalid VDO.Ninja base URL in settings: {{url}}", "iframeConnectedActive": "VDO.Ninja iframe connection established and active.", "streamConnectedActive": "VDO.Ninja stream connected/active: \"{{label}}\" (ID: {{id}})", "autoAddingStream": "Auto-adding stream {{id}} to OBS.", "streamDisconnectedInactive": "VDO.Ninja stream disconnected/inactive: \"{{label}}\" (ID: {{id}})", "autoRemovingStream": "Auto-removing stream {{id}} from OBS.", "streamLabelUpdated": "VDO.Ninja stream label updated for ID {{id}}: \"{{newLabel}}\" (was \"{{oldLabel}}\")", "connectionLostResetting": "VDO.Ninja connection lost (no activity from iframe). Attempting to reset." }, "obs": { "disconnecting": "Disconnecting from OBS WebSocket...", "errorCameraPrefixRequired": "Error: General Camera prefix is required for OBS connection.", "errorUrlRequired": "Error: OBS WebSocket URL is required.", "attemptingConnection": "Attempting to connect to OBS WebSocket at {{url}}...", "connectionTimeout": "OBS WebSocket connection attempt timed out.", "connectionOpenedWaitingHello": "OBS WebSocket connection opened. Waiting for Server Hello...", "receivedHelloSendingIdentify": "Received Hello from OBS. Sending Identify...", "authDataPrepared": "Authentication data prepared for Identify message.", "warningAuthRequiredNoPassword": "Warning: OBS server requires authentication, but no password provided.", "authSuccessConnected": "OBS WebSocket Authentication successful! Connection established.", "requestError": "OBS Request Error (Type: {{type}}, ID: {{id}}): {{error}} (Code: {{code}})", "eventSceneListChanged": "OBS Event: Scene list changed. Re-fetching scenes.", "errorProcessingMessage": "Error processing OBS WebSocket message: {{message}}. Data: {{data}}", "webSocketError": "OBS WebSocket Error: {{error}}", "authFailedReason": "Authentication Failed - incorrect password or auth required and not provided.", "connectionClosedReasonCode": "Code: {{code}}{{wasClean}}", "uncleanDisconnection": " (Unclean disconnection)", "connectionClosed": "OBS WebSocket Connection Closed. Reason: {{reason}}", "errorCreatingConnection": "Error creating OBS WebSocket connection: {{message}}", "authGenerationError": "OBS Authentication generation error: {{message}}", "cannotSendRequestNotConnected": "Cannot send request '{{requestType}}': Not connected to OBS.", "errorSendingRequest": "Error sending OBS request '{{requestType}}': {{message}}", "requestTimeout": "OBS Request '{{requestType}}' (ID: {{id}}) timed out.", "connectionEstablishedFetchingData": "OBS Connection fully established. Fetching initial data...", "errorPostConnectionSetup": "Error during post-OBS connection setup (fetching scenes): {{message}}", "connectionClosedOrLost": "OBS Connection has been closed or lost.", "cannotFetchScenesNotConnected": "Cannot fetch OBS scenes: Not connected to OBS.", "fetchingScenes": "Fetching OBS scenes...", "fetchedScenesCount": "Fetched {{count}} scenes from OBS.", "failedToFetchScenes": "Failed to fetch OBS scenes or no scenes returned.", "errorFetchingScenes": "Error fetching OBS scenes: {{message}}", "cannotHighlightNotConnected": "Cannot highlight: Not connected to OBS.", "highlightLayoutActiveManualLegacy": "Scene '{{sceneName}}' uses the new Highlight Layout. Manual highlight button may have limited effect or is superseded.", "stoppingScreenShareForHighlight": "A screen share is active ({{id}}). Stopping it before highlighting.", "cannotScreenShareNotConnected": "Cannot start screen share: Not connected to OBS.", "unhighlightingForScreenShare": "A camera is highlighted ({{id}}). Unhighlighting it before starting screen share.", "cannotAddScreenShareNotConnected": "Cannot add screen share: Not connected to OBS.", "cannotAddScreenShareNoRoom": "Cannot add screen share: VDO.Ninja Room name is required for screen sharing URLs.", "cannotAddScreenShareNoReactionLayoutScene": "Cannot add screen share: No scenes are configured with a 'Reaction' layout.", "addingUpdatingScreenShareToReactionScenes": "Adding/Updating screen share source '{{sourceName}}' (URL: {{url}}) to Reaction scenes. Primary creation in '{{primaryScene}}'. ({{count}} scenes total)", "reconfiguringExistingScreenShare": "Reconfiguring existing screen share source '{{oldName}}' to be '{{newName}}'.", "reconfiguredRenamedScreenShare": "Reconfigured and renamed existing screen share source to '{{sourceName}}'.", "screenShareExistsUpdating": "Screen share source '{{sourceName}}' already exists. Updating its settings.", "creatingNewScreenShareInScene": "Creating new screen share source '{{sourceName}}' in scene '{{sceneName}}'.", "ensuredTransformedScreenShareReaction": "Ensured and transformed screen share source '{{sourceName}}' in Reaction scene '{{sceneName}}'.", "sourceFoundInSceneEnabled": "Source '{{sourceName}}' found in scene '{{sceneName}}', item ID: {{itemId}}. Ensuring it's enabled.", "sourceAddedToScene": "Source '{{sourceName}}' added to scene '{{sceneName}}', item ID: {{itemId}}.", "errorCreatingSceneItem": "Error creating scene item for '{{sourceName}}' in '{{sceneName}}': {{message}}", "errorCheckingSceneForItem": "Error checking for '{{sourceName}}' in scene '{{sceneName}}': {{message}}", "errorApplyingInitialTransform": "Error applying initial default transform to '{{sourceName}}' in '{{sceneName}}': {{message}}", "successConfigScreenShare": "Successfully configured screen share for stream {{streamId}} ({{label}}).", "errorAddingUpdatingScreenShare": "Error adding/updating screen share source '{{sourceName}}': {{message}}.", "cannotToggleHighlightNotConnected": "Cannot toggle highlight: Not connected to OBS.", "streamUnhighlighted": "Stream {{id}} unhighlighted (renamed from {{oldName}} to {{newName}}).", "streamSuccessfullyUnhighlighted": "Stream {{id}} successfully unhighlighted (renamed to {{newName}}).", "streamSuccessfullyHighlighted": "Stream {{id}} successfully highlighted (renamed to {{newName}}).", "unhighlightError": "Could not unhighlight (rename) {{sourceName}}, it might not exist or another error: {{message}}", "highlightError": "Could not highlight (rename) {{sourceName}}, it might not exist or another error: {{message}}", "legacyHighlightUnhighlightOnRemove": "Stream {{id}} was legacy highlighted. Unhighlighting.", "cannotAddStreamNotConnected": "Cannot add stream \"{{label}}\" ({{id}}) to OBS: Not connected to OBS.", "cannotAddStreamNoTargetSceneName": "Cannot add stream \"{{label}}\" ({{id}}): Target OBS scene name is required but not set (no default and no mapping).", "foundExistingStandardSource": "Found existing standard source '{{sourceName}}' for stream {{streamId}}.", "foundExistingHighlightForConnectingStream": "Found existing OBS source '{{sourceName}}' which matches highlight naming for connecting stream {{streamId}}. Updating internal highlight state.", "foundExistingHighlightedSource": "Found existing highlighted source '{{sourceName}}' for stream {{streamId}}.", "noExistingSourceFoundWillCreate": "Neither standard ('{{baseSourceName}}') nor highlighted ('{{highlightedSourceName}}') source found for stream {{streamId}}. Will create new.", "noExistingStandardSourceSamePrefix": "Standard source '{{sourceName}}' not found for stream {{streamId}} (highlight prefix is same). Will create new.", "errorGettingCanvasSize": "Error getting OBS canvas size: {{message}}. Using default {{width}}x{{height}}.", "sourceNotGlobalCreating": "Source '{{sourceName}}' does not exist globally. Creating it in scene '{{sceneName}}'.", "sourceCreatedAddedToScene": "Source '{{sourceName}}' created and added to scene '{{sceneName}}'.", "sourceGlobalUpdatingWithUrl": "Source '{{sourceName}}' already exists globally. Updating its settings. URL: {{url}}", "sourceFoundAsItemInScene": "Source '{{sourceName}}' found as item in scene '{{sceneName}}'.", "sourceNotInSceneAdding": "Source '{{sourceName}}' not in scene '{{sceneName}}'. Adding it.", "cloningToMainScene": "Cloning source '{{sourceName}}' to main scene (from checkboxes) '{{sceneName}}' due to mapping rule.", "addingSourceToOtherScene": "Adding source '{{sourceName}}' as item to other selected scene '{{sceneName}}'.", "switchingProgramScene": "Switching OBS current program scene to '{{sceneName}}'.", "successfullyProcessedStream": "Successfully processed stream \"{{label}}\" ({{id}}), effective OBS source: '{{sourceName}}'.", "errorManagingStream": "Error managing stream '{{sourceName}}' (\"{{label}}\") in OBS: {{message}}", "applyTransformAndGridCalled": "applyTransformAndGrid called for {{sourceName}} in {{sceneName}}. Triggering full layout update for scene.", "triggeringLayoutUpdate": "Triggering layout update for scene '{{sceneName}}'...", "gridFallbackSourceChanged": "Grid settings for '{{sourceGridScene}}' (primary fallback candidate) changed. Triggering update for Reaction/Highlight scene '{{dependentScene}}'.", "noSceneItemsForLayout": "No scene items found in scene '{{sceneName}}' for layout update.", "applyingConfiguredLayout": "Applying '{{layoutType}}' layout to scene '{{sceneName}}'. ({{count}} items)", "unknownLayoutType": "Unknown layout type '{{layoutType}}' for scene '{{sceneName}}'. Applying default sizing.", "noSpecificLayoutApplyingDefault": "No specific layout for scene '{{sceneName}}'. Applying default source sizing ('{{sizing}}').", "errorTriggeringLayoutUpdate": "Error during layout update for scene '{{sceneName}}': {{message}}", "applyingDefaultSizing": "Applying default source sizing ('{{sizing}}') to {{count}} items in scene '{{sceneName}}'.", "errorApplyingDefaultTransformItem": "Error applying default transform to {{sourceName}} in {{sceneName}}: {{message}}", "noSourcesForGridLayout": "No VDO.Ninja camera sources to apply Grid layout in scene '{{sceneName}}'.", "applyingGridLayoutScene": "Applying Grid Layout to {{count}} VDO.Ninja camera sources in scene '{{sceneName}}'.", "applyingGridSplitScreen": "Applying 2-camera split screen grid layout to scene '{{sceneName}}'.", "reactionMainNotFoundFallbackGrid": "Main content for Reaction layout in scene '{{sceneName}}' not found/visible. Applying fallback grid of cameras.", "highlightMainNotFoundFallbackGrid": "Main content for Highlight layout in scene '{{sceneName}}' not found/visible. Applying fallback grid of cameras.", "usingGridSceneFallbackSettings": "Using grid settings from the first configured 'Grid' layout scene ('{{sceneName}}') as fallback for '{{targetScene}}'.", "usingDefaultGridFallbackSettings": "No 'Grid' layout scene found. Using default grid settings as fallback for '{{targetScene}}'.", "applyingFallbackGridToCameras": "Applying fallback Grid layout to {{count}} camera sources in scene '{{sceneName}}'.", "noCamerasForFallbackGridHidingAll": "No camera items to display in fallback grid for scene '{{sceneName}}'. All VDO sources in this scene will be hidden.", "cannotRemoveStreamNotConnected": "Cannot remove stream {{id}} from OBS: Not connected to OBS.", "triggeredRemoval": "User or auto-triggered removal of stream '{{id}}' from OBS. Base source: '{{baseName}}'.", "streamScreenSharingRemoving": "Stream {{id}} was actively screen sharing. Initiating screen share removal.", "finishedRemovingSourceItems": "Finished removing source items for stream {{id}} from specified OBS scenes.", "cannotRemoveScreenShareNotConnected": "Cannot remove screen share: Not connected to OBS.", "attemptingRemoveScreenShareSource": "Attempting to fully remove screen share source '{{sourceName}}' from OBS (all scenes and input).", "removingGlobalInput": "Removing global input '{{sourceName}}' from OBS.", "successfullyRemovedInput": "Successfully removed input '{{sourceName}}'.", "errorScreenShareRemovalProcess": "Error during screen share removal process for '{{sourceName}}': {{message}}", "removingSourceItemFromScene": "Removing source item '{{sourceName}}' (ID: {{itemId}}) from scene '{{sceneName}}'.", "errorTryingRemoveSourceItem": "Error trying to remove source item '{{sourceName}}' from scene '{{sceneName}}': {{message}}", "codecChangedUpdatingSources": "Codec changed to: {{codec}}. Updating OBS sources...", "codecChangedNotConnected": "OBS is not connected. Sources will not be updated with the new codec until reconnection and a new action.", "sourceUpdatedWithUrlAndCss": "Source '{{sourceName}}' updated with URL: {{url}} and custom CSS.", "screenShareSourceUpdatedWithUrlAndCss": "Screen share source '{{sourceName}}' updated with URL: {{url}} and custom CSS.", "codecUpdateComplete": "Codec update for OBS sources complete.", "codecCssUpdateComplete": "Codec/CSS update for OBS sources complete.", "inputNameChanged": "OBS Event: Input name changed from '{{oldName}}' to '{{newName}}'. Checking relevant scenes for layout updates.", "foundPreExistingHighlight": "Found pre-existing highlighted source in OBS: '{{sourceName}}'. Setting active highlight to stream ID: {{streamId}}.", "errorCheckingMainContentEnabled": "Error checking if main content {{sourceName}} is enabled: {{message}}" } } }