anycoder-2f0b2c45 / index.html
Ezequield's picture
Upload folder using huggingface_hub
2678652 verified
Raw History Blame Contribute Delete
24.6 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VCR-2001 Video Deck</title>
<!-- Import Google Fonts for that Retro Digital look -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=VT323&display=swap" rel="stylesheet">
<!-- FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--vcr-body: #2b2b2b;
--vcr-faceplate: #1a1a1a;
--vcr-accent: #4a90e2; /* 2001 Tech Blue */
--vcr-led-red: #ff3333;
--vcr-led-green: #33ff33;
--crt-bg: #050505;
--crt-scanline: rgba(18, 16, 16, 0.5);
--metal-gradient: linear-gradient(135deg, #444 0%, #222 50%, #111 100%);
}
* {
box-sizing: border-box;
user-select: none;
}
body {
margin: 0;
padding: 0;
background-color: #121212;
background-image: radial-gradient(#222 1px, transparent 1px);
background-size: 20px 20px;
font-family: 'Orbitron', sans-serif;
color: #eee;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
overflow-x: hidden;
}
/* --- Header / Branding --- */
header {
width: 100%;
padding: 1rem;
text-align: center;
background: rgba(0, 0, 0, 0.8);
border-bottom: 2px solid var(--vcr-accent);
margin-bottom: 2rem;
position: relative;
z-index: 10;
}
.brand-title {
font-size: 1.5rem;
text-transform: uppercase;
letter-spacing: 4px;
color: var(--vcr-accent);
text-shadow: 0 0 10px var(--vcr-accent);
}
.anycoder-link {
font-family: 'VT323', monospace;
font-size: 1.2rem;
color: #888;
text-decoration: none;
margin-top: 5px;
display: inline-block;
transition: color 0.3s;
}
.anycoder-link:hover {
color: #fff;
text-shadow: 0 0 5px #fff;
}
/* --- Main VCR Unit --- */
.vcr-unit {
width: 90%;
max-width: 800px;
background: var(--metal-gradient);
border-radius: 10px;
padding: 20px;
box-shadow:
0 20px 50px rgba(0,0,0,0.8),
inset 0 1px 1px rgba(255,255,255,0.2),
0 0 0 2px #000;
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
}
/* Screws in corners */
.screw {
width: 12px;
height: 12px;
background: #555;
border-radius: 50%;
position: absolute;
box-shadow: inset 1px 1px 2px #000, 1px 1px 1px rgba(255,255,255,0.2);
}
.screw::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 80%;
height: 2px;
background: #222;
transform: translate(-50%, -50%) rotate(45deg);
}
.screw.tl { top: 10px; left: 10px; }
.screw.tr { top: 10px; right: 10px; }
.screw.bl { bottom: 10px; left: 10px; }
.screw.br { bottom: 10px; right: 10px; }
/* --- The CRT Screen --- */
.crt-container {
width: 100%;
background: #000;
border-radius: 20px / 10px; /* Slight curvature */
padding: 20px 20px 40px 20px; /* More padding bottom for bezel */
border: 4px solid #111;
box-shadow: inset 0 0 20px rgba(0,0,0,1);
position: relative;
overflow: hidden;
aspect-ratio: 4/3;
}
.screen-glass {
width: 100%;
height: 100%;
background: var(--crt-bg);
position: relative;
overflow: hidden;
border-radius: 4px; /* Inner screen radius */
box-shadow: 0 0 10px rgba(0,0,0,0.5);
}
video {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
filter: contrast(1.1) saturation(1.1);
}
/* CRT Effects Overlay */
.crt-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 5;
background:
/* Scanlines */
linear-gradient(
rgba(18, 16, 16, 0) 50%,
rgba(0, 0, 0, 0.25) 50%
),
/* Subtle RGB shift */
linear-gradient(
90deg,
rgba(255, 0, 0, 0.06),
rgba(0, 255, 0, 0.02),
rgba(0, 0, 255, 0.06)
);
background-size: 100% 4px, 6px 100%;
animation: flicker 0.15s infinite;
}
.screen-text {
position: absolute;
color: var(--vcr-led-green);
font-family: 'VT323', monospace;
font-size: 2rem;
z-index: 6;
text-shadow: 0 0 5px var(--vcr-led-green);
pointer-events: none;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
text-align: center;
}
.screen-text.hidden {
display: none;
}
.no-signal-anim {
animation: blink 1s step-end infinite;
}
/* --- VCR Faceplate & Controls --- */
.faceplate {
background: var(--vcr-faceplate);
width: 100%;
border-radius: 4px;
padding: 20px;
display: grid;
grid-template-columns: 1fr 2fr;
gap: 20px;
border: 1px solid #333;
box-shadow: inset 0 0 10px #000;
}
/* Left Side: Info & Brand */
.info-panel {
display: flex;
flex-direction: column;
justify-content: space-between;
border-right: 1px solid #333;
padding-right: 20px;
}
.vcr-logo {
font-size: 2rem;
font-weight: bold;
font-style: italic;
background: linear-gradient(180deg, #ddd, #888);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 10px;
}
.lcd-display {
background: #0a2a0a; /* Dark green LCD background */
color: var(--vcr-led-green);
font-family: 'VT323', monospace;
padding: 10px;
border-radius: 4px;
border: 2px inset #333;
font-size: 1.2rem;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: inset 0 0 5px rgba(0,0,0,0.8);
}
.counter {
letter-spacing: 2px;
}
.status-led {
width: 10px;
height: 10px;
background: #333;
border-radius: 50%;
box-shadow: inset 0 1px 2px rgba(0,0,0,0.5);
transition: background 0.2s, box-shadow 0.2s;
}
.status-led.on {
background: var(--vcr-led-red);
box-shadow: 0 0 8px var(--vcr-led-red);
}
/* Right Side: Controls */
.controls-panel {
display: flex;
flex-direction: column;
gap: 15px;
}
/* Tape Slot Area */
.tape-slot {
height: 50px;
background: #000;
border-radius: 4px;
border: 1px solid #333;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.tape-label {
font-size: 0.8rem;
color: #555;
text-transform: uppercase;
letter-spacing: 2px;
}
/* Main Buttons */
.button-row {
display: flex;
justify-content: space-between;
gap: 10px;
background: #222;
padding: 10px;
border-radius: 6px;
border: 1px solid #444;
}
.vcr-btn {
background: linear-gradient(180deg, #555, #333);
border: none;
border-radius: 4px;
color: #ccc;
padding: 10px 15px;
font-family: 'Orbitron', sans-serif;
font-size: 0.8rem;
cursor: pointer;
box-shadow:
0 4px 0 #111,
0 5px 5px rgba(0,0,0,0.5);
transition: transform 0.1s, box-shadow 0.1s;
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
flex: 1;
}
.vcr-btn i {
font-size: 1.2rem;
margin-bottom: 2px;
}
.vcr-btn:active {
transform: translateY(4px);
box-shadow:
0 0 0 #111,
inset 0 2px 5px rgba(0,0,0,0.5);
}
.vcr-btn.play-btn { color: var(--vcr-led-green); }
.vcr-btn.stop-btn { color: var(--vcr-led-red); }
.vcr-btn.upload-btn {
background: linear-gradient(180deg, var(--vcr-accent), #2a5a8c);
color: #fff;
}
/* Tracking Slider */
.tracking-control {
display: flex;
align-items: center;
gap: 10px;
}
.tracking-label {
font-size: 0.7rem;
color: #888;
text-transform: uppercase;
width: 60px;
}
input[type=range] {
-webkit-appearance: none;
width: 100%;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 20px;
width: 10px;
border-radius: 2px;
background: #ccc;
cursor: pointer;
margin-top: -8px;
box-shadow: 1px 1px 3px #000;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: #111;
border-radius: 2px;
border: 1px solid #333;
}
/* --- Animations --- */
@keyframes flicker {
0% { opacity: 0.95; }
5% { opacity: 0.85; }
10% { opacity: 0.95; }
100% { opacity: 0.95; }
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes scanline-scroll {
0% { transform: translateY(-100%); }
100% { transform: translateY(100%); }
}
/* Noise overlay for static */
.static-noise {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;utf8,%3Csvg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"%3E%3Cfilter id="noiseFilter"%3E%3CfeTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="3" stitchTiles="stitch"/%3E%3C/filter%3E%3Crect width="100%25" height="100%25" filter="url(%23noiseFilter)" opacity="0.4"/%3E%3C/svg%3E');
opacity: 0;
pointer-events: none;
z-index: 4;
mix-blend-mode: overlay;
}
.static-noise.active {
opacity: 1;
animation: noiseAnim 0.2s steps(4) infinite;
}
@keyframes noiseAnim {
0% { transform: translate(0, 0); }
10% { transform: translate(-5%, -5%); }
20% { transform: translate(-10%, 5%); }
30% { transform: translate(5%, -10%); }
40% { transform: translate(-5%, 15%); }
50% { transform: translate(-10%, 5%); }
60% { transform: translate(15%, 0); }
70% { transform: translate(0, 10%); }
80% { transform: translate(-15%, 0); }
90% { transform: translate(10%, 5%); }
100% { transform: translate(5%, 0); }
}
/* --- Responsive --- */
@media (max-width: 600px) {
.faceplate {
grid-template-columns: 1fr;
}
.info-panel {
border-right: none;
border-bottom: 1px solid #333;
padding-right: 0;
padding-bottom: 15px;
}
.vcr-btn span {
display: none; /* Hide text labels on small screens */
}
.vcr-btn {
justify-content: center;
}
}
</style>
</head>
<body>
<header>
<div class="brand-title">VCR-2001</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
Built with anycoder
</a>
</header>
<main class="vcr-unit">
<!-- Decorative Screws -->
<div class="screw tl"></div>
<div class="screw tr"></div>
<div class="screw bl"></div>
<div class="screw br"></div>
<!-- CRT Screen Section -->
<div class="crt-container">
<div class="screen-glass">
<video id="videoPlayer" playsinline loop></video>
<!-- Static Noise Layer -->
<div class="static-noise" id="noiseLayer"></div>
<!-- CRT Scanline Overlay -->
<div class="crt-overlay"></div>
<!-- On-screen Display / No Signal -->
<div class="screen-text" id="screenText">
<div class="no-signal-anim">NO SIGNAL</div>
<div style="font-size: 1rem; margin-top: 10px; color: #888;">[INSERT TAPE]</div>
</div>
</div>
</div>
<!-- Controls Section -->
<div class="faceplate">
<!-- Left: Info & LCD -->
<div class="info-panel">
<div>
<div class="vcr-logo">CYBERDECK</div>
<div style="font-size: 0.7rem; color: #666; letter-spacing: 1px;">HI-FI STEREO VHS</div>
</div>
<div class="lcd-display">
<span id="clockDisplay">12:00 AM</span>
<div class="status-led" id="statusLed"></div>
</div>
<div style="font-size: 0.7rem; color: #555;">
REC: <span id="recStatus">OFF</span>
</div>
</div>
<!-- Right: Buttons & Inputs -->
<div class="controls-panel">
<!-- Hidden File Input -->
<input type="file" id="fileInput" accept="video/*" style="display: none;">
<div class="tape-slot">
<span class="tape-label" id="tapeLabel">NO TAPE</span>
</div>
<div class="button-row">
<button class="vcr-btn" id="ejectBtn" title="Eject">
<i class="fa-solid fa-eject"></i>
<span>EJECT</span>
</button>
<button class="vcr-btn upload-btn" id="uploadBtn" title="Load Tape">
<i class="fa-solid fa-floppy-disk"></i>
<span>LOAD</span>
</button>
</div>
<div class="button-row">
<button class="vcr-btn" id="rewindBtn" title="Rewind">
<i class="fa-solid fa-backward"></i>
<span>REW</span>
</button>
<button class="vcr-btn play-btn" id="playBtn" title="Play">
<i class="fa-solid fa-play"></i>
<span>PLAY</span>
</button>
<button class="vcr-btn stop-btn" id="stopBtn" title="Stop">
<i class="fa-solid fa-stop"></i>
<span>STOP</span>
</button>
<button class="vcr-btn" id="ffBtn" title="Fast Forward">
<i class="fa-solid fa-forward"></i>
<span>FF</span>
</button>
</div>
<div class="tracking-control">
<div class="tracking-label">TRACKING</div>
<input type="range" id="trackingSlider" min="-20" max="20" value="0">
</div>
</div>
</div>
</main>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Elements
const video = document.getElementById('videoPlayer');
const fileInput = document.getElementById('fileInput');
const uploadBtn = document.getElementById('uploadBtn');
const playBtn = document.getElementById('playBtn');
const stopBtn = document.getElementById('stopBtn');
const ejectBtn = document.getElementById('ejectBtn');
const rewindBtn = document.getElementById('rewindBtn');
const ffBtn = document.getElementById('ffBtn');
const trackingSlider = document.getElementById('trackingSlider');
const screenText = document.getElementById('screenText');
const tapeLabel = document.getElementById('tapeLabel');
const statusLed = document.getElementById('statusLed');
const noiseLayer = document.getElementById('noiseLayer');
const clockDisplay = document.getElementById('clockDisplay');
const recStatus = document.getElementById('recStatus');
let isPlaying = false;
let playbackSpeed = 1.0;
let videoLoaded = false;
// --- Clock Functionality ---
function updateClock() {
const now = new Date();
let hours = now.getHours();
const minutes = String(now.getMinutes()).padStart(2, '0');
const ampm = hours >= 12 ? 'PM' : 'AM';
hours = hours % 12;
hours = hours ? hours : 12;
clockDisplay.textContent = `${hours}:${minutes} ${ampm}`;
}
setInterval(updateClock, 1000);
updateClock();
// --- File Upload Logic ---
uploadBtn.addEventListener('click', () => {
fileInput.click();
});
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
const fileURL = URL.createObjectURL(file);
video.src = fileURL;
tapeLabel.textContent = file.name.substring(0, 20) + (file.name.length > 20 ? '...' : '');
// UI Updates
screenText.classList.add('hidden');
noiseLayer.classList.remove('active');
// Simulate loading delay
statusLed.classList.add('on'); // Red light for "Loading/Processing"
recStatus.textContent = "LOADING";
video.onloadeddata = () => {
videoLoaded = true;
statusLed.classList.remove('on'); // Turn off loading light
recStatus.textContent = "READY";
playVideo();
};
}
});
// --- Playback Controls ---
function playVideo() {
if (!videoLoaded) return;
video.play()
.then(() => {
isPlaying = true;
statusLed.classList.add('on'); // Red light for "Play/Rec"
playBtn.innerHTML = '<i class="fa-solid fa-pause"></i><span>PAUSE</span>';
noiseLayer.classList.remove('active');
recStatus.textContent = "PLAY";
})
.catch(err => console.error("Play error:", err));
}
function pauseVideo() {
video.pause();
isPlaying = false;
statusLed.classList.remove('on');
playBtn.innerHTML = '<i class="fa-solid fa-play"></i><span>PLAY</span>';
recStatus.textContent = "PAUSE";
}
function stopVideo() {
video.pause();
video.currentTime = 0;
isPlaying = false;
statusLed.classList.remove('on');
playBtn.innerHTML = '<i class="fa-solid fa-play"></i><span>PLAY</span>';
recStatus.textContent = "STOP";
// Show static briefly
noiseLayer.classList.add('active');
setTimeout(() => {
if(!isPlaying) noiseLayer.classList.remove('active');
}, 500);
}
function ejectTape() {
stopVideo();
video.src = "";
videoLoaded = false;
tapeLabel.textContent = "NO TAPE";
screenText.classList.remove('hidden');
recStatus.textContent = "NO TAPE";
fileInput.value = ""; // Reset input
}
// --- Event Listeners ---
playBtn.addEventListener('click', () => {
if (isPlaying) {
pauseVideo();
} else {
playVideo();
}
});
stopBtn.addEventListener('click', stopVideo);
ejectBtn.addEventListener('click', ejectTape);
rewindBtn.addEventListener('click', () => {
if(!videoLoaded) return;
video.playbackRate = 2.0;
video.currentTime -= 5; // Jump back slightly
recStatus.textContent = "<<<";
setTimeout(() => { video.playbackRate = 1.0; }, 500);
});
ffBtn.addEventListener('click', () => {
if(!videoLoaded) return;
video.playbackRate = 2.0;
video.currentTime += 5; // Jump forward slightly
recStatus.textContent = ">>>";
setTimeout(() => { video.playbackRate = 1.0; }, 500);
});
// --- Tracking Effect (Visual Only) ---
trackingSlider.addEventListener('input', (e) => {
const val = e.target.value;
// Apply vertical shift and blur to simulate tracking error
video.style.transform = `translateY(${val}px)`;
video.style.filter = `blur(${Math.abs(val) / 5}px) contrast(1.1)`;
});
// Reset tracking on mouse up
trackingSlider.addEventListener('mouseup', () => {
trackingSlider.value = 0;
video.style.transform = `translateY(0px)`;
video.style.filter = `blur(0px) contrast(1.1)`;
});
// Video Ended Event
video.addEventListener('ended', () => {
isPlaying = false;
statusLed.classList.remove('on');
playBtn.innerHTML = '<i class="fa-solid fa-play"></i><span>PLAY</span>';
recStatus.textContent = "END";
});
});
</script>
</body>
</html>