Skip to content

Commit

Permalink
Merge pull request #13 from markschwartzkopf/reorder-feeds
Browse files Browse the repository at this point in the history
Reorder feeds
  • Loading branch information
markschwartzkopf authored Jun 10, 2022
2 parents b8965f3 + fe6d565 commit a1c56ab
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 56 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gdq-viewport-assign",
"version": "0.9.1",
"version": "0.9.2",
"description": "",
"main": "dist/index.js",
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion src/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
238 changes: 185 additions & 53 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ const gdqGreen2 = [0, 255, 0];
let screenshotBase64 = '';
let connectedToOBS = false;
let obsConnectionError = '';
let cropItem: null | (sceneItemRef & crop & { width: number; height: number }) =
null;
let obsUpdateTimeout: NodeJS.Timeout | null = 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;
Expand Down Expand Up @@ -398,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;
}
Expand Down Expand Up @@ -543,14 +552,15 @@ 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', {
'scene-name': selectedFeedsScene,
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,
Expand All @@ -560,61 +570,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);
}
Expand Down Expand Up @@ -759,7 +805,8 @@ async function refreshViewportsDiv() {
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;
Expand All @@ -781,13 +828,88 @@ 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, 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;
downIcon.onclick = () => {
swapFeeds(viewportFeeds, j, j + 1);
};
sourceDiv.appendChild(downIcon);
}
viewportSourcesDiv.appendChild(sourceDiv);
}
listDiv.appendChild(viewportSourcesDiv);
}
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++) {
Expand Down Expand Up @@ -840,6 +962,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();
Expand Down Expand Up @@ -1167,6 +1295,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,
Expand Down
2 changes: 1 addition & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit a1c56ab

Please sign in to comment.