@@ -9,8 +9,14 @@ let screenshotBase64 = '';
99let connectedToOBS = false ;
1010let obsConnectionError = '' ;
1111let obsUpdateTimeout : NodeJS . Timeout | null = null ;
12- let cropItem : null | ( SceneItemRef & Crop & { width : number ; height : number } ) =
13- null ;
12+ let cropItem :
13+ | null
14+ | ( SceneItemRef &
15+ Crop & {
16+ width : number ;
17+ height : number ;
18+ scaleX : number ;
19+ } ) = null ;
1420let cropSide : 'left' | 'right' | 'top' | 'bottom' | null = null ;
1521let targetCrop : Crop | null = null ;
1622let initialCrop : Crop | null = null ;
@@ -19,6 +25,8 @@ let cropWidthSide: 'left' | 'right' | null = null;
1925let cropHeightSide : 'top' | 'bottom' | null = null ;
2026let clickLoc = { clientX : 0 , clientY : 0 } ;
2127let activelyCropping = false ;
28+ let activeArAdjust = false ;
29+ let targetAr : number | null = null ;
2230let currentSceneViewports : Viewport [ ] = [ ] ;
2331let unassignedFeeds : Viewport [ 'assignedFeeds' ] = [ ] ;
2432let currentSceneFeedScenes : number [ ] = [ ] ;
@@ -37,6 +45,7 @@ if (localStorage.getItem('obsPassword'))
3745 obsPassword = localStorage . getItem ( 'obsPassword' ) ! ;
3846
3947//setup cropping controls
48+ const arControl = document . getElementById ( 'ar' ) as HTMLInputElement ;
4049const cropDiv = document . getElementById ( 'primary-crop' ) as HTMLDivElement ;
4150const cropFrame = document . getElementById ( 'crop-frame' ) as HTMLDivElement ;
4251const cropImg = document . getElementById ( 'crop-image' ) as HTMLImageElement ;
@@ -343,9 +352,71 @@ function cropItemToTarget(check?: 'check') {
343352 cropItemToTarget ( 'check' ) ;
344353 } , 30 ) ;
345354 } )
346- . catch ( obsError ) ;
355+ . catch ( ( err ) => {
356+ activelyCropping = false ;
357+ obsError ( err ) ;
358+ } ) ;
347359 } else activelyCropping = false ;
348360}
361+ document . getElementById ( 'thinify' ) ! . onclick = ( ) => {
362+ arControl . value = '0.75' ;
363+ targetAr = 0.75 ;
364+ adjustAr ( ) ;
365+ } ;
366+ document . getElementById ( 'revert-ar' ) ! . onclick = ( ) => {
367+ arControl . value = '1' ;
368+ targetAr = 1 ;
369+ adjustAr ( ) ;
370+ } ;
371+ document . getElementById ( 'wideify' ) ! . onclick = ( ) => {
372+ arControl . value = ( 4 / 3 ) . toString ( ) ;
373+ targetAr = 4 / 3 ;
374+ adjustAr ( ) ;
375+ } ;
376+ arControl . oninput = ( ) => {
377+ const val = parseFloat ( arControl . value ) ;
378+ if ( ! isNaN ( val ) ) {
379+ targetAr = parseFloat ( arControl . value ) ;
380+ adjustAr ( ) ;
381+ }
382+ } ;
383+ function adjustAr ( check ?: 'check' ) {
384+ if ( ! cropItem || targetAr == null ) {
385+ obsError ( "Can't adjust aspect ratio without cropitem and targetAr" ) ;
386+ activeArAdjust = false ;
387+ targetAr = null ;
388+ return ;
389+ }
390+ if ( activeArAdjust && ! check ) return ;
391+ activeArAdjust = true ;
392+ if ( Math . round ( targetAr * 300 ) != Math . round ( cropItem . scaleX * 300 ) ) {
393+ const newTransform : Partial < ObsSceneItemTransform > = {
394+ scaleX : targetAr ,
395+ scaleY : 1 ,
396+ } ;
397+ obs
398+ . call ( 'SetSceneItemTransform' , {
399+ sceneName : cropItem . sceneName ,
400+ sceneItemId : cropItem . sceneItemId ,
401+ sceneItemTransform : newTransform ,
402+ } )
403+ . then ( ( ) => {
404+ if ( cropItem && targetAr !== null ) {
405+ cropItem . scaleX = targetAr ;
406+ }
407+ setTimeout ( ( ) => {
408+ adjustAr ( 'check' ) ;
409+ } , 30 ) ;
410+ } )
411+ . catch ( ( err ) => {
412+ activeArAdjust = false ;
413+ obsError ( err ) ;
414+ } ) ;
415+ } else {
416+ targetAr = null ;
417+ activeArAdjust = false ;
418+ }
419+ }
349420document . getElementById ( 'camera-crop' ) ! . onclick = ( ) => {
350421 cropViewportFeed ( 'camera' ) ;
351422} ;
@@ -450,8 +521,7 @@ function initOBS() {
450521 obsError ( "Can't init, not connected" ) ;
451522 return ;
452523 }
453- if ( cropItem ) return ;
454- if ( inInit ) {
524+ if ( inInit || cropItem ) {
455525 initBuffered = true ;
456526 return ;
457527 }
@@ -490,16 +560,16 @@ function initOBS() {
490560 . catch ( obsError )
491561 . then ( ( ) => {
492562 inInit = false ;
493- if ( initBuffered ) {
494- initBuffered = false ;
495- if ( obsUpdateTimeout ) {
563+ if ( initBuffered ) {
564+ initBuffered = false ;
565+ if ( obsUpdateTimeout ) {
496566 clearTimeout ( obsUpdateTimeout ) ;
497567 }
498568 obsUpdateTimeout = setTimeout ( ( ) => {
499569 initOBS ( ) ;
500570 obsUpdateTimeout = null ;
501571 } , 200 ) ;
502- }
572+ }
503573 } ) ;
504574}
505575
@@ -581,6 +651,7 @@ function populateViewportsFromActiveFeed() {
581651 right : sceneItemList [ i ] . sceneItemTransform . cropRight ,
582652 top : sceneItemList [ i ] . sceneItemTransform . cropTop ,
583653 bottom : sceneItemList [ i ] . sceneItemTransform . cropBottom ,
654+ scaleX : sceneItemList [ i ] . sceneItemTransform . scaleX ,
584655 width : sceneItemList [ i ] . sceneItemTransform . sourceWidth ,
585656 height : sceneItemList [ i ] . sceneItemTransform . sourceHeight ,
586657 x : sceneItemList [ i ] . sceneItemTransform . positionX ,
@@ -679,6 +750,7 @@ function populateViewportsFromActiveFeed() {
679750async function refreshViewportsDiv ( ) {
680751 document . getElementById ( 'viewports' ) ! . classList . remove ( 'hide' ) ;
681752 document . getElementById ( 'crop' ) ! . classList . add ( 'hide' ) ;
753+ document . getElementById ( 'aspect-ratio' ) ! . classList . add ( 'hide' ) ;
682754 cropImg . src = '' ;
683755 const listDiv = document . getElementById ( 'viewports-list' ) ! ;
684756 listDiv . innerHTML = '' ;
@@ -1169,6 +1241,22 @@ function refreshFooter() {
11691241 if ( cropItem ) {
11701242 let button = document . createElement ( 'div' ) ;
11711243 button . classList . add ( 'icon' , 'footer' ) ;
1244+ button . style . width = 'auto' ;
1245+ button . style . padding = '0px 5px' ;
1246+ button . innerHTML = 'Aspect Ratio' ;
1247+ button . onclick = ( ) => {
1248+ cropSide = null ;
1249+ if (
1250+ document . getElementById ( 'aspect-ratio' ) ! . classList . contains ( 'hide' ) &&
1251+ cropItem
1252+ ) {
1253+ document . getElementById ( 'aspect-ratio' ) ! . classList . remove ( 'hide' ) ;
1254+ arControl . value = cropItem . scaleX . toString ( ) ;
1255+ } else document . getElementById ( 'aspect-ratio' ) ! . classList . add ( 'hide' ) ;
1256+ } ;
1257+ footer . appendChild ( button ) ;
1258+ button = document . createElement ( 'div' ) ;
1259+ button . classList . add ( 'icon' , 'footer' ) ;
11721260 let icon = document . createElement ( 'img' ) ;
11731261 icon . width = 16 ;
11741262 icon . classList . add ( 'icon' , 'footer' ) ;
@@ -1188,6 +1276,8 @@ function refreshFooter() {
11881276 cropRight : 0 ,
11891277 cropTop : 0 ,
11901278 cropBottom : 0 ,
1279+ scaleX : 1 ,
1280+ scaleY : 1 ,
11911281 } ,
11921282 } )
11931283 . then ( ( ) => {
@@ -1196,6 +1286,7 @@ function refreshFooter() {
11961286 cropItem . right = 0 ;
11971287 cropItem . top = 0 ;
11981288 cropItem . bottom = 0 ;
1289+ arControl . value = '1' ;
11991290 } else obsError ( 'No cropItem' ) ;
12001291 refreshCropImage ( ) ;
12011292 } )
@@ -1311,6 +1402,7 @@ async function addSourceToViewport(source: ObsSceneItem, viewport: Viewport) {
13111402 right : 0 ,
13121403 top : 0 ,
13131404 bottom : 0 ,
1405+ scaleX : source . sceneItemTransform . scaleX ,
13141406 width : source . sceneItemTransform . sourceWidth ,
13151407 height : source . sceneItemTransform . sourceHeight ,
13161408 x : viewport . x ,
@@ -1386,7 +1478,6 @@ function cropViewportFeed(cropType: 'camera' | 'game1' | 'game2') {
13861478 const game1 = { left : - 1 , right : - 1 , top : - 1 , bottom : - 1 } ;
13871479 const game2 = { left : - 1 , right : - 1 , top : - 1 , bottom : - 1 } ;
13881480 temp = findGreenBlock ( ctx , 'y' , y1 , y2 , x1 , true ) ;
1389- console . log ( temp ) ;
13901481 if ( temp [ 1 ] < y2 ) game2 . top = temp [ 1 ] ;
13911482 camera . bottom = temp [ 0 ] ;
13921483 temp = findGreenBlock ( ctx , 'y' , y1 , 0 , x1 , true ) ;
@@ -1408,9 +1499,6 @@ function cropViewportFeed(cropType: 'camera' | 'game1' | 'game2') {
14081499 game2 . left = temp [ 1 ] == - 1 ? 0 : temp [ 1 ] ;
14091500 temp = findGreenBlock ( ctx , 'y' , y2 , height - 1 , x1 , true ) ;
14101501 game2 . bottom = temp [ 0 ] == - 1 ? height - 1 : temp [ 0 ] ;
1411- console . log ( camera ) ;
1412- console . log ( game1 ) ;
1413- console . log ( game2 ) ;
14141502 switch ( cropType ) {
14151503 case 'game1' :
14161504 newItemRec = game1 ;
0 commit comments