├── .DS_Store ├── .gitignore ├── README.md ├── assets └── icons │ ├── mac │ └── icon.icns │ ├── png │ └── 1024x1024.png │ └── win │ └── icon.ico ├── package-lock.json ├── package.json └── src ├── README.html ├── default.txt ├── defaultOriginal.txt ├── extraResources └── icon.png ├── index.css ├── index.html ├── index.js └── osctester.html /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jshea2/OSC-for-OBS/d3ca802d830b8a7b083ed542ad101b6207142c69/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | release-builds 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OSC for OBS 2 | #### Control and listen to [OBS](https://obsproject.com/) via OSC protocol 3 | 4 | ![OSCforOBS copySmall](https://user-images.githubusercontent.com/70780576/109406368-3c793e00-792d-11eb-90f0-bca884b79e80.png) 5 | 6 | ## Setup 7 | *Requires: [obs-websocket plugin](https://github.com/Palakis/obs-websocket/releases) v5.0 and above (This is included in OBS v28)* 8 | 9 | * NOTE: *If you're using obs-websocket v4.9 and lower download [*OSC for OBS (v2.7.1)*](https://github.com/jshea2/OBSosc/releases)* 10 | 11 | ## [Download OSC for OBS (v3.0) Now](https://github.com/jshea2/OSC-for-OBS/releases/tag/v3.0) 12 | 13 | Screen Shot 2021-11-21 at 4 17 23 PM 14 | 15 | 16 | - [Download *OSC for OBS*](https://github.com/jshea2/OBSosc/releases) 17 | 18 | -Make sure you place the application in a folder with permissions to read and write: 19 | - If on Mac: Place *OSC for OBS* in Applications folder 20 | - If on PC: Place *OSC for OBS* in a root folder such as C:/ 21 | - If you don't you might get [this error](https://github.com/jshea2/OSC-for-OBS/blob/master/README.md#troubleshooting) on close 22 | - Configure the input fields 23 | - Successfully "Connect" 24 | - The window will open a "DevTools" window 25 | - Make sure it's on the "Console" tab, not "Elements" 26 | - In the "Console" tab it will log IP and Port info, the number of available scenes, and a list of all scenes with numbers 27 | - This can be used as an index argument in your **/scene** OSC message 28 | - To save a config file with "Save As" please "Connect" first 29 | - When you close *OSC for OBS* it will save your configuration for when you open next 30 | - There is an option in 'File > Automatically Connect on Startup' that if enabled will automatically connect on opening *OSC for OBS* 31 | 32 | ## Application OSC -> OSC for OBS 33 | 34 | - In your OSC Application (QLab for example) patch your targeted OSC to match the "OSC IN" inputs on OSC for OBS. 35 | - default is... IP: `127.0.0.1`, Port: `3333` 36 | - **DO NOT** Use UNDERSCORES in OBS. Only use them in your OSC Address. So, If your OBS Scene or Source name contains a SPACE, then in your OSC message replace the SPACE with an UNDERSCORE. 37 | - *Example:* if OBS Scene name is `Scene 1`, then your OSC address should use `Scene_1`. 38 | - *Note:* If OBS names contain UNDERSCORES this will not work. 39 | - OSC for OBS can be used for any application that transmits and/or receives unbundled OSC. 40 | - This was designed with QLab, so the example show file of all the OSC commands is available in Assets on the Github or in the downloaded folder: "Qlab OBSosc Examples.qlab4" 41 | - **Note:** Sending OSC Commands requires a paid version of Qlab. 42 | 43 | # 44 | # OSC Command List: 45 | 46 | *Note: a space in the notated syntax indicates the separation between address and argument's* 47 | 48 | *Example:* `/scene "Scene 1"` would be... 49 | 50 | | address | argument 1 | argument 2 | argument ect... | 51 | |---------|------------|------------|-----------------| 52 | | /scene | Scene 1 | | | 53 | 54 | # 55 | ## **- Trigger Scenes: -** 56 | # 57 | 58 | ### by Name in Argument 59 | **`/scene [scene name]`** 60 | - activate a scene by name in the OSC argument. 61 | - *Example:* **`/scene Wide`** will activate a scene named "Wide" in OBS. 62 | - *Note:* SPACES will work ok for this format, so `/scene Webcam 1` is valid syntax 63 | - In QLab you can use quotations to wrap into a single argument: `/scene "Webcam 1"` 64 | 65 | ### by Index as Scene 66 | **`/scene [index number]`** 67 | - activate a scene with the index associated from the logged list. 68 | - *Example:* `/scene 1` will activate the first scene in OBS. 69 | - *Note:* Floats will be rounded down. 70 | - *Example:* `/scene 1.9` = `/scene 1` 71 | 72 | ### by Name in Address 73 | **`/scene/[scene_name]`** 74 | - activate a scene by name in the OSC address. 75 | - *Example:* `/scene/Wide` will activate a scene named "Wide" in OBS. 76 | - *Note:* If a scene name contains a SPACE, replace with "_", so if OBS has a scene "Webcam 1" make OSC message: `/scene/Webcam_1` 77 | 78 | ### by TouchOSC 79 | **`/scene/[scene_name]`** 80 | Value `0` to `1` 81 | - activates a scene with name in address and a value of 1 with TouchOSC 82 | - *Example:* `/scene/Wide` and a value of `0` to `1` will activate the scene "Wide" in OBS 83 | - *Note:* If a scene name contains a SPACE, replace with "_", so if OBS has a scene "Webcam 1" make OSC message: `/scene/Webcam_1` 84 | - See "OBS -> Application" for feedback of active scenes 85 | 86 | ### Next Scene 87 | **`/go`** 88 | - activates the next scene in the list, if this is triggered on the last scene it will go to the first scene 89 | 90 | ### Previous Scene 91 | **`/back`** 92 | - activates the previous scene in the list, if this is triggered on the first scene it will go to the last scene 93 | 94 | # 95 | ## **- Set Transition Type and Duration: -** 96 | # 97 | **`/[type]/transition [duration]`** 98 | - sets the desired transition and duration(in milliseconds). 99 | - *Example:* `/Cut/transition` will change the current Scene Transition to "Cut". `/Fade/transition 5000` will set the current Scene Transition to "Fade" with a "5000" millisecond duration (5 seconds). 100 | - *Note:* `[type]` must be identical to Transition Type name in OBS: 101 | - `Fade`, `Cut`, `Stinger`, `Fade_to_Color`, `Luma_Wipe`, `Slide`, `Swipe`, and Custom Transition Names. This also works with the `Move` [plugin](https://obsproject.com/forum/resources/move-transition.913/). 102 | - If you do not set a duration in the 1st argument it will keep the current duration and log what it is. 103 | # 104 | ## **- Set Source Visibility On/Off: -** 105 | # 106 | **`/[scene_name]/[source_name]/visible [0, off, false or 1, on, true]`** 107 | - turn on or off the source's visibility. 108 | - *Example:* `/Webcam_1/Audio_Input_Capture/visible 0` will turn OFF "Audio Input Capture" source in OBS from scene "Webcam 1". Where as `/Webcam_1/Text_1/visible 1` will turn it ON. 109 | - *NOTE:* If a scene name or source name contains a SPACE, replace with "_", so if OBS has a scene "Webcam 1" and a source name of "Text 1" make OSC message the example from above. 110 | # 111 | ## **- Set Filter Visibility On/Off: -** 112 | # 113 | **`/[scene_name]/[source_name]/filterVisibility [0, off, false or 1, on, true]`** 114 | - turn on or off the source's filter visibility. 115 | - *Example:* `/Video_Input/Color_Correction/filterVisibiltity 0` will turn OFF filter named "Color Correction" in OBS from source "Video Input". Where as `/Video_Input/Color_Correction/filterVisibility 1` will turn it ON. 116 | - *Note:* If a filter name or source name contains a SPACE, replace with "_", so if OBS has a filter "Color Correction" and a source name of "Video Input" make OSC message the example from above. 117 | 118 | # 119 | ## **- Set Text (for FreeType 2 ONLY): -** 120 | # 121 | **`/[source_name]/setText [text content] [size(optional)] [font (optional)]`** 122 | - change text content, size, and font. 123 | - *Note:* Size and Font are optional 124 | - *Note:* This will not work with 'GDI+', so use 'FreeType 2' instead. On PC you can find it in 'Add > Depricated' 125 | - *Example:* `/text_1/setText "Hello World."` will change the text contents of source "text 1" to "Hello World." 126 | - *Example 2:* `/text_1/setText "Hello World." 150 Arial` will change the same as above and change the size to 150 pt and Font to 'Arial' 127 | 128 | # 129 | ## **- Set Opacity: -** 130 | # 131 | **`/[source_name]/[color_correction_filter_name]/opacity [0 thru 1]`** 132 | - adjust the Opacity of a source via the "Color Correction" filter in OBS of the current scene. 133 | - *Example:* `/Text_1/Color_Correction/opacity 0.5` will make "Text 1" half transparent. 134 | - *Note:* If a source name or filter name contains a SPACE, replace with "_", so if OBS has a source "Text 1" and filter "Color Correction" make OSC message the example from above) 135 | # 136 | ## **- Set "Color Correction" Filter Properties: -** 137 | # 138 | ### Gamma 139 | **`/[source_name]/[color_correction_filter_name]/gamma [-3 thru 3]`** 140 | - Adjust the Gamma of a source via the "Color Correction" filter in OBS of the current scene. 141 | - `0` is the default vaule 142 | - *Example:* `/Text_1/Color_Correction/gamma 3` will make "Text 1" gamma at highest setting. 143 | - *Note:* If a source name or filter name contains a SPACE, replace with "_", so if OBS has a source "Text 1" and filter "Color Correction" make OSC message the example from above) 144 | 145 | ### Contrast 146 | **`/[source_name]/[color_correction_filter_name]/contrast [-2 thru 2]`** 147 | - Adjust the Contrast of a source via the "Color Correction" filter in OBS of the current scene. 148 | - `0` is the default vaule 149 | - *Example:* `/Text_1/Color_Correction/contrast 2` will make "Text 1" contrast at highest setting. 150 | - *Note:* If a source name or filter name contains a SPACE, replace with "_", so if OBS has a source "Text 1" and filter "Color Correction" make OSC message the example from above) 151 | 152 | ### Brightness 153 | **`/[source_name]/[color_correction_filter_name]/brightness [-1 thru 1]`** 154 | - Adjust the Brightness of a source via the "Color Correction" filter in OBS of the current scene. 155 | - `0` is the default vaule 156 | - *Example:* `/Text_1/Color_Correction/brightness 1` will make "Text 1" brightness at highest setting. 157 | - *Note:* If a source name or filter name contains a SPACE, replace with "_", so if OBS has a source "Text 1" and filter "Color Correction" make OSC message the example from above) 158 | 159 | 160 | ### Saturation 161 | **`/[source_name]/[color_correction_filter_name]/saturation [-1 thru 5]`** 162 | - Adjust the Contrast of a source via the "Color Correction" filter in OBS of the current scene. 163 | - `0` is the default vaule 164 | - *Example:* `/Text_1/Color_Correction/contrast 5` will make "Text 1" saturation at highest setting. 165 | - *Note:* If a source name or filter name contains a SPACE, replace with "_", so if OBS has a source "Text 1" and filter "Color Correction" make OSC message the example from above) 166 | 167 | ### Hue Shift 168 | **`/[source_name]/[color_correction_filter_name]/hueShift [-180 thru 180]`** 169 | - Adjust the Contrast of a source via the "Color Correction" filter in OBS of the current scene. 170 | - `0` is the default vaule 171 | - *Example:* `/Text_1/Color_Correction/contrast 180` will make "Text 1" hue shift at highest setting. 172 | - *Note:* If a source name or filter name contains a SPACE, replace with "_", so if OBS has a source "Text 1" and filter "Color Correction" make OSC message the example from above) 173 | 174 | # 175 | ## **- Translate Source's Position: -** 176 | # 177 | **`/[scene_name]/[source_name]/position [x] [y]`** 178 | - change sources position by x and y values. This also changes the alignment to center the bounding box to the x and y values. 179 | - *Example:* `/Webcam_1/Text_1/position 0 0` this changes the source to center of the screen, as long as the alignment is cewnter. See "Qlab OSC Examples" file for how to automate positions. 180 | - *Note:* This is only tested for a "Canvas Size" of 1920x1080 in OBS. Also, If a scene name or source name contains a SPACE, replace with "_", so if OBS has a scene "Webcam 1" and a source name of "Text 1" make OSC message the example from above. 181 | # 182 | ## **- Translate Source's Scale: -** 183 | # 184 | **`/[scene_name]/[source_name]/scale [float]`** 185 | - change the source's scale. `0` is nothing, `1` is original full size. Negative numbers will invert the source. Numbers larger than "1" will be larger than original size. 186 | - *Example:* `/Webcam_1/Text_1/scale 2` this will make the size of the "Text 1" source double the size of original. 187 | - *Note:* See "Qlab OSC Examples" file for how to automate scale. Also, If a scene name or source name contains a SPACE, replace with "_", so if OBS has a scene "Webcam 1" and a source name of "Text 1" make OSC message the example from above.) 188 | # 189 | ## **- Translate Source's Rotation: -** 190 | # 191 | **`/[scene_name]/[source_name]/rotate [float]`** 192 | - change the source's scale. `0` is nothing, `360` is full 360 rotation clockwise. Negative numbers will rotate counterclockwise. Numbers larger than "360" will be more roations. 193 | - *Example:* `/Webcam_1/Text_1/rotate 90` this will make the rotation of the "Text 1" source 90º clockwise. 194 | - *Note:* See "Qlab OSC Examples" file for how to automate rotation. Also, If a scene name or source name contains a SPACE, replace with "_", so if OBS has a scene "Webcam 1" and a source name of "Text 1" make OSC message the example from above.) 195 | # 196 | ## **- Audio Controls: -** 197 | # 198 | ### Mute 199 | **`/[source_name]/mute`** 200 | - Mute the specified source 201 | 202 | ### Unmute 203 | **`/[source_name]/unmute`** 204 | - Unmute the specified source 205 | 206 | ### Toggle Audio 207 | **`/[source_name]/audioToggle [0 or 1]`** 208 | - Unmute and Mute the specified source by toggling 209 | 210 | ### Volume 211 | **`/[source_name]/volume [0 thru 1]`** 212 | - Adjust specified source's volume. 213 | - *Example:* `/Audio_Input/volume 0.5` sets source "Audio_Input" audio to 50% volume 214 | 215 | ### Monitor Off 216 | **`/[source_name]/monitorOff`** 217 | - Sets source's audio monitor type to "Monitor Off" 218 | - *Example:* `/Audio_Input/monitorOff` sets audio source "Audio Input" to "Monitor Off". 219 | - *Note:* This does work, but you won't see a visual change on OBS unless you close "Advaced Audio" window and reopen. 220 | 221 | ### Monitor Only 222 | **`/[source_name]/monitorOnly`** 223 | - Sets source's audio monitor type to "Monitor Only" 224 | - *Example:* `/Audio_Input/monitorOnly` sets audio source "Audio Input" to "Monitor Only". 225 | - *Note:* This does work, but you won't see a visual change on OBS unless you close "Advaced Audio" window and reopen. 226 | 227 | ### Monitor and Output 228 | **`/[source_name]/monitorAndOutput`** 229 | - Sets source's audio monitor type to "Monitor and Output" 230 | - *Example:* `/Audio_Input/monitorAndOutput` sets audio source "Audio Input" to "Monitor and Output". 231 | - *Note:* This does work, but you won't see a visual change on OBS unless you close "Advaced Audio" window and reopen. 232 | 233 | # 234 | ## **- Media Controls: -** 235 | # 236 | 237 | ### Play 238 | **`/[source_name]/mediaPlay`** 239 | - Play specified "Media Source" 240 | 241 | ### Pause 242 | **`/[source_name]/mediaPause`** 243 | - Pause specified "Media Source" 244 | 245 | ### Restart 246 | **`/[source_name]/mediaRestart`** 247 | - Restart specified "Media Source" 248 | 249 | ### Stop 250 | **`/[source_name]/mediaStop`** 251 | - Stop specified "Media Source" 252 | 253 | ### Cursor 254 | **`/[source_name]/mediaCursor [int]`** 255 | - Sets the cursor position of specified "Media Source" 256 | # 257 | ## **- Browser Controls: -** 258 | # 259 | **`/[browser_source]/refreshBrowser`** 260 | - Refresh specified Browser Source in OBS 261 | 262 | # 263 | 264 | ## **- Projector Controls: -** 265 | # 266 | 267 | **`/[type]/openProjector [display_number] [sceneorsource_name]`** 268 | - Opens type of projector to specified display 269 | - `[type]` options: 270 | - `StudioProgram`, `Multiview`, `Preview`, `Scene`, `Source` 271 | - `[display_number]` is an integer 272 | - *Example:* `0` would be your main screen and `1` would be your secondary monitor 273 | - *Note:* If you use `-1` or empty(`null`), then this would make a windowed projector of specified `[type]` 274 | - `[sceneorsource_name]` is an **optional** for the 2nd argument. This can **ONLY** be used if using `Scene` or `Source` for `[type]` 275 | - This would be the name of a Source or Scene in OBS that would open a specified projector 276 | - *Examples:* 277 | - `/StudioProgram/openProjector 1` 278 | - This would open a Studio Program Feed on the secondary monitor 279 | - `/Preview/openProjector` 280 | - This would open a Preview Feed in a windowed projector 281 | - `/Scene/openProjector 0 "Scene 1"` 282 | - This would open a feed from "Scene 1" on the main monitor. 283 | - `/Multiview/openProjector -1` 284 | - This would open a Multiview Feed to a windowed projector 285 | 286 | # 287 | 288 | ## **- Studio Mode: -** 289 | # 290 | 291 | ### Set StudioMode 292 | **`/setStudioMode [0 or 1]`** 293 | - if argument is `1` then Studio Mode enables, if `0` it disables in OBS 294 | 295 | ### Enable 296 | **`/enableStudioMode`** 297 | - enable Studio Mode in OBS 298 | 299 | ### Disable 300 | **`/disableStudioMode`** 301 | - disable Studio Mode in OBS 302 | 303 | ### Toggle 304 | **`/toggleStudioMode`** 305 | - toggle Studio Mode On/Off in OBS 306 | 307 | # 308 | ## **- Trigger Preview Scenes: -** 309 | # 310 | 311 | ### by Name in Argument 312 | **`/previewScene [scene name]`** 313 | - activate a scene by name in the OSC argument. 314 | - *Example:* **`/previewScene Wide`** will activate a scene named "Wide" in the Preview in OBS. 315 | - In QLab you can use quotations to wrap into a single argument: `/scene "Webcam 1"` 316 | 317 | ### by Name in Address 318 | **`/previewScene/[scene_name]`** 319 | - activate a scene by name in the OSC address. 320 | - *Example:* `/previewScene/Wide` will activate a scene named "Wide" in the Preview in OBS. 321 | - *Note:* If a scene name contains a SPACE, replace with "_", so if OBS has a scene "Webcam 1" make OSC message: `/previewScene/Webcam_1` 322 | 323 | ### Trigger Transition to Program 324 | **`/studioTransition [type(optional)] [duration(optional)]`** 325 | - activate a Transition to Program 326 | - *Example:* `/studioTransition "Fade" 5000` will activate a transition of the Preview to Program with a Fade of 5 seconds. 327 | 328 | # 329 | ## **- Streaming and Recording: -** 330 | # 331 | 332 | ### Set Recording 333 | **`/setRecording [0 or 1]`** 334 | - start recording if argument is `1` and stop if `0` 335 | 336 | ### Start Recording 337 | **`/startRecording`** 338 | - start recording in OBS 339 | 340 | ### Stop Recording 341 | **`/stopRecording`** 342 | - stop recording in OBS 343 | 344 | ### Toggle Recording 345 | **`/toggleRecording`** 346 | - toggle the start/stop recording button in OBS 347 | 348 | ### Pause Recording 349 | **`/pauseRecording`** 350 | - pause the recording in OBS 351 | 352 | ### Resume Recording 353 | **`/resumeRecording`** 354 | - resume recording in OBS 355 | 356 | 357 | ### Set Streaming 358 | **`/setStreaming [0 or 1]`** 359 | - start streaming if argument is `1` and stop if `0` 360 | 361 | ### Start Streaming 362 | **`/startStreaming`** 363 | - start streaming in OBS 364 | 365 | ### Stop Streaming 366 | **`/stopStreaming`** 367 | - stop streaming in OBS 368 | 369 | ### Toggle Streaming 370 | **`/toggleStreaming`** 371 | - toggle the start/stop streaming button in OBS 372 | 373 | ### Set VirtualCam 374 | **`/setVirtualCam [0 or 1]`** 375 | - start VirtualCam if argument is `1` and stop if `0` 376 | 377 | ### Start VirtualCam 378 | **`/startVirtualCam`** 379 | - start VirtualCam in OBS 380 | 381 | ### Stop VirtualCam 382 | **`/stopVirtualCam`** 383 | - stop VirtualCam in OBS 384 | 385 | ### Toggle VirtualCam 386 | **`/toggleVirtualCam`** 387 | - toggle the start/stop VirtualCam button in OBS 388 | 389 | # 390 | ## **- Settings: -** 391 | # 392 | 393 | ### Set Scene Collection 394 | ***`/setSceneCollection [Scene Collection Name]`** 395 | - set the scene collection with the specified name 396 | 397 | ### Set Profile 398 | ***`/setProfile [Profile Name]`** 399 | - set the profile with the specified name 400 | 401 | # 402 | ## **- NDI and Other Outputs -** 403 | # 404 | 405 | ### List Outputs NAmes 406 | **`/listOutputs`** 407 | - Logs a list of all available outputs 408 | - *NOTE:* Use names listed for argument in start and stop commands 409 | 410 | ### Start Output 411 | **`/startOutput [name]`** 412 | - *Example:* `/startOutput "NDI Main Output"` 413 | - This will enable the "NDI Main Output" output 414 | 415 | ### Stop Output 416 | **`/stopOutput [name]`** 417 | - *Example:* `/stopOutput "NDI Main Output"` 418 | - This will disable the "NDI Main Output" output 419 | 420 | # 421 | ## **- Miscellaneous -** 422 | # 423 | 424 | ### Renaming 425 | **`/rename [current-name] [new-name]`** 426 | - rename scene or source 427 | - *Example:* `/rename "Audio" "NewAudio"` will change the source named "Audio" to "NewAudio" 428 | 429 | ### Send Closed Captions 430 | ***`/sendCC [text]`** 431 | - Send the text as embedded CEA-608 caption data 432 | - *Example:* `/sendCC "Hello world."` will send "Hello world." as an embedded closed caption 433 | 434 | 435 | ### Set Recording File Name 436 | ***`/recFileName [string]`** 437 | - sets the name of the file for when you record 438 | 439 | ### Get Source Text (FreeType2) String Repeatedly 440 | `/[text source]/getTextFreetype [1]` 441 | - Returns to the OSC Client: 442 | - `/[text_source]/text [string]` 443 | - *NOTE:* Disabling the loop is not yet implemented. Quit and reopen app to stop. 444 | - The loop returns every 500ms (half second) 445 | - ex. Possible use case would be for getting a text source that has a countdown script. 446 | 447 | ### Get Source Text (GDI) String Repeatedly 448 | `/[text source]/getTextGDI [1]` 449 | - Returns to the OSC Client: 450 | - `/[text_source]/text [string]` 451 | - *NOTE:* Disabling the loop is not yet implemented. Quit and reopen app to stop. 452 | - The loop returns every 500ms (half second) 453 | - ex. Possible use case would be for getting a text source that has a countdown script. 454 | 455 | ### Set Active Scene Item Visibility by Index 456 | -`/[index]/activeSceneItemVisibility [0 or 1]` 457 | - this uses the index of your sources to control the visibility of active scene. 458 | 459 | ### Set Specific Scene Item Visibility by Index 460 | -`/[scene_name]/[index]/activeSceneItemVisibility [0 or 1]` 461 | - this uses the index of your sources to control the visibility of a specific scene. 462 | 463 | ### Take Screenshot 464 | -`/takeScreenshot` 465 | - This saves a .png file of your current active scene and saves it to your *Documents* folder 466 | 467 | ### Trigger Hotkey 468 | -`/[modifier]/[key]/hotkey` 469 | - This triggers a hotkey from your settings. Not keyboard shortcuts. 470 | - `modifier` can be `shift` `control` and/or `alt` (order doesn't matter). `key` can be lowercase or uppercase. 471 | - ex. `/controlshift/f/hotkey` 472 | 473 | ### Open External File / URL 474 | *Might ask to allow "OSC for OBS" access* 475 | - `/openExternal [string]` 476 | - this command opens local applications, files, and/or websites with file or url path 477 | - ex. `/openExternal "https://google.com"` 478 | - opens google in default browser 479 | - ex. Mac `/openExternal "file:/System/Applications/Stocks.app"` 480 | - ex. PC `/openExternal "file://c:/windows/system32/calc.exe"` 481 | - opens 'Calculator' app 482 | - ex. Mac `/openExternal "file:/Users/joeshea/Documents/texttest1.png"` 483 | - opens 'texttest1.png' in default viewer (like Preview) 484 | 485 | ### Simulate Keypress on Front/Active Window *(BETA)* 486 | *(Java Run Time)[https://www.java.com/en/download/manual.jsp] is required for this feature. After downloading and installing restart computer.* 487 | - `/keypress [string]` 488 | - this command simulates a keypress on front window. (Use this syntax for keys)[https://github.com/garimpeiro-it/node-key-sender#list-of-key-codes] 489 | - ex. `/keypress "space"` 490 | - this simulates a space keypress on the active window 491 | - ex. `/keypress "h,i"` 492 | - this simulates a `h` and then an `i` key press to active window 493 | - ex. `/keypress "control,s"` 494 | - ex. this simulates a keycombo of `Control + S` to active window 495 | - *Note:* This feature can send multiple keypresses by using a comma `,`, but using combinations of shift, control, and alt have been buggy. Please refer to (this repository for issues)[https://github.com/garimpeiro-it/node-key-sender] 496 | 497 | 498 | 499 | ## **~ Editing Commands While Selected in OBS: ~** 500 | # 501 | *Mainly used for **[TouchOSC](https://hexler.net/products/touchosc)*** 502 | - *Layout file included in downloaded folder or in Assets on Github* 503 | - Download and install [TouchOSC Editor](https://hexler.net/products/touchosc) to open "Layout" and import to device 504 | - NodeOBS-ZoomOSC-Autocropper DEMO Video 506 | 507 | 508 | ### Add Scene Item 509 | **`/addSceneItem [scene_item]`** 510 | - This adds a specified "Scene Item" to the **selected** OBS Scene 511 | - *Note:* You can find a list of all your "Scene Items" by clicking on the toolbar: 512 | - *Scripts > Log All Available Scene Items (Sources)* 513 | 514 | ### Change Transition Override Type 515 | **`/transOverrideType/[type]`** 516 | - Set Transition Override Type for **selected** OBS Scene. 517 | - *Note:* `[type]` must be identical to Transition Override Type name in OBS: 518 | - `Fade`, `Cut`, `Stinger`, `Fade_to_Color`, `Luma_Wipe`, `Slide`, `Swipe`, and a `Custom Named` Transition. This also works with the `Move` [plugin](https://obsproject.com/forum/resources/move-transition.913/). 519 | - *Example:* `/transOverrideType/Fade_to_Color` will set the Transition Override of the **selected** Scene in OBS to "Fade to Color" 520 | 521 | ### Change Transition Override Duration 522 | **`/transOverrideDuration [duration]`** 523 | - Set Transition Override Duration for **selected** OBS Scene. 524 | - *Note:* Duration is in milliseconds (1000ms = 1sec) 525 | - *Example:* `/transOverrideDuration 2500` will set the Transition Override duration for the **selected** Scene in OBS to 2.5 seconds. 526 | 527 | ### Change Scale 528 | **`/size [float]`** 529 | - Translate scale of **selected** OBS Source. 530 | - `[float]` can take a value with decimal. 531 | - *Example:* `0` is nothing, `0.5` is half the size, `1` is original full size. Negative numbers will invert the source. Numbers more than "1" will be larger than original size. 532 | 533 | ### Change Position X and Y 534 | **`/move [x] [y]`** 535 | - Translate X and Y position from **selected** OBS Source. 536 | 537 | ### Change Position X 538 | **`/movex [x]`** 539 | - Translate X position from **selected** OBS Source. 540 | 541 | ### Change Position Y 542 | **`/movey [y]`** 543 | - Translate Y position from **selected** OBS Source. 544 | 545 | ### Change Alignment 546 | **`/align [int]`** 547 | - Set alignment for **selected** source. 548 | - *Example:* `/align 0` will center the **selected** OBS Source 549 | - `[int]` would be an integer value from this guide: 550 | 551 | | 5 | 4 | 6 | 552 | |----|---|---| 553 | | 1 | 0 | 2 | 554 | | 9 | 8 | 10 | 555 | 556 | ### Change Rotation 557 | **`/spin [int]`** 558 | - Set rotation for **selected** source 559 | 560 | ### Change Bounds to Fit to screen 561 | **`/fitToScreen`** 562 | - Set bounds to Fit to Screen 563 | 564 | ### Get Source Settings 565 | **`/getSourceSettings`** 566 | - Logs source properties and settings 567 | - Mainly used for development and debugging 568 | 569 | # 570 | 571 | # OBS -> Application: 572 | 573 | 574 | - Configure "OSC OUT" to target Application 575 | 576 | - When *Enabled* his allows OBS to send OSC to other applications when a scene is activated. 577 | 578 | - To **use/enable** this function, change the toggle button to ON 579 | Configure the prefix and suffix for how you want your OSC application to receive an OSC string from OBS 580 | 581 | - *Example:* In OBS when a Scene named "Wide Shot" is activated, Qlab recieves an OSC message "**/cue/Wide_Shot/start**") 582 | 583 | ## TouchOSC Feedback: 584 | 585 | When *Checked* this sends OSC messages back to TouchOSC for dynamic control. 586 | 587 | - OSC Out IP must be the IP of the device using TouchOSC 588 | - OSC Out Slider must be Enabled 589 | 590 | 591 | ### **Active Scene Feedback** 592 | 593 | Use the following setup for `/scene` command for a *push button* in TouchOSC Editor: 594 | 595 | Screen Shot 2021-04-13 at 4 44 31 PM 596 | 597 | - make sure "Local Feedback" is checked 598 | 599 | In OSC for OBS use the following setup: 600 | 601 | ### **Fader Volume Feedback** 602 | 603 | Use the following setup for `/[source]/volume` command for a *fader* in TouchOSC Editor: 604 | 605 | Screen Shot 2021-04-19 at 8 33 29 PM 606 | 607 | ### **Muting/UnMuting Toggle Feedback** 608 | 609 | Use the following setup for `/[source]/audioToggle` command for a *toggle button* in TouchOSC Editor: 610 | 611 | Screen Shot 2021-04-19 at 8 33 18 PM 612 | 613 | ### **Media Playback Feedback** 614 | 615 | #### **Play** 616 | - When a media is played in OBS the following OSC is sent `/[source]/mediaPlay 1`, `/[source]/mediaPause 0`, and `/[source]/mediaStop 0` 617 | 618 | #### **Pause** 619 | - When a media is paused in OBS the following OSC is sent `/[source]/mediaPause 1`, `/[source]/mediaPlay 0`, and `/[source]/mediaStop 0` 620 | 621 | #### **Stop** 622 | - When a media is stopped in OBS the following OSC is sent `/[source]/mediaStop 1`, `/[source]/mediaPause 0`, and `/[source]/mediaPlay 0` 623 | 624 | ### **Source Visibility** 625 | 626 | - When a source visibility is changed in OBS the following OSC is sent `/[scene]/[source]/visible [0 or 1]` 627 | - (`0` if off and `1` if on) 628 | 629 | ### **Transitions** 630 | 631 | #### **Transition Type** 632 | - When a transition is triggered in OBS the following OSC is sent `/transitionType [type]` 633 | 634 | #### **Transition Duration** 635 | - When a transition is triggered in OBS the following OSC is sent `/transitionDuration [int]` 636 | 637 | 638 | ### **Stats Label Feedback** 639 | 640 | ***When Streaming/Recording:*** 641 | - `/fps` 642 | - Returns frames per second `[int] fps` 643 | - `/streaming` 644 | - Returns `1` if stream started, `0` if stopped 645 | - `/recording` 646 | - Returns `1` if recording started, `0` if stopped 647 | - `/streamTime` 648 | - Returns elapsed stream time `HH:MM:SS` 649 | - `/cpuUsage` 650 | - Returns cpu usage `[int] cpu usage` 651 | - `/freeDiskSpace` 652 | - Returns free disk space `[int] free disk space` 653 | - `/averageFrameRate` 654 | - Returns average frame rate `[int] avg frame rate` 655 | - `/memoryUsage` 656 | - Returns memory usage `[int] memory usage` 657 | - `/kbpsEncoder` 658 | - Returns encoder data `[int] kbps` 659 | 660 | Screen Shot 2021-04-19 at 8 33 18 PM 661 | 662 | 663 | # Troubleshooting 664 | ### OSC Tester 665 | Screen Shot 2021-03-01 at 5 44 26 PM 666 | 667 | - *File > OSC Tester* 668 | 669 | - This window can help with testing OSC commands without leaving the app 670 | 671 | ---------------------------------------------------------- 672 | 673 | Screen Shot 2021-03-01 at 5 44 26 PM 674 | 675 | - If you're getting this error on Mac after you close the app make sure you drag and drop *OSC for OBS* from *Downloads* to *Applications* 676 | 677 | # Editing The Code Yourself 678 | *OSC for OBS* is an Electron application that is powered by HTML, CSS, Javascript, and Node.js. You can access and edit any of these files by doing the following: 679 | 680 | ### Mac 681 | - Right-click on application > Show Package Contents > Contents > Resources > app > src. 682 | - 'index.js' is where majority of the code is located 683 | 684 | ### PC 685 | - In the downloaded folder > Resources > app > src 686 | - 'index.js' is where majority of the code is located 687 | 688 | ## Acknowledgement 689 | ### This was inspired by [ObSC](https://github.com/CarloCattano/ObSC?fbclid=IwAR1zGJ_iFVq9o887hWw71lWaGZKqdAP0mMaVFyXau9x0sDgs-5KjS9HNLrk) 690 | # 691 | ## Support The Project ❤️ 692 | ### If OSC for OBS helped you, consider helping the project by making a one time donation via **[PayPal](http://paypal.me/joeshea2)** 693 | 694 | # 695 | ## Join the Discord Community 696 | 697 | 698 | chat on Discord 700 | 701 | 702 | ## [Download *OSC for OBS (v3.1)* Now](https://github.com/jshea2/OSC-for-OBS/releases/tag/v3.0) 703 | -------------------------------------------------------------------------------- /assets/icons/mac/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jshea2/OSC-for-OBS/d3ca802d830b8a7b083ed542ad101b6207142c69/assets/icons/mac/icon.icns -------------------------------------------------------------------------------- /assets/icons/png/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jshea2/OSC-for-OBS/d3ca802d830b8a7b083ed542ad101b6207142c69/assets/icons/png/1024x1024.png -------------------------------------------------------------------------------- /assets/icons/win/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jshea2/OSC-for-OBS/d3ca802d830b8a7b083ed542ad101b6207142c69/assets/icons/win/icon.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsosc-app", 3 | "productName": "OSC for OBS", 4 | "version": "3.1.3", 5 | "description": "Control OBS via OSC", 6 | "main": "src/index.js", 7 | "scripts": { 8 | "start": "electron-forge start", 9 | "build": "electron-packager . Node-OBSosc --ignore=node_modules/electron-packager && cp electron.icns Node-OBSosc-darwin-x64/Node-OBSosc.app/Contents/Resources/electron.icns", 10 | "package": "electron-forge package", 11 | "make": "electron-forge make", 12 | "publish": "electron-forge publish", 13 | "lint": "echo \"No linting configured\"", 14 | "build-installer": "electron-builder", 15 | "make-mac": "npx @electron-forge/cli make --platform darwin", 16 | "package-mac": "electron-packager . OSC-for-OBS --ignore=node_modules/electron-packager --ignore=release_builds --overwrite --platform=darwin --arch=x64 --icon=assets/icons/mac/icon.icns --prune=true --extra-resource=./src/extraResources/icon.png --out=release-builds", 17 | "package-win": "electron-packager . OSC-for-OBS --ignore=node_modules/electron-packager --overwrite --platform=win32 --arch=ia32 --icon=assets/icons/win/icon.ico --prune=true --extra-resource=./src/extraResources/icon.png --out=release-builds --version-string.FileDescription=CE --version-string.ProductName=\"OSC-for-OBS\"", 18 | "package-linux": "electron-packager . OSC-for-OBS --ignore=node_modules/electron-packager --ignore=release_builds --ignore=release-builds --overwrite --asar=true --platform=linux --arch=x64 --icon=assets/icons/png/1024x1024.png --prune=true --out=release-builds" 19 | }, 20 | "build": { 21 | "appId": "my-app", 22 | "mac": { 23 | "icon": "src/Node-OBSosc.ico" 24 | }, 25 | "win": { 26 | "icon": "src/Node-OBSosc.ico" 27 | } 28 | }, 29 | "keywords": [], 30 | "author": { 31 | "name": "Joe Shea", 32 | "email": "jshea2@users.noreply.github.com" 33 | }, 34 | "license": "MIT", 35 | "config": { 36 | "forge": { 37 | "packagerConfig": { 38 | "icon": "./src/Node-OBSosc.ico" 39 | }, 40 | "makers": [ 41 | { 42 | "name": "@electron-forge/maker-base", 43 | "config": { 44 | "name": "OBSosc" 45 | } 46 | }, 47 | { 48 | "name": "@electron-forge/maker-base", 49 | "platforms": [ 50 | "win32" 51 | ] 52 | }, 53 | { 54 | "name": "@electron-forge/maker-base", 55 | "config": {} 56 | }, 57 | { 58 | "name": "@electron-forge/maker-base", 59 | "config": {} 60 | } 61 | ] 62 | } 63 | }, 64 | "dependencies": { 65 | "electron-pdf-window": "^1.0.12", 66 | "electron-squirrel-startup": "^1.0.0", 67 | "electronmon": "^2.0.2", 68 | "node-keys-simulator": "^1.0.0", 69 | "node-osc": "6.0.0-rc.2", 70 | "obs-websocket-js": "^5.0.1" 71 | }, 72 | "devDependencies": { 73 | "create-electron-app": "^1.0.0", 74 | "electron": "^11.0.3", 75 | "electron-packager": "^15.1.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/README.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OBSosc API 5 | 6 | 7 | 228 | 229 | 304 | 305 | 356 | 357 | 358 | 359 | 360 | 368 |

OBSosc (v2.0)

369 |

Setup

370 |

Download and Install: obs-websocket plugin 371 |

386 |

Application OSC -> OBSosc

387 | 403 |

404 |

OSC Command List:

405 |

- Trigger Scenes: -

406 |

407 |

by Name in Argument

408 |

/scene [scene name]

409 | 418 |

by Index as Scene

419 |

/scene [index number]

420 | 432 |

by Name in Address

433 |

/scene/[scene_name]

434 | 442 |

Next Scene

443 |

/go

444 | 447 |

Previous Scene

448 |

/back

449 | 452 |

453 |

- Set Transition Type and Duration: -

454 |

455 |

/[type]/transition [duration]

456 | 472 |

473 |

- Set Source Visibility On/Off: -

474 |

475 |

/[scene_name]/[source_name]/visible [0 or 1]

476 | 484 |

485 |

- Set Filter Visibility On/Off: -

486 |

487 |

/[scene_name]/[source_name]/filterVisibility [0 or 1]

488 | 496 |

497 |

- Set Opacity: -

498 |

499 |

/[source_name]/[color_correction_filter_name]/opacity [0 thru 100]

500 | 508 |

509 |

- Set "Color Correction" Filter Properties: -

510 |

511 |

Gamma

512 |

/[source_name]/[color_correction_filter_name]/gamma [-3 thru 3]

513 | 522 |

Contrast

523 |

/[source_name]/[color_correction_filter_name]/contrast [-2 thru 2]

524 | 533 |

Brightness

534 |

/[source_name]/[color_correction_filter_name]/brightness [-1 thru 1]

535 | 544 |

Saturation

545 |

/[source_name]/[color_correction_filter_name]/saturation [-1 thru 5]

546 | 555 |

Hue Shift

556 |

/[source_name]/[color_correction_filter_name]/hueShift [-180 thru 180]

557 | 566 |

567 |

- Translate Source's Position: -

568 |

569 |

/[scene_name]/[source_name]/position [x] [y]

570 | 578 |

579 |

- Translate Source's Scale: -

580 |

581 |

/[scene_name]/[source_name]/scale [float]

582 | 590 |

591 |

- Translate Source's Rotation: -

592 |

593 |

/[scene_name]/[source_name]/rotate [float]

594 | 602 |

603 |

- Audio Controls: -

604 |

605 |

Mute

606 |

/[source_name]/mute

607 | 610 |

Unmute

611 |

/[source_name]/unmute

612 | 615 |

Volume

616 |

/[source_name]/volume [0 thru 1]

617 | 624 |

Monitor Off

625 |

/[source_name]/monitorOff

626 | 634 |

Monitor Only

635 |

/[source_name]/monitorOnly

636 | 644 |

Monitor and Output

645 |

/[source_name]/monitorAndOutput

646 | 654 |

655 |

- Media Controls: -

656 |

657 |

Play

658 |

/[source_name]/mediaPlay

659 | 662 |

Pause

663 |

/[source_name]/mediaPause

664 | 667 |

Restart

668 |

/[source_name]/mediaRestart

669 | 672 |

Stop

673 |

/[source_name]/mediaStop

674 | 677 |

678 |

- Browser Controls: -

679 |

680 |

/[browser_source]/refreshBrowser

681 | 684 |

685 |

- Projector Controls: -

686 |

687 |

/[type]/openProjector [display_number] [sceneorsource_name]

688 | 731 |

732 |

- Studio Mode: -

733 |

734 |

Enable

735 |

/enableStudioMode

736 | 739 |

Disable

740 |

/disableStudioMode

741 | 744 |

Toggle

745 |

/toggleStudioMode

746 | 749 |

750 |

- Streaming and Recording: -

751 |

752 |

Start Recording

753 |

/startRecording

754 | 757 |

Stop Recording

758 |

/stopRecording

759 | 762 |

Toggle Recording

763 |

/toggleRecording

764 | 767 |

Pause Recording

768 |

/pauseRecording

769 | 772 |

Resume Recording

773 |

/resumeRecording

774 | 777 |

Start Streaming

778 |

/startStreaming

779 | 782 |

Stop Streaming

783 |

/stopStreaming

784 | 787 |

Toggle Streaming

788 |

/toggleStreaming

789 | 792 |

793 |

~ Editing Commands While Selected in OBS: ~

794 |

795 |

Mainly used for TouchOSC

796 | 800 |

Add Scene Item

801 |

/addSceneItem [scene_item]

802 | 813 |

Change Transition Override Type

814 |

/transOverrideType/[type]

815 | 824 |

Change Transition Override Duration

825 |

/transOverrideDuration [duration]

826 | 831 |

Change Scale

832 |

/size [float]

833 | 844 |

Change Position X and Y

845 |

/move [x] [y]

846 | 849 |

Change Position X

850 |

/movex [x]

851 | 854 |

Change Position Y

855 |

/movey [y]

856 | 859 |

Change Alignment

860 |

/align [int]

861 | 894 |

895 |

OBS -> Application:

896 | 912 | 913 | 914 | -------------------------------------------------------------------------------- /src/default.txt: -------------------------------------------------------------------------------- 1 | OBSosc Config File: 2 | 127.0.0.1 3 | 4455 4 | secret 5 | 127.0.0.1 6 | 3333 7 | false 8 | 127.0.0.1 9 | 53000 10 | /cue/ 11 | /start 12 | false 13 | undefined -------------------------------------------------------------------------------- /src/defaultOriginal.txt: -------------------------------------------------------------------------------- 1 | OBSosc Config File: 2 | 127.0.0.1 3 | 4455 4 | secret 5 | 127.0.0.1 6 | 3333 7 | false 8 | 127.0.0.1 9 | 53000 10 | /cue/ 11 | /start 12 | false 13 | false -------------------------------------------------------------------------------- /src/extraResources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jshea2/OSC-for-OBS/d3ca802d830b8a7b083ed542ad101b6207142c69/src/extraResources/icon.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | width: 100vw; 3 | height: 100vh; 4 | } 5 | body { 6 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; 7 | margin: auto; 8 | width: 100%; 9 | height: 100%; 10 | padding: 3%; 11 | overflow: hidden; 12 | background: rgb(25,27,37); 13 | display: flex; 14 | justify-content: center; 15 | color: white; 16 | } 17 | .box { 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | flex-direction: column; 22 | font-size: 14px; 23 | line-height: 0.95; 24 | padding: 0 30px 0 30px; 25 | } 26 | .label { 27 | font-size: 50px; 28 | font-weight: 200; 29 | color: rgb(161, 184, 255); 30 | } 31 | .label2 { 32 | font-size: 20px; 33 | font-weight: 200; 34 | color: rgb(185, 202, 255); 35 | } 36 | .switch { 37 | position: relative; 38 | display: inline-block; 39 | width: 90px; 40 | height: 27px; 41 | } 42 | 43 | .switch input {display:none;} 44 | 45 | .slider { 46 | position: absolute; 47 | cursor: pointer; 48 | top: 0; 49 | left: 0; 50 | right: 0; 51 | bottom: 0; 52 | background-color: #d15b5b; 53 | -webkit-transition: .4s; 54 | transition: .4s; 55 | } 56 | 57 | .slider:before { 58 | position: absolute; 59 | content: ""; 60 | height: 17px; 61 | width: 17px; 62 | left: 4px; 63 | bottom: 4px; 64 | background-color: white; 65 | -webkit-transition: .4s; 66 | transition: .4s; 67 | } 68 | 69 | input:checked + .slider { 70 | background-color: #6cda73; 71 | } 72 | 73 | input:focus + .slider { 74 | box-shadow: 0 0 1px #2196F3; 75 | } 76 | 77 | input:checked + .slider:before { 78 | -webkit-transform: translateX(66px); 79 | -ms-transform: translateX(66px); 80 | transform: translateX(66px); 81 | } 82 | 83 | /*------ ADDED CSS ---------*/ 84 | .on 85 | { 86 | display: none; 87 | } 88 | 89 | .on, .off 90 | { 91 | color: white; 92 | position: absolute; 93 | transform: translate(-50%,-50%); 94 | top: 50%; 95 | left: 50%; 96 | font-size: 10px; 97 | font-family: Verdana, sans-serif; 98 | } 99 | 100 | input:checked+ .slider .on 101 | {display: block;} 102 | 103 | input:checked + .slider .off 104 | {display: none;} 105 | 106 | /*--------- END --------*/ 107 | 108 | /* Rounded sliders */ 109 | .slider.round { 110 | border-radius: 34px; 111 | } 112 | 113 | .slider.round:before { 114 | border-radius: 50%;} 115 | 116 | .button { 117 | display: inline-block; 118 | border-radius: 4px; 119 | background-color: rgb(41,77,183); 120 | border: none; 121 | color: #ffffff; 122 | text-align: center; 123 | font-size: 15px; 124 | padding: 20px; 125 | width: 150px; 126 | transition: all 0.5s; 127 | cursor: pointer; 128 | margin: 3px; 129 | } 130 | 131 | .button span { 132 | cursor: pointer; 133 | display: inline-block; 134 | position: relative; 135 | transition: 0.5s; 136 | } 137 | 138 | .button span:after { 139 | content: '\00bb'; 140 | position: absolute; 141 | opacity: 0; 142 | top: 0; 143 | right: -20px; 144 | transition: 0.5s; 145 | } 146 | 147 | .button:hover span { 148 | padding-right: 25px; 149 | } 150 | 151 | .button:hover span:after { 152 | opacity: 1; 153 | right: 0; 154 | } 155 | 156 | .center { 157 | display: block; 158 | margin-left: auto; 159 | margin-right: auto; 160 | width: 50%; 161 | } 162 | 163 | .dotGrey { 164 | height: 20px; 165 | width: 20px; 166 | background-color: #bbb; 167 | border-radius: 50%; 168 | position:absolute; 169 | bottom:0; 170 | right:0; 171 | } 172 | 173 | .dotGreen { 174 | height: 20px; 175 | width: 20px; 176 | background-color: rgb(0, 255, 0); 177 | border-radius: 50%; 178 | position:absolute; 179 | bottom:0; 180 | right:0; 181 | } 182 | 183 | .dotRed { 184 | height: 20px; 185 | width: 20px; 186 | background-color: rgb(255, 0, 0); 187 | border-radius: 50%; 188 | position:absolute; 189 | bottom:0; 190 | right:0; 191 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OSC for OBS 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 |
15 | OBS Websocket 16 |
17 | IP: 18 | 19 |
20 | Port: 21 | 22 |
23 | Password: 24 | 25 |
26 | OSC IN 27 |
28 | IP: 29 | 30 |
31 | Port: 32 | 33 |
34 | OSC OUT 35 |
36 | IP: 37 | 38 |
39 | Port: 40 | 41 |
42 | 43 | On OBS Active Scene Output OSC: 44 |
45 |
46 | 55 |
56 |

57 | 58 | [SceneName] 59 | 60 | 61 |
62 | TouchOSC Feedback 63 | 64 |

65 | 66 | 67 |
68 | 69 |
70 |
71 | 72 |
73 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | //OSC for OBS 2 | //by Joe Shea 3 | 4 | 5 | const { app, BrowserWindow, ipcRenderer, ipcMain, Menu, dialog } = require('electron'); 6 | const fs = require('fs') 7 | const path = require('path'); 8 | //const os = require('os-utils') 9 | const {default: OBSWebSocket} = require('obs-websocket-js'); 10 | const { Client, Server } = require('node-osc'); 11 | const { error, Console } = require('console'); 12 | const PDFWindow = require('electron-pdf-window') 13 | const obs = new OBSWebSocket(); 14 | const ks = require('node-keys-simulator'); 15 | 16 | function toHHMMSS(sec_num) { 17 | var hours = Math.floor(sec_num / 3600); 18 | var minutes = Math.floor((sec_num - (hours * 3600)) / 60); 19 | var seconds = sec_num - (hours * 3600) - (minutes * 60); 20 | 21 | if (hours < 10) {hours = "0"+hours;} 22 | if (minutes < 10) {minutes = "0"+minutes;} 23 | if (seconds < 10) {seconds = "0"+seconds;} 24 | return hours + ':' + minutes + ':' + seconds; 25 | } 26 | 27 | let qlabCue 28 | let listSceneItems 29 | let autoConnect 30 | let sceneNow 31 | let currentScene 32 | let lastBundle = [] 33 | let windowSizeWidthPre = 240 34 | let windowSizeWidthPost = 500 35 | let windowSizelHeight = 625 36 | let loadDelay = 1500 37 | 38 | // Handle creating/removing shortcuts on Windows when installing/uninstalling. 39 | if (require('electron-squirrel-startup')) { // eslint-disable-line global-require 40 | app.quit(); 41 | } 42 | 43 | let logging 44 | 45 | //Start App 46 | 47 | const createWindow = () => { 48 | // Create the browser window. 49 | const mainWindow = new BrowserWindow({ 50 | width: windowSizeWidthPre, 51 | height: windowSizelHeight, 52 | webPreferences: { 53 | nodeIntegration: true 54 | } 55 | }); 56 | 57 | const createWindowOSCTest = () => { 58 | const testWindow = new BrowserWindow({ 59 | width: 190, 60 | height: 220, 61 | webPreferences: { 62 | nodeIntegration: true 63 | } 64 | }) 65 | testWindow.loadFile(path.join(__dirname, 'osctester.html')); 66 | } 67 | 68 | 69 | 70 | //Make boolean if Mac 71 | const isMac = process.platform === 'darwin' 72 | 73 | //Construct Menu Toolbar ------ 74 | const template = [ 75 | // { role: 'appMenu' } 76 | ...(isMac ? [{ 77 | label: "OBSosc", 78 | submenu: [ 79 | { role: 'about' }, 80 | { type: 'separator' }, 81 | { role: 'services' }, 82 | { type: 'separator' }, 83 | { role: 'hide' }, 84 | { role: 'hideothers' }, 85 | { role: 'unhide' }, 86 | { type: 'separator' }, 87 | { role: 'quit' } 88 | ] 89 | }] : []), 90 | // { role: 'fileMenu' } 91 | { 92 | label: 'File', 93 | submenu: [ 94 | isMac ? { role: 'close' } : { role: 'quit' }, 95 | { 96 | label: 'Save As...', 97 | accelerator: 'Shift+CommandOrControl+s', 98 | click() { 99 | saveAsFile() 100 | } 101 | }, 102 | { 103 | label: 'Open', 104 | accelerator: 'CommandOrControl+o', 105 | click() { 106 | openFile() 107 | } 108 | 109 | }, 110 | { 111 | label: 'Open/Connect', 112 | accelerator: 'CommandOrControl+Shift+o', 113 | click() { 114 | openFileConnect() 115 | } 116 | 117 | }, 118 | { 119 | label: 'Automatically Connect on Startup', 120 | type: 'checkbox', 121 | checked: false, 122 | click: function (item) { 123 | if (item.checked == false) { 124 | autoConnect = false 125 | } else if (item.checked == true) { 126 | autoConnect = true 127 | } 128 | } 129 | }, 130 | { 131 | label: 'OSC Tester', 132 | accelerator: 'CommandOrControl+T', 133 | click(){ 134 | createWindowOSCTest(); 135 | } 136 | }, 137 | { 138 | label: 'Revert to Default Values', 139 | accelerator: 'CommandOrControl+Shift+/', 140 | click() { 141 | openOriginalFile() 142 | } 143 | 144 | } 145 | ] 146 | }, 147 | // { role: 'editMenu' } 148 | { 149 | label: 'Edit', 150 | submenu: [ 151 | { role: 'undo' }, 152 | { role: 'redo' }, 153 | { type: 'separator' }, 154 | { role: 'cut' }, 155 | { role: 'copy' }, 156 | { role: 'paste' }, 157 | ...(isMac ? [ 158 | { role: 'pasteAndMatchStyle' }, 159 | { role: 'delete' }, 160 | { role: 'selectAll' }, 161 | { type: 'separator' }, 162 | { 163 | label: 'Speech', 164 | submenu: [ 165 | { role: 'startSpeaking' }, 166 | { role: 'stopSpeaking' } 167 | ] 168 | } 169 | ] : [ 170 | { role: 'delete' }, 171 | { type: 'separator' }, 172 | { role: 'selectAll' } 173 | ]) 174 | ] 175 | }, 176 | // { role: 'viewMenu' } 177 | { 178 | label: 'View', 179 | submenu: [ 180 | { role: 'reload' }, 181 | { role: 'forceReload' }, 182 | { role: 'toggleDevTools' }, 183 | { type: 'separator' }, 184 | { role: 'resetZoom' }, 185 | { role: 'zoomIn' }, 186 | { role: 'zoomOut' }, 187 | { type: 'separator' }, 188 | { role: 'togglefullscreen' } 189 | ] 190 | }, 191 | // { role: 'windowMenu' } 192 | { 193 | label: 'Window', 194 | submenu: [ 195 | { role: 'minimize' }, 196 | { role: 'zoom' }, 197 | ...(isMac ? [ 198 | { type: 'separator' }, 199 | { role: 'front' }, 200 | { type: 'separator' }, 201 | { role: 'window' } 202 | ] : [ 203 | { role: 'close' } 204 | ]) 205 | ] 206 | }, 207 | { 208 | label: 'Scripts', 209 | submenu: [ 210 | { 211 | label: 'Populate QLab OSC Cues From OBS Scenes', 212 | accelerator: 'CommandOrControl+1', 213 | click(){ 214 | qlabCue(); 215 | } 216 | }, 217 | { 218 | label: 'Log All Available Scene Items (Sources)', 219 | accelerator: 'CommandOrControl+2', 220 | click(){ 221 | listSceneItems(); 222 | } 223 | }, 224 | ] 225 | }, 226 | { 227 | role: 'help', 228 | submenu: [ 229 | { 230 | label: 'Getting Started', 231 | click: async () => { 232 | const { shell } = require('electron') 233 | await shell.openExternal('https://github.com/jshea2/OBSosc#setup') 234 | } 235 | }, 236 | { 237 | label: 'OSC API', 238 | click: async () => { 239 | const { shell } = require('electron') 240 | await shell.openExternal('https://github.com/jshea2/OBSosc#osc-command-list') 241 | } 242 | }, 243 | { 244 | label: 'Get Support', 245 | click: async () => { 246 | const { shell } = require('electron') 247 | await shell.openExternal('https://discord.com/invite/FJ79AKPgSk') 248 | } 249 | }, 250 | { 251 | label: 'Donate to the Project', 252 | click: async () => { 253 | const { shell } = require('electron') 254 | await shell.openExternal('https://www.paypal.com/paypalme/joeshea2') 255 | } 256 | } 257 | ] 258 | } 259 | ] 260 | 261 | const menu = Menu.buildFromTemplate(template) 262 | Menu.setApplicationMenu(menu) 263 | 264 | 265 | //Open File 266 | function openFile() { 267 | dialog.showOpenDialog(mainWindow, { 268 | properties: ['openFile'], 269 | filters: [{ name: 'Text', extensions: ['txt'] }] 270 | }).then((result) => { 271 | mainWindow.setSize(windowSizeWidthPost,windowSizelHeight) 272 | mainWindow.webContents.openDevTools(); 273 | const file = result.filePaths 274 | //console.log(file) 275 | const fileContent = fs.readFileSync(file[0], {encoding:'utf8', flag:'r'}) 276 | // Make file an array 277 | let openArray = fileContent.split('\n') 278 | if(openArray[0] !== "OBSosc Config File:"){ 279 | logEverywhere("Invalid File Type!") 280 | return 281 | } 282 | //OBS IP 283 | obsIp = openArray[1] 284 | console.log(obsIp) 285 | mainWindow.webContents.send("obsip", obsIp) 286 | //OBS Port 287 | obsPort = openArray[2] 288 | console.log(obsPort) 289 | mainWindow.webContents.send("obsport", obsPort) 290 | //OBS Password 291 | obsPassword = openArray[3] 292 | console.log(obsPassword) 293 | mainWindow.webContents.send("obspassword", obsPassword) 294 | //OSC IN IP 295 | oscServerIp = openArray[4] 296 | console.log(oscServerIp) 297 | mainWindow.webContents.send("oscinip", oscServerIp) 298 | //OSC IN IP 299 | oscPortIn = openArray[5] 300 | console.log(oscPortIn) 301 | mainWindow.webContents.send("oscinport", oscPortIn) 302 | //Enable OSC Command Out on Active OBS Scene 303 | enableObs2App = openArray[6] 304 | console.log(enableObs2App) 305 | mainWindow.webContents.send("enableobs2app", enableObs2App) 306 | //OSC OUT IP 307 | oscClientIp = openArray[7] 308 | console.log(oscClientIp) 309 | mainWindow.webContents.send("oscoutip", oscClientIp) 310 | //OSC OUT Port 311 | oscPortOut = openArray[8] 312 | console.log(oscPortOut) 313 | mainWindow.webContents.send("oscoutport", oscPortOut) 314 | //OSC OUT Prefix Message 315 | oscOutPrefix = openArray[9] 316 | console.log(oscOutPrefix) 317 | mainWindow.webContents.send("oscoutprefix", oscOutPrefix) 318 | //OSC OUT Suffix Message 319 | oscOutSuffix = openArray[10] 320 | console.log(oscOutSuffix) 321 | mainWindow.webContents.send("oscoutsuffix", oscOutSuffix) 322 | //OSC OUT TouchOSC Feedback 323 | oscOutSuffix = openArray[11] 324 | console.log(isTouchOSC) 325 | mainWindow.webContents.send("istouchosc", isTouchOSC) 326 | 327 | logEverywhere("File Opened!") 328 | }) 329 | } 330 | 331 | //Open File and Connect 332 | function openFileConnect() { 333 | dialog.showOpenDialog(mainWindow, { 334 | properties: ['openFile'], 335 | filters: [{ name: 'Text', extensions: ['txt'] }] 336 | }).then((result) => { 337 | mainWindow.setSize(windowSizeWidthPost,windowSizelHeight) 338 | mainWindow.webContents.openDevTools(); 339 | const file = result.filePaths 340 | console.log(file) 341 | const fileContent = fs.readFileSync(file[0], {encoding:'utf8', flag:'r'}) 342 | // Make file an array 343 | let openArray = fileContent.split('\n') 344 | if(openArray[0] !== "OBSosc Config File:"){ 345 | logEverywhere("Invalid File Type!") 346 | return 347 | } 348 | //OBS IP 349 | obsIp = openArray[1] 350 | console.log(obsIp) 351 | mainWindow.webContents.send("obsip", obsIp) 352 | //OBS Port 353 | obsPort = openArray[2] 354 | console.log(obsPort) 355 | mainWindow.webContents.send("obsport", obsPort) 356 | //OBS Password 357 | obsPassword = openArray[3] 358 | console.log(obsPassword) 359 | mainWindow.webContents.send("obspassword", obsPassword) 360 | //OSC IN IP 361 | oscServerIp = openArray[4] 362 | console.log(oscServerIp) 363 | mainWindow.webContents.send("oscinip", oscServerIp) 364 | //OSC IN IP 365 | oscPortIn = openArray[5] 366 | console.log(oscPortIn) 367 | mainWindow.webContents.send("oscinport", oscPortIn) 368 | //Enable OSC Command Out on Active OBS Scene 369 | enableObs2App = openArray[6] 370 | console.log(enableObs2App) 371 | mainWindow.webContents.send("enableobs2app", enableObs2App) 372 | //OSC OUT IP 373 | oscClientIp = openArray[7] 374 | console.log(oscClientIp) 375 | mainWindow.webContents.send("oscoutip", oscClientIp) 376 | //OSC OUT Port 377 | oscPortOut = openArray[8] 378 | console.log(oscPortOut) 379 | mainWindow.webContents.send("oscoutport", oscPortOut) 380 | //OSC OUT Prefix Message 381 | oscOutPrefix = openArray[9] 382 | console.log(oscOutPrefix) 383 | mainWindow.webContents.send("oscoutprefix", oscOutPrefix) 384 | //OSC OUT Suffix Message 385 | oscOutSuffix = openArray[10] 386 | console.log(oscOutSuffix) 387 | mainWindow.webContents.send("oscoutsuffix", oscOutSuffix) 388 | //OSC OUT TouchOSC Feedback 389 | oscOutSuffix = openArray[11] 390 | console.log(isTouchOSC) 391 | mainWindow.webContents.send("istouchosc", isTouchOSC) 392 | 393 | mainWindow.webContents.send("obsconnect") 394 | 395 | 396 | logEverywhere("File Opened!") 397 | }) 398 | } 399 | 400 | //Open Original File 401 | function openOriginalFile() { 402 | const fileContent = fs.readFileSync(path.join(__dirname, "defaultOriginal.txt"), {encoding:'utf8',flag:'r'}) 403 | // Make file an array 404 | let openArray = fileContent.split('\n') 405 | console.log(openArray) 406 | if(openArray[0] !== "OBSosc Config File:"){ 407 | logEverywhere("Invalid File Type!") 408 | return 409 | } 410 | //OBS IP 411 | obsIp = openArray[1] 412 | console.log(obsIp) 413 | mainWindow.webContents.send("obsip", obsIp) 414 | //OBS Port 415 | obsPort = openArray[2] 416 | console.log(obsPort) 417 | mainWindow.webContents.send("obsport", obsPort) 418 | //OBS Password 419 | obsPassword = openArray[3] 420 | console.log(obsPassword) 421 | mainWindow.webContents.send("obspassword", obsPassword) 422 | //OSC IN IP 423 | oscServerIp = openArray[4] 424 | console.log(oscServerIp) 425 | mainWindow.webContents.send("oscinip", oscServerIp) 426 | //OSC IN IP 427 | oscPortIn = openArray[5] 428 | console.log(oscPortIn) 429 | mainWindow.webContents.send("oscinport", oscPortIn) 430 | //Enable OSC Command Out on Active OBS Scene 431 | enableObs2App = openArray[6] 432 | console.log(enableObs2App) 433 | mainWindow.webContents.send("enableobs2app", enableObs2App) 434 | //OSC OUT IP 435 | oscClientIp = openArray[7] 436 | console.log(oscClientIp) 437 | mainWindow.webContents.send("oscoutip", oscClientIp) 438 | //OSC OUT Port 439 | oscPortOut = openArray[8] 440 | console.log(oscPortOut) 441 | mainWindow.webContents.send("oscoutport", oscPortOut) 442 | //OSC OUT Prefix Message 443 | oscOutPrefix = openArray[9] 444 | console.log(oscOutPrefix) 445 | mainWindow.webContents.send("oscoutprefix", oscOutPrefix) 446 | //OSC OUT Suffix Message 447 | oscOutSuffix = openArray[10] 448 | console.log(oscOutSuffix) 449 | mainWindow.webContents.send("oscoutsuffix", oscOutSuffix) 450 | //OSC OUT TouchOSC Feedback 451 | oscOutSuffix = openArray[11] 452 | console.log(isTouchOSC) 453 | mainWindow.webContents.send("istouchosc", isTouchOSC) 454 | //Set Automatically Connect on Startup Off 455 | autoConnect = false 456 | menu.items[1].submenu.items[4].checked = false 457 | 458 | logEverywhere("Reverted to Original Values.") 459 | } 460 | 461 | //Save File 462 | function saveAsFile(){ 463 | logEverywhere("Make Sure to 'Connect' before you Save!") 464 | dialog.showSaveDialog({ 465 | title: 'Select the File Path to save', 466 | defaultPath: app.getPath('documents') + '/obsosc_configfile.txt', 467 | // defaultPath: path.join(__dirname, '../assets/'), 468 | buttonLabel: 'Save', 469 | // Restricting the user to only Text Files. 470 | filters: [ 471 | { 472 | name: 'Text Files', 473 | extensions: ['txt', 'docx'] 474 | }, ], 475 | properties: [] 476 | }).then(file => { 477 | // Stating whether dialog operation was cancelled or not. 478 | console.log(file.canceled); 479 | if (!file.canceled) { 480 | console.log(file.filePath.toString()); 481 | 482 | // Creating and Writing to the sample.txt file 483 | fs.writeFile(file.filePath.toString(), 484 | `OBSosc Config File:\n${obsIp}\n${obsPort}\n${obsPassword}\n${oscServerIp}\n${oscPortIn}\n${enableObs2App}\n${oscClientIp}\n${oscPortOut}\n${oscOutPrefix}\n${oscOutSuffix}\n${isTouchOSC}`, function (err) { 485 | if (err) throw err; 486 | logEverywhere('File Saved!'); 487 | }); 488 | } 489 | }).catch(err => { 490 | console.log(err) 491 | }); 492 | } 493 | 494 | 495 | 496 | 497 | mainWindow.on('closed', function(){ 498 | // On Close it Saves New Default Values 499 | fs.writeFile(path.join(__dirname, 'default.txt').toString(), 500 | `OBSosc Config File:\n${obsIp}\n${obsPort}\n${obsPassword}\n${oscServerIp}\n${oscPortIn}\n${enableObs2App}\n${oscClientIp}\n${oscPortOut}\n${oscOutPrefix}\n${oscOutSuffix}\n${isTouchOSC}\n${autoConnect}`, function (err) { 501 | if (err) throw err; 502 | }); 503 | app.quit() 504 | }) 505 | 506 | // and load the index.html of the app. 507 | mainWindow.loadFile(path.join(__dirname, 'index.html')); 508 | 509 | //Set Path for Icon 510 | const imgpath = path.join(__dirname, 'extraResources', '/icon.png'); 511 | 512 | //Send Pathname to Renderer for Logo 513 | setTimeout(() => { 514 | mainWindow.webContents.send("imgpath", imgpath) 515 | }, loadDelay); 516 | 517 | console.log(menu.items[1].submenu.items[4].checked) 518 | 519 | 520 | // Open Default.txt and Import Values 521 | setTimeout(() => { 522 | const fileContent = fs.readFileSync(path.join(__dirname, "default.txt"), {encoding:'utf8', flag:'r'}) 523 | // Make file an array 524 | let openArray = fileContent.split('\n') 525 | if(openArray[0] !== "OBSosc Config File:"){ 526 | logEverywhere("Invalid File Type! Loading Default Values") 527 | mainWindow.setSize(windowSizeWidthPost,windowSizelHeight) 528 | mainWindow.webContents.openDevTools() 529 | openOriginalFile() 530 | return 531 | } 532 | //OBS IP 533 | obsIp = openArray[1] 534 | console.log(obsIp) 535 | mainWindow.webContents.send("obsip", obsIp) 536 | //OBS Port 537 | obsPort = openArray[2] 538 | console.log(obsPort) 539 | mainWindow.webContents.send("obsport", obsPort) 540 | //OBS Password 541 | obsPassword = openArray[3] 542 | console.log(obsPassword) 543 | mainWindow.webContents.send("obspassword", obsPassword) 544 | //OSC IN IP 545 | oscServerIp = openArray[4] 546 | console.log(oscServerIp) 547 | mainWindow.webContents.send("oscinip", oscServerIp) 548 | //OSC IN IP 549 | oscPortIn = openArray[5] 550 | console.log(oscPortIn) 551 | mainWindow.webContents.send("oscinport", oscPortIn) 552 | //Enable OSC Command Out on Active OBS Scene 553 | enableObs2App = openArray[6] 554 | console.log(enableObs2App) 555 | mainWindow.webContents.send("enableobs2app", enableObs2App) 556 | //OSC OUT IP 557 | oscClientIp = openArray[7] 558 | console.log(oscClientIp) 559 | mainWindow.webContents.send("oscoutip", oscClientIp) 560 | //OSC OUT Port 561 | oscPortOut = openArray[8] 562 | console.log(oscPortOut) 563 | mainWindow.webContents.send("oscoutport", oscPortOut) 564 | //OSC OUT Prefix Message 565 | oscOutPrefix = openArray[9] 566 | console.log(oscOutPrefix) 567 | mainWindow.webContents.send("oscoutprefix", oscOutPrefix) 568 | //OSC OUT Suffix Message 569 | oscOutSuffix = openArray[10] 570 | console.log(oscOutSuffix) 571 | mainWindow.webContents.send("oscoutsuffix", oscOutSuffix) 572 | //OSC OUT TouchOSC Feedback 573 | isTouchOSC = openArray[11] 574 | console.log(isTouchOSC) 575 | mainWindow.webContents.send("istouchosc", isTouchOSC) 576 | //Auto Connect on Startup 577 | if(openArray[12] === 'true'){ 578 | autoConnect = true 579 | menu.items[1].submenu.items[4].checked = true 580 | } else if (openArray[12] === 'false'){ 581 | autoConnect = false 582 | menu.items[1].submenu.items[4].checked = false 583 | } 584 | console.log(autoConnect) 585 | 586 | if(autoConnect === true || autoConnect === "true"){ 587 | mainWindow.setSize(windowSizeWidthPost,windowSizelHeight) 588 | mainWindow.webContents.openDevTools(); 589 | mainWindow.webContents.send("obsconnect") 590 | } 591 | 592 | }, loadDelay); 593 | 594 | 595 | //ipcMain.on gets the data from HTML 596 | 597 | ipcMain.on("submitted", (event, data) => { 598 | // Open the DevTools. 599 | mainWindow.setSize(windowSizeWidthPost,windowSizelHeight) 600 | mainWindow.webContents.openDevTools(); 601 | }) 602 | 603 | ipcMain.on("obsip", (event, data) => { 604 | obsIp = data 605 | console.log(data) 606 | }) 607 | 608 | ipcMain.on("obsport", (event, data) => { 609 | obsPort = data 610 | console.log(data) 611 | }) 612 | 613 | ipcMain.on("obspassword", (event, data) => { 614 | obsPassword = data 615 | console.log(data) 616 | }) 617 | 618 | ipcMain.on("oscinip", (event, data) => { 619 | oscServerIp = data 620 | console.log(data) 621 | }) 622 | 623 | ipcMain.on("oscinport", (event, data) => { 624 | oscPortIn = data 625 | console.log(data) 626 | }) 627 | 628 | ipcMain.on("enableoscout", (event, data) => { 629 | enableObs2App = data 630 | console.log(data) 631 | }) 632 | 633 | ipcMain.on("istouchosc", (event, data) => { 634 | isTouchOSC = data 635 | console.log(data) 636 | }) 637 | 638 | ipcMain.on("oscoutip", (event, data) => { 639 | oscClientIp = data 640 | console.log(data) 641 | }) 642 | 643 | ipcMain.on("oscoutport", (event, data) => { 644 | oscPortOut = data 645 | console.log(data) 646 | }) 647 | 648 | ipcMain.on("oscoutprepend", (event, data) => { 649 | oscOutPrefix = data 650 | console.log(data) 651 | }) 652 | 653 | ipcMain.on("oscoutpostpend", (event, data) => { 654 | oscOutSuffix = data 655 | console.log(data) 656 | }) 657 | 658 | 659 | // Function that logs to the DevTools 660 | function logEverywhere(message) { 661 | if (mainWindow && mainWindow.webContents) { 662 | mainWindow.webContents.executeJavaScript(`console.log(\`${message}\`)`); 663 | } 664 | } 665 | 666 | setInterval(() => { 667 | if(logging !== undefined){ 668 | mainWindow.webContents.send("Logging", logging) 669 | logging = undefined 670 | } 671 | }, 500); 672 | 673 | 674 | //Default values 675 | //OBS Config 676 | let obsIp = "127.0.0.1" 677 | let obsPort = 4455; 678 | let obsPassword = "secret" 679 | //OSC Server (IN) Config 680 | let oscServerIp = "127.0.0.1"; 681 | let oscPortIn = 3333; 682 | //Enable OBS -> App Control 683 | let enableObs2App = false 684 | let isTouchOSC = false 685 | //OSC Client (OUT) Config 686 | let oscClientIp = "127.0.0.1"; 687 | let oscPortOut = 53000; 688 | let oscOutPrefix = "/cue/" 689 | let oscOutSuffix = "/start" 690 | 691 | 692 | //Connect to OBS 693 | ipcMain.on("obsConnect", (event, data) => { 694 | obs.connect(`ws://${obsIp}:${obsPort}`, `${obsPassword}`) 695 | .then(() => { 696 | mainWindow.webContents.send("isstatus", "dotGreen") 697 | logging = `\nConnected & authenticated to OBS Websockets...\nIP: ${obsIp}\nPort: ${obsPort}`; 698 | console.log(logging) 699 | logEverywhere(`\nConnected & authenticated to OBS Websockets...\nIP: ${obsIp}\nPort: ${obsPort}`) 700 | }) 701 | .then(() => { 702 | return obs.call('GetVersion') 703 | }) 704 | .then(data => { 705 | logEverywhere(`Current OBS Studio Version: \n${data['obsVersion']}`) 706 | logEverywhere(`Current obs-websocket Plugin Version: \n${data['obsWebSocketVersion']}`) 707 | if (data['obsWebSocketVersion'].includes("4.")) { 708 | logEverywhere("Error: This Version of OSC for OBS only works with OBS v28 and above and obs-websocket v5 and above.") 709 | } 710 | return obs.call('GetSceneList'); //Send a Get Scene List Promise 711 | }) 712 | .then(data => { 713 | console.log(`\n${data.scenes.length} Available Scenes.` + "\n"); //Log Total Scenes 714 | console.log(data.scenes) 715 | logEverywhere(`\n${data.scenes.length} Available Scenes.` + "\n") 716 | console.log(data.scenes.forEach((thing, index) => { 717 | console.log((index + 1) + " - " + thing.sceneName); 718 | logEverywhere((index + 1) + " - " + thing.sceneName) //Log List of Available Scenes with OSC Index 719 | })); 720 | 721 | console.log('-- Reference ( Help > API ) for all OSC Commands --\n\n'); //Log OSC Scene Syntax 722 | logEverywhere('-- Reference "Help">"API" for all OSC Commands --\n\n') 723 | }) 724 | .catch(err => { 725 | mainWindow.webContents.send("isstatus", "dotRed") 726 | console.log(err); 727 | logEverywhere(err) //Log Catch Errors 728 | console.log("ERROR: Make Sure OBS is Running, Websocket Plugin is Installed, and IP/Port/Password are Correct"); 729 | logEverywhere("ERROR: Make Sure OBS is Running, Websocket Plugin is Installed, and IP/Port/Password are Correct") 730 | }); 731 | 732 | 733 | //Qlab AutoPopulate Function 734 | qlabCue = function createQlabCues() { 735 | obs.call('GetSceneList').then(data => { 736 | data.scenes.slice().reverse().forEach(i => { 737 | client.send("/new", "network", "1", (err) => { 738 | if (err) console.error(err); 739 | }) 740 | client.send("/cue/selected/customString", `/scene "${i.sceneName}"`, (err) => { 741 | if (err) console.error(err); 742 | }) 743 | client.send("/cue/selected/name", `${i.sceneName}`, (err) => { 744 | if (err) console.error(err); 745 | }) 746 | }) 747 | }) 748 | } 749 | 750 | 751 | //List Scene Items Function 752 | listSceneItems = function listSceneItems(){ 753 | return obs.call("GetSceneList").then(data => { 754 | logEverywhere("--- Available Scene Items: ---") 755 | // logEverywhere(data.sceneName) 756 | // logEverywhere(msg[1]) 757 | data.scenes.slice().reverse().forEach(i => { 758 | console.log(data) 759 | obs.call("GetSceneItemList", { 760 | 'sceneName': i.sceneName, 761 | }).then(data => { 762 | data.sceneItems.forEach(j => { 763 | console.log(j) 764 | logEverywhere(j.sourceName) 765 | }) 766 | }).catch(error => { 767 | logEverywhere("ERROR: Invalid Syntax") 768 | }) 769 | }) 770 | 771 | }) 772 | } 773 | 774 | //Get Scene Item Id 775 | let getSceneItemIdValue 776 | let getSceneItemId = (scene, source) => { 777 | obs.call("GetSceneItemId", { 778 | sceneName: scene, 779 | sourceName: source 780 | }).then(data => { 781 | console.log(data) 782 | getSceneItemIdValue = data.sceneItemId 783 | }).catch(error => { 784 | console.log("Couldn't get scene index") 785 | }) 786 | } 787 | 788 | //Get Scene Item Name 789 | let getSceneItemNameValue 790 | let getSceneItemName = (scene, sourceid) => { 791 | obs.call("GetSceneItemList", { 792 | sceneName: scene 793 | }).then(data => { 794 | getSceneItemNameValue = data.sceneItems.filter(e => e.includes(sourceid)) 795 | console.log(data) 796 | getSceneItemNameValue = getSceneItemNameValue.sourceName 797 | }).catch(error => { 798 | console.log("Couldn't get scene index") 799 | }) 800 | } 801 | 802 | obs.on("MediaInputActionTriggered", data => { 803 | //console.log(data.mediaAction) 804 | }) 805 | 806 | 807 | //Listen and Log When New Scene is Activated 808 | obs.on('CurrentProgramSceneChanged', data => { 809 | console.log(`New Active Scene: ${data.sceneName}`); 810 | logEverywhere(`New Active Scene: ${data.sceneName}`) 811 | }); 812 | 813 | let currentSceneItem 814 | let sceneItemId 815 | //Save Scene Item as Variable 816 | obs.on("SceneItemSelected", data => { 817 | sceneItemId = data.sceneItemId 818 | return obs.call('GetSceneItemList', { 819 | 'sceneName': data.sceneName 820 | }).then(data => { 821 | //console.log(data) 822 | currentSceneItem = data.sceneItems.filter(d => d['sceneItemId'] == sceneItemId) 823 | console.log(currentSceneItem[0]['sourceName']) 824 | logEverywhere("Selected Scene Item: " + currentSceneItem[0]['sourceName']) 825 | }) 826 | }) 827 | 828 | 829 | //OBS quits and app attempts to reconnect 830 | obs.on("Exiting", data => { 831 | console.log("YOU HAVE EXITED OBSSSS") 832 | let connectLoop = setInterval(connectOBS, 2000) 833 | function connectOBS() { 834 | obs.connect(`ws://${obsIp}:${obsPort}`, `${obsPassword}`) 835 | .then(() => { 836 | clearInterval(connectLoop) 837 | mainWindow.webContents.send("isstatus", "dotGreen") 838 | logging = `\nConnected & authenticated to OBS Websockets...\nIP: ${obsIp}\nPort: ${obsPort}`; 839 | console.log(logging) 840 | logEverywhere(`\nConnected & authenticated to OBS Websockets...\nIP: ${obsIp}\nPort: ${obsPort}`) 841 | }) 842 | .then(() => { 843 | return obs.call('GetVersion') 844 | }) 845 | .then(data => { 846 | logEverywhere(`Current OBS Studio Version: \n${data['obsVersion']}`) 847 | logEverywhere(`Current obs-websocket Plugin Version: \n${data['obsWebsocketVersion']}`) 848 | return obs.call('GetSceneList'); //Send a Get Scene List Promise 849 | }) 850 | .then(data => { 851 | console.log(`\n${data.scenes.length} Available Scenes.` + "\n"); //Log Total Scenes 852 | logEverywhere(`\n${data.scenes.length} Available Scenes.` + "\n") 853 | console.log(data.scenes.slice().reverse().forEach((thing, index) => { 854 | console.log((index + 1) + " - " + thing.sceneName); 855 | logEverywhere((index + 1) + " - " + thing.sceneName) //Log List of Available Scenes with OSC Index 856 | })); 857 | 858 | console.log('-- Reference ( Help > API ) for all OSC Commands --\n\n'); //Log OSC Scene Syntax 859 | logEverywhere('-- Reference "Help">"API" for all OSC Commands --\n\n') 860 | }) 861 | .catch(err => { 862 | console.log(err); 863 | logEverywhere("Reconnecting...") //Log Catch Errors 864 | console.log("ERROR: Make Sure OBS is Running, Websocket Plugin is Installed, and IP/Port/Password are Correct"); 865 | }); 866 | } 867 | logEverywhere("OBS has Disconnected") 868 | mainWindow.webContents.send("isstatus", "dotRed") 869 | connectLoop 870 | }) 871 | 872 | 873 | // Handler to Avoid Uncaught Exceptions. 874 | obs.on('error', err => { 875 | console.error('socket error:', err); 876 | logEverywhere(err) 877 | }); 878 | 879 | //Connect to OSC 880 | const client = new Client(oscClientIp, oscPortOut); 881 | var server = new Server(oscPortIn, oscServerIp); 882 | const clientLoopback = new Client(oscServerIp, oscPortIn); 883 | 884 | 885 | //OSC Server Listening (IN) 886 | server.on('listening', () => { 887 | console.log("\n\n" + 'OSC Input is listening on...\n IP: ' + oscServerIp + '\n Port: ' + oscPortIn); 888 | logEverywhere("\n\n" + 'OSC Input is listening on...\n IP: ' + oscServerIp + '\n Port: ' + oscPortIn) 889 | console.log('\nOSC Output is sending on...\n IP: ' + oscClientIp + '\n Port: ' + oscPortOut); 890 | logEverywhere('\nOSC Output is sending on...\n IP: ' + oscClientIp + '\n Port: ' + oscPortOut) 891 | 892 | let enabledisablestring 893 | if(enableObs2App){ 894 | enabledisablestring = "Enabled" 895 | console.log('\nOBS On Active Scene OSC Output is ' + enabledisablestring ); 896 | logEverywhere('\nOBS On Active Scene OSC Output is ' + enabledisablestring ) 897 | 898 | } else { 899 | enabledisablestring = "Disabled" 900 | console.log('\nOBS On Active Scene OSC Output is ' + enabledisablestring); 901 | logEverywhere('\nOBS On Active Scene OSC Output is ' + enabledisablestring) 902 | 903 | } 904 | }) 905 | 906 | //Takes in bundled OSC and converts to individual OSC messages 907 | server.on('bundle', function (bundle) { 908 | if (bundle.elements == undefined){ 909 | return 910 | } else { 911 | bundle.elements.forEach((element, i) => { 912 | 913 | //console.log(`Timestamp: ${bundle.timetag[i]}`); 914 | if (element.includes("/_samplerate")){ 915 | return 916 | } else { 917 | console.log(`Message: ${element}`); 918 | clientLoopback.send(element) 919 | } 920 | }); 921 | } 922 | }); 923 | 924 | // From ipcMain.on from the OSC Tester HTML 925 | ipcMain.on("oscMessage", (event, data) => { 926 | console.log(data) 927 | clientLoopback.send(data) 928 | }) 929 | 930 | 931 | //OSC -> OBS 932 | //When app receives OSC do.. 933 | server.on('message', (msg) => { 934 | 935 | //Trigger Scene by Index Number 936 | if (msg[0] === "/scene" && typeof msg[1] === 'number'){ 937 | console.log("number thing works") //When OSC Recieves a /scene do... 938 | var oscMessage = msg[1] - 1; //Convert Index Number to Start at 1 939 | var oscMessage = Math.floor(oscMessage); //Converts Any Float Argument to Lowest Integer 940 | return obs.call('GetSceneList').then(data => { //Request Scene List Array 941 | console.log(`OSC IN: ${msg[0]} ${oscMessage + 1} (${data.scenes[oscMessage].sceneName})`) 942 | logEverywhere(`OSC IN: ${msg[0]} ${oscMessage + 1} (${data.scenes[oscMessage].sceneName})`) 943 | obs.call("SetCurrentProgramScene", { 944 | 'sceneName': data.scenes[oscMessage].sceneName //Set to Scene from OSC 945 | }) 946 | }).catch(() => { 947 | console.log("Error: Out Of '/scene' Range"); 948 | logEverywhere(`Error: Out Of '/scene' Range\nOSC Recieved: ${msg}`) //Catch Error 949 | }); 950 | } 951 | //Trigger Scene if Argument is a String and Contains a Space 952 | else if (msg[0] === "/scene" && msg.length > 2){ //When OSC Recieves a /scene do... 953 | var firstIndex = msg.shift(); //Removes First Index from 'msg' and Stores it to Another Variable 954 | oscMultiArg = msg.join(' ') //Converts 'msg' to a String with spaces 955 | return obs.call('GetSceneList').then(data => { //Request Scene List Array 956 | console.log(`OSC IN: ${firstIndex} ${oscMultiArg}`) 957 | logEverywhere(`OSC IN: ${firstIndex} ${oscMultiArg}`) 958 | obs.call("SetCurrentProgramScene", { 959 | 'sceneName': oscMultiArg //Set to Scene from OSC 960 | }).catch(() => { 961 | console.log(`Error: There is no Scene "${oscMultiArg}" in OBS. Double check case sensitivity.`); 962 | logEverywhere(`Error: There is no Scene "${oscMultiArg}" in OBS. Double check case sensitivity.\nOSC Recieved: ${msg}`); 963 | }) 964 | }).catch((err) => { 965 | console.log(err) 966 | logEverywhere(err) //Catch Error 967 | }); 968 | } 969 | //Trigger Scene if Argument is a String 970 | else if (msg[0] === "/scene" && typeof msg[1] === 'string'){ //When OSC Recieves a /scene do... 971 | var oscMessage = msg[1]; 972 | return obs.call('GetSceneList').then(data => { //Request Scene List Array 973 | console.log(`OSC IN: ${msg[0]} ${oscMessage}`) 974 | logEverywhere(`OSC IN: ${msg[0]} ${oscMessage}`) 975 | obs.call("SetCurrentProgramScene", { 976 | 'sceneName': oscMessage //Set to Scene from OSC 977 | }).catch(() => { 978 | console.log(`Error: There is no Scene "${msg[1]}" in OBS. Double check case sensitivity.`); 979 | logEverywhere(`Error: There is no Scene "${msg[1]}" in OBS. Double check case sensitivity.\nOSC Recieved: ${msg}`); 980 | 981 | }) 982 | }).catch((err) => { 983 | console.log(err) 984 | logEverywhere(err) //Catch Error 985 | }); 986 | } 987 | //Trigger Scene if Scene Name is in the OSC String 988 | else if (msg[0].includes('/scene') && msg.length === 1){ 989 | var msgArray = msg[0].split("/") 990 | msgArray.shift() 991 | msgArray.shift() 992 | obs.call("SetCurrentProgramScene", { 993 | 'sceneName': msgArray[0].split("_").join(" ").toString(), //Set to Scene from OSC 994 | }).catch(() => { 995 | console.log(`Error: There is no Scene "${msgArray}" in OBS. Double check case sensitivity.`); 996 | logEverywhere(`Error: There is no Scene "${msgArray}" in OBS. Double check case sensitivity.\nOSC Recieved: ${msg}`); 997 | 998 | }).catch((err) => { 999 | console.log(err) 1000 | logEverywhere(err) //Catch Error 1001 | }); 1002 | } 1003 | //Trigger Scene if Scene Name is in the OSC String and is TouchOSC 1004 | else if (msg[0].includes('/scene') && msg[1] === 1){ 1005 | var msgArray = msg[0].split("/") 1006 | msgArray.shift() 1007 | msgArray.shift() 1008 | obs.call("SetCurrentProgramScene", { 1009 | 'sceneName': msgArray[0].split("_").join(" ").toString(), //Set to Scene from OSC 1010 | }).catch(() => { 1011 | console.log(`Error: There is no Scene "${msgArray}" in OBS. Double check case sensitivity.`); 1012 | logEverywhere(`Error: There is no Scene "${msgArray}" in OBS. Double check case sensitivity.\nOSC Recieved: ${msg}`); 1013 | 1014 | }).catch((err) => { 1015 | console.log(err) 1016 | logEverywhere(err) //Catch Error 1017 | }); 1018 | } 1019 | //Trigger Preview Scene if Argument is a String 1020 | else if (msg[0] === "/previewScene" && typeof msg[1] === 'string'){ //When OSC Recieves a /scene do... 1021 | var oscMessage = msg[1]; 1022 | logEverywhere(`OSC IN: ${msg[0]} ${oscMessage}`) 1023 | obs.call("SetCurrentPreviewScene", { 1024 | 'sceneName': oscMessage //Set to Scene from OSC 1025 | }).catch(() => { 1026 | console.log(`Error: There is no Scene "${msg[1]}" in OBS. Double check case sensitivity.`); 1027 | logEverywhere(`Error: There is no Scene "${msg[1]}" in OBS. Double check case sensitivity.`); 1028 | }) 1029 | } 1030 | //Trigger Preview Scene if Scene Name is in the OSC String 1031 | else if (msg[0].includes('/previewScene') && msg.length === 1){ 1032 | var msgArray = msg[0].split("/") 1033 | msgArray.shift() 1034 | msgArray.shift() 1035 | obs.call("SetCurrentPreviewScene", { 1036 | 'sceneName': msgArray[0].split("_").join(" ").toString(), //Set to Scene from OSC 1037 | }).catch(() => { 1038 | console.log(`Error: There is no Scene "${msgArray}" in OBS. Double check case sensitivity.`); 1039 | logEverywhere(`Error: There is no Scene "${msgArray}" in OBS. Double check case sensitivity.\nOSC Recieved: ${msg}`); 1040 | 1041 | }) 1042 | } 1043 | 1044 | //Trigger StudioMode Transition 1045 | else if (msg[0] === "/studioTransition"){ 1046 | obs.call("TriggerStudioModeTransition", { 1047 | 'with-transition': { 1048 | transitionName: msg[1], 1049 | duration: msg[2] 1050 | } 1051 | }).catch(() => { 1052 | logEverywhere(`Error: Must be in Studio Mode to use this command\nOSC Recieved: ${msg}`); 1053 | }) 1054 | } 1055 | 1056 | //Triggers Previous Scene to go "BACK" 1057 | else if (msg[0] === "/back"){ //When OSC Recieves a /go do... 1058 | 1059 | return obs.call('GetSceneList').then(data => { //Request Scene List Array 1060 | 1061 | var cleanArray = [] 1062 | var rawSceneList = data //Assign Get Scene List 'data' to variable 1063 | data.scenes.forEach(element => {cleanArray.push(element.sceneName)}); //Converting Scene List To a Cleaner(Less Nested) Array (Getting the Desired Nested Values) 1064 | return obs.call("GetCurrentProgramScene").then(data => { //Request Current Scene Name 1065 | console.log(rawSceneList) 1066 | console.log(cleanArray) 1067 | var currentSceneIndex = cleanArray.indexOf(data.currentProgramSceneName) //Get the Index of the Current Scene Referenced from the Clean Array 1068 | console.log(currentSceneIndex) 1069 | if (currentSceneIndex + 1 >= rawSceneList.scenes.length){ //When the Current Scene is More than the Total Scenes... 1070 | obs.call("SetCurrentProgramScene", { 1071 | 'sceneName': rawSceneList.scenes[0].sceneName //Set the Scene to First Scene 1072 | }) 1073 | } else { 1074 | obs.call("SetCurrentProgramScene", { 1075 | 'sceneName': rawSceneList.scenes[currentSceneIndex + 1].sceneName //Set Scene to Next Scene (Referenced from the Current Scene and Array) 1076 | }) 1077 | } 1078 | }).catch(() => { 1079 | console.log("Error: Invalid OSC Message"); //Catch Error 1080 | logEverywhere(`Error: Invalid OSC Message\nOSC Recieved: ${msg}`); //Catch Error 1081 | 1082 | }); 1083 | }) 1084 | } 1085 | 1086 | //Triggers to "GO" to the Next Scene 1087 | else if (msg[0] === "/go"){ //Same Concept as Above Except Going to the Previous Scene 1088 | 1089 | return obs.call('GetSceneList').then(data => { 1090 | 1091 | var cleanArray = [] 1092 | var rawSceneList = data 1093 | data.scenes.forEach(element => {cleanArray.push(element.sceneName)}); 1094 | return obs.call("GetCurrentProgramScene").then(data => { 1095 | var currentSceneIndex = cleanArray.indexOf(data.currentProgramSceneName) 1096 | if (currentSceneIndex - 1 <= -1){ 1097 | obs.call("SetCurrentProgramScene", { 1098 | 'sceneName': rawSceneList.scenes[rawSceneList.scenes.length - 1].sceneName 1099 | }) 1100 | } else { 1101 | obs.call("SetCurrentProgramScene", { 1102 | 'sceneName': rawSceneList.scenes[currentSceneIndex - 1].sceneName 1103 | }) 1104 | } 1105 | }).catch(() => { 1106 | console.log("Error: Invalid OSC Message"); 1107 | logEverywhere(`Error: Invalid OSC Message\nOSC Recieved: ${msg}`) 1108 | }); 1109 | }) 1110 | 1111 | 1112 | } 1113 | 1114 | //Set Recording by Arg 1115 | else if (msg[0] === "/setRecording"){ 1116 | if (msg[1] == 1){ 1117 | obs.call("StartRecord").catch((err) => { 1118 | console.log(`ERROR: ${err.error}`) 1119 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1120 | }) 1121 | } else if (msg[1] == 0){ 1122 | obs.call("StopRecord").catch((err) => { 1123 | console.log(`ERROR: ${err.error}`) 1124 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1125 | }) 1126 | } 1127 | 1128 | 1129 | } 1130 | //Triggers Start Recording 1131 | else if (msg[0] === "/startRecording"){ 1132 | obs.call("StartRecord").catch((err) => { 1133 | console.log(`ERROR: ${err.error}`) 1134 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1135 | }) 1136 | 1137 | } 1138 | //Triggers Stop Recording 1139 | else if (msg[0] === "/stopRecording"){ 1140 | obs.call("StopRecord").catch((err) => { 1141 | console.log(`ERROR: ${err.error}`) 1142 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1143 | }) 1144 | 1145 | } 1146 | //Triggers Start Recording 1147 | else if (msg[0] === "/pauseRecording"){ 1148 | obs.call("PauseRecord").catch((err) => { 1149 | console.log(`ERROR: ${err.error}`) 1150 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1151 | }) 1152 | 1153 | } 1154 | //Triggers Stop Recording 1155 | else if (msg[0] === "/resumeRecording"){ 1156 | obs.call("ResumeRecord").catch((err) => { 1157 | console.log(`ERROR: ${err.error}`) 1158 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1159 | }) 1160 | 1161 | } 1162 | //Triggers Toggle Recording 1163 | else if (msg[0] === "/toggleRecording"){ 1164 | obs.call("ToggleRecord").catch((err) => { 1165 | console.log(`ERROR: ${err.error}`) 1166 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1167 | }) 1168 | 1169 | } 1170 | //Set Streaming by Arg 1171 | else if (msg[0] === "/setStreaming"){ 1172 | if (msg[1] == 1){ 1173 | obs.call("StartStream").catch((err) => { 1174 | console.log(`ERROR: ${err.error}`) 1175 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1176 | }) 1177 | } else if (msg[1] == 0){ 1178 | obs.call("StopStream").catch((err) => { 1179 | console.log(`ERROR: ${err.error}`) 1180 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1181 | }) 1182 | } 1183 | 1184 | 1185 | } 1186 | //Triggers Start Streaming 1187 | else if (msg[0] === "/startStreaming"){ 1188 | obs.call("StartStream").catch((err) => { 1189 | console.log(`ERROR: ${err.error}`) 1190 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1191 | }) 1192 | 1193 | } 1194 | //Triggers Stop Streaming 1195 | else if (msg[0] === "/stopStreaming"){ 1196 | obs.call("StopStream").catch((err) => { 1197 | console.log(`ERROR: ${err.error}`) 1198 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1199 | }) 1200 | 1201 | } 1202 | //Triggers Toggle Streaming 1203 | else if (msg[0] === "/toggleStreaming"){ 1204 | obs.call("ToggleStream").catch((err) => { 1205 | console.log(`ERROR: ${err.error}`) 1206 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1207 | }) 1208 | 1209 | } 1210 | //Set VirtualCam by Arg 1211 | else if (msg[0] === "/setVirtualCam"){ 1212 | if (msg[1] == 1){ 1213 | obs.call("StartVirtualCam").catch((err) => { 1214 | console.log(`ERROR: ${err.error}`) 1215 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1216 | }) 1217 | } else if (msg[1] == 0){ 1218 | obs.call("StopVirtualCam").catch((err) => { 1219 | console.log(`ERROR: ${err.error}`) 1220 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1221 | }) 1222 | } 1223 | 1224 | 1225 | } 1226 | //Triggers Start VirtualCam 1227 | else if (msg[0] === "/startVirtualCam"){ 1228 | obs.call("StartVirtualCam").catch((err) => { 1229 | console.log(`ERROR: ${err.error}`) 1230 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1231 | }) 1232 | 1233 | } 1234 | //Triggers Stop VirtualCam 1235 | else if (msg[0] === "/stopVirtualCam"){ 1236 | obs.call("StopVirtualCam").catch((err) => { 1237 | console.log(`ERROR: ${err.error}`) 1238 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1239 | }) 1240 | 1241 | } 1242 | //Triggers Toggle VirtualCam 1243 | else if (msg[0] === "/toggleVirtualCam"){ 1244 | obs.call("ToggleVirtualCam").catch((err) => { 1245 | console.log(`ERROR: ${err.error}`) 1246 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1247 | }) 1248 | 1249 | } 1250 | //Triggers Pause Recording 1251 | else if (msg[0] === "/pauseRecording"){ 1252 | obs.call("PauseRecord").catch((err) => { 1253 | console.log(`ERROR: ${err.error}`) 1254 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1255 | }) 1256 | 1257 | } 1258 | //Triggers Resume Recording 1259 | else if (msg[0] === "/resumeRecording"){ 1260 | obs.call("ResumeRecord").catch((err) => { 1261 | console.log(`ERROR: ${err.error}`) 1262 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1263 | }) 1264 | 1265 | } 1266 | //Set StudioMode by Arg 1267 | else if (msg[0] === "/setStudioMode"){ 1268 | if (msg[1] == 1){ 1269 | obs.call("SetStudioModeEnabled", { 1270 | studioModeEnabled : true 1271 | }).catch((err) => { 1272 | console.log(`ERROR: ${err.error}`) 1273 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1274 | }) 1275 | } else if (msg[1] == 0){ 1276 | obs.call("SetStudioModeEnabled", { 1277 | studioModeEnabled : false 1278 | }).catch((err) => { 1279 | console.log(`ERROR: ${err.error}`) 1280 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1281 | }) 1282 | } 1283 | 1284 | 1285 | } 1286 | //Triggers Enable Studio Mode 1287 | else if (msg[0] === "/enableStudioMode"){ 1288 | obs.call("SetStudioModeEnabled", { 1289 | studioModeEnabled : true 1290 | }).catch((err) => { 1291 | console.log(`ERROR: ${err.error}`) 1292 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1293 | }) 1294 | 1295 | } 1296 | //Triggers Disable Studio Mode 1297 | else if (msg[0] === "/disableStudioMode"){ 1298 | obs.call("SetStudioModeEnabled", { 1299 | studioModeEnabled : false 1300 | }).catch((err) => { 1301 | console.log(`ERROR: ${err.error}`); 1302 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1303 | }) 1304 | 1305 | } 1306 | //Triggers Toggle Studio Mode 1307 | else if (msg[0] === "/toggleStudioMode"){ 1308 | obs.call("GetStudioModeEnabled").then(data => { 1309 | obs.call("SetStudioModeEnabled", { 1310 | studioModeEnabled : !data.studioModeEnabled 1311 | }).catch((err) => { 1312 | console.log(err) 1313 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1314 | }) 1315 | }).catch((err) => { 1316 | console.log(err) 1317 | logEverywhere(`ERROR: ${err}\nOSC Recieved: ${msg}`) 1318 | }) 1319 | } 1320 | 1321 | //Triggers Source Visibility On/Off 1322 | else if (msg[0].includes('visible')){ 1323 | console.log(`OSC IN: ${msg}`) 1324 | logEverywhere(`OSC IN: ${msg}`) 1325 | var msgArray = msg[0].split("/") 1326 | msgArray.shift() 1327 | obs.call("GetSceneItemId", { 1328 | sceneName: msgArray[0].split('_').join(' ').toString(), 1329 | sourceName: msgArray[1].split('_').join(' ').toString() 1330 | }).then(data => { 1331 | console.log(data) 1332 | var visible; 1333 | if(msg[1] === 0 || msg[1] === 'off' || msg[1] === '0' || msg[1] === 'false'){ 1334 | visible = false 1335 | } else if(msg[1] === 1 || msg[1] === 'on' || msg[1] === '1' || msg[1] === 'true'){ 1336 | visible = true 1337 | } 1338 | obs.call("SetSceneItemEnabled", { 1339 | 'sceneName': msgArray[0].split('_').join(' ').toString(), 1340 | 'sceneItemId': data.sceneItemId, 1341 | 'sceneItemEnabled': visible, 1342 | }).catch(() => { 1343 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Scene Name]/[Source Name]/visible 0 or 1, example: /Wide/VOX/visible 1") 1344 | logEverywhere(`Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Scene Name]/[Source Name]/visible 0 or 1, example: /Wide/VOX/visible 1\nOSC Recieved: ${msg}`) 1345 | 1346 | }) 1347 | }) 1348 | //getSceneItemId(msgArray[0].split('_').join(' ').toString(), msgArray[1].split('_').join(' ').toString()) 1349 | 1350 | } 1351 | //Triggers Filter Visibility On/Off 1352 | else if (msg[0].includes('filterVisibility')){ 1353 | console.log(`OSC IN: ${msg}`) 1354 | logEverywhere(`OSC IN: ${msg}`) 1355 | var msgArray = msg[0].split("/") 1356 | msgArray.shift() 1357 | var visiblef; 1358 | if(msg[1] === 0 || msg[1] === 'off' || msg[1] === '0' || msg[1] === 'false'){ 1359 | visiblef = false 1360 | } else if(msg[1] === 1 || msg[1] === 'on' || msg[1] === '1' || msg[1] === 'true'){ 1361 | visiblef = true 1362 | } 1363 | obs.call("SetSourceFilterEnabled", { 1364 | 'sourceName': msgArray[0].split('_').join(' '), 1365 | 'filterName': msgArray[1].split('_').join(' '), 1366 | 'filterEnabled': visiblef 1367 | }).catch(() => { 1368 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Source Name and Filter name. /[Scene Name]/[Source Name]/visible 0 or 1, example: /Wide/VOX/visible 1") 1369 | logEverywhere(`Error: Invalid Syntax. Make Sure There Are NO SPACES in Source Name and Filter name. /[Scene Name]/[Source Name]/visible 0 or 1, example: /Wide/VOX/visible 1\nOSC Recieved: ${msg}`) 1370 | 1371 | }) 1372 | } 1373 | 1374 | //Set Text 1375 | else if (msg[0].includes('setText')){ 1376 | obs.call("GetInputSettings", { 1377 | inputName : "text" 1378 | }).then(data => { 1379 | console.log(data) 1380 | }) 1381 | console.log(`OSC IN: ${msg}`) 1382 | logEverywhere(`OSC IN: ${msg}`) 1383 | var msgArray = msg[0].split("/") 1384 | msgArray.shift() 1385 | console.log(msgArray[0]) 1386 | console.log(msg[1]); 1387 | obs.call("SetInputSettings", { 1388 | 'inputName': msgArray[0].split('_').join(' '), 1389 | 'inputSettings':{ 1390 | 'text': msg[1].toString() 1391 | } 1392 | // 'font': { 1393 | // size: msg[2], 1394 | // face: msg[3] 1395 | // } 1396 | }).catch(() => { 1397 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Source Name and Filter name. [Source_Name]/setText [string], example: /text1/setText 'new text who dis?'") 1398 | logEverywhere(`Error: Invalid Syntax. Make Sure There Are NO SPACES in Source Name and Filter name. [Source_Name]/setText [string], example: /text1/setText 'new text who dis?'\nOSC Recieved: ${msg}`) 1399 | 1400 | }) 1401 | } 1402 | 1403 | //Triggers the Source Opacity (via Filter > Color Correction) 1404 | else if (msg[0].includes('opacity')){ 1405 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1406 | logEverywhere(`OSC IN: ${msg[0]} ${msg[1]}`) 1407 | var msgArray = msg[0].split("/") 1408 | msgArray.shift() 1409 | obs.call("SetSourceFilterSettings", { 1410 | 'sourceName': msgArray[0].split('_').join(' '), 1411 | 'filterName': msgArray[1].split('_').join(' '), 1412 | 'filterSettings': {'opacity' : msg[1]} 1413 | }).catch(() => { 1414 | console.log("ERROR: Opacity Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference") 1415 | logEverywhere(`ERROR: Opacity Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference\nOSC Recieved: ${msg}`) 1416 | 1417 | }) 1418 | } 1419 | 1420 | //Adjsuts the Source's Gamma via Color Correction Filter 1421 | else if (msg[0].includes('/gamma')){ 1422 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1423 | logEverywhere(`OSC IN: ${msg[0]} ${msg[1]}`) 1424 | var msgArray = msg[0].split("/") 1425 | msgArray.shift() 1426 | obs.call("SetSourceFilterSettings", { 1427 | 'sourceName': msgArray[0].split('_').join(' '), 1428 | 'filterName': msgArray[1].split('_').join(' '), 1429 | 'filterSettings': { 'gamma' : parseFloat(msg[1]) } 1430 | }).catch(() => { 1431 | console.log("ERROR: Opacity Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference") 1432 | logEverywhere(`ERROR: Gamma Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference\nOSC Recieved: ${msg}`) 1433 | 1434 | }) 1435 | } 1436 | //Adjsuts the Source's Contrast via Color Correction Filter 1437 | else if (msg[0].includes('/contrast')){ 1438 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1439 | logEverywhere(`OSC IN: ${msg[0]} ${msg[1]}`) 1440 | var msgArray = msg[0].split("/") 1441 | msgArray.shift() 1442 | obs.call("SetSourceFilterSettings", { 1443 | 'sourceName': msgArray[0].split('_').join(' '), 1444 | 'filterName': msgArray[1].split('_').join(' '), 1445 | 'filterSettings': { 'contrast' : parseFloat(msg[1]) } 1446 | }).catch(() => { 1447 | console.log("ERROR: Opacity Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference") 1448 | logEverywhere(`ERROR: Contrast Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference\nOSC Recieved: ${msg}`) 1449 | 1450 | }) 1451 | } 1452 | //Adjsuts the Source's Brightness via Color Correction Filter 1453 | else if (msg[0].includes('/brightness')){ 1454 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1455 | logEverywhere(`OSC IN: ${msg[0]} ${msg[1]}`) 1456 | var msgArray = msg[0].split("/") 1457 | msgArray.shift() 1458 | obs.call("SetSourceFilterSettings", { 1459 | 'sourceName': msgArray[0].split('_').join(' '), 1460 | 'filterName': msgArray[1].split('_').join(' '), 1461 | 'filterSettings': { 'brightness' : parseFloat(msg[1]) } 1462 | }).catch(() => { 1463 | console.log("ERROR: Opacity Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference") 1464 | logEverywhere(`ERROR: Brightness Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference\nOSC Recieved: ${msg}`) 1465 | 1466 | }) 1467 | } 1468 | //Adjsuts the Source's Saturation via Color Correction Filter 1469 | else if (msg[0].includes('/saturation')){ 1470 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1471 | logEverywhere(`OSC IN: ${msg[0]} ${msg[1]}`) 1472 | var msgArray = msg[0].split("/") 1473 | msgArray.shift() 1474 | obs.call("SetSourceFilterSettings", { 1475 | 'sourceName': msgArray[0].split('_').join(' '), 1476 | 'filterName': msgArray[1].split('_').join(' '), 1477 | 'filterSettings': { 'saturation' : parseFloat(msg[1]) } 1478 | }).catch(() => { 1479 | console.log("ERROR: Opacity Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference") 1480 | logEverywhere(`ERROR: Saturation Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference\nOSC Recieved: ${msg}`) 1481 | 1482 | }) 1483 | } 1484 | //Adjsuts the Source's Hue Shift via Color Correction Filter 1485 | else if (msg[0].includes('/hueShift')){ 1486 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1487 | logEverywhere(`OSC IN: ${msg[0]} ${msg[1]}`) 1488 | var msgArray = msg[0].split("/") 1489 | msgArray.shift() 1490 | obs.call("SetSourceFilterSettings", { 1491 | 'sourceName': msgArray[0].split('_').join(' '), 1492 | 'filterName': msgArray[1].split('_').join(' '), 1493 | 'filterSettings': { 'hue_shift' : parseFloat(msg[1]) } 1494 | }).catch(() => { 1495 | console.log("ERROR: Opacity Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference") 1496 | logEverywhere(`ERROR: Hue Shift Command Syntax is Incorrect. Refer to Node OBSosc Github for Reference\nOSC Recieved: ${msg}`) 1497 | 1498 | }) 1499 | } 1500 | 1501 | //Set Transition Type and Duration 1502 | else if (msg[0].includes('/transition')){ 1503 | console.log(`OSC IN: ${msg}`) 1504 | //logEverywhere(`OSC IN: ${msg}`) 1505 | var msgArray = msg[0].split("/") 1506 | msgArray.shift() 1507 | if (msgArray[0] === "Cut" || msgArray[0] === "Stinger") { 1508 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1509 | logEverywhere(`OSC IN: ${msg}`) 1510 | obs.call("SetCurrentSceneTransition", { 1511 | 'transitionName': msgArray[0].toString() 1512 | }).catch(() => { 1513 | console.log("Whoops") 1514 | logEverywhere(`Error: Transition Syntax Error. See Help > API`) 1515 | }) 1516 | } else { 1517 | if (msg[1] == undefined){ 1518 | obs.call("GetCurrentSceneTransition").then(data => { 1519 | var tranisionTime = data["transitionDuration"] 1520 | console.log(`OSC IN: ${msg[0]} ${msg[1]}\nCurrent Duration: ${tranisionTime}`) 1521 | logEverywhere(`OSC IN: ${msg[0]}\nCurrent Duration: ${tranisionTime}`) 1522 | }).catch(() => { 1523 | console.log("Whoops") 1524 | logEverywhere(`Error: Transition Syntax Error. See Help > API`) 1525 | }) 1526 | } else { 1527 | console.log(`OSC IN: ${msg[0]} ${msg[1]}`) 1528 | logEverywhere(`OSC IN: ${msg[0]} ${msg[1]}`) 1529 | } 1530 | var makeSpace = msgArray[0].split('_').join(' '); 1531 | obs.call("SetCurrentSceneTransition", { 1532 | 'transitionName': makeSpace.toString() 1533 | }) 1534 | if(msg.length === 2){ 1535 | console.log("duration is working properly") 1536 | obs.call("SetCurrentSceneTransitionDuration", { 1537 | 'transitionDuration': msg[1] 1538 | }).catch(() => { 1539 | console.log("Whoops") 1540 | logEverywhere(`Error: Transition Syntax Error. See Help > API`) 1541 | }) 1542 | } else if (msg.length === 1) { 1543 | return 1544 | } else { 1545 | console.log("ERROR: Invalid Transition Name. It's Case Sensitive. Or if it contains SPACES use '_' instead") 1546 | logEverywhere(`ERROR: Invalid Transition Name. See Help > API\nOSC Recieved: ${msg}`) 1547 | } 1548 | } 1549 | } 1550 | 1551 | //Source Position Translate 1552 | else if (msg[0].includes('position')){ 1553 | console.log(`OSC IN: ${msg}`) 1554 | logEverywhere(`OSC IN: ${msg}`) 1555 | var msgArray = msg[0].split("/") 1556 | msgArray.shift() 1557 | var x = msg[1] //+ 960 1558 | var y = msg[2] //- (msg[2] * 2) 1559 | let getSceneItemIdValue 1560 | obs.call("GetSceneItemId", { 1561 | sceneName: msgArray[0].split('_').join(' ').toString(), 1562 | sourceName: msgArray[1].split('_').join(' ').toString() 1563 | }).then(data => { 1564 | console.log(data) 1565 | getSceneItemIdValue = data.sceneItemId 1566 | let canvasW 1567 | let canvasH 1568 | obs.call("GetVideoSettings").then(data => { 1569 | canvasW = data.baseWidth 1570 | canvasH = data.baseHeight 1571 | obs.call("SetSceneItemTransform", { 1572 | 'sceneName': msgArray[0].toString().split('_').join(' '), 1573 | 'sceneItemId': getSceneItemIdValue, 1574 | sceneItemTransform: { 1575 | 'positionX': x + (canvasW / 2), 1576 | 'positionY': y + (canvasH / 2) 1577 | } 1578 | }).catch(() => { 1579 | console.log("ERROR: Invalid Position Syntax") 1580 | logEverywhere("ERROR: Invalid Position Name. See Help > API") 1581 | }) 1582 | }).catch(error => { 1583 | console.log("Couldn't get scene index") 1584 | }) 1585 | obs.call("GetSceneItemTransform", { 1586 | 'sceneName' : msgArray[0].toString().split('_').join(' '), 1587 | 'sceneItemId': getSceneItemIdValue 1588 | }).then(data => { 1589 | console.log(data) 1590 | }) 1591 | }).catch(error => { 1592 | console.log("Couldn't get scene index") 1593 | }) 1594 | } 1595 | //Source Scale Translate 1596 | else if (msg[0].includes('scale')){ 1597 | console.log(`OSC IN: ${msg}`) 1598 | logEverywhere(`OSC IN: ${msg}`) 1599 | var msgArray = msg[0].split("/") 1600 | msgArray.shift() 1601 | let getSceneItemIdValue 1602 | obs.call("GetSceneItemId", { 1603 | sceneName: msgArray[0].split('_').join(' ').toString(), 1604 | sourceName: msgArray[1].split('_').join(' ').toString() 1605 | }).then(data => { 1606 | console.log(data) 1607 | getSceneItemIdValue = data.sceneItemId 1608 | obs.call("SetSceneItemTransform", { 1609 | 'sceneName': msgArray[0].toString().split('_').join(' '), 1610 | 'sceneItemId': getSceneItemIdValue, 1611 | sceneItemTransform: { 1612 | 'scaleX': msg[1], 1613 | 'scaleY': msg[1] 1614 | } 1615 | }).catch(error => { 1616 | console.log("Couldn't get scene index") 1617 | }) 1618 | obs.call("GetSceneItemTransform", { 1619 | 'sceneName' : msgArray[0].toString().split('_').join(' '), 1620 | 'sceneItemId': getSceneItemIdValue 1621 | }).then(data => { 1622 | console.log(data) 1623 | }) 1624 | }).catch(error => { 1625 | console.log("Couldn't get scene index") 1626 | }) 1627 | } 1628 | //Source Rotation Translate 1629 | else if (msg[0].includes('rotate')){ 1630 | console.log(`OSC IN: ${msg}`) 1631 | logEverywhere(`OSC IN: ${msg}`) 1632 | var msgArray = msg[0].split("/") 1633 | msgArray.shift() 1634 | let getSceneItemIdValue 1635 | obs.call("GetSceneItemId", { 1636 | sceneName: msgArray[0].split('_').join(' ').toString(), 1637 | sourceName: msgArray[1].split('_').join(' ').toString() 1638 | }).then(data => { 1639 | console.log(data) 1640 | getSceneItemIdValue = data.sceneItemId 1641 | obs.call("SetSceneItemTransform", { 1642 | 'sceneName': msgArray[0].toString().split('_').join(' '), 1643 | 'sceneItemId': getSceneItemIdValue, 1644 | sceneItemTransform: { 1645 | 'rotation': msg[1], 1646 | } 1647 | }).catch(error => { 1648 | console.log("Couldn't get scene index") 1649 | }) 1650 | obs.call("GetSceneItemTransform", { 1651 | 'sceneName' : msgArray[0].toString().split('_').join(' '), 1652 | 'sceneItemId': getSceneItemIdValue 1653 | }).then(data => { 1654 | console.log(data) 1655 | }) 1656 | }).catch(error => { 1657 | console.log("Couldn't get scene index") 1658 | }) 1659 | } 1660 | 1661 | //Source Alignment 1662 | else if (msg[0].includes('alignment')){ 1663 | console.log(`OSC IN: ${msg}`) 1664 | logEverywhere(`OSC IN: ${msg}`) 1665 | var msgArray = msg[0].split("/") 1666 | msgArray.shift() 1667 | let getSceneItemIdValue 1668 | obs.call("GetSceneItemId", { 1669 | sceneName: msgArray[0].split('_').join(' ').toString(), 1670 | sourceName: msgArray[1].split('_').join(' ').toString() 1671 | }).then(data => { 1672 | console.log(data) 1673 | getSceneItemIdValue = data.sceneItemId 1674 | obs.call("SetSceneItemTransform", { 1675 | 'sceneName': msgArray[0].toString().split('_').join(' '), 1676 | 'sceneItemId': getSceneItemIdValue, 1677 | sceneItemTransform: { 1678 | 'alignment': msg[1], 1679 | } 1680 | }).catch(error => { 1681 | console.log("Couldn't get scene index") 1682 | }) 1683 | obs.call("GetSceneItemTransform", { 1684 | 'sceneName' : msgArray[0].toString().split('_').join(' '), 1685 | 'sceneItemId': getSceneItemIdValue 1686 | }).then(data => { 1687 | console.log(data) 1688 | }) 1689 | }).catch(error => { 1690 | console.log("Couldn't get scene index") 1691 | }) 1692 | } 1693 | 1694 | //Triggers Source UnMute 1695 | else if (msg[0].includes('unmute')){ 1696 | console.log(`OSC IN: ${msg}`) 1697 | logEverywhere(`OSC IN: ${msg}`) 1698 | var msgArray = msg[0].split("/") 1699 | msgArray.shift() 1700 | obs.call("SetInputMute", { 1701 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1702 | 'inputMuted': false, 1703 | }).catch(() => { 1704 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mute 0 or 1, example: /Audio/mute 1") 1705 | logEverywhere("ERROR: Invalid Unmute Syntax. See Help > API") 1706 | }) 1707 | } 1708 | //Triggers Source Mute 1709 | else if (msg[0].includes('mute')){ 1710 | console.log(`OSC IN: ${msg}`) 1711 | logEverywhere(`OSC IN: ${msg}`) 1712 | var msgArray = msg[0].split("/") 1713 | msgArray.shift() 1714 | obs.call("SetInputMute", { 1715 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1716 | 'inputMuted': true, 1717 | }).catch(() => { 1718 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mute 0 or 1, example: /Audio/mute 1") 1719 | logEverywhere("ERROR: Invalid Mute Syntax. See Help > API") 1720 | }) 1721 | } 1722 | //Triggers Source Mute Toggle 1723 | else if (msg[0].includes('audioToggle')){ 1724 | console.log(`OSC IN: ${msg}`) 1725 | logEverywhere(`OSC IN: ${msg}`) 1726 | var msgArray = msg[0].split("/") 1727 | msgArray.shift() 1728 | obs.call("ToggleInputMute", { 1729 | 'inputName': msgArray[0].split('_').join(' ').toString() 1730 | }).catch(() => { 1731 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mute 0 or 1, example: /Audio/mute 1") 1732 | logEverywhere("ERROR: Invalid Mute Syntax. See Help > API") 1733 | }) 1734 | } 1735 | //Adjust Source Volume 1736 | else if (msg[0].includes('volume')){ 1737 | console.log(`OSC IN: ${msg}`) 1738 | logEverywhere(`OSC IN: ${msg}`) 1739 | var msgArray = msg[0].split("/") 1740 | msgArray.shift() 1741 | obs.call("SetInputVolume", { 1742 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1743 | 'inputVolumeMul': msg[1], 1744 | }).catch(() => { 1745 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/volume 0-1, example: /Audio/volume 1") 1746 | logEverywhere("ERROR: Invalid Volume Syntax. See Help > API") 1747 | 1748 | }) 1749 | } 1750 | //Set Sources Audio Monitor Off 1751 | else if (msg[0].includes('monitorOff')){ 1752 | console.log(`OSC IN: ${msg}`) 1753 | logEverywhere(`OSC IN: ${msg}`) 1754 | var msgArray = msg[0].split("/") 1755 | msgArray.shift() 1756 | console.log(msgArray[0]) 1757 | obs.call("SetInputAudioMonitorType", { 1758 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1759 | 'monitorType': "OBS_MONITORING_TYPE_NONE", 1760 | }).catch(() => { 1761 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/volume 0-1, example: /Audio/volume 1") 1762 | logEverywhere("ERROR: Invalid Monitor Off Syntax. See Help > API") 1763 | 1764 | }) 1765 | } 1766 | //Set Sources Audio Monitor Only 1767 | else if (msg[0].includes('monitorOnly')){ 1768 | console.log(`OSC IN: ${msg}`) 1769 | logEverywhere(`OSC IN: ${msg}`) 1770 | var msgArray = msg[0].split("/") 1771 | msgArray.shift() 1772 | console.log(msgArray[0]) 1773 | obs.call("SetInputAudioMonitorType", { 1774 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1775 | 'monitorType': "OBS_MONITORING_TYPE_MONITOR_ONLY", 1776 | }).catch(() => { 1777 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/volume 0-1, example: /Audio/volume 1") 1778 | logEverywhere("ERROR: Invalid Monitor Only Syntax. See Help > API") 1779 | 1780 | }) 1781 | } 1782 | //Set Sources Audio MonitorandOutput 1783 | else if (msg[0].includes('monitorAndOutput')){ 1784 | console.log(`OSC IN: ${msg}`) 1785 | logEverywhere(`OSC IN: ${msg}`) 1786 | var msgArray = msg[0].split("/") 1787 | msgArray.shift() 1788 | console.log(msgArray[0]) 1789 | obs.call("SetInputAudioMonitorType", { 1790 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1791 | 'monitorType': "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT", 1792 | }).catch(() => { 1793 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/volume 0-1, example: /Audio/volume 1") 1794 | logEverywhere("ERROR: Invalid Monitor and Output Syntax. See Help > API") 1795 | 1796 | }) 1797 | } 1798 | 1799 | //Open Video Mix Projector 1800 | else if (msg[0].includes('openProjector')){ 1801 | let projectorType 1802 | console.log(`OSC IN: ${msg}`) 1803 | logEverywhere(`OSC IN: ${msg}`) 1804 | var msgArray = msg[0].split("/") 1805 | msgArray.shift() 1806 | if (msgArray[0] == "Source" || msgArray[0] == "Scene") { 1807 | obs.call("OpenSourceProjector", { 1808 | //'type': msgArray[0].split('_').join(' ').toString(), 1809 | 'sourceName': msg[2], 1810 | 'monitorIndex': msg[1] 1811 | }).catch(() => { 1812 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaPlay, example: /Media_Source/mediaPlay") 1813 | logEverywhere("ERROR: Invalid Open Projector Syntax. See Help > API") 1814 | 1815 | }) 1816 | return 1817 | } 1818 | if (msgArray[0] == "StudioProgram") { 1819 | projectorType = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PROGRAM" 1820 | } else if (msgArray[0] == "Preview") { 1821 | projectorType = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_PREVIEW" 1822 | } else if (msgArray[0] == "Multiview") { 1823 | projectorType = "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW" 1824 | } 1825 | obs.call("OpenVideoMixProjector", { 1826 | 'videoMixType': projectorType, 1827 | 'monitorIndex': msg[1], 1828 | }).catch(() => { 1829 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaPlay, example: /Media_Source/mediaPlay") 1830 | logEverywhere("ERROR: Invalid Open Projector Syntax. See Help > API") 1831 | 1832 | }) 1833 | } 1834 | 1835 | 1836 | 1837 | //Media Play 1838 | else if (msg[0].includes('mediaPlay')){ 1839 | console.log(`OSC IN: ${msg}`) 1840 | logEverywhere(`OSC IN: ${msg}`) 1841 | var msgArray = msg[0].split("/") 1842 | msgArray.shift() 1843 | obs.call("TriggerMediaInputAction", { 1844 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1845 | 'mediaAction': "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY", 1846 | }).catch(() => { 1847 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaPlay, example: /Media_Source/mediaPlay") 1848 | logEverywhere("ERROR: Invalid Media Play Syntax. See Help > API") 1849 | 1850 | }) 1851 | } 1852 | //Media Pause 1853 | else if (msg[0].includes('mediaPause')){ 1854 | console.log(`OSC IN: ${msg}`) 1855 | logEverywhere(`OSC IN: ${msg}`) 1856 | var msgArray = msg[0].split("/") 1857 | msgArray.shift() 1858 | obs.call("TriggerMediaInputAction", { 1859 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1860 | 'mediaAction': "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE", 1861 | }).catch(() => { 1862 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaPause, example: /Media_Source/mediaPause") 1863 | logEverywhere("ERROR: Invalid Media Pause Syntax. See Help > API") 1864 | 1865 | }) 1866 | } 1867 | //Media Restart 1868 | else if (msg[0].includes('mediaRestart')){ 1869 | console.log(`OSC IN: ${msg}`) 1870 | logEverywhere(`OSC IN: ${msg}`) 1871 | var msgArray = msg[0].split("/") 1872 | msgArray.shift() 1873 | obs.call("TriggerMediaInputAction", { 1874 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1875 | 'mediaAction': "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART", 1876 | }).catch(() => { 1877 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaRestart, example: /Media_Source/mediaRestart") 1878 | logEverywhere("ERROR: Invalid Media Restart Syntax. See Help > API") 1879 | 1880 | }) 1881 | } 1882 | //Media Stop 1883 | else if (msg[0].includes('mediaStop')){ 1884 | console.log(`OSC IN: ${msg}`) 1885 | logEverywhere(`OSC IN: ${msg}`) 1886 | var msgArray = msg[0].split("/") 1887 | msgArray.shift() 1888 | obs.call("TriggerMediaInputAction", { 1889 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1890 | 'mediaAction': "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP", 1891 | }).catch(() => { 1892 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaStop, example: /Media_Source/mediaStop") 1893 | logEverywhere("ERROR: Invalid Media Stop Syntax. See Help > API") 1894 | 1895 | }) 1896 | } 1897 | //Media Cursor 1898 | else if (msg[0].includes('mediaCursor')){ 1899 | console.log(`OSC IN: ${msg}`) 1900 | logEverywhere(`OSC IN: ${msg}`) 1901 | var msgArray = msg[0].split("/") 1902 | msgArray.shift() 1903 | obs.call("TriggerMediaInputAction", { 1904 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1905 | 'mediaCursor': msg[1], 1906 | }).catch(() => { 1907 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaStop, example: /Media_Source/mediaStop") 1908 | logEverywhere("ERROR: Invalid Media Stop Syntax. See Help > API") 1909 | 1910 | }) 1911 | } 1912 | //Browser Refresh 1913 | else if (msg[0].includes('refreshBrowser')){ 1914 | console.log(`OSC IN: ${msg}`) 1915 | logEverywhere(`OSC IN: ${msg}`) 1916 | var msgArray = msg[0].split("/") 1917 | msgArray.shift() 1918 | obs.call("PressInputPropertiesButton", { 1919 | 'inputName': msgArray[0].split('_').join(' ').toString(), 1920 | 'propertyName': "refreshnocache" 1921 | }).catch(() => { 1922 | console.log("Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Source Name]/mediaStop, example: /Media_Source/mediaStop") 1923 | logEverywhere("ERROR: Invalid Refresh Browser Syntax. See Help > API") 1924 | 1925 | }) 1926 | } 1927 | 1928 | //Set Source Collection 1929 | else if (msg[0] === "/setSceneCollection"){ 1930 | obs.call("SetCurrentSceneCollection", { 1931 | 'sceneCollectionName': msg[1].toString() 1932 | }).catch(() => { 1933 | logEverywhere("ERROR: Invalid) Refresh Browser Syntax. See Help > API") 1934 | }) 1935 | } 1936 | //Set Profile 1937 | else if (msg[0] === "/setProfile"){ 1938 | obs.call("SetCurrentProfile", { 1939 | 'profileName': msg[1].toString() 1940 | }).catch(() => { 1941 | logEverywhere("ERROR: Invalid) Refresh Browser Syntax. See Help > API") 1942 | }) 1943 | } 1944 | //Set Recording File Name (Depricated) 1945 | // else if (msg[0] === "/recFileName"){ 1946 | // obs.call("SetFilenameFormatting", { 1947 | // 'filename-formatting': msg[1].toString() 1948 | // }).catch(() => { 1949 | // logEverywhere("ERROR: Invalid) Refresh Browser Syntax. See Help > API") 1950 | // }) 1951 | // } 1952 | 1953 | // ----- TouchOSC COMMANDS: ------ 1954 | 1955 | //Log ALL Scene Items 1956 | else if (msg[0] === '/logAllSceneItems'){ 1957 | return obs.call("GetSceneList").then(data => { 1958 | console.log(`OSC IN: ${msg}`) 1959 | logEverywhere(`OSC IN: ${msg}`) 1960 | logEverywhere("--- Available Scene Items: ---") 1961 | // logEverywhere(data.sceneName) 1962 | // logEverywhere(msg[1]) 1963 | data.scenes.forEach(i => { 1964 | obs.call("GetSceneItemList", { 1965 | 'sceneName': i.sceneName, 1966 | }).then(data => { 1967 | data.sceneItems.forEach(j => { 1968 | console.log(j) 1969 | logEverywhere(j.sourceName) 1970 | }) 1971 | }).catch(() => { 1972 | console.log("ERROR: Invalis Position Syntax") 1973 | logEverywhere("ERROR: Invalid Log Scene Items Syntax. See Help > API") 1974 | }) 1975 | }) 1976 | 1977 | }) 1978 | } 1979 | //Create Scene Item 1980 | else if (msg[0] === '/addSceneItem'){ 1981 | return obs.call("GetCurrentProgramScene").then(data => { 1982 | console.log(`OSC IN: ${msg}`) 1983 | logEverywhere(`OSC IN: ${msg}`) 1984 | // logEverywhere(data.sceneName) 1985 | // logEverywhere(msg[1]) 1986 | obs.call("CreateInput", { 1987 | 'sceneName': data.currentProgramSceneName, 1988 | 'inputName': msg[1].toString(), 1989 | 'inputKind': msg[2].toString() 1990 | }).catch(() => { 1991 | console.log("ERROR: Invalis Position Syntax") 1992 | logEverywhere("ERROR: Invalid Add Scene Item Syntax. See Help > API") 1993 | }) 1994 | }) 1995 | } 1996 | //Source Position Select Move 1997 | else if (msg[0] === '/move'){ 1998 | return obs.call("GetCurrentProgramScene").then(data => { 1999 | console.log(`OSC IN: ${msg}`) 2000 | logEverywhere(`OSC IN: ${msg}`) 2001 | var msgArray = msg[0].split("/") 2002 | msgArray.shift() 2003 | var x = Math.floor((msg[2]*2000)+540) 2004 | var y = Math.floor((msg[1]*2000) + 960) 2005 | console.log(x + " " + y) 2006 | logEverywhere(x + " " + y) 2007 | let currentProgramSceneName = data.currentProgramSceneName 2008 | console.log(`${currentProgramSceneName} ${currentSceneItem[0].sceneItemId}`) 2009 | obs.call("SetSceneItemTransform", { 2010 | 'sceneName': currentProgramSceneName.toString().split('_').join(' '), 2011 | 'sceneItemId': currentSceneItem[0].sceneItemId, 2012 | sceneItemTransform: { 2013 | 'positionX': x, 2014 | 'positionY': y 2015 | } 2016 | }).catch(error => { 2017 | console.log("Couldn't get scene index") 2018 | }) 2019 | }) 2020 | } 2021 | //Source Position Select MoveX 2022 | else if (msg[0] === '/movex'){ 2023 | return obs.call("GetCurrentProgramScene").then(data => { 2024 | console.log(`OSC IN: ${msg}`) 2025 | logEverywhere(`OSC IN: ${msg}`) 2026 | var msgArray = msg[0].split("/") 2027 | msgArray.shift() 2028 | var x = Math.floor((msg[1]*2000)+540) 2029 | var y = Math.floor((msg[1]*2000) + 960) 2030 | console.log(x + " " + y) 2031 | logEverywhere(x + " " + y) 2032 | let currentProgramSceneName = data.currentProgramSceneName 2033 | console.log(`${currentProgramSceneName} ${currentSceneItem[0].sceneItemId}`) 2034 | obs.call("SetSceneItemTransform", { 2035 | 'sceneName': currentProgramSceneName.toString().split('_').join(' '), 2036 | 'sceneItemId': currentSceneItem[0].sceneItemId, 2037 | sceneItemTransform: { 2038 | 'positionX': x 2039 | } 2040 | }).catch(error => { 2041 | console.log("Couldn't get scene index") 2042 | }) 2043 | }) 2044 | } 2045 | //Source Position Select MoveY 2046 | else if (msg[0] === '/movey'){ 2047 | return obs.call("GetCurrentProgramScene").then(data => { 2048 | console.log(`OSC IN: ${msg}`) 2049 | logEverywhere(`OSC IN: ${msg}`) 2050 | var msgArray = msg[0].split("/") 2051 | msgArray.shift() 2052 | var x = Math.floor((msg[2]*2000)) 2053 | var y = Math.floor((msg[1]*2000) + 960) 2054 | console.log(x + " " + y) 2055 | logEverywhere(x + " " + y) 2056 | let currentProgramSceneName = data.currentProgramSceneName 2057 | console.log(`${currentProgramSceneName} ${currentSceneItem[0].sceneItemId}`) 2058 | obs.call("SetSceneItemTransform", { 2059 | 'sceneName': currentProgramSceneName.toString().split('_').join(' '), 2060 | 'sceneItemId': currentSceneItem[0].sceneItemId, 2061 | sceneItemTransform: { 2062 | 'positionY': y 2063 | } 2064 | }).catch(error => { 2065 | console.log("Couldn't get scene index") 2066 | }) 2067 | }) 2068 | } 2069 | //Source Align 2070 | else if (msg[0] === '/align'){ 2071 | return obs.call("GetCurrentProgramScene").then(data => { 2072 | console.log(`OSC IN: ${msg}`) 2073 | logEverywhere(`OSC IN: ${msg}`) 2074 | console.log("Scene NAme: " + data.currentProgramSceneName) 2075 | console.log("Scene NAme: " + currentSceneItem) 2076 | console.log(currentSceneItem) 2077 | let currentProgramSceneName = data.currentProgramSceneName 2078 | console.log(`${currentProgramSceneName} ${currentSceneItem[0].sceneItemId}`) 2079 | obs.call("SetSceneItemTransform", { 2080 | 'sceneName': currentProgramSceneName.toString().split('_').join(' '), 2081 | 'sceneItemId': currentSceneItem[0].sceneItemId, 2082 | sceneItemTransform: { 2083 | 'alignment': msg[1] 2084 | } 2085 | }).catch(error => { 2086 | console.log("Couldn't get scene index") 2087 | }) 2088 | }) 2089 | } 2090 | //Set Transition Override 2091 | else if(msg[0].includes('/transOverrideType')){ 2092 | console.log(`OSC IN: ${msg}`) 2093 | logEverywhere(`OSC IN: ${msg}`) 2094 | var msgArray = msg[0].split("/") 2095 | msgArray.shift() 2096 | console.log("Messge array: " + msgArray) 2097 | //logEverywhere("Messge array: " + msgArray) 2098 | return obs.call("GetCurrentProgramScene").then(data => { 2099 | obs.call("SetSceneSceneTransitionOverride", { 2100 | 'sceneName': data.currentProgramSceneName, 2101 | 'transitionName': msgArray[1].split('_').join(' ').toString(), 2102 | 'transitionDuration': msg[1] 2103 | }).catch(() => { 2104 | logEverywhere("ERROR: Invalid Transition Override Type Syntax. See Help > API") 2105 | }) 2106 | }) 2107 | } 2108 | //Source Size 2109 | else if (msg[0] === '/size'){ 2110 | return obs.call("GetCurrentProgramScene").then(data => { 2111 | console.log(`OSC IN: ${msg}`) 2112 | logEverywhere(`OSC IN: ${msg}`) 2113 | let currentProgramSceneName = data.currentProgramSceneName 2114 | console.log(`${currentProgramSceneName} ${currentSceneItem[0].sceneItemId}`) 2115 | obs.call("SetSceneItemTransform", { 2116 | 'sceneName': currentProgramSceneName.toString().split('_').join(' '), 2117 | 'sceneItemId': currentSceneItem[0].sceneItemId, 2118 | sceneItemTransform: { 2119 | 'scaleX': msg[1], 2120 | 'scaleY': msg[1] 2121 | } 2122 | }).catch(error => { 2123 | console.log("Couldn't get scene index") 2124 | }) 2125 | }) 2126 | } 2127 | //Source Rotate 2128 | else if (msg[0] === '/spin'){ 2129 | return obs.call("GetCurrentProgramScene").then(data => { 2130 | console.log(`OSC IN: ${msg}`) 2131 | logEverywhere(`OSC IN: ${msg}`) 2132 | let currentProgramSceneName = data.currentProgramSceneName 2133 | console.log(`${currentProgramSceneName} ${currentSceneItem[0].sceneItemId}`) 2134 | obs.call("SetSceneItemTransform", { 2135 | 'sceneName': currentProgramSceneName.toString().split('_').join(' '), 2136 | 'sceneItemId': currentSceneItem[0].sceneItemId, 2137 | sceneItemTransform: { 2138 | 'rotation': msg[1] 2139 | } 2140 | }).catch(error => { 2141 | console.log("Couldn't get scene index") 2142 | }) 2143 | }) 2144 | } 2145 | else if (msg[0] === '/getSceneItemTransform'){ 2146 | obs.call("GetSceneItemTransform", { 2147 | sceneItemId: sceneItemId, 2148 | sceneName: "Scene Last" 2149 | }).then(data => { 2150 | logEverywhere(JSON.stringify(data)) 2151 | }).catch(err => { 2152 | console.log(err) 2153 | }) 2154 | } 2155 | 2156 | //Trigger Hotkey 2157 | else if (msg[0].includes('/hotkey')){ 2158 | let shift 2159 | let control 2160 | let alt 2161 | let command 2162 | let hotkey 2163 | var msgArray = msg[0].split("/") 2164 | msgArray.shift() 2165 | msgArray.pop() 2166 | hotkey = msgArray[1].toUpperCase() 2167 | console.log(msgArray) 2168 | if (msgArray[0].includes("shift")){ 2169 | shift = true 2170 | } else { 2171 | shift = false 2172 | } 2173 | if (msgArray[0].includes("control")){ 2174 | control = true 2175 | } else { 2176 | control = false 2177 | } 2178 | if (msgArray[0].includes("alt")){ 2179 | alt = true 2180 | } else { 2181 | alt = false 2182 | } 2183 | if (msgArray[0].includes("command")){ 2184 | command = true 2185 | } else { 2186 | command = false 2187 | } 2188 | obs.call("TriggerHotkeyByKeySequence", { 2189 | keyId: `OBS_KEY_${msgArray[1]}`, 2190 | keyModifiers:{ 2191 | shift: shift, 2192 | control: control, 2193 | alt: alt, 2194 | command: command 2195 | } 2196 | 2197 | }).then(() => { 2198 | console.log(`${shift} + ${control} + ${alt} + ${command} + ${hotkey}`) 2199 | }).catch(err => { 2200 | logEverywhere(`Error: There is no Hotkey for ${msgArray[0]} + ${msgArray[1]}`) 2201 | }) 2202 | } 2203 | 2204 | //Fit to Screen 2205 | else if (msg[0] === '/fitToScreen'){ 2206 | return obs.call("GetCurrentProgramScene").then(data => { 2207 | console.log(`OSC IN: ${msg}`) 2208 | logEverywhere(`OSC IN: ${msg}`) 2209 | let currentScene = data 2210 | obs.call("GetVideoSettings").then(data => { 2211 | 2212 | obs.call("SetSceneItemTransform", { 2213 | "sceneName": currentScene.currentProgramSceneName.toString(), 2214 | "sceneItemId": sceneItemId, 2215 | "sceneItemTransform": { 2216 | alignment: 5, 2217 | positionX: 0, 2218 | positionY: 0, 2219 | boundsType: 'OBS_BOUNDS_NONE', 2220 | //boundsWidth: data.baseWidth, 2221 | //boundsHeight: data.baseHeight, 2222 | width: 1920, 2223 | height: 1080 2224 | } 2225 | }).catch((err) => { 2226 | console.log("Error set: Select A Scene Item in OBS for Size" + err) 2227 | logEverywhere("ERROR: Invalid Size Syntax. See Help > API") 2228 | }) 2229 | 2230 | }).catch(() => { 2231 | console.log("Error get: Select A Scene Item in OBS for Size") 2232 | logEverywhere("ERROR: Invalid Size Syntax. See Help > API") 2233 | }) 2234 | }).catch(() => { 2235 | console.log("Error: Select A Scene Item in OBS for Size") 2236 | logEverywhere("ERROR: Invalid Size Syntax. See Help > API") 2237 | }) 2238 | } 2239 | //Duplicate Current Scene 2240 | else if (msg[0] === '/duplicateCurrentScene'){ 2241 | return obs.call("GetCurrentProgramScene").then(data => { 2242 | console.log(`OSC IN: ${msg}`) 2243 | logEverywhere(`OSC IN: ${msg}`) 2244 | let currentScene = data 2245 | console.log(currentScene) 2246 | console.log(currentScene.sources[0].sceneName) 2247 | obs.call("CreateScene", { 2248 | sceneName: `${currentScene.sceneName} 2` 2249 | }).then(() => { 2250 | currentScene.sources.forEach(item => { 2251 | obs.call("DuplicateSceneItem", { 2252 | fromScene: currentScene.sceneName, 2253 | toScene: `${currentScene.sceneName} 2`, 2254 | item: { 2255 | name: item.sceneName, 2256 | id: item.id 2257 | } 2258 | }) 2259 | }) 2260 | }).catch((err) => { 2261 | console.log(err) 2262 | if (err.error === "scene with this name already exists"){ 2263 | obs.call("CreateScene", { 2264 | sceneName: `${currentScene.sceneName} 2 3` 2265 | }).then(() => { 2266 | currentScene.sources.forEach(item => { 2267 | obs.call("DuplicateSceneItem", { 2268 | fromScene: currentScene.sceneName, 2269 | toScene: `${currentScene.sceneName} 2 3`, 2270 | item: { 2271 | name: item.sceneName, 2272 | id: item.id 2273 | } 2274 | }) 2275 | }) 2276 | }).catch(()=>{console.log("Same Name")}) 2277 | } 2278 | console.log("Error: Select A Scene Item in OBS for Size") 2279 | }) 2280 | }).catch(() => { 2281 | console.log("Error: Select A Scene Item in OBS for Size") 2282 | logEverywhere("ERROR: Invalid Size Syntax. See Help > API") 2283 | }) 2284 | } 2285 | 2286 | 2287 | // Rename Source 2288 | else if (msg[0] === '/rename'){ 2289 | logEverywhere(`OSC IN: ${msg}`) 2290 | obs.call('SetInputName', { 2291 | inputName: msg[1].split("_").join(" ").toString(), 2292 | newInputName: msg[2].split("_").join(" ").toString() 2293 | }).then(() => { 2294 | logEverywhere(`Renamed ${msg[1]} to ${msg[2]}`) 2295 | }).catch((err) => { 2296 | console.log(err) 2297 | }) 2298 | } 2299 | 2300 | // Send CC 2301 | else if (msg[0] === '/sendCC'){ 2302 | logEverywhere(`OSC IN: ${msg}`) 2303 | obs.call('SendStreamCaption', { 2304 | captionText: msg[1].split("_").join(" ").toString() 2305 | }).then(() => { 2306 | logEverywhere(`Captions "${msg[1]}" were sent.`) 2307 | }).catch(() => { 2308 | console.log("Error: Select A Scene Item in OBS for Size") 2309 | logEverywhere("ERROR: Invalid CC Syntax. See Help > API") 2310 | }) 2311 | } 2312 | 2313 | //Active Scene Item Visibility by Index 2314 | else if (msg[0].includes("/activeSceneItemVisibility")){ 2315 | let visible 2316 | var msgArray = msg[0].split("/") 2317 | let sceneArray = [] 2318 | msgArray.shift() 2319 | msgArray.pop() 2320 | if (msg[1] == "1" || msg[1] == 1){ 2321 | visible = true 2322 | } else if (msg[1] == "0" || msg[1] == 0){ 2323 | visible = false 2324 | } 2325 | obs.call("GetSceneItemList") 2326 | .then(data => { 2327 | data.sceneItems.forEach(e => { 2328 | sceneArray.push(e['sourceName'].toString()) 2329 | }) 2330 | }) 2331 | .then(() => { 2332 | sceneArray.reverse() 2333 | console.log(msgArray[0]) 2334 | obs.call("SetSceneItemProperties", { 2335 | 'item': sceneArray[parseInt(msgArray[0])], 2336 | 'visible': visible, 2337 | }).catch(() => { 2338 | console.log("yes") 2339 | logEverywhere(`Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Scene Name]/[Source Name]/visible 0 or 1, example: /Wide/VOX/visible 1\nOSC Recieved: ${msg}`) 2340 | 2341 | }) 2342 | }) 2343 | } 2344 | 2345 | //Scene Item Visibility by Index 2346 | else if (msg[0].includes("/itemVisibility")){ 2347 | let visible 2348 | var msgArray = msg[0].split("/") 2349 | msgArray.shift() 2350 | let sceneName = msgArray[0] 2351 | let sceneArray = [] 2352 | msgArray.shift() 2353 | msgArray.pop() 2354 | if (msg[1] == "1" || msg[1] == 1){ 2355 | visible = true 2356 | } else if (msg[1] == "0" || msg[1] == 0){ 2357 | visible = false 2358 | } 2359 | obs.call("GetSceneItemList", { 2360 | 'sceneName': sceneName.split('_').join(' ').toString() 2361 | }) 2362 | .then(data => { 2363 | data.sceneItems.forEach(e => { 2364 | sceneArray.push(e['sourceName'].toString()) 2365 | }) 2366 | }) 2367 | .then(() => { 2368 | sceneArray.reverse() 2369 | console.log(msgArray[0]) 2370 | obs.call("SetSceneItemEnabled", { 2371 | 'sceneName': sceneName.split('_').join(' ').toString(), 2372 | 'sceneItem': sceneArray[parseInt(msgArray[0])], 2373 | 'sceneItemEnabled': visible, 2374 | }).catch(() => { 2375 | console.log("yes") 2376 | logEverywhere(`Error: Invalid Syntax. Make Sure There Are NO SPACES in Scene Name and Source Name. /[Scene Name]/[Source Name]/visible 0 or 1, example: /Wide/VOX/visible 1\nOSC Recieved: ${msg}`) 2377 | 2378 | }) 2379 | }) 2380 | } 2381 | 2382 | //Get Text (Freetype) repeatedly 2383 | else if (msg[0].includes('/getTextFreetype')){ 2384 | var msgArray = msg[0].split("/") 2385 | msgArray.shift() 2386 | console.log(msgArray[0]) 2387 | function getTextLoop(){ 2388 | obs.call('GetInputSettings', { 2389 | inputName: msgArray[0].split('_').join(' ').toString() 2390 | }).then((data) => { 2391 | 2392 | client.send(`/${msgArray[0]}text`, data.inputSettings.text, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2393 | if (err) console.log(err); 2394 | }) 2395 | logEverywhere(`OSC OUT: /${msgArray[0]}text ${data.inputSettings.text}`) 2396 | if (msg[1] == 0){ 2397 | clearInterval(loopGetText) 2398 | logEverywhere("STOPPPPP") 2399 | } 2400 | }).catch((err) =>{ 2401 | console.log(err) 2402 | }) 2403 | } 2404 | 2405 | if (msg[1] == 1){ 2406 | let loopGetText = setInterval(getTextLoop,500) 2407 | loopGetText 2408 | } 2409 | 2410 | 2411 | } 2412 | 2413 | //Get Text (GDI) repeatedly 2414 | else if (msg[0].includes('/getTextGDI')){ 2415 | var msgArray = msg[0].split("/") 2416 | msgArray.shift() 2417 | console.log(msgArray[0]) 2418 | function getTextLoop(){ 2419 | obs.call('GetInputSettings', { 2420 | inputName: msgArray[0].split('_').join(' ').toString() 2421 | }).then((data) => { 2422 | 2423 | client.send(`/${msgArray[0]}text`, data.inputSettings.text, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2424 | if (err) console.log(err); 2425 | }) 2426 | logEverywhere(`OSC OUT: /${msgArray[0]}text ${data.text}`) 2427 | if (msg[1] == 0){ 2428 | clearInterval(loopGetText) 2429 | logEverywhere("STOPPPPP") 2430 | } 2431 | }).catch((err) =>{ 2432 | console.log(err) 2433 | }) 2434 | } 2435 | 2436 | if (msg[1] == 1){ 2437 | let loopGetText = setInterval(getTextLoop,500) 2438 | loopGetText 2439 | } 2440 | 2441 | 2442 | } 2443 | 2444 | //Start Output NDI 2445 | else if (msg[0] === "/startOutput"){ 2446 | obs.call('StartOutput', { 2447 | outputName: `${msg[1]}` 2448 | }).catch((err)=>{ 2449 | console.log(err) 2450 | }) 2451 | } 2452 | //Stop Output NDI 2453 | else if (msg[0] === "/stopOutput"){ 2454 | obs.call('StopOutput', { 2455 | outputName: `${msg[1]}` 2456 | }).catch((err)=>{ 2457 | console.log(err) 2458 | }) 2459 | } 2460 | //List Outputs 2461 | else if (msg[0] === "/listOutputs"){ 2462 | logEverywhere("--List of Outputs names:--") 2463 | obs.call('GetOutputList').then((data)=>{ 2464 | data.outputs.forEach((i) => { 2465 | logEverywhere(i.outputName) 2466 | }) 2467 | }).catch((err)=>{ 2468 | console.log(err) 2469 | }) 2470 | } 2471 | 2472 | //Take Screenshot 2473 | else if (msg[0] === "/takeScreenshot"){ 2474 | obs.call("GetCurrentProgramScene").then(data => { 2475 | obs.call("SaveSourceScreenshot", { 2476 | sourceName: data.currentProgramSceneName, 2477 | imageFormat: "png", 2478 | imageFilePath: app.getPath('documents') + `/OBS_Screenshot${Date.now()}.png`, 2479 | 2480 | }).catch((err) =>{ 2481 | console.log(err) 2482 | }) 2483 | }).catch((err) =>{ 2484 | console.log(err) 2485 | }) 2486 | logEverywhere(`Screenshot Taken\nOSC Recieved: ${msg}`) 2487 | } 2488 | 2489 | //Open External File 2490 | else if (msg[0] === "/openExternal"){ 2491 | const { shell } = require('electron') 2492 | logEverywhere(`Open External ${msg[1]}\nOSC Recieved: ${msg}`) 2493 | shell.openExternal(`${msg[1]}`).catch((err) =>{logEverywhere(err)}) 2494 | } 2495 | 2496 | //Simulate Keypress 2497 | else if (msg[0] === "/keypress"){ 2498 | logEverywhere(`Keypress ${msg[1]}\nOSC Recieved: ${msg}`) 2499 | if (msg[1].includes(',')){ 2500 | let msgArray = msg[1].split(",") 2501 | if (msg[1].includes('shift') || msg[1].includes('control') || msg[1].includes('alt') || msg[1].includes('@55')){ 2502 | ks.sendCombination(msgArray).catch(err => { 2503 | logEverywhere("Error: Make sure Java is installed and restart computer") 2504 | }); 2505 | console.log(msgArray) 2506 | } else { 2507 | ks.sendKeys(msgArray).catch(err => { 2508 | logEverywhere("Error: Make sure Java is installed and restart computer") 2509 | }); 2510 | console.log("justkeys") 2511 | } 2512 | } else { 2513 | ks.sendKey(msg[1].toString()).catch(err => { 2514 | logEverywhere("Error: Make sure Java is installed and restart computer") 2515 | }) 2516 | console.log("single key") 2517 | } 2518 | } 2519 | 2520 | //Get Source Settings 2521 | else if (msg[0] === ('/getSourceSettings')){ 2522 | obs.call('GetInputSettings', { 2523 | inputName: msg[1].split("_").join(" ").toString() 2524 | }).then((data) => { 2525 | let json = JSON.stringify(data) 2526 | json = json.split(",").join("\n") 2527 | logEverywhere(`Get Source Settings \nOSC Recieved: ${msg}`) 2528 | logEverywhere(json) 2529 | }).catch((err) => { 2530 | console.log(err) 2531 | }) 2532 | } 2533 | 2534 | // //Next Media 2535 | // else if (msg[0] === ('/nextMedia')){ 2536 | // obs.call('NextMedia', { 2537 | // sourceName: msg[1].split("_").join(" ").toString() 2538 | // }) 2539 | // } 2540 | 2541 | // //Previous Media 2542 | // else if (msg[0] === ('/previousMedia')){ 2543 | // obs.call('PreviousMedia', { 2544 | // sourceName: msg[1].split("_").join(" ").toString() 2545 | // }) 2546 | // } 2547 | 2548 | //Slideshow Update 2549 | // else if (msg[0] === "/slideshowSpeed"){ 2550 | // obs.call('SetSourceSettings', { 2551 | // sourceName: 'img', 2552 | // sourceSettings: { 2553 | // transitionSpeed: msg[1] 2554 | // } 2555 | // }).then(() => {logEverywhere(`Slideshow Speed at ${msg[1]}`)}) 2556 | // .catch((err) => {console.error(err);}) 2557 | // } 2558 | 2559 | 2560 | 2561 | //Log Error 2562 | else { 2563 | console.log("Error: Invalid OSC command. Please refer to Node OBSosc on Github for Command List") 2564 | logEverywhere("Error: Invalid OSC command. Please refer to OBSosc OSC Command List in 'Help' > 'API'") 2565 | } 2566 | }); 2567 | 2568 | //OBS -> OSC Client (OUT) 2569 | obs.on('CurrentProgramSceneChanged', data => { 2570 | 2571 | 2572 | 2573 | obs.call('GetCurrentProgramScene').then(data => { 2574 | currentScene = data.currentProgramSceneName 2575 | }) 2576 | 2577 | if(enableObs2App === "false"){ 2578 | enableObs2App = false 2579 | } else if(enableObs2App === "true"){ 2580 | enableObs2App = true 2581 | } 2582 | //sceneNow = data['fromScene'] 2583 | // if (!data['fromScene']){ 2584 | // sceneNow = currentScene 2585 | // } 2586 | //console.log(currentScene + " " + sceneNow) 2587 | //logEverywhere(`New Active Scene: ${currentScene}`) 2588 | if (enableObs2App && !isTouchOSC){ 2589 | client.send(`${oscOutPrefix}${data.sceneName.split(' ').join('_').toString()}${oscOutSuffix}`, 1, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2590 | if (err) console.log(err); 2591 | }); 2592 | } else if (enableObs2App && isTouchOSC){ 2593 | 2594 | //Update TouchOSC Scenes 2595 | for (let i = 0; i < 50; i++){ 2596 | client.send(`/item/${i}/visibility`, 0, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2597 | if (err) console.log(err); 2598 | }) 2599 | client.send(`/item/${i}/name`, "-", (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2600 | if (err) console.log(err); 2601 | }) 2602 | } 2603 | 2604 | let sceneArray = [] 2605 | let visibleArray = [] 2606 | obs.call("GetCurrentProgramScene").then(data => { 2607 | currentScene = data.currentProgramSceneName 2608 | obs.call("GetSceneItemList", { 2609 | sceneName: currentScene 2610 | }).then(data => { 2611 | console.log(data.sceneItems) 2612 | data.sceneItems.forEach(e => { 2613 | sceneArray.push(e['sourceName'].toString()) 2614 | }) 2615 | }) 2616 | .then(() => { 2617 | sceneArray.reverse() 2618 | console.log("testing testing") 2619 | console.log(sceneArray) 2620 | sceneArray.forEach((element, index) => { 2621 | let currentVisible 2622 | obs.call('GetSceneItemId', { 2623 | sceneName: currentScene, 2624 | sourceName: `${element}` 2625 | }).then(data => { 2626 | console.log("testingagain") 2627 | let currentSceneItemId = data.sceneItemId 2628 | obs.call('GetSceneItemEnabled', { 2629 | sceneName: currentScene, 2630 | sceneItemId: currentSceneItemId 2631 | }) 2632 | .then(data => { 2633 | console.log("testingagain22222") 2634 | console.log(data.sceneItemEnabled) 2635 | currentVisible = data.sceneItemEnabled 2636 | if (currentVisible == true){ 2637 | currentVisible = 1 2638 | } else if (currentVisible == false){ 2639 | currentVisible = 0 2640 | } 2641 | }) 2642 | .then(() => { 2643 | visibleArray.push(currentVisible) 2644 | //console.log(visibleArray) 2645 | }) 2646 | .catch((err) => { 2647 | console.log(err) 2648 | }) 2649 | 2650 | setTimeout(() => { 2651 | client.send(`/item/${index}/visibility`, currentVisible, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2652 | if (err) console.log(err); 2653 | }) 2654 | client.send(`/item/${index}/name`, element, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2655 | if (err) console.log(err); 2656 | }) 2657 | }, 200); 2658 | 2659 | // client.send(`/activeScene`, data.currentProgramSceneName, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2660 | // if (err) console.log(err); 2661 | // }); 2662 | // client.send(`/scene/${sceneNow.split(' ').join('_').toString()}`, 0, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2663 | // if (err) console.log(err); 2664 | // }); 2665 | // client.send(`/scene/${data['toScene'].split(' ').join('_').toString()}`, 1, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2666 | // if (err) console.log(err); 2667 | // }); 2668 | 2669 | obs.call('GetCurrentSceneTransition').then(data => { 2670 | console.log("transitionyoooooo") 2671 | client.send(`/transitionType`, data.transitionName, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2672 | if (err) console.log(err); 2673 | }) 2674 | client.send(`/transitionDuration`, data.transitionDuration, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2675 | if (err) console.log(err); 2676 | }) 2677 | }) 2678 | }) 2679 | }) 2680 | }) 2681 | }) 2682 | } 2683 | }) 2684 | 2685 | if(enableObs2App && isTouchOSC){ 2686 | let streaming 2687 | let recording 2688 | let mutedState 2689 | 2690 | obs.on('SceneTransitionEnded', data => { 2691 | obs.call("GetCurrentProgramScene").then(data => { 2692 | client.send(`/activeSceneCompleted`, data.currentProgramSceneName.toString(), (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2693 | if (err) console.log(err); 2694 | }); 2695 | }) 2696 | }) 2697 | 2698 | obs.on("InputVolumeChanged", data => { 2699 | console.log(Math.round(data.inputVolumeMul * 100) / 100) 2700 | client.send(`/${data.inputName.split(' ').join('_').toString()}/volume`, Math.round(data.inputVolumeMul * 100) / 100, (err) => { 2701 | console.log(err) 2702 | }) 2703 | }) 2704 | 2705 | obs.on('MediaInputPlaybackStarted', data => { 2706 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaPlay`, 1, (err) => { 2707 | console.log(err) 2708 | }) 2709 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaPause`, 0, (err) => { 2710 | console.log(err) 2711 | }) 2712 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaStop`, 0, (err) => { 2713 | console.log(err) 2714 | }) 2715 | }) 2716 | 2717 | obs.on('MediaInputPlaybackEnded', data => { 2718 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaStop`, 1, (err) => { 2719 | console.log(err) 2720 | }) 2721 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaPlay`, 0, (err) => { 2722 | console.log(err) 2723 | }) 2724 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaPause`, 0, (err) => { 2725 | console.log(err) 2726 | }) 2727 | }) 2728 | 2729 | obs.on('MediaInputActionTriggered', data => { 2730 | if (data.mediaAction == "OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE") 2731 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaPause`, 1, (err) => { 2732 | console.log(err) 2733 | }) 2734 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaStop`, 0, (err) => { 2735 | console.log(err) 2736 | }) 2737 | client.send(`/${data.inputName.split(' ').join('_').toString()}/mediaPlay`, 0, (err) => { 2738 | console.log(err) 2739 | }) 2740 | }) 2741 | 2742 | obs.on('InputMuteStateChanged', data => { 2743 | if (data.inputMuted == true){ 2744 | mutedState = 1 2745 | } else { 2746 | mutedState = 0 2747 | } 2748 | client.send(`/${data.inputName.split(' ').join('_').toString()}/audioToggle`, mutedState, (err) => { 2749 | console.log(err) 2750 | }) 2751 | }) 2752 | 2753 | obs.on('SceneItemEnableStateChanged', data => { 2754 | let visibilityState 2755 | if (data['sceneItemEnabled'] === true){ 2756 | visibilityState = 1 2757 | } else if (data['sceneItemEnabled'] === false){ 2758 | visibilityState = 0 2759 | } 2760 | client.send(`/${data['sceneName'].split(' ').join('_').toString()}/${data['item-name'].split(' ').join('_').toString()}/visible`, visibilityState, (err) => { 2761 | console.log(err) 2762 | }) 2763 | //Update TouchOSC Scenes 2764 | let sceneArray = [] 2765 | let visibleArray = [] 2766 | obs.call("GetCurrentProgramScene").then(data => { 2767 | currentScene = data.currentProgramSceneName 2768 | obs.call("GetSceneItemList", { 2769 | sceneName: currentScene 2770 | }).then(data => { 2771 | console.log(data.sceneItems) 2772 | data.sceneItems.forEach(e => { 2773 | sceneArray.push(e['sourceName'].toString()) 2774 | }) 2775 | }) 2776 | .then(() => { 2777 | sceneArray.reverse() 2778 | console.log("testing testing") 2779 | console.log(sceneArray) 2780 | sceneArray.forEach((element, index) => { 2781 | let currentVisible 2782 | obs.call('GetSceneItemId', { 2783 | sceneName: currentScene, 2784 | sourceName: `${element}` 2785 | }).then(data => { 2786 | console.log("testingagain") 2787 | let currentSceneItemId = data.sceneItemId 2788 | obs.call('GetSceneItemEnabled', { 2789 | sceneName: currentScene, 2790 | sceneItemId: currentSceneItemId 2791 | }) 2792 | .then(data => { 2793 | console.log("testingagain22222") 2794 | console.log(data.sceneItemEnabled) 2795 | currentVisible = data.sceneItemEnabled 2796 | if (currentVisible == true){ 2797 | currentVisible = 1 2798 | } else if (currentVisible == false){ 2799 | currentVisible = 0 2800 | } 2801 | }) 2802 | .then(() => { 2803 | visibleArray.push(currentVisible) 2804 | //console.log(visibleArray) 2805 | }) 2806 | .catch((err) => { 2807 | console.log(err) 2808 | }) 2809 | 2810 | setTimeout(() => { 2811 | client.send(`/item/${index}/visibility`, currentVisible, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2812 | if (err) console.log(err); 2813 | }) 2814 | client.send(`/item/${index}/name`, element, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2815 | if (err) console.log(err); 2816 | }) 2817 | }, 200); 2818 | 2819 | // client.send(`/activeScene`, data.currentProgramSceneName, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2820 | // if (err) console.log(err); 2821 | // }); 2822 | // client.send(`/scene/${sceneNow.split(' ').join('_').toString()}`, 0, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2823 | // if (err) console.log(err); 2824 | // }); 2825 | // client.send(`/scene/${data['toScene'].split(' ').join('_').toString()}`, 1, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2826 | // if (err) console.log(err); 2827 | // }); 2828 | 2829 | obs.call('GetCurrentSceneTransition').then(data => { 2830 | console.log("transitionyoooooo") 2831 | client.send(`/transitionType`, data.transitionName, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2832 | if (err) console.log(err); 2833 | }) 2834 | client.send(`/transitionDuration`, data.transitionDuration, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2835 | if (err) console.log(err); 2836 | }) 2837 | }) 2838 | }) 2839 | }) 2840 | }) 2841 | }) 2842 | }) 2843 | 2844 | obs.on('SourceOrderChanged', () => { 2845 | //Update TouchOSC Scenes 2846 | let sceneArray = [] 2847 | let visibleArray = [] 2848 | obs.call("GetSceneItemList").then(data => { 2849 | data.sceneItems.forEach(e => { 2850 | sceneArray.push(e['sourceName'].toString()) 2851 | }) 2852 | }) 2853 | .then(() => { 2854 | sceneArray.reverse() 2855 | console.log(sceneArray) 2856 | sceneArray.forEach((element, index) => { 2857 | let currentVisible 2858 | obs.call('GetSceneItemProperties', { 2859 | item: `${element}` 2860 | }) 2861 | .then(data => { 2862 | console.log(data.visible) 2863 | currentVisible = data.visible 2864 | if (currentVisible == true){ 2865 | currentVisible = 1 2866 | } else if (currentVisible == false){ 2867 | currentVisible = 0 2868 | } 2869 | }) 2870 | .then(() => { 2871 | visibleArray.push(currentVisible) 2872 | //console.log(visibleArray) 2873 | }) 2874 | .catch((err) => { 2875 | console.log(err) 2876 | }) 2877 | 2878 | setTimeout(() => { 2879 | client.send(`/item/${index}/visibility`, currentVisible, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2880 | if (err) console.log(err); 2881 | }) 2882 | client.send(`/item/${index}/name`, element, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2883 | if (err) console.log(err); 2884 | }) 2885 | }, 200); 2886 | 2887 | }) 2888 | }) 2889 | }) 2890 | 2891 | obs.on('SceneItemAdded', () => { 2892 | //Update TouchOSC Scenes 2893 | let sceneArray = [] 2894 | let visibleArray = [] 2895 | for (i = 0; i < 50; i++) { 2896 | client.send(`/item/${i}/visibility`, 0, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2897 | if (err) console.log(err); 2898 | }) 2899 | client.send(`/item/${i}/name`, "", (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2900 | if (err) console.log(err); 2901 | }) 2902 | } 2903 | obs.call("GetSceneItemList").then(data => { 2904 | data.sceneItems.forEach(e => { 2905 | sceneArray.push(e['sourceName'].toString()) 2906 | }) 2907 | }) 2908 | .then(() => { 2909 | sceneArray.reverse() 2910 | console.log(sceneArray) 2911 | sceneArray.forEach((element, index) => { 2912 | let currentVisible 2913 | obs.call('GetSceneItemProperties', { 2914 | item: `${element}` 2915 | }) 2916 | .then(data => { 2917 | console.log(data.visible) 2918 | currentVisible = data.visible 2919 | if (currentVisible == true){ 2920 | currentVisible = 1 2921 | } else if (currentVisible == false){ 2922 | currentVisible = 0 2923 | } 2924 | }) 2925 | .then(() => { 2926 | visibleArray.push(currentVisible) 2927 | //console.log(visibleArray) 2928 | }) 2929 | .catch((err) => { 2930 | console.log(err) 2931 | }) 2932 | 2933 | setTimeout(() => { 2934 | client.send(`/item/${index}/visibility`, currentVisible, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2935 | if (err) console.log(err); 2936 | }) 2937 | client.send(`/item/${index}/name`, element, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2938 | if (err) console.log(err); 2939 | }) 2940 | }, 200); 2941 | 2942 | }) 2943 | }) 2944 | }) 2945 | 2946 | obs.on('SceneItemRemoved', () => { 2947 | //Update TouchOSC Scenes 2948 | let sceneArray = [] 2949 | let visibleArray = [] 2950 | for (i = 0; i < 50; i++) { 2951 | client.send(`/item/${i}/visibility`, 0, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2952 | if (err) console.log(err); 2953 | }) 2954 | client.send(`/item/${i}/name`, "", (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2955 | if (err) console.log(err); 2956 | }) 2957 | } 2958 | obs.call("GetSceneItemList").then(data => { 2959 | data.sceneItems.forEach(e => { 2960 | sceneArray.push(e['sourceName'].toString()) 2961 | }) 2962 | }) 2963 | .then(() => { 2964 | sceneArray.reverse() 2965 | console.log(sceneArray) 2966 | sceneArray.forEach((element, index) => { 2967 | let currentVisible 2968 | obs.call('GetSceneItemProperties', { 2969 | item: `${element}` 2970 | }) 2971 | .then(data => { 2972 | console.log(data.visible) 2973 | currentVisible = data.visible 2974 | if (currentVisible == true){ 2975 | currentVisible = 1 2976 | } else if (currentVisible == false){ 2977 | currentVisible = 0 2978 | } 2979 | }) 2980 | .then(() => { 2981 | visibleArray.push(currentVisible) 2982 | //console.log(visibleArray) 2983 | }) 2984 | .catch((err) => { 2985 | console.log(err) 2986 | }) 2987 | 2988 | setTimeout(() => { 2989 | client.send(`/item/${index}/visibility`, currentVisible, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2990 | if (err) console.log(err); 2991 | }) 2992 | client.send(`/item/${index}/name`, element, (err) => { //Takes OBS Scene Name and Sends it Out as OSC String (Along with Prefix and Suffix) 2993 | if (err) console.log(err); 2994 | }) 2995 | }, 200); 2996 | 2997 | }) 2998 | }) 2999 | }) 3000 | 3001 | 3002 | 3003 | obs.on("RecordStateChanged", data => { 3004 | if (data.outputActive == true){ 3005 | recording = 1 3006 | } else { 3007 | recording = 0 3008 | } 3009 | client.send(`/recording`, recording, (err) => { 3010 | console.log(err) 3011 | }) 3012 | }) 3013 | 3014 | obs.on("StreamStateChanged", data => { 3015 | if (data.outputActive == true){ 3016 | streaming = 1 3017 | } else { 3018 | streaming = 0 3019 | } 3020 | client.send(`/streaming`, streaming, (err) => { 3021 | console.log(err) 3022 | }) 3023 | }) 3024 | 3025 | obs.on('GetStats', data => { 3026 | client.send(`/fps`, `${Math.floor(data.activeFps)} fps`, (err) => { 3027 | console.log(err) 3028 | }) 3029 | console.log(data.activeFps) 3030 | 3031 | client.send(`/streamTime`, toHHMMSS(data['total-stream-time']), (err) => { 3032 | console.log(err) 3033 | }) 3034 | client.send(`/cpuUsage`, `${Math.round(data['cpuUsage'])}% cpu`, (err) => { 3035 | console.log(err) 3036 | }) 3037 | client.send(`/freeDiskSpace`, `${Math.round(data['availableDiskSpace'])} free disk space`, (err) => { 3038 | console.log(err) 3039 | }) 3040 | client.send(`/averageFrameTime`, `${Math.round(data['averageFrameRenderTime'])} avg frames dropped`, (err) => { 3041 | console.log(err) 3042 | }) 3043 | client.send(`/memoryUsage`, `${Math.round(data['memoryUsage'])} memory usage`, (err) => { 3044 | console.log(err) 3045 | }) 3046 | client.send(`/kbpsEncoder`, `${Math.round(data['kbits-per-sec'])} kbps`, (err) => { 3047 | console.log(err) 3048 | }) 3049 | }) 3050 | } 3051 | 3052 | }) 3053 | } 3054 | 3055 | // This method will be called when Electron has finished 3056 | // initialization and is ready to create browser windows. 3057 | // Some APIs can only be used after this event occurs. 3058 | app.on('ready', createWindow); 3059 | 3060 | // Quit when all windows are closed, except on macOS. There, it's common 3061 | // for applications and their menu bar to stay active until the user quits 3062 | // explicitly with Cmd + Q. 3063 | app.on('window-all-closed', () => { 3064 | if (process.platform !== 'darwin') { 3065 | app.quit(); 3066 | } 3067 | }); 3068 | 3069 | app.on('activate', () => { 3070 | // On OS X it's common to re-create a window in the app when the 3071 | // dock icon is clicked and there are no other windows open. 3072 | if (BrowserWindow.getAllWindows().length === 0) { 3073 | createWindow(); 3074 | } 3075 | }); 3076 | 3077 | 3078 | // In this file you can include the rest of your app's specific main process 3079 | // code. You can also put them in separate files and import them here. 3080 | -------------------------------------------------------------------------------- /src/osctester.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OSC for OBS 6 | 7 | 8 | 9 |
10 |
11 | 12 |
13 |
14 | OSC Tester 15 |
16 | Address: 17 | 18 |
19 | Argument: 20 | 21 |
22 |
23 | 24 |
25 |
26 | 57 | 58 | --------------------------------------------------------------------------------