const ua = navigator.userAgent.toLowerCase(); const isIOS = ua.match("iphone os"); const isMobile = ua.match("android") || ua.match("iphone os"); const isSafari = ua.match("safari/"); const isFirefox = ua.match("firefox/"); const isOldFirefox = ua.match("firefox/") && ua.split("firefox/")[1].split('.')[0] < 103; const switchers = { "theme": ["auto", "light", "dark"], "vCodec": ["h264", "av1", "vp9"], "vQuality": ["720", "max", "2160", "1440", "1080", "480", "360", "240", "144"], "aFormat": ["mp3", "best", "ogg", "wav", "opus"], "audioMode": ["false", "true"], "filenamePattern": ["classic", "pretty", "basic", "nerdy"] } const checkboxes = [ "alwaysVisibleButton", "downloadPopup", "fullTikTokAudio", "muteAudio", "reduceTransparency", "disableAnimations", "disableMetadata", "twitterGif", "plausible_ignore", "ytDub", "tiktokH265" ] const bottomPopups = ["error", "download"] let store = {}; const validLink = (link) => { try { return /^https:/i.test(new URL(link).protocol); } catch { return false } } const fixApiUrl = (url) => { return url.endsWith('/') ? url.slice(0, -1) : url } let apiURL = fixApiUrl(defaultApiUrl); const changeApi = (url) => { apiURL = fixApiUrl(url); return true } const eid = (id) => { return document.getElementById(id) } const sGet = (id) =>{ return localStorage.getItem(id) } const sSet = (id, value) => { localStorage.setItem(id, value) } const enable = (id) => { eid(id).dataset.enabled = "true"; } const disable = (id) => { eid(id).dataset.enabled = "false"; } const opposite = (state) => { return state === "true" ? "false" : "true"; } const lazyGet = (key) => { const value = sGet(key); if (key in switchers) { if (switchers[key][0] !== value) return value; } else if (checkboxes.includes(key)) { if (value === 'true') return true; } } const changeDownloadButton = (action, text) => { switch (action) { case "hidden": // hidden, but only visible when alwaysVisibleButton is true eid("download-button").disabled = true if (sGet("alwaysVisibleButton") === "true") { eid("download-button").value = '>>' eid("download-button").style.padding = '0 1rem' } else { eid("download-button").value = '' eid("download-button").style.padding = '0' } break; case "disabled": eid("download-button").disabled = true eid("download-button").value = text eid("download-button").style.padding = '0 1rem' break; default: eid("download-button").disabled = false eid("download-button").value = '>>' eid("download-button").style.padding = '0 1rem' break; } } const button = () => { let regexTest = validLink(eid("url-input-area").value); eid("url-clear").style.display = "none"; if ((eid("url-input-area").value).length > 0) { eid("url-clear").style.display = "block"; } if (regexTest) { changeDownloadButton() } else { changeDownloadButton("hidden") } } const clearInput = () => { eid("url-input-area").value = ''; button(); } const copy = (id, data) => { let target = document.getElementById(id); target.classList.add("text-backdrop"); setTimeout(() => { target.classList.remove("text-backdrop") }, 600); if (data) { navigator.clipboard.writeText(data) } else { navigator.clipboard.writeText(e.innerText) } } const share = url => navigator?.share({ url }).catch(() => {}); const detectColorScheme = () => { let theme = "auto"; let localTheme = sGet("theme"); if (localTheme) { theme = localTheme; } else if (!window.matchMedia) { theme = "dark" } document.documentElement.setAttribute("data-theme", theme); } const updateFilenamePreview = () => { let videoFilePreview = ``; let audioFilePreview = ``; let resMatch = { "max": "3840x2160", "2160": "3840x2160", "1440": "2560x1440", "1080": "1920x1080", "720": "1280x720", "480": "854x480", "360": "640x360", } switch(sGet("filenamePattern")) { case "classic": videoFilePreview = `youtube_dQw4w9WgXcQ_${resMatch[sGet('vQuality')]}_${sGet('vCodec')}` + `${sGet("muteAudio") === "true" ? "_mute" : ""}` + `.${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; audioFilePreview = `youtube_dQw4w9WgXcQ_audio` + `.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; break; case "basic": videoFilePreview = `${loc.FilenamePreviewVideoTitle} ` + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ` + `${sGet('vCodec')}${sGet("muteAudio") === "true" ? ", mute" : ""})` + `.${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor}` + `.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; break; case "pretty": videoFilePreview = `${loc.FilenamePreviewVideoTitle} ` + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}, ` + `${sGet("muteAudio") === "true" ? "mute, " : ""}youtube)` + `.${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor} (soundcloud)` + `.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; break; case "nerdy": videoFilePreview = `${loc.FilenamePreviewVideoTitle} ` + `(${sGet('vQuality') === "max" ? "2160p" : `${sGet('vQuality')}p`}, ${sGet('vCodec')}, ` + `${sGet("muteAudio") === "true" ? "mute, " : ""}youtube, dQw4w9WgXcQ)` + `.${sGet('vCodec') === "vp9" ? 'webm' : 'mp4'}`; audioFilePreview = `${loc.FilenamePreviewAudioTitle} - ${loc.FilenamePreviewAudioAuthor} ` + `(soundcloud, 1242868615)` + `.${sGet('aFormat') !== "best" ? sGet('aFormat') : 'opus'}`; break; } eid("video-filename-text").innerHTML = videoFilePreview eid("audio-filename-text").innerHTML = audioFilePreview } const changeTab = (evnt, tabId, tabClass) => { if (tabId === "tab-settings-other") updateFilenamePreview(); let tabcontent = document.getElementsByClassName(`tab-content-${tabClass}`); let tablinks = document.getElementsByClassName(`tab-${tabClass}`); for (let i = 0; i < tabcontent.length; i++) { tabcontent[i].dataset.enabled = "false"; } for (let i = 0; i < tablinks.length; i++) { tablinks[i].dataset.enabled = "false"; } evnt.currentTarget.dataset.enabled = "true"; eid(tabId).dataset.enabled = "true"; eid(tabId).parentElement.scrollTop = 0; } const expandCollapsible = (evnt) => { let classlist = evnt.currentTarget.parentNode.classList; let c = "expanded"; !classlist.contains(c) ? classlist.add(c) : classlist.remove(c); } const hideAllPopups = () => { let filter = document.getElementsByClassName('popup'); for (let i = 0; i < filter.length; i++) { filter[i].classList.remove("visible"); } eid("popup-backdrop").classList.remove("visible"); store.isPopupOpen = false; // clear the picker eid("picker-holder").innerHTML = ''; eid("picker-download").href = '/'; eid("picker-download").classList.remove("visible"); } const popup = (type, action, text) => { if (action === 1) { hideAllPopups(); // hide the previous popup before showing a new one store.isPopupOpen = true; switch (type) { case "about": let tabId = "about"; if (text) tabId = text; eid(`tab-button-${type}-${tabId}`).click(); break; case "settings": eid(`tab-button-${type}-video`).click(); break; case "error": eid("desc-error").innerHTML = text; break; case "download": eid("pd-download").href = text; eid("pd-copy").setAttribute("onClick", `copy('pd-copy', '${text}')`); eid("pd-share").setAttribute("onClick", `share('${text}')`); if (navigator.canShare) eid("pd-share").style.display = "flex"; break; case "picker": eid("picker-title").innerHTML = loc.MediaPickerTitle; eid("picker-subtitle").innerHTML = isMobile ? loc.MediaPickerExplanationPhone : loc.MediaPickerExplanationPC; switch (text.type) { case "images": eid("picker-holder").classList.remove("various"); eid("picker-download").href = text.audio; eid("picker-download").classList.add("visible"); for (let i in text.arr) { eid("picker-holder").innerHTML += `` + `` + `` } break; default: eid("picker-holder").classList.add("various"); for (let i in text.arr) { eid("picker-holder").innerHTML += `` + `
${text.arr[i].type}
` + (text.arr[i].type === 'photo' ? '' : '
') + `` + `
` } eid("picker-download").classList.remove("visible"); break; } break; default: break; } } else { store.isPopupOpen = false; if (type === "picker") { eid("picker-download").href = '/'; eid("picker-download").classList.remove("visible"); eid("picker-holder").innerHTML = '' } } if (bottomPopups.includes(type)) eid(`popup-${type}-container`).classList.toggle("visible"); eid("popup-backdrop").classList.toggle("visible"); eid(`popup-${type}`).classList.toggle("visible"); eid(`popup-${type}`).focus(); } const changeSwitcher = (li, b) => { if (b) { if (!switchers[li].includes(b)) b = switchers[li][0]; sSet(li, b); for (let i in switchers[li]) { (switchers[li][i] === b) ? enable(`${li}-${b}`) : disable(`${li}-${switchers[li][i]}`) } if (li === "theme") detectColorScheme(); if (li === "filenamePattern") updateFilenamePreview(); } else { let pref = switchers[li][0]; sSet(li, pref); for (let i in switchers[li]) { (switchers[li][i] === pref) ? enable(`${li}-${pref}`) : disable(`${li}-${switchers[li][i]}`) } } } const checkbox = (action) => { sSet(action, !!eid(action).checked); switch(action) { case "alwaysVisibleButton": button(); break; case "reduceTransparency": eid("cobalt-body").classList.toggle('no-transparency'); break; case "disableAnimations": eid("cobalt-body").classList.toggle('no-animation'); break; } } const changeButton = (type, text) => { switch (type) { case "error": //error eid("url-input-area").disabled = false eid("url-clear").style.display = "block"; changeDownloadButton("disabled", '!!'); popup("error", 1, text); setTimeout(() => { changeButton("default") }, 2500); break; case "default": //enable back changeDownloadButton(); eid("url-clear").style.display = "block"; eid("url-input-area").disabled = false break; case "error-default": //enable back + information popup popup("error", 1, text); changeDownloadButton(); eid("url-clear").style.display = "block"; eid("url-input-area").disabled = false break; } } const internetError = () => { eid("url-input-area").disabled = false changeDownloadButton("disabled", '!!'); setTimeout(() => { changeButton("default") }, 2500); popup("error", 1, loc.ErrorNoInternet); } const resetSettings = () => { localStorage.clear(); window.location.reload(); } const download = async(url) => { changeDownloadButton("disabled", '...'); eid("url-clear").style.display = "none"; eid("url-input-area").disabled = true; let req = { url, vCodec: lazyGet("vCodec"), vQuality: lazyGet("vQuality"), aFormat: lazyGet("aFormat"), filenamePattern: lazyGet("filenamePattern"), isAudioOnly: lazyGet("audioMode"), isTTFullAudio: lazyGet("fullTikTokAudio"), isAudioMuted: lazyGet("muteAudio"), disableMetadata: lazyGet("disableMetadata"), dubLang: lazyGet("ytDub"), twitterGif: lazyGet("twitterGif"), tiktokH265: lazyGet("tiktokH265"), } let j = await fetch(`${apiURL}/api/json`, { method: "POST", body: JSON.stringify(req), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }).then(r => r.json()).catch(() => {}); if (!j) { internetError(); return; } if ((j.status === "error" || j.status === "rate-limit") && j && j.text) { changeButton("error", j.text); return; } if (j.text && (!j.url || !j.picker)) { if (j.status === "success") { changeButton("error-default", j.text) } else { changeButton("error", loc.ErrorNoUrlReturned); } } switch (j.status) { case "redirect": changeDownloadButton("disabled", '>>>'); setTimeout(() => { changeButton("default") }, 1500); if (sGet("downloadPopup") === "true") { popup('download', 1, j.url) } else { window.open(j.url, '_blank') } break; case "stream": changeDownloadButton("disabled", '?..'); let probeStream = await fetch(`${j.url}&p=1`).then(r => r.json()).catch(() => {}); if (!probeStream) return internetError(); if (probeStream.status !== "continue") { changeButton("error", probeStream.text); return; } changeDownloadButton("disabled", '>>>'); if (sGet("downloadPopup") === "true") { popup('download', 1, j.url) } else { if (isMobile || isSafari) { window.location.href = j.url; } else { window.open(j.url, '_blank'); } } setTimeout(() => { changeButton("default") }, 2500); break; case "picker": if (j.audio && j.picker) { changeDownloadButton("disabled", '>>>'); popup('picker', 1, { audio: j.audio, arr: j.picker, type: j.pickerType }); setTimeout(() => { changeButton("default") }, 2500); } else if (j.picker) { changeDownloadButton("disabled", '>>>'); popup('picker', 1, { arr: j.picker, type: j.pickerType }); setTimeout(() => { changeButton("default") }, 2500); } else { changeButton("error", loc.ErrorNoUrlReturned); } break; case "success": changeButton("error-default", j.text); break; default: changeButton("error", loc.ErrorUnknownStatus); break; } } const pasteClipboard = async() => { try { let clipboard = await navigator.clipboard.readText(); let onlyURL = clipboard.match(/https:\/\/[^\s]+/g) if (onlyURL) { eid("url-input-area").value = onlyURL; download(eid("url-input-area").value); } } catch (e) { let errorMessage = loc.FeatureErrorGeneric; let doError = true; let error = String(e).toLowerCase(); if (error.includes("denied")) errorMessage = loc.ClipboardErrorNoPermission; if (error.includes("dismissed") || isIOS) doError = false; if (error.includes("function") && isFirefox) errorMessage = loc.ClipboardErrorFirefox; if (doError) popup("error", 1, errorMessage); } } const loadCelebrationsEmoji = async() => { let aboutButtonBackup = eid("about-footer").innerHTML; try { let j = await fetch(`/onDemand?blockId=1`).then(r => r.json()).catch(() => {}); if (j && j.status === "success" && j.text) { eid("about-footer").innerHTML = eid("about-footer").innerHTML.replace( ``, j.text ) } } catch { eid("about-footer").innerHTML = aboutButtonBackup; } } const loadOnDemand = async(elementId, blockId) => { store.historyButton = eid(elementId).innerHTML; eid(elementId).innerHTML = `
...
`; try { if (!store.historyContent) { let j = await fetch(`/onDemand?blockId=${blockId}`).then(r => r.json()).catch(() => {}); if (!j) throw new Error(); if (j.status === "success") { store.historyContent = j.text } } eid(elementId).innerHTML = ` ${store.historyContent}`; } catch { eid(elementId).innerHTML = store.historyButton; internetError() } } const restoreUpdateHistory = () => { eid("changelog-history").innerHTML = store.historyButton; } const loadSettings = () => { if (sGet("alwaysVisibleButton") === "true") { eid("alwaysVisibleButton").checked = true; eid("download-button").value = '>>' eid("download-button").style.padding = '0 1rem'; } if (sGet("downloadPopup") === "true" && !isIOS) { eid("downloadPopup").checked = true; } if (sGet("reduceTransparency") === "true" || isOldFirefox) { eid("cobalt-body").classList.add('no-transparency'); } if (sGet("disableAnimations") === "true") { eid("cobalt-body").classList.add('no-animation'); } for (let i = 0; i < checkboxes.length; i++) { try { if (sGet(checkboxes[i]) === "true") eid(checkboxes[i]).checked = true; } catch { console.error(`checkbox ${checkboxes[i]} failed to initialize`) } } for (let i in switchers) { changeSwitcher(i, sGet(i)) } updateFilenamePreview() } window.onload = () => { loadCelebrationsEmoji(); loadSettings(); detectColorScheme(); changeDownloadButton("hidden"); eid("url-input-area").value = ""; if (isIOS) { sSet("downloadPopup", "true"); eid("downloadPopup-chkbx").style.display = "none"; } eid("home").style.visibility = 'visible'; eid("home").classList.toggle("visible"); const pageQuery = new URLSearchParams(window.location.search); if (pageQuery.has("u") && validLink(pageQuery.get("u"))) { eid("url-input-area").value = pageQuery.get("u"); button() } window.history.replaceState(null, '', window.location.pathname); // fix for animations not working in Safari if (isIOS) { document.addEventListener('touchstart', () => {}, true); } } eid("url-input-area").addEventListener("keydown", (e) => { button(); }) eid("url-input-area").addEventListener("keyup", (e) => { if (e.key === 'Enter') eid("download-button").click(); }) document.addEventListener("keydown", (event) => { if (event.key === "Tab") { eid("download-button").value = '>>' eid("download-button").style.padding = '0 1rem' } }) document.onkeydown = (e) => { if (!store.isPopupOpen) { if (e.metaKey || e.ctrlKey || e.key === "/") eid("url-input-area").focus(); if (e.key === "Escape" || e.key === "Clear") clearInput(); if (e.target === eid("url-input-area")) return; // top buttons if (e.key === "D") pasteClipboard(); if (e.key === "K") changeSwitcher('audioMode', 'false'); if (e.key === "L") changeSwitcher('audioMode', 'true'); // popups if (e.key === "B") popup('about', 1, 'about'); // open about if (e.key === "N") popup('about', 1, 'changelog'); // open changelog if (e.key === "M") popup('settings', 1); } else { if (e.key === "Escape") hideAllPopups(); } }