199 lines
4.2 KiB
Svelte
199 lines
4.2 KiB
Svelte
<script>
|
|
import { Link, useNavigate } from "svelte-navigator";
|
|
import { onMount } from "svelte";
|
|
import Spinner from "./components/Spinner.svelte";
|
|
const navigate = useNavigate();
|
|
|
|
export let audioId;
|
|
export let videoId;
|
|
|
|
// @ts-ignore
|
|
let ytReady = Boolean(window.YT);
|
|
|
|
let videoUrl = "";
|
|
let audioUrl = "";
|
|
|
|
let videoReady = false;
|
|
let audioReady = false;
|
|
|
|
let videoEl;
|
|
let audioEl;
|
|
|
|
let videoPlayer;
|
|
let audioPlayer;
|
|
|
|
function extract(url) {
|
|
const match = url.match(/(youtu\.be\/|v=)([\w-]+)/);
|
|
if (match) {
|
|
return match[2];
|
|
} else {
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
function synchronize(what) {
|
|
if (what == "audio") {
|
|
audioPlayer.seekTo(videoPlayer.getCurrentTime(), true);
|
|
} else {
|
|
videoPlayer.seekTo(audioPlayer.getCurrentTime(), true);
|
|
}
|
|
}
|
|
|
|
async function init() {
|
|
const currentResult = await fetch("grid.php");
|
|
if (!currentResult.ok) {
|
|
alert(currentResult.status);
|
|
return;
|
|
}
|
|
const current = await currentResult.json();
|
|
videoUrl = current.find((link) => link.id == videoId).url;
|
|
audioUrl = current.find((link) => link.id == audioId).url;
|
|
|
|
videoEl.innerHTML = "";
|
|
let videoPlayerEl = document.createElement("div");
|
|
videoEl.appendChild(videoPlayerEl);
|
|
|
|
// @ts-ignore
|
|
videoPlayer = new YT.Player(videoPlayerEl, {
|
|
videoId: extract(videoUrl),
|
|
events: {
|
|
onReady: () => {
|
|
videoReady = true;
|
|
},
|
|
onStateChange: (ev) => {
|
|
// Paused
|
|
if (ev.data == 2) {
|
|
synchronize("audio");
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
audioEl.innerHTML = "";
|
|
let audioPlayerEl = document.createElement("div");
|
|
audioEl.appendChild(audioPlayerEl);
|
|
|
|
// @ts-ignore
|
|
audioPlayer = new YT.Player(audioPlayerEl, {
|
|
videoId: extract(audioUrl),
|
|
events: {
|
|
onReady: () => {
|
|
audioReady = true;
|
|
},
|
|
onStateChange: (ev) => {
|
|
// Paused
|
|
if (ev.data == 2) {
|
|
synchronize("video");
|
|
}
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
onMount(() => {
|
|
const tag = document.createElement("script");
|
|
tag.src = "https://www.youtube.com/iframe_api";
|
|
const firstScriptTag = document.getElementsByTagName("script")[0];
|
|
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
|
|
|
// @ts-ignore
|
|
window.onYouTubeIframeAPIReady = () => {
|
|
ytReady = true;
|
|
};
|
|
});
|
|
|
|
$: {
|
|
audioId;
|
|
videoId;
|
|
|
|
if (ytReady) {
|
|
init();
|
|
}
|
|
}
|
|
|
|
function go() {
|
|
videoPlayer.playVideo();
|
|
videoPlayer.mute();
|
|
audioPlayer.playVideo();
|
|
}
|
|
|
|
async function onBroken(id) {
|
|
const result = await fetch(`grid.php?id=${id}`, {
|
|
method: "DELETE",
|
|
});
|
|
if (result.ok) {
|
|
alert(`ID ${id} marked as broken, thanks!`);
|
|
navigate("/");
|
|
} else {
|
|
alert(`ERR: ${result.status} ${result.statusText}`);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<main>
|
|
<a
|
|
class="link"
|
|
class:disabled={!videoReady || !audioReady}
|
|
href="javascript:;"
|
|
on:click={go}>START</a
|
|
>
|
|
|
|
<div id="player">
|
|
<h2 title={videoUrl}>Video</h2>
|
|
{#if !videoReady}<Spinner />{/if}
|
|
<div id="videoPlayer" bind:this={videoEl} />
|
|
<a class="broken" href="javascript:;" on:click={() => onBroken(videoId)}
|
|
>broken?</a
|
|
>
|
|
<h2 title={audioUrl}>Audio</h2>
|
|
{#if !audioReady}<Spinner />{/if}
|
|
<div id="audioPlayer" bind:this={audioEl} />
|
|
<a class="broken" href="javascript:;" on:click={() => onBroken(audioId)}
|
|
>broken?</a
|
|
>
|
|
</div>
|
|
|
|
<Link class="link" to="/">BACK</Link>
|
|
</main>
|
|
|
|
<style>
|
|
main {
|
|
text-align: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
:global(.link) {
|
|
font-family: inherit;
|
|
font-size: inherit;
|
|
padding: 1em 2em;
|
|
margin: 2rem;
|
|
color: white;
|
|
background-color: rgba(255, 255, 255, 0.25);
|
|
border-radius: 2em;
|
|
border: 2px solid rgba(255, 255, 255, 0);
|
|
outline: none;
|
|
width: 200px;
|
|
font-variant-numeric: tabular-nums;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
}
|
|
|
|
:global(#audioPlayer > *) {
|
|
height: 128px;
|
|
}
|
|
|
|
a {
|
|
color: red;
|
|
opacity: 0.5;
|
|
font-size: 80%;
|
|
}
|
|
|
|
.disabled {
|
|
color: gray;
|
|
cursor: not-allowed;
|
|
}
|
|
</style>
|