diff --git a/package.json b/package.json index e94f1b11..f0a2e512 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cobalt", "description": "save what you love", - "version": "5.5.1", + "version": "5.6", "author": "wukko", "exports": "./src/cobalt.js", "type": "module", @@ -34,6 +34,6 @@ "node-cache": "^5.1.2", "url-pattern": "1.0.3", "xml-js": "^1.6.11", - "youtubei.js": "^5.0.0" + "youtubei.js": "^5.1.0" } } diff --git a/src/cobalt.js b/src/cobalt.js index 118ddfb1..990f5d31 100644 --- a/src/cobalt.js +++ b/src/cobalt.js @@ -23,6 +23,7 @@ import { buildFront } from "./modules/build.js"; import { changelogHistory } from "./modules/pageRender/onDemand.js"; import { sha256 } from "./modules/sub/crypto.js"; import findRendered from "./modules/pageRender/findRendered.js"; +import { celebrationsEmoji } from "./modules/pageRender/elements.js"; if (process.env.selfURL && process.env.port) { const commitHash = shortCommit(); @@ -138,21 +139,29 @@ if (process.env.selfURL && process.env.port) { break; case 'onDemand': if (req.query.blockId) { - let blockId = req.query.blockId.slice(0, 3) + let blockId = req.query.blockId.slice(0, 3); let r, j; switch(blockId) { - case "0": + case "0": // changelog history r = changelogHistory(); j = r ? apiJSON(3, { t: r }) : apiJSON(0, { t: "couldn't render this block" }) break; + case "1": // celebrations emoji + r = celebrationsEmoji(); + j = r ? apiJSON(3, { t: r }) : false + break; default: j = apiJSON(0, { t: "couldn't find a block with this id" }) break; } - res.status(j.status).json(j.body); + if (j.body) { + res.status(j.status).json(j.body) + } else { + res.status(204).end() + } } else { - let j = apiJSON(0, { t: "no block id" }) - res.status(j.status).json(j.body); + let j = apiJSON(0, { t: "no block id" }); + res.status(j.status).json(j.body) } break; default: diff --git a/src/front/cobalt.css b/src/front/cobalt.css index a0fb336b..f7034a66 100644 --- a/src/front/cobalt.css +++ b/src/front/cobalt.css @@ -8,6 +8,7 @@ --line-height: 1.65rem; --red: rgb(255, 0, 61); --gap: 0.6rem; + --rainbow-gradient: linear-gradient(161deg,#ffe454,#ff6964,#fe85e5,#bd26fe,#587ae9,#8ded95); } @media (prefers-color-scheme: dark) { :root { @@ -655,6 +656,19 @@ button:active, display: block; text-align: right; } +#about-donate-footer::before { + content: ""; + position: absolute; + height: 110%; + width: 32%; + background: var(--rainbow-gradient); + z-index: -2; + filter: blur(5px); + opacity: 0.65; +} +#about-donate-footer:active::before { + opacity: 0; +} /* adapt the page according to screen size */ @media screen and (min-width: 2300px) { html { @@ -797,6 +811,10 @@ button:active, flex-direction: column; align-items: stretch; } + #about-donate-footer::before { + height: 50%; + width: 50%; + } .footer-pair .footer-button { width: 100%!important; } diff --git a/src/front/cobalt.js b/src/front/cobalt.js index 8b8d9732..adc84a31 100644 --- a/src/front/cobalt.js +++ b/src/front/cobalt.js @@ -1,13 +1,11 @@ -let ua = navigator.userAgent.toLowerCase(); -let isIOS = ua.match("iphone os"); -let isMobile = ua.match("android") || ua.match("iphone os"); -let version = 26; -let regex = new RegExp(/https:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/); -let notification = `
` +const ua = navigator.userAgent.toLowerCase(); +const isIOS = ua.match("iphone os"); +const isMobile = ua.match("android") || ua.match("iphone os"); +const version = 26; +const regex = new RegExp(/https:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/); +const notification = ``; -let store = {} - -let switchers = { +const switchers = { "theme": ["auto", "light", "dark"], "vCodec": ["h264", "av1", "vp9"], "vQuality": ["1080", "max", "2160", "1440", "720", "480", "360"], @@ -15,11 +13,15 @@ let switchers = { "dubLang": ["original", "auto"], "vimeoDash": ["false", "true"], "audioMode": ["false", "true"] -} -let checkboxes = ["disableTikTokWatermark", "fullTikTokAudio", "muteAudio"]; -let exceptions = { // used for mobile devices +}; +const checkboxes = ["disableTikTokWatermark", "fullTikTokAudio", "muteAudio"]; +const exceptions = { // used for mobile devices "vQuality": "720" -} +}; + +const apiURL = ''; + +let store = {}; function eid(id) { return document.getElementById(id) @@ -333,65 +335,83 @@ async function download(url) { if (url.includes("youtube.com/") || url.includes("/youtu.be/")) req.vCodec = sGet("vCodec").slice(0, 4); if ((url.includes("tiktok.com/") || url.includes("douyin.com/")) && sGet("disableTikTokWatermark") === "true") req.isNoTTWatermark = true; } - await fetch('/api/json', { method: "POST", body: JSON.stringify(req), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }).then(async (r) => { - let j = await r.json(); - if (j.status !== "error" && j.status !== "rate-limit") { - if (j.url || j.picker) { - switch (j.status) { - case "redirect": - changeDownloadButton(2, '>>>'); - setTimeout(() => { changeButton(1); }, 1500); - sGet("downloadPopup") === "true" ? popup('download', 1, j.url) : window.open(j.url, '_blank'); - break; - case "picker": - if (j.audio && j.picker) { - changeDownloadButton(2, '?..') - fetch(`${j.audio}&p=1`).then(async (res) => { - let jp = await res.json(); - if (jp.status === "continue") { - changeDownloadButton(2, '>>>'); - popup('picker', 1, { audio: j.audio, arr: j.picker, type: j.pickerType }); - setTimeout(() => { changeButton(1) }, 2500); - } else { - changeButton(0, jp.text); - } - }).catch((error) => internetError()); - } else if (j.picker) { + + let j = await fetch(`${apiURL}/api/json`, { + method: "POST", + body: JSON.stringify(req), + headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } + }).then((r) => { return r.json() }).catch((e) => { return false }); + if (!j) { + internetError(); + return + } + + if (j && j.status !== "error" && j.status !== "rate-limit") { + if (j.text && (!j.url || !j.picker)) { + if (j.status === "success") { + changeButton(2, j.text) + } else changeButton(0, loc.noURLReturned); + } + switch (j.status) { + case "redirect": + changeDownloadButton(2, '>>>'); + setTimeout(() => { changeButton(1); }, 1500); + sGet("downloadPopup") === "true" ? popup('download', 1, j.url) : window.open(j.url, '_blank'); + break; + case "picker": + if (j.audio && j.picker) { + changeDownloadButton(2, '?..') + fetch(`${j.audio}&p=1`).then(async (res) => { + let jp = await res.json(); + if (jp.status === "continue") { changeDownloadButton(2, '>>>'); - popup('picker', 1, { arr: j.picker, type: j.pickerType }); + popup('picker', 1, { audio: j.audio, arr: j.picker, type: j.pickerType }); setTimeout(() => { changeButton(1) }, 2500); } else { - changeButton(0, loc.noURLReturned); + changeButton(0, jp.text); } - break; - case "stream": - changeDownloadButton(2, '?..') - fetch(`${j.url}&p=1`).then(async (res) => { - let jp = await res.json(); - if (jp.status === "continue") { - changeDownloadButton(2, '>>>'); window.location.href = j.url; - setTimeout(() => { changeButton(1) }, 2500); - } else { - changeButton(0, jp.text); - } - }).catch((error) => internetError()); - break; - case "success": - changeButton(2, j.text); - break; - default: - changeButton(0, loc.unknownStatus); - break; + }).catch((error) => internetError()); + } else if (j.picker) { + changeDownloadButton(2, '>>>'); + popup('picker', 1, { arr: j.picker, type: j.pickerType }); + setTimeout(() => { changeButton(1) }, 2500); + } else { + changeButton(0, loc.noURLReturned); } - } else { - if (j.status === "success") { - changeButton(2, j.text) - } else changeButton(0, loc.noURLReturned); - } - } else { - changeButton(0, j.text); + break; + case "stream": + changeDownloadButton(2, '?..') + fetch(`${j.url}&p=1`).then(async (res) => { + let jp = await res.json(); + if (jp.status === "continue") { + changeDownloadButton(2, '>>>'); window.location.href = j.url; + setTimeout(() => { changeButton(1) }, 2500); + } else { + changeButton(0, jp.text); + } + }).catch((error) => internetError()); + break; + case "success": + changeButton(2, j.text); + break; + default: + changeButton(0, loc.unknownStatus); + break; } - }).catch((error) => internetError()); + } else if (j && j.text) { + changeButton(0, j.text); + } +} +async function loadCelebrationsEmoji() { + let bac = eid("about-footer").innerHTML; + try { + let j = await fetch(`${apiURL}/api/onDemand?blockId=1`).then((r) => { if (r.status === 200) { return r.json() } else { return false } }).catch(() => { return false }); + if (j && j.status === "success" && j.text) { + eid("about-footer").innerHTML = eid("about-footer").innerHTML.replace('