Makes stumblechat webcams 320x240
// ==UserScript==
// @name Small Cams Stumblechat
// @namespace http://tampermonkey.net/
// @version 0.8
// @description Makes stumblechat webcams 320x240
// @author You
// @match https://stumblechat.com/*
// @grant GM_addStyle
// @license none
// ==/UserScript==
(function() {
'use strict';
console.log("Script started.");
// Add styles using GM_addStyle for compacting video wrappers and camera badges
let css = `
#videos-content {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.videos-items > .js-video {
width: 100%;
}
.video-wrapper {
position: relative;
width: 100%;
height: 100%;
}
#regularvideos {
display: grid;
grid-template-columns: repeat(5, 320px); // 5 columns of 320px each
grid-template-rows: repeat(5, 240px); // 5 rows of 240px each
gap: 5px;
width: 100%;
height: 100%;
}
.video-cell {
box-sizing: border-box;
position: relative;
}
.video-badge {
width: 16px;
height: 16px;
background-image: url(../styles/svg/webcam.svg);
background-position: center center;
background-repeat: no-repeat;
position: absolute;
top: 5px;
left: 5px;
}
`;
if (typeof GM_addStyle !== "undefined") {
GM_addStyle(css);
} else {
let styleNode = document.createElement("style");
styleNode.appendChild(document.createTextNode(css));
(document.querySelector("head") || document.documentElement).appendChild(styleNode);
}
// Create a 5x5 grid table for webcam videos
const videosContent = document.getElementById("videos-content");
// Adjust positioning of the videos-content div
videosContent.style.position = 'absolute';
videosContent.style.bottom = '0';
videosContent.style.left = '0';
videosContent.style.width = '100%';
const table = document.createElement("table");
table.style.borderCollapse = 'collapse';
table.style.width = '100%';
// Create rows and cells in a 5x5 grid (320x240 per cell)
for (let i = 0; i < 5; i++) {
const row = document.createElement("tr");
for (let j = 0; j < 5; j++) {
const cell = document.createElement("td");
cell.style.padding = '5px';
cell.style.textAlign = 'center';
cell.style.border = '1px solid #ddd';
cell.style.height = '240px';
cell.style.width = '320px';
cell.classList.add('video-cell');
row.appendChild(cell);
}
table.appendChild(row);
}
// Insert the table into the "regularvideos" div inside the "videos-content" container
const regularVideosDiv = videosContent.querySelector("#regularvideos");
if (!regularVideosDiv) {
console.error("Could not find #regularvideos container.");
return;
}
regularVideosDiv.appendChild(table);
// Function to find the first empty cell in the grid
function findEmptyCell() {
const cells = table.getElementsByTagName("td");
for (let cell of cells) {
if (!cell.hasChildNodes()) {
return cell;
}
}
return null; // If no empty cells are found
}
// Function to add the camera badge
function addCameraBadge(userElement) {
const videoBadge = document.createElement('div');
videoBadge.classList.add('video-badge');
const statusElement = userElement.querySelector('.status');
if (statusElement) {
statusElement.appendChild(videoBadge); // Append it to the user's status element
}
}
// MutationObserver to monitor DOM for video-wrapper additions
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.classList.contains('js-video') && !node.classList.contains('hidden')) {
console.log("New video-wrapper detected:", node);
// Modify the video element within the wrapper
const videoElement = node.querySelector('video');
if (videoElement) {
console.log("Video element found:", videoElement);
// Find an empty cell in the 5x5 grid and place the entire video wrapper
const emptyCell = findEmptyCell();
if (emptyCell) {
console.log("Placing video wrapper in empty cell...");
// Move the entire js-video element into the table cell
emptyCell.appendChild(node);
// Ensure the video takes up 100% of the cell's width and height
videoElement.style.width = '100%';
videoElement.style.height = '100%';
// Adjust video wrapper styles if needed
const videoWrapper = node.querySelector('.video-wrapper');
if (videoWrapper) {
videoWrapper.style.width = '100%';
videoWrapper.style.height = '100%';
}
// Add camera badge if the user has their camera on
const userElement = node.closest('.user-item'); // Replace with the correct selector for user elements
const cameraStatus = userElement ? userElement.getAttribute('data-camera-status') : null; // Replace with the appropriate attribute or logic
if (cameraStatus === 'active') { // Check if camera is active
addCameraBadge(userElement);
}
}
} else {
console.warn("No video element found inside video-wrapper.");
}
}
});
});
});
// Start observing the DOM
const targetNode = document.body;
const config = { childList: true, subtree: true };
observer.observe(targetNode, config);
console.log("Observer initialized. Watching for js-video elements.");
// Adding placeholder if needed (ensure vertical <br> appears in empty cells)
function addPlaceholders() {
const cells = table.getElementsByTagName("td");
for (let cell of cells) {
if (!cell.hasChildNodes()) {
const placeholder = document.createElement('br');
cell.appendChild(placeholder);
}
}
}
// Periodically check for empty cells and add placeholders
setInterval(addPlaceholders, 1000);
})();