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/utils.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_flies.js"></script>
|
||||
<script src="./src/vcr-display.js"></script>
|
||||
@ -137,118 +138,6 @@
|
||||
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 ---
|
||||
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