From 672b93bf4547088756010597e3d352f53011305a Mon Sep 17 00:00:00 2001 From: markschwartzkopf Date: Fri, 8 Apr 2022 14:58:26 -0500 Subject: [PATCH 1/5] feat: up arrow swap works --- src/icons.ts | 3 +- src/main.ts | 229 ++++++++++++++++++++++++++++++++++++----------- types/index.d.ts | 2 +- 3 files changed, 181 insertions(+), 53 deletions(-) diff --git a/src/icons.ts b/src/icons.ts index 3c15660..b79b90d 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -30,7 +30,8 @@ const icons = { left: `data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20fill%3D%27none%27%20stroke%3D%27%23d2d2d2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20d%3D%27m6%209%206%206%206-6%27%20transform%3D%27matrix(0%20-1.7072%20-1.7538%200%2032.495%2032.495)%27%2F%3E%3C%2Fsvg%3E`, right: `data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20fill%3D%27none%27%20stroke%3D%27%23d2d2d2%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20d%3D%27m6%209%206%206%206-6%27%20transform%3D%27matrix(0%201.7072%201.7538%200%20-8.519%20-8.48)%27%2F%3E%3C%2Fsvg%3E`, addRow: `data:image/svg+xml,%3Csvg%20fill%3D%27none%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cdefs%3E%3Cmask%20id%3D%27a%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Ccircle%20r%3D%278%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3Cmask%20id%3D%27b%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Cpath%20d%3D%27M9%2016h6m-3%203v-6%27%20stroke%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3C%2Fdefs%3E%3Cpath%20d%3D%27M1%202v12h22V2Z%27%20stroke%3D%27%23d2d2d2%27%20mask%3D%27url%28%23a%29%27%2F%3E%3Ccircle%20r%3D%276%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23d2d2d2%27%20mask%3D%27url%28%23b%29%27%2F%3E%3C%2Fsvg%3E`, - removeRow: 'data:image/svg+xml,%3Csvg%20fill%3D%27none%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cdefs%3E%3Cmask%20id%3D%27a%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Ccircle%20r%3D%278%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3Cmask%20id%3D%27b%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Cpath%20d%3D%27M9%2016h6%27%20stroke%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3C%2Fdefs%3E%3Cpath%20d%3D%27M1%202v12h22V2Z%27%20stroke%3D%27%23d2d2d2%27%20mask%3D%27url%28%23a%29%27%2F%3E%3Ccircle%20r%3D%276%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23d2d2d2%27%20mask%3D%27url%28%23b%29%27%2F%3E%3C%2Fsvg%3E' + removeRow: 'data:image/svg+xml,%3Csvg%20fill%3D%27none%27%20stroke-linejoin%3D%27round%27%20stroke-linecap%3D%27round%27%20stroke-width%3D%272%27%20viewBox%3D%270%200%2024%2024%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3Cdefs%3E%3Cmask%20id%3D%27a%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Ccircle%20r%3D%278%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3Cmask%20id%3D%27b%27%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20fill%3D%27%23fff%27%2F%3E%3Cpath%20d%3D%27M9%2016h6%27%20stroke%3D%27%23000%27%2F%3E%3C%2Fmask%3E%3C%2Fdefs%3E%3Cpath%20d%3D%27M1%202v12h22V2Z%27%20stroke%3D%27%23d2d2d2%27%20mask%3D%27url%28%23a%29%27%2F%3E%3Ccircle%20r%3D%276%27%20cx%3D%2712%27%20cy%3D%2716%27%20fill%3D%27%23d2d2d2%27%20mask%3D%27url%28%23b%29%27%2F%3E%3C%2Fsvg%3E', + blank: `data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20fill%3D%27%23d2d2d2%27%20viewBox%3D%270%200%208%208%27%3E%3C%2Fsvg%3E`, }; export { icons }; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 0f81265..5aa9955 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,8 +9,9 @@ const gdqGreen2 = [0, 255, 0]; let screenshotBase64 = ''; let connectedToOBS = false; let obsConnectionError = ''; -let cropItem: null | (sceneItemRef & crop & { width: number; height: number }) = - null; +let cropItem: + | null + | (sceneItemRef & crop & { width: number; height: number }) = null; let cropSide: 'left' | 'right' | 'top' | 'bottom' | null = null; let targetCrop: crop | null = null; let initialCrop: crop | null = null; @@ -543,6 +544,7 @@ function populateViewportsFromActiveFeed() { .send('GetSceneItemList', { sceneName: selectedFeedsScene }) .then(async (data) => { sceneItemList = data.sceneItems; + const viewportFeeds: viewport['assignedFeeds'] = []; for (let i = 0; i < data.sceneItems.length; i++) { await obs .send('GetSceneItemProperties', { @@ -550,7 +552,7 @@ function populateViewportsFromActiveFeed() { item: { id: data.sceneItems[i].itemId }, }) .then((data) => { - const viewportFeed: viewport['assignedFeeds'][number] = { + viewportFeeds.push({ 'scene-name': selectedFeedsScene, item: { id: data.itemId, name: data.name }, type: sceneItemList[i].sourceKind, @@ -560,61 +562,97 @@ function populateViewportsFromActiveFeed() { bottom: data.crop.bottom, width: data.sourceWidth, height: data.sourceHeight, - }; - let assigned = false; - for (let i = 0; i < currentSceneViewports.length; i++) { + x: data.position.x, + y: data.position.y, + boundsWidth: data.bounds.x, + boundsHeight: data.bounds.y, + }); + }) + .catch(obsError); + } + let i = 0; + let productiveLoop = false; + while (i < viewportFeeds.length) { + let assigned = false; + for (let j = 0; j < currentSceneViewports.length; j++) { + if ( + viewportSearchBoxes[j].x == viewportFeeds[i].x && + viewportSearchBoxes[j].y == viewportFeeds[i].y + ) { + if ( + viewportSearchBoxes[j].width == viewportFeeds[i].boundsWidth && + viewportSearchBoxes[j].height == viewportFeeds[i].boundsHeight + ) { + currentSceneViewports[j].assignedFeeds.push(viewportFeeds[i]); if ( - viewportSearchBoxes[i].x == data.position.x && - viewportSearchBoxes[i].y == data.position.y - ) { - if ( - viewportSearchBoxes[i].width == data.bounds.x && - viewportSearchBoxes[i].height == data.bounds.y - ) { - currentSceneViewports[i].assignedFeeds.push(viewportFeed); - if ( - currentSceneViewports[i].rows > 1 || - currentSceneViewports[i].columns > 1 - ) - viewportSearchBoxes[i] = getViewPortBoundingBoxes( - currentSceneViewports[i] - )[currentSceneViewports[i].assignedFeeds.length]; + currentSceneViewports[j].rows > 1 || + currentSceneViewports[j].columns > 1 + ) + viewportSearchBoxes[j] = getViewPortBoundingBoxes( + currentSceneViewports[j] + )[currentSceneViewports[j].assignedFeeds.length]; + if (!viewportSearchBoxes[j]) + viewportSearchBoxes[j] = { + x: -1, + y: -1, + width: -1, + height: -1, + }; + productiveLoop = true; + assigned = true; + viewportFeeds.splice(i, 1); + break; + } else if (currentSceneViewports[j].assignedFeeds.length == 0) { + const possibleWidth: number[] = [NaN]; //array index corresponds to number of columns, which can't be zero + for (let k = 1; k <= 4; k++) { + possibleWidth.push( + Math.round(currentSceneViewports[j].width / k) + ); + } + const columns = possibleWidth.indexOf( + viewportFeeds[i].boundsWidth + ); + if (columns > 0) { + const possibleHeight: number[] = [NaN]; //array index corresponds to number of rows, which can't be zero + for (let k = 1; k <= 4; k++) { + possibleHeight.push( + Math.round(currentSceneViewports[j].height / k) + ); + } + const rows = possibleHeight.indexOf( + viewportFeeds[i].boundsHeight + ); + if (rows > 0) { + currentSceneViewports[j].rows = rows; + currentSceneViewports[j].columns = columns; + currentSceneViewports[j].assignedFeeds.push(viewportFeeds[i]); + viewportSearchBoxes[j] = getViewPortBoundingBoxes( + currentSceneViewports[j] + )[currentSceneViewports[j].assignedFeeds.length]; + if (!viewportSearchBoxes[j]) + viewportSearchBoxes[j] = { + x: -1, + y: -1, + width: -1, + height: -1, + }; + productiveLoop = true; assigned = true; + viewportFeeds.splice(i, 1); break; - } else if (currentSceneViewports[i].assignedFeeds.length == 0) { - const possibleWidth: number[] = [NaN]; - for (let j = 1; j <= 4; j++) { - possibleWidth.push( - Math.round(currentSceneViewports[i].width / j) - ); - } - const columns = possibleWidth.indexOf(data.bounds.x); - if (columns > 0) { - const possibleHeight: number[] = [NaN]; - for (let j = 1; j <= 4; j++) { - possibleHeight.push( - Math.round(currentSceneViewports[i].height / j) - ); - } - const rows = possibleHeight.indexOf(data.bounds.y); - if (rows > 0) { - currentSceneViewports[i].rows = rows; - currentSceneViewports[i].columns = columns; - currentSceneViewports[i].assignedFeeds.push(viewportFeed); - viewportSearchBoxes[i] = getViewPortBoundingBoxes( - currentSceneViewports[i] - )[currentSceneViewports[i].assignedFeeds.length]; - assigned = true; - break; - } - } } } } - if (!assigned) unassignedFeeds.push(viewportFeed); - }) - .catch(obsError); + } + } + //if (!assigned) unassignedFeeds.push(viewportFeeds[i]); + if (!assigned) i++; + if (i == viewportFeeds.length && productiveLoop == true) { + i = 0; + productiveLoop = false; + } } + unassignedFeeds = viewportFeeds; }) .catch(obsError); } @@ -756,6 +794,14 @@ async function refreshViewportsDiv() { }) .then(() => { viewportFeeds.splice(j, 1); + if ( + currentSceneViewports[i] && + currentSceneViewports[i].rows > + currentSceneViewports[i].assignedFeeds.length + ) + console.log( + 'yo:' + currentSceneViewports[i].assignedFeeds.length + ); if ( currentSceneViewports[i] && currentSceneViewports[i].rows > @@ -781,6 +827,24 @@ async function refreshViewportsDiv() { refreshCropDiv(); }; sourceDiv.appendChild(cropIcon); + const upIcon = document.createElement('img'); + upIcon.classList.add('icon'); + upIcon.style.float = 'right'; + if (j > 0) { + upIcon.src = icons.up; + upIcon.onclick = () => { + //swapFeeds(viewportFeeds[j], viewportFeeds[j - 1]); + swapFeeds(viewportFeeds, j, j-1); + }; + } else upIcon.src = icons.blank; + sourceDiv.appendChild(upIcon); + if (j < viewportFeeds.length - 1) { + const downIcon = document.createElement('img'); + downIcon.classList.add('icon'); + downIcon.style.float = 'right'; + downIcon.src = icons.down; + sourceDiv.appendChild(downIcon); + } viewportSourcesDiv.appendChild(sourceDiv); } listDiv.appendChild(viewportSourcesDiv); @@ -788,6 +852,59 @@ async function refreshViewportsDiv() { refreshFooter(); } +function swapFeeds( + viewportFeeds: typeof unassignedFeeds, index1: number, index2: number +) { + const feed1 = viewportFeeds[index1]; + const feed2 = viewportFeeds[index2]; + const swapX = feed2.x; + const swapY = feed2.y; + const swapWidth = feed2.boundsWidth; + const swapHeight = feed2.boundsHeight; + feed2.x = feed1.x; + feed2.y = feed1.y; + feed2.boundsWidth = feed1.boundsWidth; + feed2.boundsHeight = feed1.boundsHeight; + feed1.x = swapX; + feed1.y = swapY; + feed1.boundsWidth = swapWidth; + feed1.boundsHeight = swapHeight + unsubscribeToChanges(); + obs + .send('SetSceneItemProperties', { + 'scene-name': selectedFeedsScene, + item: feed1.item, + position: { x: feed1.x, y: feed1.y }, + scale: {}, + crop: {}, + bounds: { + x: feed1.boundsWidth, + y: feed1.boundsHeight, + }, + }) + .then(() => { + return obs.send('SetSceneItemProperties', { + 'scene-name': selectedFeedsScene, + item: feed2.item, + position: { x: feed2.x, y: feed2.y }, + scale: {}, + crop: {}, + bounds: { + x: feed2.boundsWidth, + y: feed2.boundsHeight, + }, + }); + }) + .then(() => { + const swap = viewportFeeds[index2]; + viewportFeeds[index2] = viewportFeeds[index1]; + viewportFeeds[index1] = swap; + refreshViewportsDiv(); + subscribeToChanges(); + }) + .catch(console.error); +} + async function removeUnassignedSources() { unsubscribeToChanges(); for (let i = 0; i < unassignedFeeds.length; i++) { @@ -840,6 +957,12 @@ async function arrangeViewportFeeds(viewport: viewport) { y: boxes[i].height, }, }) + .then(() => { + feed.x = boxes[i].x; + feed.y = boxes[i].y; + feed.boundsWidth = boxes[i].width; + feed.boundsHeight = boxes[i].height; + }) .catch(console.error); } subscribeToChanges(); @@ -1167,6 +1290,10 @@ async function addSourceToViewport( bottom: 0, width: source.source_cx, height: source.source_cy, + x: viewport.x, + y: viewport.y, + boundsWidth: viewport.width, + boundsHeight: viewport.height, }; return obs.send('SetSceneItemProperties', { 'scene-name': scene, diff --git a/types/index.d.ts b/types/index.d.ts index 5896991..7eb9700 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -28,7 +28,7 @@ type viewport = { height: number; rows: number; columns: number; - assignedFeeds: (sceneItemRef & crop & { type: string, width: number, height: number })[]; + assignedFeeds: (sceneItemRef & crop & { type: string, width: number, height: number, x: number, y: number, boundsWidth: number, boundsHeight: number })[]; }; type obsSceneItems = { itemId: number; From 7a023f8449f543dd20e1c3566f6627026248926f Mon Sep 17 00:00:00 2001 From: markschwartzkopf Date: Fri, 10 Jun 2022 13:08:47 -0500 Subject: [PATCH 2/5] feat: feed swapping works --- src/main.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main.ts b/src/main.ts index 5aa9955..2a06313 100644 --- a/src/main.ts +++ b/src/main.ts @@ -797,15 +797,7 @@ async function refreshViewportsDiv() { if ( currentSceneViewports[i] && currentSceneViewports[i].rows > - currentSceneViewports[i].assignedFeeds.length - ) - console.log( - 'yo:' + currentSceneViewports[i].assignedFeeds.length - ); - if ( - currentSceneViewports[i] && - currentSceneViewports[i].rows > - currentSceneViewports[i].assignedFeeds.length + currentSceneViewports[i].assignedFeeds.length && currentSceneViewports[i].assignedFeeds.length > 0 ) currentSceneViewports[i].rows = currentSceneViewports[i].assignedFeeds.length; @@ -833,7 +825,6 @@ async function refreshViewportsDiv() { if (j > 0) { upIcon.src = icons.up; upIcon.onclick = () => { - //swapFeeds(viewportFeeds[j], viewportFeeds[j - 1]); swapFeeds(viewportFeeds, j, j-1); }; } else upIcon.src = icons.blank; @@ -843,6 +834,9 @@ async function refreshViewportsDiv() { downIcon.classList.add('icon'); downIcon.style.float = 'right'; downIcon.src = icons.down; + downIcon.onclick = () => { + swapFeeds(viewportFeeds, j, j+1); + }; sourceDiv.appendChild(downIcon); } viewportSourcesDiv.appendChild(sourceDiv); From 8605935e6fad356d8afc68b63d04c77221935e73 Mon Sep 17 00:00:00 2001 From: markschwartzkopf Date: Fri, 10 Jun 2022 13:09:36 -0500 Subject: [PATCH 3/5] chore: format --- src/main.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main.ts b/src/main.ts index 2a06313..77d3c1c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -797,7 +797,8 @@ async function refreshViewportsDiv() { if ( currentSceneViewports[i] && currentSceneViewports[i].rows > - currentSceneViewports[i].assignedFeeds.length && currentSceneViewports[i].assignedFeeds.length > 0 + currentSceneViewports[i].assignedFeeds.length && + currentSceneViewports[i].assignedFeeds.length > 0 ) currentSceneViewports[i].rows = currentSceneViewports[i].assignedFeeds.length; @@ -825,7 +826,7 @@ async function refreshViewportsDiv() { if (j > 0) { upIcon.src = icons.up; upIcon.onclick = () => { - swapFeeds(viewportFeeds, j, j-1); + swapFeeds(viewportFeeds, j, j - 1); }; } else upIcon.src = icons.blank; sourceDiv.appendChild(upIcon); @@ -835,7 +836,7 @@ async function refreshViewportsDiv() { downIcon.style.float = 'right'; downIcon.src = icons.down; downIcon.onclick = () => { - swapFeeds(viewportFeeds, j, j+1); + swapFeeds(viewportFeeds, j, j + 1); }; sourceDiv.appendChild(downIcon); } @@ -847,7 +848,9 @@ async function refreshViewportsDiv() { } function swapFeeds( - viewportFeeds: typeof unassignedFeeds, index1: number, index2: number + viewportFeeds: typeof unassignedFeeds, + index1: number, + index2: number ) { const feed1 = viewportFeeds[index1]; const feed2 = viewportFeeds[index2]; @@ -862,7 +865,7 @@ function swapFeeds( feed1.x = swapX; feed1.y = swapY; feed1.boundsWidth = swapWidth; - feed1.boundsHeight = swapHeight + feed1.boundsHeight = swapHeight; unsubscribeToChanges(); obs .send('SetSceneItemProperties', { From 15c1823fe2409574b484e5e232c4a7f292e44d3c Mon Sep 17 00:00:00 2001 From: markschwartzkopf Date: Fri, 10 Jun 2022 13:56:39 -0500 Subject: [PATCH 4/5] fix: scene collection changes do not crash OBS --- src/main.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 77d3c1c..d41f779 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ const gdqGreen2 = [0, 255, 0]; let screenshotBase64 = ''; let connectedToOBS = false; let obsConnectionError = ''; +let obsUpdateTimeout: NodeJS.Timeout | null = null; let cropItem: | null | (sceneItemRef & crop & { width: number; height: number }) = null; @@ -399,7 +400,14 @@ const reInitEvents = [ function subscribeToChanges() { if (!subscribed) for (let i = 0; i < reInitEvents.length; i++) { - obs.on(reInitEvents[i], initOBS); + obs.on(reInitEvents[i], () => { + if (obsUpdateTimeout) { + clearTimeout(obsUpdateTimeout); + } + obsUpdateTimeout = setTimeout(() => { + initOBS(); + }, 200); + }); } subscribed = true; } From fe6d565703023ebc94140de442e18536482091ce Mon Sep 17 00:00:00 2001 From: markschwartzkopf Date: Fri, 10 Jun 2022 13:59:03 -0500 Subject: [PATCH 5/5] chore: ver 0.9.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81603ca..568ac33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gdq-viewport-assign", - "version": "0.9.1", + "version": "0.9.2", "description": "", "main": "dist/index.js", "scripts": {