const tokenInput = document.querySelector('#token'); const ffmpegPathInput = document.querySelector('#ffmpeg-path'); const rememberTokenCheckbox = document.querySelector('#remember-token'); const setTokenButton = document.querySelector('#set-token'); const rememberFFmpegPathCheckbox = document.querySelector('#remember-ffmpeg-path'); const setFFmpegPathButton = document.querySelector('#set-ffmpeg-path'); const lessonInput = document.querySelector('#lesson-query'); const getLessonButton = document.querySelector('#get-lesson'); let token = localStorage.getItem('token') || ''; let ffmpegPath = localStorage.getItem('ffmpegPath') || ''; if (token !== '') { tokenInput.value = token; rememberTokenCheckbox.checked = true; fetch("/api/v1/token", { method: 'POST', body: token, }).then(rsp => { if (rsp.status !== 200) { setTokenButton.innerHTML = `:( (${rsp.status})`; } }); } if (ffmpegPath !== '') { ffmpegPathInput.value = token; rememberFFmpegPathCheckbox.checked = true; fetch("/api/v1/ffmpeg-path", { method: 'POST', body: token, }).then(rsp => { if (rsp.status !== 200) { setTokenButton.innerHTML = `:( (${rsp.status})`; } }); } function randomString(length) { return `a${Math.random().toString(36).slice(2, length + 2)}`; } // React simulator function getResult() { const element = document.createElement("div"); element.className = "block"; document.body.appendChild(document.createElement("br")); document.body.appendChild(element); return element; } setTokenButton.addEventListener('click', async () => { token = tokenInput.value; const rsp = await fetch("/api/v1/token", { method: 'POST', body: token, }); if (rsp.status !== 200) { setTokenButton.innerHTML = `:( (${rsp.status})`; return; } if (rememberTokenCheckbox.checked) { localStorage.setItem('token', token); } else { localStorage.removeItem('token'); } }); setFFmpegPathButton.addEventListener('click', async () => { ffmpegPath = ffmpegPathInput.value; const rsp = await fetch("/api/v1/ffmpeg-path", { method: 'POST', body: ffmpegPath, }); if (rsp.status !== 200) { setFFmpegPathButton.innerHTML = `:( (${rsp.status})`; return; } if (rememberFFmpegPathCheckbox.checked) { localStorage.setItem('ffmpegPath', ffmpegPath); } else { localStorage.removeItem('ffmpegPath'); } }); getLessonButton.addEventListener('click', async () => { const input = lessonInput.value; let id = Number.parseInt(input); if (Number.isNaN(id)) { const url = input.split('/'); url.pop(); id = Number.parseInt(url.pop()); } const rsp = await fetch(`/api/v1/lessons/${id}`); const result = getResult(); const data = await rsp.json(); const ids = {}; // React :nerd: result.innerHTML = "

Lesson Information

\n"; result.innerHTML += `Name: ${decodeURIComponent(data.name)}`; result.innerHTML += "

Videos available

"; for (const video of data.videos) { const uniqueId = randomString(15); ids[uniqueId] = video; result.innerHTML += `
`; } let videoDlResult = null; const promises = []; const downloadSelectedButton = document.createElement("button"); downloadSelectedButton.innerHTML = "Download Selected"; downloadSelectedButton.addEventListener('click', async () => { for (const [id, video] of Object.entries(ids)) { promises.push(async () => { const checkbox = document.querySelector(`#${id}`); // Disable the checkbox since the user can't download the same video twice checkbox.disabled = true; if (!checkbox.checked) { return; } const videoRsp = await fetch(`/api/v1/videos/${video.id}`); if (videoRsp.status !== 200) { console.error(`Failed to get video ${video.name}`); return; } const videoData = await videoRsp.json(); if (videoDlResult === null) { result.appendChild(document.createElement("br")); result.appendChild(document.createElement("br")); videoDlResult = document.createElement("div"); videoDlResult.className = "block"; result.appendChild(videoDlResult); } const progressId = randomString(15); const progressStrId = randomString(15); videoDlResult.innerHTML += `${video.name}.mp4 0%
` const rsp = await fetch(`/api/v1/download-video?${new URLSearchParams({ url: encodeURIComponent(videoData.m3u8), output: encodeURIComponent(`videos/${video.name}.mp4`), })}`); const reader = rsp.body.getReader(); while (true) { // wait for next encoded chunk const { done, value } = await reader.read(); // check if stream is done if (done) break; // Decodes data chunk and yields it const progress = Math.floor(Number.parseFloat(new TextDecoder().decode(value))); document.querySelector(`#${progressId}`).value = progress; document.querySelector(`#${progressStrId}`).innerText = `${progress}%`; } if (rsp.status !== 200) { console.error(`Failed to download ${video.name}`); } }); } await Promise.all(promises.map(p => p())); }); result.appendChild(downloadSelectedButton); // result.innerHTML += ""; });