diff --git a/tv-player/index.html b/tv-player/index.html
index ba3fc4e..4b86fa8 100644
--- a/tv-player/index.html
+++ b/tv-player/index.html
@@ -48,6 +48,7 @@
+
@@ -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() {
diff --git a/tv-player/src/video-player.js b/tv-player/src/video-player.js
new file mode 100644
index 0000000..7adde0a
--- /dev/null
+++ b/tv-player/src/video-player.js
@@ -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);
+}
\ No newline at end of file