Compare commits

..

No commits in common. '10c516215fe6280f8f7e55a7290fa461a3f4ae70' and '4c268dd6524d3615c32b71d9017ab19058ac366e' have entirely different histories.

  1. 5
      index.html
  2. 26
      index.js
  3. 197
      script.js

5
index.html

@ -7,6 +7,11 @@
</head> </head>
<body> <body>
<div id="visualizer"> <div id="visualizer">
<div class="video-layer">
</div>
<div class="video-layer">
</div>
<canvas id="videoCanvas"></canvas> <canvas id="videoCanvas"></canvas>

26
index.js

@ -1,11 +1,20 @@
const express = require('express'); const express = require('express');
const app = express(); const app = express();
const path = require('path'); const path = require('path');
const fs = require('fs');
const port = 8000 const port = 8000
const assetsDir = path.join(__dirname, 'assets'); const videoFiles = [
{ "src": "assets/Blender Physics Simulations-8TGNRJDZX_o_compressed.webm"},
{ "src": "assets/Death Grips - Birds-XX5wk-6Mn5s_compressed.webm"},
{ "src": "assets/Jon Hopkins - 'Open Eye Signal' (Official Music Video)-Q04ILDXe3QE_compressed.webm"},
{ "src": "assets/Best Scene From Hackers The Movie-IyWv6snuZLk_compressed.webm"},
{ "src": "assets/$UICIDEBOY$ - O PANA!-VSXg2swBmrY_compressed.webm"},
{ "src": "assets/3D Fractals (Render Test 1)-xQ6nJYanKCY_compressed.webm"},
{ "src": "assets/Beyond The Minds Eye-wKxH51vPtHk -4_compressed.webm"},
{ "src": "assets/A Scene From Troll 2-HyophYBP_w4_compressed.webm"}
// Add more video files as needed
];
app.use(express.static(path.join(__dirname))); app.use(express.static(path.join(__dirname)));
@ -14,18 +23,7 @@ app.get('/', (req, res) => {
}); });
app.get('/api/videos', (req, res) => { app.get('/api/videos', (req, res) => {
fs.readdir(assetsDir, (err, files) => { res.json(videoFiles);
if (err) {
console.error('Error reading directory:', err);
res.status(500).send('Internal Server Error');
return;
}
const webmFiles = files.filter(file => file.endsWith('.webm'));
const videoFiles = webmFiles.map(file => ({ src: `assets/${file}` }));
res.json(videoFiles);
});
}); });
app.listen(port, () => { app.listen(port, () => {

197
script.js

@ -1,72 +1,48 @@
// Define a debug mode variable // Define a debug mode variable
let debugMode = true let debugMode = true;
const canvas = document.getElementById("videoCanvas") const canvas = document.getElementById("videoCanvas");
const ctx = canvas.getContext("2d") const ctx = canvas.getContext("2d");
let videosToLoad = 2 const videos = [createVideoElement(), createVideoElement()];
const maxVideosToLoad = 9 let videosLoaded = 0;
const videos = [] let aspectRatio = 1;
let videosLoaded = 0
let aspectRatio = 1
let cachedVideos = [] window.addEventListener("DOMContentLoaded", () => {
let previousVideo
let showText = true videos.forEach((video) => {
previousVideo = selectRandomVideo(previousVideo || video)
function debugLog(...args) { });
if (debugMode) { })
console.log(...args)
}
}
function loadVideos(num) {
debugLog(`Loading ${num} videos`)
while (num > 0) {
let video = createVideoElement()
video.addEventListener("loadedmetadata", handleVideoLoaded)
video.addEventListener("ended", handleVideoEnded)
video.addEventListener("error", handleVideoEnded)
video.src = selectRandomVideo(video)
videos.push(video)
num--
}
while (num < 0) {
let video = videos.pop()
video.removeEventListener("loadedmetadata", handleVideoLoaded)
video.removeEventListener("ended", handleVideoEnded)
num++
}
}
videos.forEach((video) => { videos.forEach((video) => {
video.addEventListener("loadedmetadata", handleVideoLoaded) video.addEventListener("loadedmetadata", handleVideoLoaded);
video.addEventListener("ended", handleVideoEnded) video.addEventListener("ended", handleVideoEnded);
}) });
function createVideoElement() { function createVideoElement() {
const video = document.createElement("video") const video = document.createElement("video");
video.autoplay = true video.autoplay = true;
video.muted = true video.muted = true;
return video return video;
} }
function handleVideoLoaded() { function handleVideoLoaded() {
videosLoaded++ videosLoaded++;
if (videosLoaded === videos.length) { if (videosLoaded === videos.length) {
updateCanvasSize() updateCanvasSize();
drawVideos() drawVideos();
} }
} }
function handleVideoEnded(event) { function handleVideoEnded(event) {
const video = event.target const video = event.target;
//debugLog(video,event) selectRandomVideo(video);
selectRandomVideo(video)
} }
function drawVideos() { function drawVideos() {
ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.clearRect(0, 0, canvas.width, canvas.height);
let blendModes = ["source-over","overlay","screen"] let blendModes = ["source-over","overlay","screen"]
@ -84,133 +60,112 @@ function drawVideos() {
// Use default blend mode for first video (i=0), repeat the rest of blendModes // Use default blend mode for first video (i=0), repeat the rest of blendModes
if (i === 0) { if (i === 0) {
ctx.globalCompositeOperation = blendModes[0] ctx.globalCompositeOperation = blendModes[0];
} else { } else {
ctx.globalCompositeOperation = blendModes[(i - 1) % (blendModes.length - 1) + 1] ctx.globalCompositeOperation = blendModes[(i - 1) % (blendModes.length - 1) + 1];
} }
// Draw the current video frame onto the canvas // Draw the current video frame onto the canvas
ctx.drawImage(video, offsetX, offsetY, scaledWidth, scaledHeight) ctx.drawImage(video, offsetX, offsetY, scaledWidth, scaledHeight)
// Reset composite operation // Reset composite operation
ctx.globalCompositeOperation = "source-over" ctx.globalCompositeOperation = "source-over";
}) })
// If showText is true, draw debug text // If debugMode is true, draw debug text
if (showText) { if (debugMode) {
drawDebugText() drawDebugText();
} }
// Request next frame // Request next frame
requestAnimationFrame(drawVideos) requestAnimationFrame(drawVideos);
} }
function drawDebugText() { function drawDebugText() {
// Set font style and color // Set font style and color
ctx.font = "16px Arial" ctx.font = "16px Arial";
ctx.fillStyle = "white" ctx.fillStyle = "white";
let corners = [[0, 0], [1, 1], [0, 1], [1, 0]]; // Top-left, bottom-right, top-right, bottom-left let corners = [[0, 0], [1, 1], [0, 1], [1, 0]]; // Top-left, bottom-right, top-right, bottom-left
let padding = 20 let padding = 20
videos.forEach((video, i) => { videos.forEach((video, i) => {
// Get the corner coordinates for the current video // Get the corner coordinates for the current video
const corner = corners[i] const corner = corners[i];
// Calculate the position of the text in the corner // Calculate the position of the text in the corner
let positionX = corner[0] ? canvas.width : padding let positionX = corner[0] ? canvas.width : padding
let positionY = corner[1] ? canvas.height : padding let positionY = corner[1] ? canvas.height : padding
// Adjust position to ensure text is within the canvas bounds // Adjust position to ensure text is within the canvas bounds
//positionX = Math.max(0, Math.min(positionX, canvas.width - ctx.measureText(getFilename(video.src)).width)) //positionX = Math.max(0, Math.min(positionX, canvas.width - ctx.measureText(getFilename(video.src)).width));
positionY = Math.max(0, Math.min(positionY, canvas.height - padding*3)) positionY = Math.max(0, Math.min(positionY, canvas.height - padding*3));
// Set text alignment based on corner // Set text alignment based on corner
ctx.textAlign = corner[0] ? "right" : "left" ctx.textAlign = corner[0] ? "right" : "left"
ctx.textBaseline = corner[1] ? "bottom" : "top" ctx.textBaseline = corner[1] ? "bottom" : "top"
// Draw debug text for the video // Draw debug text for the video
ctx.fillText(getFilename(video.src), positionX, positionY) ctx.fillText(getFilename(video.src), positionX, positionY);
ctx.fillText(`Dimensions: ${video.videoWidth}x${video.videoHeight} ()`, positionX, positionY + 20) ctx.fillText(`Dimensions: ${video.videoWidth}x${video.videoHeight} ()`, positionX, positionY + 20);
ctx.fillText(formatTime(video.currentTime) + "/" + formatTime(video.duration), positionX, positionY + 40) ctx.fillText(formatTime(video.currentTime) + "/" + formatTime(video.duration), positionX, positionY + 40);
// Add more debug information as needed // Add more debug information as needed
}) });
} }
// Function to extract filename from full path // Function to extract filename from full path
function getFilename(src) { function getFilename(src) {
const parts = src.split('/') const parts = src.split('/');
return decodeURIComponent(parts[parts.length - 1].replace(/\%20/g, ' ')) return decodeURIComponent(parts[parts.length - 1].replace(/\%20/g, ' '));
} }
// Helper function to format time in HH:MM:SS format // Helper function to format time in HH:MM:SS format
function formatTime(seconds) { function formatTime(seconds) {
const hours = Math.floor(seconds / 3600) const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60) const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = Math.floor(seconds % 60) const remainingSeconds = Math.floor(seconds % 60);
return `${(hours?hours.toString().padStart(2, '0')+':':'')}${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}` return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
} }
function updateCanvasSize() { function updateCanvasSize() {
aspectRatio = Math.max( aspectRatio = Math.max(
videos[0].videoWidth / videos[0].videoHeight, videos[0].videoWidth / videos[0].videoHeight,
videos[1].videoWidth / videos[1].videoHeight videos[1].videoWidth / videos[1].videoHeight
) );
canvas.width = window.innerWidth canvas.width = window.innerWidth;
canvas.height = window.innerWidth / aspectRatio canvas.height = window.innerWidth / aspectRatio;
} }
window.addEventListener("resize", updateCanvasSize) window.addEventListener("resize", updateCanvasSize);
function getSourceFiles() { updateCanvasSize()
debugLog("Getting source files")
return fetch("/api/videos")
.then(response => response.json())
.then(videoFiles => {
debugLog("Success")
cachedVideos = videoFiles;
})
.catch(error => console.error("Error fetching videos:", error));
}
function selectRandomVideo(videoElement) { function selectRandomVideo(videoElement) {
if (cachedVideos.length) { console.log("selectRandomVideo",videoElement)
const currentSrc = videoElement.src; fetch("/api/videos")
const filteredVideos = cachedVideos.filter(video => video.src !== currentSrc); .then((response) => response.json())
if (filteredVideos.length > 0) { .then((videoFiles) => {
const randomIndex = Math.floor(Math.random() * filteredVideos.length); const otherVideo = videos.find((video) => video !== videoElement);
const randomVideo = filteredVideos[randomIndex];
videoElement.src = randomVideo.src; let randomVideo;
} else { do {
debugLog("No other videos available."); const randomIndex = Math.floor(Math.random() * videoFiles.length);
getSourceFiles().then(()=>{ randomVideo = videoFiles[randomIndex];
selectRandomVideo(videoElement) } while (randomVideo === otherVideo);
})
} videoElement.src = randomVideo.src;
} else { videoElement.play();
debugLog("Cache empty, doing new request") return videoElement
getSourceFiles().then(()=>{ });
selectRandomVideo(videoElement)
})
}
} }
document.addEventListener('keydown', function(event) { document.addEventListener('keydown', function(event) {
console.log('keyDown',event)
// Check if the pressed key is the one you want to bind // Check if the pressed key is the one you want to bind
if (event.key === 'Enter') { if (event.key === 'Enter') { // Change 'Enter' to the desired key
showText = !showText debugMode = !debugMode
} console.log("debugMode:",debugMode)
if (event.key === 'ArrowDown') {
loadVideos(-1)
} }
if (event.key === 'ArrowUp') { });
loadVideos(1)
}
})
getSourceFiles().then(()=>{
loadVideos(videosToLoad)
updateCanvasSize()
})
Loading…
Cancel
Save