Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>V Corp</title> | |
<meta name="twitter:image" content="https://i.imgur.com/JcRLYji.jpeg" /> | |
<meta property="og:image" content="https://i.imgur.com/JcRLYji.jpeg" /> | |
<link rel="stylesheet" href="index.css" /> | |
<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=Inter:wght@900&family=Satisfy&display=swap" | |
rel="stylesheet" | |
/> | |
<audio id="audioPlayer" src="only.mp3" autoplay></audio> | |
<script src="/mpegts.js"></script> | |
</head> | |
<body> | |
<div | |
style=" | |
background-image: url('https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExMWZyb3d2ZnM2cGJ5NHNmamtiYWxubTFoa2Jib2NxcmQxY21odDBkaCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/qyXsup1970N5UJNHgI/giphy.gif'); | |
" | |
class="bg-no-repeat bg-center bg-cover bg-fixed h-screen w-screen grid place-content-center place-items-center overflow-hidden bg-gradient-to-b from-slate-900 to-black" | |
id="bgimg" | |
> | |
<div class="flex place-content-center justify-between flex-row"> | |
<h2 class="cursive text-6xl font-bold text-blue-300">Vgony</h2> | |
<h1 class="cursive display-flex text-4xl font-black text-grey-300">TV</h1> | |
<a id="eyeButton" class="flex grid place-content-end mr-25"> | |
<img src="eye.png" alt="Eye Icon" height="30px" width="auto" /> | |
</a> | |
</div> | |
<div> | |
<a href="https://vgony.tech" class="exit-simulation-button"> | |
<img id="exitButton" src="exit.svg" alt="Exit button" /> | |
</a> | |
</div> | |
<div x-data="app()" x-init="init()" class="container mx-auto z-10"> | |
<div class="flex grid place-content-center justify-center w-full p-1"> | |
<video | |
id="videoElement" | |
class="aspect-video mx-auto w-auto border-4 border-6 border-slate-900/30 rounded-full" | |
preload="auto" | |
muted | |
autoplay | |
></video> | |
</div> | |
<div class="flex justify-center bottom-0 pb-2 z-10"> | |
<div class="container mx-auto px-4 opacity-85"> | |
<div class="flex-row items-center justify-center"> | |
<div | |
class="flex items-center space-x-6 text-xs focus:cursor-pointer" | |
> | |
<template x-for="(chan, index) in channels"> | |
<div | |
class="text-sm capitalize truncate mr-2" | |
:class="chan.id === channel.id ? 'font-semibold cursor-pointer text-white' : 'text-slate-100 cursor-pointer hover:underline'" | |
@click="window.location = `${window.location.origin}/?channel=${chan.id}`" | |
x-text="chan.label" | |
> | |
<span | |
class="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-blue-500 opacity-75" | |
></span> | |
</div> | |
</template> | |
</div> | |
<div class="flex-col justify-center items-center"> | |
<div class="flex items-center justify-center"> | |
<a class="text-white font-bold" x-text="''"></a> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="flex-col items-center justify-center pt-14"> | |
<button | |
id="playPauseBtn" | |
type="button" | |
class="dark:bg-slate-100 dark:text-slate-700 flex-none -my-2 mx-auto w-10 h-10 rounded-full shadow-md flex items-center justify-center opacity-90" | |
aria-label="Pause" | |
> | |
<img | |
src="headphones.svg" | |
alt="Pause Button" | |
width="32" | |
height="32" | |
/> | |
</button> | |
</div> | |
</div> | |
</div> | |
<div id="placeholder" class="w-auto h-vh relative inset-0 z-0 bg-fixed" | |
style=" | |
background-image: url('https://media2.giphy.com/media/v1.Y2lkPTc5MGI3NjExMnBqODlnemxoanp0cGpuMGR6aG12NWxqbmxkMjdiaDZvZWgxdmozeSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/24FW7JSoZ66w9ALKli/giphy.gif'); | |
background-position: center; | |
background-repeat: no-repeat; | |
background-size: 50%; | |
background-color: black; | |
transition: background-image 0.5s ease-in-out; | |
"></div> | |
<div id="placeholder" class="w-auto h-vh relative inset-0 z-0 bg-fixed" | |
style=" | |
background-image: url('grimreaper.gif'); | |
background-position: center; | |
background-repeat: no-repeat; | |
background-size: 58%; | |
background-color: black; | |
transition: background-image 0.5s ease-in-out; | |
"></div> | |
<script> | |
const eyeButton = document.getElementById("eyeButton"); | |
const videoWrapper = document.getElementById("videoElement"); | |
const audioPlayer = document.getElementById("audioPlayer"); | |
const playPauseBtn = document.getElementById("playPauseBtn"); | |
const audioFiles = [ | |
"only.mp3", | |
"only.mp3", | |
// Add more audio file paths here | |
]; | |
// Shuffle the audio files array | |
function shuffleArray(array) { | |
for (let i = array.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[array[i], array[j]] = [array[j], array[i]]; | |
} | |
return array; | |
} | |
eyeButton.addEventListener("click", () => { | |
videoWrapper.classList.toggle("fade-in"); | |
}); | |
// Play the next audio file in the shuffled array | |
function playNextAudio() { | |
const currentIndex = audioFiles.findIndex((file) => file === audioPlayer.src); | |
const nextIndex = (currentIndex + 1) % audioFiles.length; | |
audioPlayer.src = audioFiles[nextIndex]; | |
audioPlayer.play(); | |
} | |
// Handle clicking the play/pause button | |
playPauseBtn.addEventListener("click", () => { | |
if (audioPlayer.paused) { | |
audioPlayer.play(); | |
playPauseBtn.querySelector("img").src = "pause.svg"; | |
} else { | |
audioPlayer.pause(); | |
playPauseBtn.querySelector("img").src = "headphones.svg"; | |
} | |
}); | |
// Setup audio player | |
shuffleArray(audioFiles); | |
audioPlayer.src = audioFiles[0]; | |
audioPlayer.play(); | |
audioPlayer.addEventListener("ended", playNextAudio); | |
// Display the video once loaded | |
document.addEventListener("DOMContentLoaded", function () { | |
setTimeout(function () { | |
showVideo(); | |
}, 1000); | |
}); | |
function showVideo() { | |
var videoElement = document.getElementById("videoElement"); | |
videoElement.classList.add("fade-in"); | |
} | |
</script> | |
<script> | |
// disable analytics (we don't use VideoJS yet anyway) | |
window.HELP_IMPROVE_VIDEOJS = false; | |
</script> | |
<script | |
defer | |
src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" | |
></script> | |
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.2/iframeResizer.contentWindow.min.js"></script> | |
<script> | |
function app() { | |
return { | |
enabled: false, | |
channels: { | |
/* | |
legacy: { | |
id: 'legacy', | |
label: '#older', | |
audience: 0, | |
online: false, | |
visible: false, | |
url: 'https://jbilcke-hf-media-server.hf.space/live/legacy.flv', | |
resolution: '576x320', | |
model: 'zeroscope_v2_576w', | |
modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_576w', | |
}, | |
*/ | |
/* | |
hdtv: { | |
id: 'hdtv', | |
label: '#old', | |
audience: 0, | |
online: false, | |
visible: true, | |
url: 'https://jbilcke-hf-media-server.hf.space/live/hdtv.flv', | |
resolution: '1024x576_8FPS', | |
model: 'zeroscope_v2_XL', | |
modelUrl: 'https://huggingface.co/cerspense/zeroscope_v2_XL', | |
}, | |
*/ | |
random: { | |
id: "random", | |
label: "#Documentary", | |
audience: 0, | |
online: false, | |
visible: true, | |
url: "https://jbilcke-hf-media-server.hf.space/live/random.flv", | |
resolution: "1024x576_24FPS", | |
model: "zeroscope_v2_XL", | |
modelUrl: "https://huggingface.co/cerspense/zeroscope_v2_XL", | |
}, | |
comedy: { | |
id: "comedy", | |
label: "#Entertainment", | |
audience: 0, | |
online: false, | |
visible: true, | |
url: "https://jbilcke-hf-media-server.hf.space/live/comedy.flv", | |
resolution: "1024x576_24FPS", | |
model: "zeroscope_v2_XL", | |
modelUrl: "https://huggingface.co/cerspense/zeroscope_v2_XL", | |
}, | |
documentary: { | |
id: "documentary", | |
label: "#Reset", | |
audience: 0, | |
online: false, | |
visible: true, | |
url: "https://jbilcke-hf-media-server.hf.space/live/documentary.flv", | |
resolution: "1024x576_24FPS", | |
model: "zeroscope_v2_XL", | |
modelUrl: "https://huggingface.co/cerspense/zeroscope_v2_XL", | |
}, | |
}, | |
muted: true, | |
initialized: false, | |
activityTimeout: null, | |
defaultChannelId: "random", | |
video: null, | |
channel: {}, | |
wakeUp() { | |
this.showToolbar = true; | |
}, | |
toggleAudio() { | |
if (this.video.muted) { | |
this.video.muted = false; | |
this.muted = false; | |
} else { | |
this.video.muted = true; | |
this.muted = true; | |
} | |
}, | |
async checkAudience() { | |
let audience = {}; | |
try { | |
const res = await fetch("/stats"); | |
audience = await res.json(); | |
} catch (err) { | |
console.log("failed to check the audience, something is wrong"); | |
} | |
window.DEBUGME = Object.entries(this.channels); | |
this.channels = Object.entries(this.channels).reduce( | |
(acc, [channel, data]) => ( | |
console.log("debug:", { | |
...data, | |
audience: audience[channel] || 0, | |
}), | |
{ | |
...acc, | |
[channel]: { | |
...data, | |
audience: audience[channel] || 0, | |
}, | |
} | |
), | |
{} | |
); | |
this.channel = this.channels[this.channel.id]; | |
}, | |
fullscreen() { | |
if (this.video.requestFullscreen) { | |
this.video.requestFullscreen(); | |
} else if (this.video.mozRequestFullScreen) { | |
this.video.mozRequestFullScreen(); | |
} else if (this.video.webkitRequestFullscreen) { | |
this.video.webkitRequestFullscreen(); | |
} else if (this.video.msRequestFullscreen) { | |
this.video.msRequestFullscreen(); | |
} | |
}, | |
init() { | |
if (this.initialized) { | |
console.log("already initialized"); | |
return; | |
} | |
this.initialized = true; | |
console.log("initializing.."); | |
const urlParams = new URLSearchParams(window.location.search); | |
const requestedChannelId = `${ | |
urlParams.get("channel") || "random" | |
}`; | |
this.enabled = true; | |
// this.enabled = `${urlParams.get('beta') || 'false'}` === 'true' | |
if (!this.enabled) { | |
return; | |
} | |
this.video = document.getElementById("videoElement"); | |
const defaultChannel = this.channels[this.defaultChannelId]; | |
this.channel = this.channels[requestedChannelId] || defaultChannel; | |
console.log(`Selected channel: ${this.channel.label}`); | |
console.log(`Stream URL: ${this.channel.url}`); | |
const handleActivity = () => { | |
this.wakeUp(); | |
}; | |
handleActivity(); | |
this.checkAudience(); | |
setInterval(() => { | |
this.checkAudience(); | |
}, 1000); | |
// detect mute/unmute events | |
this.video.addEventListener("mute", () => { | |
this.muted = true; | |
}); | |
this.video.addEventListener("unmute", () => { | |
this.muted = false; | |
}); | |
// as a bonus, we also allow fullscreen on double click | |
this.video.addEventListener("dblclick", () => { | |
this.fullscreen(); | |
}); | |
// some devices such as the iPhone don't support MSE Live Playback | |
if (mpegts.getFeatureList().mseLivePlayback) { | |
var player = mpegts.createPlayer({ | |
type: "flv", // could also be mpegts, m2ts, flv | |
isLive: true, | |
url: this.channel.url, | |
}); | |
player.attachMediaElement(this.video); | |
player.on(mpegts.Events.ERROR, function (err) { | |
console.log("got an error:", err); | |
if (err.type === mpegts.ErrorTypes.NETWORK_ERROR) { | |
console.log("Network error"); | |
} | |
}); | |
player.load(); | |
// due to an issue with our stream when the FFMPEG playlist ends, | |
// the stream gets interrupted for ~1sec, which causes the frontend to hangs up | |
// the following code tries to restart the page when that happens, but in the long term | |
// we should fix the issue on the server side (fix our FFMPEG bash script) | |
this.video.addEventListener( | |
"ended", | |
function () { | |
console.log("Stream ended, trying to reload..."); | |
setTimeout(() => { | |
console.log("Reloading the page.."); | |
// Unloading and loading the source again isn't enough it seems | |
// player.unload() | |
// player.load() | |
window.location.reload(); | |
}, 1200); | |
}, | |
false | |
); | |
// Handle autoplay restrictions. | |
let promise = this.video.play(); | |
if (promise !== undefined) { | |
this.video.addEventListener("click", function () { | |
this.video.play(); | |
}); | |
} | |
player.play(); | |
} | |
}, | |
}; | |
} | |
</script> | |
</body> | |
</html> | |