Refactoring: move out video contoller
This commit is contained in:
parent
8ea8ad27ce
commit
942ab5e0ee
@ -48,6 +48,7 @@
|
|||||||
<script src="./src/global-variables.js"></script>
|
<script src="./src/global-variables.js"></script>
|
||||||
<script src="./src/utils.js"></script>
|
<script src="./src/utils.js"></script>
|
||||||
<script src="./src/scene.js"></script>
|
<script src="./src/scene.js"></script>
|
||||||
|
<script src="./src/video-player.js"></script>
|
||||||
<script src="./src/effects_dust.js"></script>
|
<script src="./src/effects_dust.js"></script>
|
||||||
<script src="./src/effects_flies.js"></script>
|
<script src="./src/effects_flies.js"></script>
|
||||||
<script src="./src/vcr-display.js"></script>
|
<script src="./src/vcr-display.js"></script>
|
||||||
@ -137,118 +138,6 @@
|
|||||||
return `${paddedMinutes}:${paddedSeconds}`;
|
return `${paddedMinutes}:${paddedSeconds}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Play video by index ---
|
|
||||||
function playVideoByIndex(index) {
|
|
||||||
currentVideoIndex = index;
|
|
||||||
const url = videoUrls[index];
|
|
||||||
|
|
||||||
// Dispose of previous texture to free resources
|
|
||||||
if (videoTexture) {
|
|
||||||
videoTexture.dispose();
|
|
||||||
videoTexture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index < 0 || index >= videoUrls.length) {
|
|
||||||
console.info('End of playlist reached. Reload tapes to start again.');
|
|
||||||
screenLight.intensity = 0.0;
|
|
||||||
tvScreen.material.dispose();
|
|
||||||
tvScreen.material = new THREE.MeshPhongMaterial({
|
|
||||||
color: 0x0a0a0a, // Deep black
|
|
||||||
shininess: 5,
|
|
||||||
specular: 0x111111
|
|
||||||
});
|
|
||||||
tvScreen.material.needsUpdate = true;
|
|
||||||
isVideoLoaded = false;
|
|
||||||
lastUpdateTime = -1; // force VCR to redraw
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
videoElement.src = url;
|
|
||||||
videoElement.muted = true;
|
|
||||||
videoElement.load();
|
|
||||||
|
|
||||||
// Set loop property: only loop if it's the only video loaded
|
|
||||||
videoElement.loop = false; //videoUrls.length === 1;
|
|
||||||
|
|
||||||
|
|
||||||
videoElement.onloadeddata = () => {
|
|
||||||
// 1. Create the Three.js texture
|
|
||||||
videoTexture = new THREE.VideoTexture(videoElement);
|
|
||||||
videoTexture.minFilter = THREE.LinearFilter;
|
|
||||||
videoTexture.magFilter = THREE.LinearFilter;
|
|
||||||
videoTexture.format = THREE.RGBAFormat;
|
|
||||||
videoTexture.needsUpdate = true;
|
|
||||||
|
|
||||||
// 2. Apply the video texture to the screen mesh
|
|
||||||
tvScreen.material.dispose();
|
|
||||||
tvScreen.material = new THREE.MeshBasicMaterial({ map: videoTexture });
|
|
||||||
tvScreen.material.needsUpdate = true;
|
|
||||||
|
|
||||||
// 3. Start playback
|
|
||||||
videoElement.play().then(() => {
|
|
||||||
isVideoLoaded = true;
|
|
||||||
// Use the defined base intensity for screen glow
|
|
||||||
screenLight.intensity = originalScreenIntensity;
|
|
||||||
// Initial status message with tape count
|
|
||||||
console.info(`Playing tape ${currentVideoIndex + 1} of ${videoUrls.length}.`);
|
|
||||||
}).catch(error => {
|
|
||||||
screenLight.intensity = originalScreenIntensity * 0.5; // Dim the light if playback fails
|
|
||||||
console.error(`Playback blocked for tape ${currentVideoIndex + 1}. Click Next Tape to try again.`);
|
|
||||||
console.error('Playback Error: Could not start video playback.', error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
videoElement.onerror = (e) => {
|
|
||||||
screenLight.intensity = 0.1; // Keep minimum intensity for shadow map
|
|
||||||
console.error(`Error loading tape ${currentVideoIndex + 1}.`);
|
|
||||||
console.error('Video Load Error:', e);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Cycle to the next video ---
|
|
||||||
function playNextVideo() {
|
|
||||||
// Determine the next index, cycling back to 0 if we reach the end
|
|
||||||
let nextIndex = currentVideoIndex + 1;
|
|
||||||
if (nextIndex < videoUrls.length) {
|
|
||||||
baseTime += videoElement.duration;
|
|
||||||
}
|
|
||||||
playVideoByIndex(nextIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --- Video Loading Logic (handles multiple files) ---
|
|
||||||
function loadVideoFile(event) {
|
|
||||||
const files = event.target.files;
|
|
||||||
if (files.length === 0) {
|
|
||||||
console.info('File selection cancelled.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Clear previous URLs and revoke object URLs to prevent memory leaks
|
|
||||||
videoUrls.forEach(url => URL.revokeObjectURL(url));
|
|
||||||
videoUrls = [];
|
|
||||||
|
|
||||||
// 2. Populate the new videoUrls array
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
const file = files[i];
|
|
||||||
if (file.type.startsWith('video/')) {
|
|
||||||
videoUrls.push(URL.createObjectURL(file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoUrls.length === 0) {
|
|
||||||
console.info('No valid video files selected.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Start playback of the first video
|
|
||||||
console.info(`Loaded ${videoUrls.length} tapes. Starting playback...`);
|
|
||||||
loadTapeButton.classList.add("hidden");
|
|
||||||
|
|
||||||
const startDelay = 5;
|
|
||||||
console.info(`Video will start in ${startDelay} seconds.`);
|
|
||||||
setTimeout(() => { playVideoByIndex(0); }, startDelay * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Animation Loop ---
|
// --- Animation Loop ---
|
||||||
function animate() {
|
function animate() {
|
||||||
|
|||||||
112
tv-player/src/video-player.js
Normal file
112
tv-player/src/video-player.js
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// --- Play video by index ---
|
||||||
|
function playVideoByIndex(index) {
|
||||||
|
currentVideoIndex = index;
|
||||||
|
const url = videoUrls[index];
|
||||||
|
|
||||||
|
// Dispose of previous texture to free resources
|
||||||
|
if (videoTexture) {
|
||||||
|
videoTexture.dispose();
|
||||||
|
videoTexture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < 0 || index >= videoUrls.length) {
|
||||||
|
console.info('End of playlist reached. Reload tapes to start again.');
|
||||||
|
screenLight.intensity = 0.0;
|
||||||
|
tvScreen.material.dispose();
|
||||||
|
tvScreen.material = new THREE.MeshPhongMaterial({
|
||||||
|
color: 0x0a0a0a, // Deep black
|
||||||
|
shininess: 5,
|
||||||
|
specular: 0x111111
|
||||||
|
});
|
||||||
|
tvScreen.material.needsUpdate = true;
|
||||||
|
isVideoLoaded = false;
|
||||||
|
lastUpdateTime = -1; // force VCR to redraw
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
videoElement.src = url;
|
||||||
|
videoElement.muted = true;
|
||||||
|
videoElement.load();
|
||||||
|
|
||||||
|
// Set loop property: only loop if it's the only video loaded
|
||||||
|
videoElement.loop = false; //videoUrls.length === 1;
|
||||||
|
|
||||||
|
|
||||||
|
videoElement.onloadeddata = () => {
|
||||||
|
// 1. Create the Three.js texture
|
||||||
|
videoTexture = new THREE.VideoTexture(videoElement);
|
||||||
|
videoTexture.minFilter = THREE.LinearFilter;
|
||||||
|
videoTexture.magFilter = THREE.LinearFilter;
|
||||||
|
videoTexture.format = THREE.RGBAFormat;
|
||||||
|
videoTexture.needsUpdate = true;
|
||||||
|
|
||||||
|
// 2. Apply the video texture to the screen mesh
|
||||||
|
tvScreen.material.dispose();
|
||||||
|
tvScreen.material = new THREE.MeshBasicMaterial({ map: videoTexture });
|
||||||
|
tvScreen.material.needsUpdate = true;
|
||||||
|
|
||||||
|
// 3. Start playback
|
||||||
|
videoElement.play().then(() => {
|
||||||
|
isVideoLoaded = true;
|
||||||
|
// Use the defined base intensity for screen glow
|
||||||
|
screenLight.intensity = originalScreenIntensity;
|
||||||
|
// Initial status message with tape count
|
||||||
|
console.info(`Playing tape ${currentVideoIndex + 1} of ${videoUrls.length}.`);
|
||||||
|
}).catch(error => {
|
||||||
|
screenLight.intensity = originalScreenIntensity * 0.5; // Dim the light if playback fails
|
||||||
|
console.error(`Playback blocked for tape ${currentVideoIndex + 1}. Click Next Tape to try again.`);
|
||||||
|
console.error('Playback Error: Could not start video playback.', error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
videoElement.onerror = (e) => {
|
||||||
|
screenLight.intensity = 0.1; // Keep minimum intensity for shadow map
|
||||||
|
console.error(`Error loading tape ${currentVideoIndex + 1}.`);
|
||||||
|
console.error('Video Load Error:', e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Cycle to the next video ---
|
||||||
|
function playNextVideo() {
|
||||||
|
// Determine the next index, cycling back to 0 if we reach the end
|
||||||
|
let nextIndex = currentVideoIndex + 1;
|
||||||
|
if (nextIndex < videoUrls.length) {
|
||||||
|
baseTime += videoElement.duration;
|
||||||
|
}
|
||||||
|
playVideoByIndex(nextIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Video Loading Logic (handles multiple files) ---
|
||||||
|
function loadVideoFile(event) {
|
||||||
|
const files = event.target.files;
|
||||||
|
if (files.length === 0) {
|
||||||
|
console.info('File selection cancelled.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Clear previous URLs and revoke object URLs to prevent memory leaks
|
||||||
|
videoUrls.forEach(url => URL.revokeObjectURL(url));
|
||||||
|
videoUrls = [];
|
||||||
|
|
||||||
|
// 2. Populate the new videoUrls array
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
if (file.type.startsWith('video/')) {
|
||||||
|
videoUrls.push(URL.createObjectURL(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoUrls.length === 0) {
|
||||||
|
console.info('No valid video files selected.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Start playback of the first video
|
||||||
|
console.info(`Loaded ${videoUrls.length} tapes. Starting playback...`);
|
||||||
|
loadTapeButton.classList.add("hidden");
|
||||||
|
|
||||||
|
const startDelay = 5;
|
||||||
|
console.info(`Video will start in ${startDelay} seconds.`);
|
||||||
|
setTimeout(() => { playVideoByIndex(0); }, startDelay * 1000);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user