6 changed files with 264 additions and 0 deletions
@ -0,0 +1,25 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<title>VFX2</title> |
||||||
|
<link rel="stylesheet" href="styles.css"> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="visualizer"> |
||||||
|
<div class="video-layer"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="video-layer"> |
||||||
|
</div> |
||||||
|
|
||||||
|
<canvas id="videoCanvas"></canvas> |
||||||
|
|
||||||
|
<div class="glitch-layer"> |
||||||
|
<!-- Add occasional glitch effects here --> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script src="script.js"></script> |
||||||
|
</body> |
||||||
|
</html> |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
const express = require('express'); |
||||||
|
const app = express(); |
||||||
|
const path = require('path'); |
||||||
|
|
||||||
|
const port = 8000 |
||||||
|
|
||||||
|
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.get('/', (req, res) => { |
||||||
|
res.sendFile(path.join(__dirname, 'index.html')); |
||||||
|
}); |
||||||
|
|
||||||
|
app.get('/api/videos', (req, res) => { |
||||||
|
res.json(videoFiles); |
||||||
|
}); |
||||||
|
|
||||||
|
app.listen(port, () => { |
||||||
|
console.log('Server started on port', port); |
||||||
|
}); |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
{ |
||||||
|
"name": "vfx2", |
||||||
|
"version": "1.0.0", |
||||||
|
"description": "", |
||||||
|
"main": "script.js", |
||||||
|
"scripts": { |
||||||
|
"dev": "node index.js", |
||||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||||
|
}, |
||||||
|
"keywords": [], |
||||||
|
"author": "", |
||||||
|
"license": "ISC", |
||||||
|
"dependencies": { |
||||||
|
"express": "^4.18.2", |
||||||
|
"http-server": "^14.1.1" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,178 @@ |
|||||||
|
// Define a debug mode variable
|
||||||
|
let debugMode = true; |
||||||
|
|
||||||
|
const canvas = document.getElementById("videoCanvas"); |
||||||
|
const ctx = canvas.getContext("2d"); |
||||||
|
|
||||||
|
const videos = [createVideoElement(), createVideoElement()]; |
||||||
|
let videosLoaded = 0; |
||||||
|
let aspectRatio = 1; |
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", () => { |
||||||
|
let previousVideo |
||||||
|
videos.forEach((video) => { |
||||||
|
previousVideo = selectRandomVideo(previousVideo || video) |
||||||
|
}); |
||||||
|
}) |
||||||
|
|
||||||
|
videos.forEach((video) => { |
||||||
|
video.addEventListener("loadedmetadata", handleVideoLoaded); |
||||||
|
video.addEventListener("ended", handleVideoEnded); |
||||||
|
}); |
||||||
|
|
||||||
|
function createVideoElement() { |
||||||
|
const video = document.createElement("video"); |
||||||
|
video.autoplay = true; |
||||||
|
video.muted = true; |
||||||
|
return video; |
||||||
|
} |
||||||
|
|
||||||
|
function handleVideoLoaded() { |
||||||
|
videosLoaded++; |
||||||
|
if (videosLoaded === videos.length) { |
||||||
|
updateCanvasSize(); |
||||||
|
drawVideos(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function handleVideoEnded(event) { |
||||||
|
const video = event.target; |
||||||
|
selectRandomVideo(video); |
||||||
|
} |
||||||
|
|
||||||
|
// Function to extract filename from full path and unescape it
|
||||||
|
function extractFilename(src) { |
||||||
|
// Split the path by '/'
|
||||||
|
const parts = src.split('/'); |
||||||
|
// Get the last part of the path (filename)
|
||||||
|
const filename = parts[parts.length - 1]; |
||||||
|
// Unescape the filename and replace '%20' with whitespace
|
||||||
|
return decodeURIComponent(filename.replace(/\%20/g, ' ')); |
||||||
|
} |
||||||
|
|
||||||
|
// Function to draw debug text on the canvas
|
||||||
|
function drawDebugText() { |
||||||
|
// Set font style and color
|
||||||
|
ctx.font = "16px Arial"; |
||||||
|
ctx.fillStyle = "white"; |
||||||
|
|
||||||
|
let corners = [[0,0],[1,1],[0,1],[1,0]]; |
||||||
|
let padding = 20; |
||||||
|
|
||||||
|
videos.forEach((video,i) => { |
||||||
|
//console.log("Drawing debug text for video",i,video);
|
||||||
|
|
||||||
|
let corner = corners[i]; |
||||||
|
let positionX = corner[0] === 0 ? padding : canvas.width - padding; |
||||||
|
let positionY = corner[1] === 0 ? padding : canvas.height - padding; |
||||||
|
|
||||||
|
// Adjust position for bottom right corner to stay within canvas bounds
|
||||||
|
if (corner[0] === 1 && positionY + 40 > canvas.height) { |
||||||
|
positionY = canvas.height - 40; |
||||||
|
} |
||||||
|
|
||||||
|
// Set text alignment based on corner
|
||||||
|
ctx.textAlign = corner[0] === 0 ? "left" : "right"; |
||||||
|
ctx.textBaseline = corner[1] === 0 ? "top" : "bottom"; |
||||||
|
|
||||||
|
// Draw debug text for the video
|
||||||
|
ctx.fillText(getFilename(video.src), positionX, positionY); |
||||||
|
ctx.fillText("Dimensions: " + video.videoWidth + "x" + video.videoHeight, positionX, positionY + 20); |
||||||
|
ctx.fillText(formatTime(video.currentTime) + "/" + formatTime(video.duration), positionX, positionY + 40); |
||||||
|
// Add more debug information as needed
|
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Function to extract filename from full path
|
||||||
|
function getFilename(src) { |
||||||
|
const parts = src.split('/'); |
||||||
|
return decodeURIComponent(parts[parts.length - 1].replace(/\%20/g, ' ')); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Helper function to format time in HH:MM:SS format
|
||||||
|
function formatTime(seconds) { |
||||||
|
const hours = Math.floor(seconds / 3600); |
||||||
|
const minutes = Math.floor((seconds % 3600) / 60); |
||||||
|
const remainingSeconds = Math.floor(seconds % 60); |
||||||
|
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; |
||||||
|
} |
||||||
|
|
||||||
|
// Modify the drawVideos function to include debug text drawing
|
||||||
|
function drawVideos() { |
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||||
|
|
||||||
|
|
||||||
|
// Resize the canvas to accommodate both videos side by side
|
||||||
|
canvas.width = Math.max(videos[0].videoWidth, videos[1].videoWidth); |
||||||
|
canvas.height = Math.max(videos[0].videoHeight, videos[1].videoHeight); |
||||||
|
|
||||||
|
// Calculate positions for each video
|
||||||
|
const video1X = (canvas.width - videos[0].videoWidth) / 2; |
||||||
|
const video1Y = (canvas.height - videos[0].videoHeight) / 2; |
||||||
|
const video2X = (canvas.width - videos[1].videoWidth) / 2; |
||||||
|
const video2Y = (canvas.height - videos[1].videoHeight) / 2; |
||||||
|
|
||||||
|
// Draw video 1
|
||||||
|
ctx.drawImage(videos[0], video1X, video1Y, videos[0].videoWidth, videos[0].videoHeight); |
||||||
|
|
||||||
|
// Set composite operation to overlay
|
||||||
|
ctx.globalCompositeOperation = "overlay"; |
||||||
|
|
||||||
|
// Draw video 2
|
||||||
|
ctx.drawImage(videos[1], video2X, video2Y, videos[1].videoWidth, videos[1].videoHeight); |
||||||
|
|
||||||
|
// Reset composite operation
|
||||||
|
ctx.globalCompositeOperation = "source-over"; |
||||||
|
|
||||||
|
// Draw debug text if debug mode is enabled
|
||||||
|
if (debugMode) { |
||||||
|
drawDebugText(); |
||||||
|
} |
||||||
|
// Request next frame
|
||||||
|
requestAnimationFrame(drawVideos); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function updateCanvasSize() { |
||||||
|
aspectRatio = Math.max( |
||||||
|
videos[0].videoWidth / videos[0].videoHeight, |
||||||
|
videos[1].videoWidth / videos[1].videoHeight |
||||||
|
); |
||||||
|
|
||||||
|
canvas.width = window.innerWidth; |
||||||
|
canvas.height = window.innerWidth / aspectRatio; |
||||||
|
} |
||||||
|
|
||||||
|
window.addEventListener("resize", updateCanvasSize); |
||||||
|
|
||||||
|
updateCanvasSize() |
||||||
|
|
||||||
|
function selectRandomVideo(videoElement) { |
||||||
|
console.log("selectRandomVideo",videoElement) |
||||||
|
fetch("/api/videos") |
||||||
|
.then((response) => response.json()) |
||||||
|
.then((videoFiles) => { |
||||||
|
const otherVideo = videos.find((video) => video !== videoElement); |
||||||
|
|
||||||
|
let randomVideo; |
||||||
|
do { |
||||||
|
const randomIndex = Math.floor(Math.random() * videoFiles.length); |
||||||
|
randomVideo = videoFiles[randomIndex]; |
||||||
|
} while (randomVideo === otherVideo); |
||||||
|
|
||||||
|
videoElement.src = randomVideo.src; |
||||||
|
videoElement.play(); |
||||||
|
return videoElement |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
document.addEventListener('keydown', function(event) { |
||||||
|
// Check if the pressed key is the one you want to bind
|
||||||
|
if (event.key === 'Enter') { // Change 'Enter' to the desired key
|
||||||
|
debugMode = !debugMode |
||||||
|
console.log("debugMode:",debugMode) |
||||||
|
} |
||||||
|
}); |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
body, html { |
||||||
|
background: black; |
||||||
|
height: 100%; |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
|
||||||
|
#videoCanvas { |
||||||
|
position: absolute; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
} |
||||||
Loading…
Reference in new issue