5.6: tiny quality of life improvements

- remember celebratory emoji changes? they've been fixed, and are now dynamically loaded!
- changelog history now lets you try to load it again if first attempt failed for whatever reason.
- added glow to the donation button to make it more visible.
- cleaned up frontend js a little bit.
- updated some links in tests.
This commit is contained in:
wukko 2023-05-17 02:13:11 +06:00
parent ece4899415
commit d85205649e
9 changed files with 164 additions and 96 deletions

View file

@ -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"
}
}

View file

@ -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:

View file

@ -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;
}

View file

@ -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 = `<div class="notification-dot"></div>`
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 = `<div class="notification-dot"></div>`;
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('<img class="emoji" draggable="false" height="22" width="22" alt="🐲" src="emoji/dragon_face.svg">', j.text);
}
} catch (e) {
eid("about-footer").innerHTML = bac;
}
}
async function loadOnDemand(elementId, blockId) {
store.historyButton = eid(elementId).innerHTML;
@ -401,17 +421,15 @@ async function loadOnDemand(elementId, blockId) {
if (store.historyContent) {
j = store.historyContent;
} else {
await fetch(`/api/onDemand?blockId=${blockId}`).then(async (r) => {
j = await r.json();
if (j.status === "success") store.historyContent = j;
})
}
if (j.status === "success" && j.status !== "rate-limit") {
if (j.text) {
eid(elementId).innerHTML = `<button class="switch bottom-margin" onclick="restoreUpdateHistory()">${loc.collapseHistory}</button>${j.text}`;
j = await fetch(`${apiURL}/api/onDemand?blockId=${blockId}`).then((r) => { if (r.status === 200) { return r.json() } else { return false } }).catch(() => { return false });
if (j && j.status === "success") {
store.historyContent = j;
} else {
throw new Error()
throw new Error();
}
}
if (j.text) {
eid(elementId).innerHTML = `<button class="switch bottom-margin" onclick="restoreUpdateHistory()">${loc.collapseHistory}</button>${j.text}`;
} else {
throw new Error()
}
@ -431,6 +449,7 @@ window.onload = () => {
eid("footer").style.visibility = 'visible';
eid("url-input-area").value = "";
notificationCheck();
loadCelebrationsEmoji();
if (isIOS) sSet("downloadPopup", "true");
let urlQuery = new URLSearchParams(window.location.search).get("u");
if (urlQuery !== null && regex.test(urlQuery)) {

View file

@ -94,7 +94,7 @@
"ChangelogPressToHide": "collapse",
"Donate": "donate",
"DonateSub": "help me keep it up",
"DonateExplanation": "{appName} does not (and will never) serve ads or sell your data, therefore it's <span class=\"text-backdrop\">completely free to use</span>. but turns out developing and keeping up a web service used by over 80 thousand people is not that easy.\n\nif you ever found {appName} useful and want to keep it online, or simply want to thank the developer, consider chipping in! every cent helps and is VERY appreciated :D",
"DonateExplanation": "{appName} does not (and will never) serve ads or sell your data, therefore it's <span class=\"text-backdrop\">completely free to use</span>. but turns out developing and keeping up a web service used by over 150,000 people is not that easy.\n\nif you ever found {appName} useful and want to help continue its development and support, or simply want to thank the developer, consider chipping in! every cent helps and is VERY appreciated :D\n\ncurrently, i have big (scaling) plans, and i need your help. {appName}'s usage is growing daily, so i need to make up for it. <span class=\"text-backdrop\">donations are more appreciated than ever.</span>\n\ni am yet to earn anything from {appName}, everything goes back to users, so you're essentially helping everyone.",
"DonateVia": "donate via",
"DonateHireMe": "...or you can <a class=\"text-backdrop italic\" href=\"{s}\" target=\"_blank\">hire me</a> :)",
"SettingsVideoMute": "mute audio",

View file

@ -2,7 +2,7 @@ import * as fs from "fs";
import { appName, repo } from "../modules/config.js";
import loadJson from "../modules/sub/loadJSON.js";
const locPath = './src/localization/languages'
const locPath = './src/localization/languages';
let loc = {}
let languages = [];

View file

@ -1,4 +1,5 @@
import { celebrations } from "../config.js";
import emoji from "../emoji.js";
export function switcher(obj) {
let items = ``;
@ -151,12 +152,20 @@ export function footerButtons(obj) {
items += `<button id="${obj[i]["name"]}-footer" class="switch footer-button" onclick="${obj[i]["action"]}()" aria-label="${obj[i]["aria"]}">${obj[i]["text"]}</button>`;
break;
case "popup":
let context = obj[i]["context"] ? `, '${obj[i]["context"]}'` : ''
let context2 = obj[i+1] && obj[i+1]["context"] ? `, '${obj[i+1]["context"]}'` : ''
let buttonName = obj[i]["context"] ? `${obj[i]["name"]}-${obj[i]["context"]}` : obj[i]["name"],
context = obj[i]["context"] ? `, '${obj[i]["context"]}'` : '',
buttonName2,
context2;
if (obj[i+1]) {
buttonName2 = obj[i+1]["context"] ? `${obj[i+1]["name"]}-${obj[i+1]["context"]}` : obj[i+1]["name"];
context2 = obj[i+1]["context"] ? `, '${obj[i+1]["context"]}'` : '';
}
items += `
<div class="footer-pair">
<button id="${obj[i]["name"]}-footer" class="switch footer-button" onclick="popup('${obj[i]["name"]}', 1${context})" aria-label="${obj[i]["aria"]}">${obj[i]["text"]}</button>
${obj[i+1] ? `<button id="${obj[i+1]["name"]}-footer" class="switch footer-button" onclick="popup('${obj[i+1]["name"]}', 1${context2})" aria-label="${obj[i+1]["aria"]}">${obj[i+1]["text"]}</button>`: ''}
<button id="${buttonName}-footer" class="switch footer-button" onclick="popup('${obj[i]["name"]}', 1${context})" aria-label="${obj[i]["aria"]}">${obj[i]["text"]}</button>
${obj[i+1] ? `<button id="${buttonName2}-footer" class="switch footer-button" onclick="popup('${obj[i+1]["name"]}', 1${context2})" aria-label="${obj[i+1]["aria"]}">${obj[i+1]["text"]}</button>`: ''}
</div>`;
i++;
break;
@ -169,7 +178,12 @@ export function explanation(text) {
return `<div class="explanation">${text}</div>`
}
export function celebrationsEmoji() {
let n = new Date().toISOString().split('T')[0].split('-');
let dm = `${n[1]}-${n[2]}`;
return Object.keys(celebrations).includes(dm) ? celebrations[dm] : "🐲";
try {
let n = new Date().toISOString().split('T')[0].split('-');
let dm = `${n[1]}-${n[2]}`;
let f = Object.keys(celebrations).includes(dm) ? celebrations[dm] : "🐲";
return f != "🐲" ? emoji(f, 22) : false;
} catch (e) {
return false
}
}

View file

@ -1,4 +1,4 @@
import { backdropLink, celebrationsEmoji, checkbox, collapsibleList, explanation, footerButtons, multiPagePopup, popup, popupWithBottomButtons, sep, settingsCategory, switcher, socialLink } from "./elements.js";
import { backdropLink, checkbox, collapsibleList, explanation, footerButtons, multiPagePopup, popup, popupWithBottomButtons, sep, settingsCategory, switcher, socialLink } from "./elements.js";
import { services as s, appName, authorInfo, version, repo, donations, supportedAudio } from "../config.js";
import { getCommitInfo } from "../sub/currentCommit.js";
import loc from "../../localization/manager.js";
@ -392,7 +392,7 @@ export default function(obj) {
footerButtons([{
name: "about",
type: "popup",
text: `${emoji(celebrationsEmoji() , 22)} ${t('AboutTab')}`,
text: `${emoji("🐲" , 22)} ${t('AboutTab')}`,
aria: t('AccessibilityOpenAbout')
}, {
name: "about",

View file

@ -36,8 +36,8 @@
"status": "redirect"
}
}, {
"name": "picker: mixed media (3 gifs + image)",
"url": "https://twitter.com/emerald_pedrod/status/1582418163521581063?s=20",
"name": "picker: mixed media (2 videos)",
"url": "https://twitter.com/taehyungsflow/status/1583411488433516544",
"params": {
"aFormat": "mp3",
"isAudioOnly": false,
@ -633,7 +633,7 @@
}
}, {
"name": "images",
"url": "https://vt.tiktok.com/ZS8JP89eB/",
"url": "https://www.tiktok.com/@matryoshk4/video/7231234675476532526",
"params": {},
"expected": {
"code": 200,
@ -820,6 +820,14 @@
"code": 200,
"status": "redirect"
}
}, {
"name": "regular video",
"url": "https://www.instagram.com/p/CmCVWoIr9OH/",
"params": {},
"expected": {
"code": 200,
"status": "redirect"
}
}, {
"name": "reel (isAudioOnly)",
"url": "https://www.instagram.com/reel/CoEBV3eM4QR/",