From 6653fdc8df030d837a2f04911ba660cb5eb44052 Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Tue, 10 Sep 2024 19:17:51 +0000 Subject: [PATCH 01/79] changelogs/6.0: fix broken link --- web/changelogs/6.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/web/changelogs/6.0.md b/web/changelogs/6.0.md index dfaeddb6..3d9d1c19 100644 --- a/web/changelogs/6.0.md +++ b/web/changelogs/6.0.md @@ -10,6 +10,7 @@ hey! long time no see, hopefully over 40 changes will make up for it :) cobalt now has an official community discord server. you can go there for news, support, or just to chat. [go check it out!](https://discord.gg/pQPt8HBUPu) tl;dr + - new infra, new hosting structure, new main instance api url. developers, [get it here](https://github.com/imputnet/cobalt/blob/main/docs/api.md). - added support for pinterest, vine archive, tumblr audio, youtube vr videos. - better web app performance and look. From 6e3589c8ce3056acb05010a6b96edb3b14f2e2d2 Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Tue, 10 Sep 2024 19:19:16 +0000 Subject: [PATCH 02/79] web/changelogs: fix broken bullet lists --- web/changelogs/5.1.md | 1 + web/changelogs/5.2.md | 1 + web/changelogs/5.4.md | 1 + web/changelogs/7.0.md | 1 + 4 files changed, 4 insertions(+) diff --git a/web/changelogs/5.1.md b/web/changelogs/5.1.md index 1b37d145..27b0c37b 100644 --- a/web/changelogs/5.1.md +++ b/web/changelogs/5.1.md @@ -9,6 +9,7 @@ hey, ever wanted to download a youtube video without a hassle? cobalt is here to not only that, but it also introduces features never before seen in a downloader, such as youtube dub downloads! read below to see what's up :) tl;dr: + - audio in youtube videos FINALLY no longer gets cut off. - you now can pick any video resolution you want (from 360p to 8k) and any possible youtube video codec (h264/av1/vp9). - you now can download youtube videos with dubs in your native language. just check settings > audio. diff --git a/web/changelogs/5.2.md b/web/changelogs/5.2.md index d1114825..36fd9ea1 100644 --- a/web/changelogs/5.2.md +++ b/web/changelogs/5.2.md @@ -8,6 +8,7 @@ banner: hey, notice anything different? well, at very least the page loaded way faster! this update includes many improvements and fixes, but also some new features. tl;dr: + - twitter retweet links are now supported. - all vimeo videos should now be possible to download. - you now can download audio from vimeo. diff --git a/web/changelogs/5.4.md b/web/changelogs/5.4.md index 6dfa58b2..d5e08241 100644 --- a/web/changelogs/5.4.md +++ b/web/changelogs/5.4.md @@ -8,6 +8,7 @@ banner: something many of you've been waiting for is finally here! try it out and let me know what you think :) tl;dr: + - added experimental instagram support! download any reels or videos you like, and make sure to report any issues you encounter. yes, you can convert either to audio. - fixed support for on.soundcloud links. - added share button to "how to save?" popup. diff --git a/web/changelogs/7.0.md b/web/changelogs/7.0.md index 594d4316..6b65b931 100644 --- a/web/changelogs/7.0.md +++ b/web/changelogs/7.0.md @@ -8,6 +8,7 @@ banner: hey! this update is huge and mostly aimed to refresh the ui, but there are also some other nice fixes/additions. read below for more info :) tl;dr: + - entirety of web app has been refreshed. it's more prettier and optimized than ever, both on phone and desktop. - if you're on ios, try adding cobalt to home screen! it'll look and act like a native app. - all soundcloud links are now supported and audio quality is higher than before. From d7454a073b86766654fd97355be747cf34fb0e3c Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 11 Sep 2024 14:42:54 +0600 Subject: [PATCH 03/79] web/download: assume userActivation expired if agent doesn't support it --- web/src/lib/download.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/web/src/lib/download.ts b/web/src/lib/download.ts index b0c80d31..78e465de 100644 --- a/web/src/lib/download.ts +++ b/web/src/lib/download.ts @@ -77,8 +77,10 @@ export const downloadFile = ({ url, file }: { url?: string, file?: File }) => { navigator.userActivation.isActive makes sure that we're still able to invoke an action without the user agent interrupting it. if not, we show a saving dialog for user to re-invoke that action. + + if browser is old or doesn't support this API, we just assume that it expired. */ - if (!navigator.userActivation.isActive) { + if (!navigator?.userActivation?.isActive) { return openSavingDialog({ url, file, From 2122e87e66d32e9aa8047dc736803ea4d0ec37a1 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 11 Sep 2024 16:41:04 +0600 Subject: [PATCH 04/79] web/NotchSticker: fix sticking out on XR and 11 --- web/src/components/misc/NotchSticker.svelte | 19 ++++++++++++++++++- web/src/routes/+layout.svelte | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/web/src/components/misc/NotchSticker.svelte b/web/src/components/misc/NotchSticker.svelte index 22a98ae5..386bb04f 100644 --- a/web/src/components/misc/NotchSticker.svelte +++ b/web/src/components/misc/NotchSticker.svelte @@ -7,6 +7,7 @@ // i spent 4 hours switching between simulators and devices to get the best way to do this $: safeAreaTop = 0; + $: safeAreaBottom = 0; $: state = "hidden"; // "notch", "island", "notch x" const islandValues = [ @@ -24,8 +25,16 @@ .trim(); }; + const getSafeAreaBottom = () => { + const root = document.documentElement; + return getComputedStyle(root) + .getPropertyValue("--safe-area-inset-bottom") + .trim(); + }; + onMount(() => { safeAreaTop = Number(getSafeAreaTop().replace("px", "")); + safeAreaBottom = Number(getSafeAreaBottom().replace("px", "")); }); $: if (safeAreaTop > 20) { @@ -36,6 +45,10 @@ if (xNotch.includes(safeAreaTop)) { state = "notch x"; } + // exception for XR and 11 at regular screen zoom + if (safeAreaTop === 48 && safeAreaBottom === 34) { + state = "notch"; + } } @@ -84,8 +97,12 @@ } } - /* plus iphone size, dynamic island, larger text display mode */ + /* regular & plus iphone size, dynamic island, larger text display mode */ @media screen and (max-width: 375px) { + #cobalt-notch-sticker.island :global(svg) { + height: 26px; + } + #cobalt-notch-sticker.island { padding-top: 11px; } diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index ce6066ac..78421620 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -135,6 +135,7 @@ ); --safe-area-inset-top: env(safe-area-inset-top); + --safe-area-inset-bottom: env(safe-area-inset-bottom); --switcher-padding: var(--sidebar-inner-padding); From 4af48dd2f9a3db6ed8d7644b65c82de9e9b8f1b1 Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Wed, 11 Sep 2024 09:34:49 +0000 Subject: [PATCH 05/79] web: add UserActivation polyfill for browsers that don't have it --- web/src/lib/polyfills.ts | 1 + web/src/lib/polyfills/user-activation.ts | 54 ++++++++++++++++++++++++ web/src/lib/types/generic.ts | 1 + web/src/routes/+layout.svelte | 1 + 4 files changed, 57 insertions(+) create mode 100644 web/src/lib/polyfills.ts create mode 100644 web/src/lib/polyfills/user-activation.ts diff --git a/web/src/lib/polyfills.ts b/web/src/lib/polyfills.ts new file mode 100644 index 00000000..b5917b5c --- /dev/null +++ b/web/src/lib/polyfills.ts @@ -0,0 +1 @@ +import "./polyfills/user-activation"; diff --git a/web/src/lib/polyfills/user-activation.ts b/web/src/lib/polyfills/user-activation.ts new file mode 100644 index 00000000..fc2e968e --- /dev/null +++ b/web/src/lib/polyfills/user-activation.ts @@ -0,0 +1,54 @@ +import { browser } from "$app/environment"; +import type { Writeable } from "$lib/types/generic"; + +if (browser && !navigator.userActivation) { + const TRANSIENT_TIMEOUT = navigator.userAgent.includes('Firefox') ? 5000 : 2000; + let _timeout: number | undefined; + + const userActivation: Writeable = { + isActive: false, + hasBeenActive: false + }; + + const receiveEvent = (e: Event) => { + // An activation triggering input event is any event whose isTrusted attribute is true [...] + if (!e.isTrusted) return; + + // and whose type is one of: + if (e instanceof PointerEvent) { + if ( + // "pointerdown", provided the event's pointerType is "mouse"; + (e.type === 'pointerdown' && e.pointerType !== 'mouse') + // "pointerup", provided the event's pointerType is not "mouse"; + || (e.type === 'pointerup' && e.pointerType === 'mouse') + ) + return; + } else if (e instanceof KeyboardEvent) { + // "keydown", provided the key is neither the Esc key nor a shortcut key + // reserved by the user agent; + if (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey) + return; + + // the handling for this is a bit more complex, + // but this is fine for our use case + if (e.key !== 'Return' && e.key !== 'Enter' && e.key.length > 1) + return; + } + + userActivation.hasBeenActive = true; + userActivation.isActive = true; + + clearTimeout(_timeout); + _timeout = window.setTimeout(() => { + userActivation.isActive = false; + _timeout = undefined; + }, TRANSIENT_TIMEOUT); + } + + // https://html.spec.whatwg.org/multipage/interaction.html#the-useractivation-interface + for (const event of [ 'keydown', 'mousedown', 'pointerdown', 'pointerup', 'touchend' ]) { + window.addEventListener(event, receiveEvent); + } + + (navigator.userActivation as UserActivation) = userActivation; +} diff --git a/web/src/lib/types/generic.ts b/web/src/lib/types/generic.ts index 9807cea5..59844106 100644 --- a/web/src/lib/types/generic.ts +++ b/web/src/lib/types/generic.ts @@ -9,3 +9,4 @@ export type RecursivePartial = { export type DefaultImport = () => Promise<{ default: T }>; export type Optional = T | undefined; +export type Writeable = { -readonly [P in keyof T]: T[P] }; diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 78421620..5263ffa2 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -8,6 +8,7 @@ import { browser } from "$app/environment"; import { afterNavigate } from "$app/navigation"; + import "$lib/polyfills"; import env from "$lib/env"; import settings from "$lib/state/settings"; import locale from "$lib/i18n/locale"; From 64173f7a03bc67dfb57ec35fcd9061322be50540 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 11 Sep 2024 22:18:51 +0600 Subject: [PATCH 06/79] api/create-filename: don't push youtubeFormat if it doesn't exist oops --- api/src/processing/create-filename.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api/src/processing/create-filename.js b/api/src/processing/create-filename.js index d1758c00..4f248377 100644 --- a/api/src/processing/create-filename.js +++ b/api/src/processing/create-filename.js @@ -2,16 +2,16 @@ export default (f, style, isAudioOnly, isAudioMuted) => { let filename = ''; let infoBase = [f.service, f.id]; - - let classicTags = infoBase.concat([ - f.resolution, - f.youtubeFormat, - ]); - - let basicTags = [f.qualityLabel, f.youtubeFormat]; + let classicTags = [...infoBase, f.resolution]; + let basicTags = [f.qualityLabel]; const title = `${f.title} - ${f.author}`; + if (f.youtubeFormat) { + classicTags.push(f.youtubeFormat); + basicTags.push(f.youtubeFormat); + } + if (isAudioMuted) { classicTags.push("mute"); basicTags.push("mute"); From 80a01494c7eb0c0369f93d6617cd668d8e97851d Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 12 Sep 2024 14:30:21 +0600 Subject: [PATCH 07/79] api/match-action: add missing twitch case to redirect group closes #741 --- api/src/processing/match-action.js | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/processing/match-action.js b/api/src/processing/match-action.js index 2e7b7f1b..6b5395d4 100644 --- a/api/src/processing/match-action.js +++ b/api/src/processing/match-action.js @@ -148,6 +148,7 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab case "streamable": case "snapchat": case "loom": + case "twitch": responseType = "redirect"; break; } From e768e7f6faeea1c3638b3af0b83dc01b076432f4 Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 12 Sep 2024 14:35:42 +0600 Subject: [PATCH 08/79] api/create-filename: don't assign any of potentially blank tags --- api/src/processing/create-filename.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/api/src/processing/create-filename.js b/api/src/processing/create-filename.js index 4f248377..216b15a4 100644 --- a/api/src/processing/create-filename.js +++ b/api/src/processing/create-filename.js @@ -2,11 +2,19 @@ export default (f, style, isAudioOnly, isAudioMuted) => { let filename = ''; let infoBase = [f.service, f.id]; - let classicTags = [...infoBase, f.resolution]; - let basicTags = [f.qualityLabel]; + let classicTags = [...infoBase]; + let basicTags = []; const title = `${f.title} - ${f.author}`; + if (f.resolution) { + classicTags.push(f.resolution); + } + + if (f.qualityLabel) { + basicTags.push(f.qualityLabel); + } + if (f.youtubeFormat) { classicTags.push(f.youtubeFormat); basicTags.push(f.youtubeFormat); From b90a58f4f0d61add36a93c7820e4b1c2e5f1878a Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 12 Sep 2024 14:38:36 +0600 Subject: [PATCH 09/79] api/tests/twitch: fix expected twitch status --- api/src/util/tests.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/util/tests.json b/api/src/util/tests.json index 86658d88..bc7adae2 100644 --- a/api/src/util/tests.json +++ b/api/src/util/tests.json @@ -1113,7 +1113,7 @@ "params": {}, "expected": { "code": 200, - "status": "tunnel" + "status": "redirect" } }, { From acb4e49e18a4ed9b2b49284b77d8ae477eca0ace Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Thu, 12 Sep 2024 08:40:37 +0000 Subject: [PATCH 10/79] ci: increase timeout on api sanity check --- .github/test.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/test.sh b/.github/test.sh index e46f4ad6..3d175da9 100755 --- a/.github/test.sh +++ b/.github/test.sh @@ -14,7 +14,7 @@ waitport() { test_api() { waitport 3000 curl -m 3 http://localhost:3000/ - API_RESPONSE=$(curl -m 3 http://localhost:3000/ \ + API_RESPONSE=$(curl -m 10 http://localhost:3000/ \ -X POST \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ @@ -24,7 +24,7 @@ test_api() { STATUS=$(echo "$API_RESPONSE" | jq -r .status) STREAM_URL=$(echo "$API_RESPONSE" | jq -r .url) [ "$STATUS" = tunnel ] || exit 1; - S=$(curl -I -m 3 "$STREAM_URL") + S=$(curl -I -m 10 "$STREAM_URL") CONTENT_LENGTH=$(echo "$S" \ | grep -i content-length \ @@ -64,4 +64,4 @@ else exit 1 fi -wait || exit $? \ No newline at end of file +wait || exit $? From ca538a2e6ca453ebe52ec4c33198c666b56194ce Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 12 Sep 2024 20:07:56 +0600 Subject: [PATCH 11/79] api/youtube: use webm container for av1 and opus --- api/src/processing/services/youtube.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/processing/services/youtube.js b/api/src/processing/services/youtube.js index 7551765e..cf83fbd0 100644 --- a/api/src/processing/services/youtube.js +++ b/api/src/processing/services/youtube.js @@ -18,8 +18,8 @@ const codecMatch = { }, av1: { videoCodec: "av01", - audioCodec: "mp4a", - container: "mp4" + audioCodec: "opus", + container: "webm" }, vp9: { videoCodec: "vp9", From 474c8e284fff2b4a2bbf88ca87f4185ca4f98981 Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 12 Sep 2024 20:08:20 +0600 Subject: [PATCH 12/79] web/i18n/settings: update av1 codec string --- web/i18n/en/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/i18n/en/settings.json b/web/i18n/en/settings.json index 01f56081..3cf181a4 100644 --- a/web/i18n/en/settings.json +++ b/web/i18n/en/settings.json @@ -31,7 +31,7 @@ "video.youtube.codec": "youtube video codec and container", "video.youtube.codec.h264": "h264 (mp4)", - "video.youtube.codec.av1": "av1 (mp4)", + "video.youtube.codec.av1": "av1 (webm)", "video.youtube.codec.vp9": "vp9 (webm)", "video.youtube.codec.description": "h264: best compatibility, average bitrate. max quality is 1080p. \nav1: best quality, efficiency, and bitrate. supports 8k & HDR. \nvp9: same quality & bitrate as av1, but file is approximately two times bigger. supports 4k & HDR.\n\nav1 and vp9 aren't as widely supported as h264.", From a1feadb9176db41b61d8c717498d18fe6f1cdeb0 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 13 Sep 2024 09:54:05 +0600 Subject: [PATCH 13/79] api/bluesky: add support for recordWithMedia embed type & catch various api errors --- api/src/processing/services/bluesky.js | 31 ++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/api/src/processing/services/bluesky.js b/api/src/processing/services/bluesky.js index 84571bcd..5f5cbcec 100644 --- a/api/src/processing/services/bluesky.js +++ b/api/src/processing/services/bluesky.js @@ -2,8 +2,8 @@ import HLS from "hls-parser"; import { cobaltUserAgent } from "../../config.js"; import { createStream } from "../../stream/manage.js"; -const extractVideo = async ({ getPost, filename }) => { - const urlMasterHLS = getPost?.thread?.post?.embed?.playlist; +const extractVideo = async ({ media, filename }) => { + const urlMasterHLS = media?.playlist; if (!urlMasterHLS) return { error: "fetch.empty" }; if (!urlMasterHLS.startsWith("https://video.bsky.app/")) return { error: "fetch.empty" }; @@ -77,14 +77,37 @@ export default async function ({ user, post, alwaysProxy }) { } }).then(r => r.json()).catch(() => {}); - if (!getPost || getPost?.error) return { error: "fetch.empty" }; + if (!getPost) return { error: "fetch.empty" }; + + if (getPost.error) { + switch (getPost.error) { + case "NotFound": + case "InternalServerError": + return { error: "content.post.unavailable" }; + case "InvalidRequest": + return { error: "link.unsupported" }; + default: + return { error: "fetch.empty" }; + } + } const embedType = getPost?.thread?.post?.embed?.$type; const filename = `bluesky_${user}_${post}`; if (embedType === "app.bsky.embed.video#view") { - return extractVideo({ getPost, filename }); + return extractVideo({ + media: getPost.thread?.post?.embed, + filename, + }) } + + if (embedType === "app.bsky.embed.recordWithMedia#view") { + return extractVideo({ + media: getPost.thread?.post?.embed?.media, + filename, + }) + } + if (embedType === "app.bsky.embed.images#view") { return extractImages({ getPost, filename, alwaysProxy }); } From a2414682c7d7ede8aa7a2fff8f1b622f08539e95 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 13 Sep 2024 09:55:06 +0600 Subject: [PATCH 14/79] api/tests: update bluesky tests --- api/src/util/tests.json | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/api/src/util/tests.json b/api/src/util/tests.json index bc7adae2..dee2f291 100644 --- a/api/src/util/tests.json +++ b/api/src/util/tests.json @@ -1401,7 +1401,16 @@ "bsky": [ { "name": "horizontal video", - "url": "https://bsky.app/profile/samuel.bsky.team/post/3l2udah76ch2c", + "url": "https://bsky.app/profile/haileyok.com/post/3l3giwtwp222m", + "params": {}, + "expected": { + "code": 200, + "status": "tunnel" + } + }, + { + "name": "horizontal video, recordWithMedia", + "url": "https://bsky.app/profile/juicysteak117.gay/post/3l3wonhk23g2i", "params": {}, "expected": { "code": 200, @@ -1410,7 +1419,7 @@ }, { "name": "vertical video", - "url": "https://bsky.app/profile/samuel.bsky.team/post/3l2uftgmitr2p", + "url": "https://bsky.app/profile/haileyok.com/post/3l3jhpomhjk2m", "params": {}, "expected": { "code": 200, @@ -1419,7 +1428,7 @@ }, { "name": "vertical video (muted)", - "url": "https://bsky.app/profile/samuel.bsky.team/post/3l2uftgmitr2p", + "url": "https://bsky.app/profile/haileyok.com/post/3l3jhpomhjk2m", "params": { "downloadMode": "mute" }, @@ -1430,7 +1439,7 @@ }, { "name": "vertical video (audio)", - "url": "https://bsky.app/profile/samuel.bsky.team/post/3l2uftgmitr2p", + "url": "https://bsky.app/profile/haileyok.com/post/3l3jhpomhjk2m", "params": { "downloadMode": "audio" }, @@ -1456,6 +1465,15 @@ "code": 200, "status": "picker" } + }, + { + "name": "deleted post", + "url": "https://bsky.app/profile/samuel.bsky.team/post/3l2udah76ch2c", + "params": {}, + "expected": { + "code": 400, + "status": "error" + } } ] } \ No newline at end of file From f830a1219d2d96b18350fa50c0c54fd67a77a8dd Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 13 Sep 2024 12:35:58 +0600 Subject: [PATCH 15/79] docs/run-an-instance: update tutorial for running the api locally --- docs/run-an-instance.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/run-an-instance.md b/docs/run-an-instance.md index 8e3afd48..fb339c4c 100644 --- a/docs/run-an-instance.md +++ b/docs/run-an-instance.md @@ -32,13 +32,19 @@ cobalt package will update automatically thanks to watchtower. it's highly recommended to use a reverse proxy (such as nginx) if you want your instance to face the public internet. look up tutorials online. -## using regular node.js (useful for local development) -setup script installs all needed `npm` dependencies, but you have to install `node.js` *(version 18 or above)* and `git` yourself. +## run cobalt api outside of docker (useful for local development) +requirements: +- node.js >= 18 +- git +- pnpm 1. clone the repo: `git clone https://github.com/imputnet/cobalt`. -2. run setup script and follow instructions: `npm run setup`. you need to host api and web instances separately, so pick whichever applies. -3. run cobalt via `npm start`. -4. done. +2. go to api/src directory: `cd cobalt/api/src`. +4. install dependencies: `pnpm install`. +5. create `.env` file in the same directory. +6. add needed environment variables to `.env` file. only `API_URL` is required to run cobalt. + - if you don't know what api url to use for local development, use `http://localhost:9000/`. +8. run cobalt: `pnpm start`. ### ubuntu 22.04 workaround `nscd` needs to be installed and running so that the `ffmpeg-static` binary can resolve DNS ([#101](https://github.com/imputnet/cobalt/issues/101#issuecomment-1494822258)): @@ -72,4 +78,4 @@ sudo service nscd start setting a `FREEBIND_CIDR` allows cobalt to pick a random IP for every download and use it for all requests it makes for that particular download. to use freebind in cobalt, you need to follow its [setup instructions](https://github.com/imputnet/freebind.js?tab=readme-ov-file#setup) first. if you configure this option while running cobalt in a docker container, you also need to set the `API_LISTEN_ADDRESS` env to `127.0.0.1`, and set -`network_mode` for the container to `host`. \ No newline at end of file +`network_mode` for the container to `host`. From 9c2babfc1bfafcd1a703233ab37cfdffc393c1a2 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 13 Sep 2024 12:39:10 +0600 Subject: [PATCH 16/79] docs/run-an-instance: teaching myself how to count to 6 sorry guys, it takes a ton of practice :( --- docs/run-an-instance.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/run-an-instance.md b/docs/run-an-instance.md index fb339c4c..8144c037 100644 --- a/docs/run-an-instance.md +++ b/docs/run-an-instance.md @@ -40,11 +40,11 @@ requirements: 1. clone the repo: `git clone https://github.com/imputnet/cobalt`. 2. go to api/src directory: `cd cobalt/api/src`. -4. install dependencies: `pnpm install`. -5. create `.env` file in the same directory. -6. add needed environment variables to `.env` file. only `API_URL` is required to run cobalt. +3. install dependencies: `pnpm install`. +4. create `.env` file in the same directory. +5. add needed environment variables to `.env` file. only `API_URL` is required to run cobalt. - if you don't know what api url to use for local development, use `http://localhost:9000/`. -8. run cobalt: `pnpm start`. +6. run cobalt: `pnpm start`. ### ubuntu 22.04 workaround `nscd` needs to be installed and running so that the `ffmpeg-static` binary can resolve DNS ([#101](https://github.com/imputnet/cobalt/issues/101#issuecomment-1494822258)): From 47625490ce37d2da3e0bbe2b0d6b5b28aa067d24 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 13 Sep 2024 21:25:27 +0600 Subject: [PATCH 17/79] web/settings/video: move codec names away from i18n --- web/i18n/en/settings.json | 3 --- web/src/routes/settings/video/+page.svelte | 8 +++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/web/i18n/en/settings.json b/web/i18n/en/settings.json index 3cf181a4..85c3d58b 100644 --- a/web/i18n/en/settings.json +++ b/web/i18n/en/settings.json @@ -30,9 +30,6 @@ "video.quality.description": "if preferred video quality isn't available, next best is picked instead.", "video.youtube.codec": "youtube video codec and container", - "video.youtube.codec.h264": "h264 (mp4)", - "video.youtube.codec.av1": "av1 (webm)", - "video.youtube.codec.vp9": "vp9 (webm)", "video.youtube.codec.description": "h264: best compatibility, average bitrate. max quality is 1080p. \nav1: best quality, efficiency, and bitrate. supports 8k & HDR. \nvp9: same quality & bitrate as av1, but file is approximately two times bigger. supports 4k & HDR.\n\nav1 and vp9 aren't as widely supported as h264.", "video.twitter.gif": "twitter/x", diff --git a/web/src/routes/settings/video/+page.svelte b/web/src/routes/settings/video/+page.svelte index 2cdaf976..38ce0d8d 100644 --- a/web/src/routes/settings/video/+page.svelte +++ b/web/src/routes/settings/video/+page.svelte @@ -8,6 +8,12 @@ import Switcher from "$components/buttons/Switcher.svelte"; import SettingsButton from "$components/buttons/SettingsButton.svelte"; import SettingsToggle from "$components/buttons/SettingsToggle.svelte"; + + const codecTitles = { + h264: "h264 (mp4)", + av1: "av1 (webm)", + vp9: "vp9 (webm)", + } - {$t(`settings.video.youtube.codec.${value}`)} + {codecTitles[value]} {/each} From 5facbc96572fa87fbe1533579511c6e59a154e72 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 13 Sep 2024 23:28:14 +0600 Subject: [PATCH 18/79] api/tests/bluesky: update deleted post test --- api/src/util/tests.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/util/tests.json b/api/src/util/tests.json index dee2f291..78340691 100644 --- a/api/src/util/tests.json +++ b/api/src/util/tests.json @@ -1467,8 +1467,8 @@ } }, { - "name": "deleted post", - "url": "https://bsky.app/profile/samuel.bsky.team/post/3l2udah76ch2c", + "name": "deleted post/invalid user", + "url": "https://bsky.app/profile/notreal.bsky.team/post/3l2udah76ch2c", "params": {}, "expected": { "code": 400, From 0ccd08470b80915b4c1bd5bd7ad2ed06acfe213e Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 13 Sep 2024 23:57:18 +0600 Subject: [PATCH 19/79] web/about/general: more clarity in privacy section --- web/src/routes/about/general/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/routes/about/general/+page.svelte b/web/src/routes/about/general/+page.svelte index 3236515c..a7ecacdf 100644 --- a/web/src/routes/about/general/+page.svelte +++ b/web/src/routes/about/general/+page.svelte @@ -18,7 +18,7 @@

leading privacy

all requests to backend are anonymous and all tunnels are encrypted. - we have a strict zero log policy and don't track anything at all. + we have a strict zero log policy and don't track anything about individual people.

to avoid caching or storing downloaded files, cobalt processes them on-fly, sending processed pieces directly to client. From 99937f61f6a4ede75fc57a6690959f76dde8809b Mon Sep 17 00:00:00 2001 From: GuriZenit Date: Sat, 14 Sep 2024 02:06:36 -0300 Subject: [PATCH 20/79] api/setup: fix wrong misc path --- api/src/util/setup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/util/setup.js b/api/src/util/setup.js index 7796ea22..e9d1beae 100644 --- a/api/src/util/setup.js +++ b/api/src/util/setup.js @@ -1,7 +1,7 @@ import { existsSync, unlinkSync, appendFileSync } from "fs"; import { createInterface } from "readline"; -import { Cyan, Bright } from "./misc/console-text.js"; -import { loadJSON } from "./misc/load-from-fs.js"; +import { Cyan, Bright } from "../misc/console-text.js"; +import { loadJSON } from "../misc/load-from-fs.js"; import { execSync } from "child_process"; const { version } = loadJSON("./package.json"); From 1bf0d983240c7b551a2d4295274623d822397132 Mon Sep 17 00:00:00 2001 From: lath <38570701+halozat@users.noreply.github.com> Date: Mon, 16 Sep 2024 07:14:29 +0200 Subject: [PATCH 21/79] web/DonateShareCard: fix copy button not using i18n (#750) --- web/src/components/donate/DonateShareCard.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/donate/DonateShareCard.svelte b/web/src/components/donate/DonateShareCard.svelte index 7da48af8..3225c7ad 100644 --- a/web/src/components/donate/DonateShareCard.svelte +++ b/web/src/components/donate/DonateShareCard.svelte @@ -59,7 +59,7 @@

- copy + {$t("button.copy")} {#if device.supports.share} From 86268eab3f46e831d7ef9d7e13fc5b2fd80ba20e Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Mon, 16 Sep 2024 13:45:58 +0000 Subject: [PATCH 22/79] api-client: add dist folder to gitignore --- packages/api-client/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/api-client/.gitignore diff --git a/packages/api-client/.gitignore b/packages/api-client/.gitignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/packages/api-client/.gitignore @@ -0,0 +1 @@ +dist From d93e97e06b78fd6de42297d8a01aece55b33f4a1 Mon Sep 17 00:00:00 2001 From: dumbmoron Date: Mon, 16 Sep 2024 15:13:24 +0000 Subject: [PATCH 23/79] web/LanguageDropdown: unbind `locale` from select dropdown --- web/src/components/settings/LanguageDropdown.svelte | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/src/components/settings/LanguageDropdown.svelte b/web/src/components/settings/LanguageDropdown.svelte index dd357dd8..89f2b44f 100644 --- a/web/src/components/settings/LanguageDropdown.svelte +++ b/web/src/components/settings/LanguageDropdown.svelte @@ -1,5 +1,4 @@
-
+
showCopy = true} + on:mouseleave={() => showCopy = false} + role="link" + tabindex="0" + >

{title}

{#if beta}
{$t("general.beta")}
{/if} + {#if showCopy} + + {/if}
diff --git a/web/src/components/settings/SettingsCategory.svelte b/web/src/components/settings/SettingsCategory.svelte index 1e475761..520be2db 100644 --- a/web/src/components/settings/SettingsCategory.svelte +++ b/web/src/components/settings/SettingsCategory.svelte @@ -1,8 +1,8 @@ @@ -63,7 +58,7 @@ padding: var(--padding) 5px; color: var(--sidebar-highlight); font-size: var(--sidebar-font-size); - opacity: 0.8; + opacity: 0.75; height: fit-content; border-radius: var(--border-radius); transition: transform 0.2s; @@ -88,19 +83,15 @@ color: var(--sidebar-bg); background: var(--sidebar-highlight); opacity: 1; - transition: none; transform: none; + animation: pressButton 0.3s; } - :global(.sidebar-tab.animate) { - animation: pressButton 0.2s; - } - - .sidebar-tab:active:not(.active) { + .sidebar-tab:active { transform: scale(0.95); } - :global([data-reduce-motion="true"]) .sidebar-tab:active:not(.active) { + :global([data-reduce-motion="true"]) .sidebar-tab:active { transform: none; } @@ -112,10 +103,10 @@ @keyframes pressButton { 0% { - transform: scale(0.95); + transform: scale(0.9); } 50% { - transform: scale(1.02); + transform: scale(1.015); } 100% { transform: scale(1); @@ -127,6 +118,7 @@ opacity: 1; background-color: var(--sidebar-hover); } + .sidebar-tab:hover:not(.active) { opacity: 1; background-color: var(--sidebar-hover); @@ -136,9 +128,15 @@ @media screen and (max-width: 535px) { .sidebar-tab { padding: 5px var(--padding); + gap: 3px; min-width: calc(var(--sidebar-width) / 2); } + .sidebar-tab :global(svg) { + height: 22px; + width: 22px; + } + .sidebar-tab.active { z-index: 2; } @@ -149,10 +147,10 @@ @keyframes pressButton { 0% { - transform: scale(0.9); + transform: scale(0.8); } - 60% { - transform: scale(1.015); + 50% { + transform: scale(1.02); } 100% { transform: scale(1); From 521eb4b64376f3dfe5e6c6469c8a0b624725270c Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 15:58:32 +0600 Subject: [PATCH 37/79] web/Sidebar: remove fixed width for tabs container --- web/src/components/sidebar/Sidebar.svelte | 2 -- 1 file changed, 2 deletions(-) diff --git a/web/src/components/sidebar/Sidebar.svelte b/web/src/components/sidebar/Sidebar.svelte index df03e6ea..ca3a963e 100644 --- a/web/src/components/sidebar/Sidebar.svelte +++ b/web/src/components/sidebar/Sidebar.svelte @@ -70,7 +70,6 @@ #sidebar-tabs { height: 100%; - width: var(--sidebar-width); justify-content: space-between; padding: var(--sidebar-inner-padding); padding-bottom: var(--border-radius); @@ -110,7 +109,6 @@ overflow-x: scroll; padding-bottom: 0; padding: var(--sidebar-inner-padding) 0; - width: unset; height: fit-content; } From 02267b4db42f752d2db45542205d88deb5e4cf7b Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 16:17:22 +0600 Subject: [PATCH 38/79] web/i18n/about: use section heading component --- web/i18n/en/about/credits.md | 16 +++++++++----- web/i18n/en/about/general.md | 36 +++++++++++++++++++++--------- web/i18n/en/about/privacy.md | 43 ++++++++++++++++++++++++------------ web/i18n/en/about/terms.md | 32 +++++++++++++++++++-------- 4 files changed, 88 insertions(+), 39 deletions(-) diff --git a/web/i18n/en/about/credits.md b/web/i18n/en/about/credits.md index 4002806a..442499e4 100644 --- a/web/i18n/en/about/credits.md +++ b/web/i18n/en/about/credits.md @@ -1,10 +1,14 @@
+ -### meowbalt meowbalt is cobalt's speedy mascot. he is an extremely expressive cat that loves fast internet. all amazing drawings of meowbalt that you see in cobalt were made by [GlitchyPSI](https://glitchypsi.xyz/). @@ -13,11 +17,14 @@ he is also the original designer of the character. you cannot use or modify GlitchyPSI's artworks of meowbalt without his explicit permission. you cannot use or modify the meowbalt character design commercially or in any form that isn't fan art. -
-
-### cobalt licenses +
+ + cobalt processing server is open source and licensed under [AGPL-3.0]({docs.apiLicense}). cobalt frontend is [source first](https://sourcefirst.com/) and licensed under [CC-BY-NC-SA 4.0]({docs.webLicense}). @@ -25,5 +32,4 @@ we decided to use this license to stop grifters from profiting off our work & fr we rely on many open source libraries, create & distribute our own. you can see the full list of dependencies on [github]({contacts.github}). -
diff --git a/web/i18n/en/about/general.md b/web/i18n/en/about/general.md index 5bdea833..1b32e815 100644 --- a/web/i18n/en/about/general.md +++ b/web/i18n/en/about/general.md @@ -1,19 +1,25 @@
+ -### best way to save what you love cobalt lets you save anything from your favorite websites: video, audio, photos or gifs — cobalt can do it all! no ads, trackers, or paywalls, no nonsense. just a convenient web app that works everywhere. -
+ -### leading privacy all requests to backend are anonymous and all tunnels are encrypted. we have a strict zero log policy and don't track *anything* about individual people. @@ -23,12 +29,14 @@ this technology is used when your request needs additional processing, such as w for even higher level of protection, you can [ask cobalt to always tunnel everything](/settings/privacy#tunnel). when enabled, cobalt will proxy everything through itself. no one will know what you download, even your network provider/admin. all they'll see is that you're using cobalt. -
+ -### blazing speed since we don't rely on any existing downloaders and develop our own from ground up, cobalt is extremely efficient and a processing server can run on basically any hardware. @@ -37,11 +45,14 @@ to reduce latency and distribute the traffic. we constantly improve our infrastructure along with our long-standing partner, [royalehosting.net]({partners.royalehosting})! you're in good hands, and will get what you need within seconds. -
-
-### open community +
+ + cobalt is used by countless artists, educators, and content creators to do what they love. we're always on the line with our community and work together to create even more useful tools for them. feel free to [join the conversation](/about/community)! @@ -51,11 +62,14 @@ at any time, we welcome all contributions and suggestions. you can use any processing instances hosted by the community, including your own. if your friend hosts one, just ask them for a domain and [add it in instance settings](/settings/instances#community). -
-
-### on-device processing +
+ + new features, such as [remuxing](/remux), work on-device. on-device processing is efficient and never sends anything over the internet. it perfectly aligns with our future goal of moving as much processing as possible to client. diff --git a/web/i18n/en/about/privacy.md b/web/i18n/en/about/privacy.md index 4a3d8b76..c76fdd4c 100644 --- a/web/i18n/en/about/privacy.md +++ b/web/i18n/en/about/privacy.md @@ -1,40 +1,55 @@
+ -### general terms cobalt's privacy policy is simple: we don't collect or store anything about you. what you do is solely your business, not ours or anyone else's. these terms are applicable only when using the official cobalt instance. in other cases, you may need to contact the hoster for accurate info. -
+
+ -### on-device processing tools that use on-device processing work offline, locally, and never send any data anywhere. they are explicitly marked as such whenever applicable. -
-
-### saving +
+ + when using saving functionality, in some cases cobalt will encrypt & temporarily store information needed for tunneling. it's stored in processing server's RAM for 90 seconds and irreversibly purged afterwards. no one has access to it, even instance owners, as long as they don't modify the official cobalt image. processed/tunneled files are never cached anywhere. everything is tunneled live. cobalt's saving functionality is essentially a fancy proxy service. -
+
+ -### encryption temporarily stored tunnel data is encrypted using the AES-256 standard. decryption keys are only included in the access link and never logged/cached/stored anywhere. only the end user has access to the link & encryption keys. keys are generated uniquely for each requested tunnel. -
{#if env.PLAUSIBLE_ENABLED}
+ -### anonymous traffic analytics for sake of privacy, we use [plausible's anonymous traffic analytics](https://plausible.io/) to get an approximate number of active cobalt users. no identifiable information about you or your requests is ever stored. all data is anonymized and aggregated. the plausible instance we use is hosted & managed by us. plausible doesn't use cookies and is fully compliant with GDPR, CCPA, and PECR. @@ -42,18 +57,18 @@ plausible doesn't use cookies and is fully compliant with GDPR, CCPA, and PECR. [learn more about plausible's dedication to privacy.](https://plausible.io/privacy-focused-web-analytics) if you wish to opt out of anonymous analytics, you can do it in privacy settings. -
- {/if}
+ -### web privacy & security we use cloudflare services for ddos & bot protection. we also use cloudflare pages for deploying & hosting the static web app. all of these are required to provide the best experience for everyone. it's the most private & reliable provider that we know of. cloudflare is fully compliant with GDPR and HIPAA. [learn more about cloudflare's dedication to privacy.](https://www.cloudflare.com/trust-hub/privacy-and-data-protection/) -
diff --git a/web/i18n/en/about/terms.md b/web/i18n/en/about/terms.md index 1c82124c..cd0c10fd 100644 --- a/web/i18n/en/about/terms.md +++ b/web/i18n/en/about/terms.md @@ -1,32 +1,46 @@ + +
+ -### general terms these terms are applicable only when using the official cobalt instance. in other cases, you may need to contact the hoster for accurate info. -
+ -### saving saving functionality simplifies downloading content from the internet and takes zero liability for what the saved content is used for. processing servers work like advanced proxies and don't ever write any content to disk. everything is handled in RAM and permanently purged once the tunnel is done. we have no downloading logs and can't identify anyone. [you can read more about how tunnels work in our privacy policy.](/about/privacy) -
+ -### responsibilities you (end user) are responsible for what you do with our tools, how you use and distribute resulting content. please be mindful when using content of others and always credit original creators. make sure you don't violate any terms or licenses. when used in educational purposes, always cite sources and credit original creators. fair use and credits benefit everyone. -
+
+ -### reporting abuse -we have no way of detecting abusive behavior automatically, as cobalt is 100% anonymous. however, you can report such activities to us and we will do our best to comply manually: [safety@imput.net](mailto:safety@imput.net) - +we have no way of detecting abusive behavior automatically, as cobalt is 100% anonymous. +however, you can report such activities to us and we will do our best to comply manually: [safety@imput.net](mailto:safety@imput.net)
From d1686be58339d92629c3ccd7b952180fc251595e Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 17:41:10 +0600 Subject: [PATCH 39/79] web/i18n/about: replace section titles with i18n strings --- web/i18n/en/about.json | 16 +++++++++++++++- web/i18n/en/about/credits.md | 6 ++++-- web/i18n/en/about/general.md | 12 +++++++----- web/i18n/en/about/privacy.md | 14 ++++++++------ web/i18n/en/about/terms.md | 9 +++++---- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/web/i18n/en/about.json b/web/i18n/en/about.json index e566faaa..b441512e 100644 --- a/web/i18n/en/about.json +++ b/web/i18n/en/about.json @@ -12,5 +12,19 @@ "community.twitter": "news account on twitter", "community.github": "github repo", "community.email": "support email", - "community.telegram": "news channel on telegram" + "community.telegram": "news channel on telegram", + + "heading.general": "general terms", + "heading.licenses": "licenses", + "heading.summary": "best way to save what you love", + "heading.privacy": "leading privacy", + "heading.speed": "blazing speed", + "heading.community": "open community", + "heading.local": "on-device processing", + "heading.saving": "saving", + "heading.encryption": "encryption", + "heading.plausible": "anonymous traffic analytics", + "heading.cloudflare": "web privacy & security", + "heading.responsibility": "user responsibilities", + "heading.abuse": "reporting abuse" } diff --git a/web/i18n/en/about/credits.md b/web/i18n/en/about/credits.md index 442499e4..27266ea4 100644 --- a/web/i18n/en/about/credits.md +++ b/web/i18n/en/about/credits.md @@ -1,11 +1,13 @@
@@ -21,7 +23,7 @@ you cannot use or modify the meowbalt character design commercially or in any fo
diff --git a/web/i18n/en/about/general.md b/web/i18n/en/about/general.md index 1b32e815..333e119e 100644 --- a/web/i18n/en/about/general.md +++ b/web/i18n/en/about/general.md @@ -1,11 +1,13 @@
@@ -16,7 +18,7 @@ no ads, trackers, or paywalls, no nonsense. just a convenient web app that works
@@ -33,7 +35,7 @@ all they'll see is that you're using cobalt.
@@ -49,7 +51,7 @@ you're in good hands, and will get what you need within seconds.
@@ -66,7 +68,7 @@ if your friend hosts one, just ask them for a domain and [add it in instance set
diff --git a/web/i18n/en/about/privacy.md b/web/i18n/en/about/privacy.md index c76fdd4c..b19ca762 100644 --- a/web/i18n/en/about/privacy.md +++ b/web/i18n/en/about/privacy.md @@ -1,11 +1,13 @@
@@ -16,7 +18,7 @@ these terms are applicable only when using the official cobalt instance. in othe
@@ -25,7 +27,7 @@ tools that use on-device processing work offline, locally, and never send any da
@@ -36,7 +38,7 @@ processed/tunneled files are never cached anywhere. everything is tunneled live.
@@ -46,7 +48,7 @@ temporarily stored tunnel data is encrypted using the AES-256 standard. decrypti {#if env.PLAUSIBLE_ENABLED}
@@ -62,7 +64,7 @@ if you wish to opt out of anonymous analytics, you can do it in diff --git a/web/i18n/en/about/terms.md b/web/i18n/en/about/terms.md index cd0c10fd..453030cf 100644 --- a/web/i18n/en/about/terms.md +++ b/web/i18n/en/about/terms.md @@ -1,10 +1,11 @@
@@ -13,7 +14,7 @@ these terms are applicable only when using the official cobalt instance. in othe
@@ -24,7 +25,7 @@ saving functionality simplifies downloading content from the internet and takes
@@ -37,7 +38,7 @@ fair use and credits benefit everyone.
From 97977efabd92375f270d1818f38de3b0682c2f19 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 18:44:24 +0600 Subject: [PATCH 40/79] web: generate `_headers` & add `Content-Security-Policy` header --- web/src/routes/_headers/+server.ts | 28 ++++++++++++++++++++++++++++ web/static/_headers | 3 --- 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 web/src/routes/_headers/+server.ts delete mode 100644 web/static/_headers diff --git a/web/src/routes/_headers/+server.ts b/web/src/routes/_headers/+server.ts new file mode 100644 index 00000000..2cbf4e88 --- /dev/null +++ b/web/src/routes/_headers/+server.ts @@ -0,0 +1,28 @@ +export async function GET() { + const CSP = [ + "default-src 'none'", + "script-src 'self' challenges.cloudflare.com", + "frame-src challenges.cloudflare.com", + ] + + const _headers = { + "/*": { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + "Content-Security-Policy": CSP.join("; "), + } + } + + return new Response( + Object.entries(_headers).map( + ([path, headers]) => [ + path, + Object.entries(headers).map( + ([key, value]) => ` ${key}: ${value}` + ) + ].flat().join("\n") + ).join("\n\n") + ); +} + +export const prerender = true; diff --git a/web/static/_headers b/web/static/_headers deleted file mode 100644 index cabbdca5..00000000 --- a/web/static/_headers +++ /dev/null @@ -1,3 +0,0 @@ -/* - Cross-Origin-Opener-Policy: same-origin - Cross-Origin-Embedder-Policy: require-corp From 732199332e6d5669545a4970501a2a06a3497299 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 19:06:46 +0600 Subject: [PATCH 41/79] web/headers: fix CSP directives & refactor --- web/src/routes/_headers/+server.ts | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/web/src/routes/_headers/+server.ts b/web/src/routes/_headers/+server.ts index 2cbf4e88..f8b3c81d 100644 --- a/web/src/routes/_headers/+server.ts +++ b/web/src/routes/_headers/+server.ts @@ -1,15 +1,32 @@ +import env from "$lib/env"; + export async function GET() { - const CSP = [ - "default-src 'none'", - "script-src 'self' challenges.cloudflare.com", - "frame-src challenges.cloudflare.com", - ] + const CSP = { + "connect-src": ["*"], + "default-src": ["'none'"], + + "font-src": ["'self'"], + "style-src": ["'self'"], + "img-src": ["'self'"], + "manifest-src": ["'self'"], + "worker-src": ["'self'"], + + "script-src": [ + "'self'", + "challenges.cloudflare.com", + env.PLAUSIBLE_HOST ? env.PLAUSIBLE_HOST : "" + ], + "frame-src": ["challenges.cloudflare.com"], + } const _headers = { "/*": { "Cross-Origin-Opener-Policy": "same-origin", "Cross-Origin-Embedder-Policy": "require-corp", - "Content-Security-Policy": CSP.join("; "), + "Content-Security-Policy": + Object.entries(CSP).map( + ([directive, values]) => `${directive} ${values.join(' ')}` + ).flat().join("; "), } } From 9024418aff0bab9cd07a54492ef19bd94ecef13b Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 19:12:13 +0600 Subject: [PATCH 42/79] web/headers: add more stuff to CSP again --- web/src/routes/_headers/+server.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/web/src/routes/_headers/+server.ts b/web/src/routes/_headers/+server.ts index f8b3c81d..96e00a8c 100644 --- a/web/src/routes/_headers/+server.ts +++ b/web/src/routes/_headers/+server.ts @@ -1,5 +1,11 @@ import env from "$lib/env"; +const allowedScriptOrigins = [ + "'self'", + "challenges.cloudflare.com", + env.PLAUSIBLE_HOST ? env.PLAUSIBLE_HOST : "" +] + export async function GET() { const CSP = { "connect-src": ["*"], @@ -7,15 +13,14 @@ export async function GET() { "font-src": ["'self'"], "style-src": ["'self'"], - "img-src": ["'self'"], + "style-src-attr": ["'self'"], + "style-src-elem": ["'self'"], + "img-src": ["'self'", "data:"], "manifest-src": ["'self'"], "worker-src": ["'self'"], - "script-src": [ - "'self'", - "challenges.cloudflare.com", - env.PLAUSIBLE_HOST ? env.PLAUSIBLE_HOST : "" - ], + "script-src": allowedScriptOrigins, + "script-src-attr": allowedScriptOrigins, "frame-src": ["challenges.cloudflare.com"], } From 52599dd90035863726f7a28631305303eb061889 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 19:16:23 +0600 Subject: [PATCH 43/79] web/headers: update csp yet again whatever dude --- web/src/routes/_headers/+server.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/web/src/routes/_headers/+server.ts b/web/src/routes/_headers/+server.ts index 96e00a8c..708d69ac 100644 --- a/web/src/routes/_headers/+server.ts +++ b/web/src/routes/_headers/+server.ts @@ -9,15 +9,7 @@ const allowedScriptOrigins = [ export async function GET() { const CSP = { "connect-src": ["*"], - "default-src": ["'none'"], - - "font-src": ["'self'"], - "style-src": ["'self'"], - "style-src-attr": ["'self'"], - "style-src-elem": ["'self'"], - "img-src": ["'self'", "data:"], - "manifest-src": ["'self'"], - "worker-src": ["'self'"], + "default-src": ["'self'"], "script-src": allowedScriptOrigins, "script-src-attr": allowedScriptOrigins, From 026cb634ecf9162dbec28e04f685f123821d199f Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 20:11:47 +0600 Subject: [PATCH 44/79] web: update & move csp to `svelte.config.js` ough --- pnpm-lock.yaml | 3 +++ web/package.json | 1 + web/src/routes/_headers/+server.ts | 21 --------------- web/svelte.config.js | 43 ++++++++++++++++++++++++++---- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d316277..90995fb6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,6 +140,9 @@ importers: compare-versions: specifier: ^6.1.0 version: 6.1.1 + dotenv: + specifier: ^16.0.1 + version: 16.4.5 eslint: specifier: ^8.57.0 version: 8.57.0 diff --git a/web/package.json b/web/package.json index 67c779bc..2d4a3e61 100644 --- a/web/package.json +++ b/web/package.json @@ -33,6 +33,7 @@ "@types/fluent-ffmpeg": "^2.1.25", "@types/node": "^20.14.10", "compare-versions": "^6.1.0", + "dotenv": "^16.0.1", "eslint": "^8.57.0", "glob": "^10.4.5", "mdsvex": "^0.11.2", diff --git a/web/src/routes/_headers/+server.ts b/web/src/routes/_headers/+server.ts index 708d69ac..cdeb1f9c 100644 --- a/web/src/routes/_headers/+server.ts +++ b/web/src/routes/_headers/+server.ts @@ -1,29 +1,8 @@ -import env from "$lib/env"; - -const allowedScriptOrigins = [ - "'self'", - "challenges.cloudflare.com", - env.PLAUSIBLE_HOST ? env.PLAUSIBLE_HOST : "" -] - export async function GET() { - const CSP = { - "connect-src": ["*"], - "default-src": ["'self'"], - - "script-src": allowedScriptOrigins, - "script-src-attr": allowedScriptOrigins, - "frame-src": ["challenges.cloudflare.com"], - } - const _headers = { "/*": { "Cross-Origin-Opener-Policy": "same-origin", "Cross-Origin-Embedder-Policy": "require-corp", - "Content-Security-Policy": - Object.entries(CSP).map( - ([directive, values]) => `${directive} ${values.join(' ')}` - ).flat().join("; "), } } diff --git a/web/svelte.config.js b/web/svelte.config.js index 52263871..28602c1e 100644 --- a/web/svelte.config.js +++ b/web/svelte.config.js @@ -1,8 +1,10 @@ -import adapter from '@sveltejs/adapter-static'; -import { mdsvex } from 'mdsvex'; -import { fileURLToPath } from 'node:url'; -import { dirname, join } from 'node:path'; -import { sveltePreprocess } from 'svelte-preprocess'; +import "dotenv/config"; +import adapter from "@sveltejs/adapter-static"; + +import { mdsvex } from "mdsvex"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { sveltePreprocess } from "svelte-preprocess"; /** @type {import('@sveltejs/kit').Config} */ const config = { @@ -46,6 +48,37 @@ const config = { precompress: false, strict: true }), + csp: { + mode: "hash", + directives: { + "connect-src": ["*"], + "default-src": ["none"], + + "font-src": ["self"], + "style-src": ["self", "unsafe-inline"], + "img-src": ["self", "data:"], + "manifest-src": ["self"], + "worker-src": ["self"], + + "object-src": ["none"], + "frame-src": [ + "self", + "challenges.cloudflare.com" + ], + + "script-src": [ + "self", + "wasm-unsafe-eval", + "challenges.cloudflare.com", + + // eslint-disable-next-line no-undef + process.env.WEB_PLAUSIBLE_HOST ? process.env.WEB_PLAUSIBLE_HOST : "", + + // hash of the theme preloader in app.html + "sha256-g67gIjM3G8yMbjbxyc3QUoVsKhdxgcQzCmSKXiZZo6s=", + ] + } + }, env: { publicPrefix: 'WEB_' }, From b30b6957ce52fc3a0f5122cbcfe72473b0702784 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 20:15:56 +0600 Subject: [PATCH 45/79] web/package: move dependencies to devDependencies --- pnpm-lock.yaml | 45 ++++++++++++++++++++++----------------------- web/package.json | 20 +++++++++----------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90995fb6..220a2cf3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,41 +84,25 @@ importers: packages/version-info: {} web: - dependencies: + devDependencies: + '@eslint/js': + specifier: ^9.5.0 + version: 9.8.0 '@fontsource-variable/noto-sans-mono': specifier: ^5.0.20 version: 5.0.20 '@fontsource/ibm-plex-mono': specifier: ^5.0.13 version: 5.0.13 + '@fontsource/redaction-10': + specifier: ^5.0.2 + version: 5.0.2 '@imput/libav.js-remux-cli': specifier: ^5.5.6 version: 5.5.6 '@imput/version-info': specifier: workspace:^ version: link:../packages/version-info - '@tabler/icons-svelte': - specifier: 3.6.0 - version: 3.6.0(svelte@4.2.18) - '@vitejs/plugin-basic-ssl': - specifier: ^1.1.0 - version: 1.1.0(vite@5.3.5(@types/node@20.14.14)) - mime: - specifier: ^4.0.4 - version: 4.0.4 - sveltekit-i18n: - specifier: ^2.4.2 - version: 2.4.2(svelte@4.2.18) - ts-deepmerge: - specifier: ^7.0.0 - version: 7.0.1 - devDependencies: - '@eslint/js': - specifier: ^9.5.0 - version: 9.8.0 - '@fontsource/redaction-10': - specifier: ^5.0.2 - version: 5.0.2 '@sveltejs/adapter-static': specifier: ^3.0.2 version: 3.0.2(@sveltejs/kit@2.5.19(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@20.14.14)))(svelte@4.2.18)(vite@5.3.5(@types/node@20.14.14))) @@ -128,6 +112,9 @@ importers: '@sveltejs/vite-plugin-svelte': specifier: ^3.0.0 version: 3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@20.14.14)) + '@tabler/icons-svelte': + specifier: 3.6.0 + version: 3.6.0(svelte@4.2.18) '@types/eslint__js': specifier: ^8.42.3 version: 8.42.3 @@ -137,6 +124,9 @@ importers: '@types/node': specifier: ^20.14.10 version: 20.14.14 + '@vitejs/plugin-basic-ssl': + specifier: ^1.1.0 + version: 1.1.0(vite@5.3.5(@types/node@20.14.14)) compare-versions: specifier: ^6.1.0 version: 6.1.1 @@ -152,6 +142,9 @@ importers: mdsvex: specifier: ^0.11.2 version: 0.11.2(svelte@4.2.18) + mime: + specifier: ^4.0.4 + version: 4.0.4 svelte: specifier: ^4.2.7 version: 4.2.18 @@ -161,6 +154,12 @@ importers: svelte-preprocess: specifier: ^6.0.2 version: 6.0.2(postcss@8.4.40)(svelte@4.2.18)(typescript@5.5.4) + sveltekit-i18n: + specifier: ^2.4.2 + version: 2.4.2(svelte@4.2.18) + ts-deepmerge: + specifier: ^7.0.1 + version: 7.0.1 tslib: specifier: ^2.4.1 version: 2.6.3 diff --git a/web/package.json b/web/package.json index 2d4a3e61..645b077d 100644 --- a/web/package.json +++ b/web/package.json @@ -25,36 +25,34 @@ "homepage": "https://cobalt.tools/", "devDependencies": { "@eslint/js": "^9.5.0", + "@fontsource-variable/noto-sans-mono": "^5.0.20", + "@fontsource/ibm-plex-mono": "^5.0.13", "@fontsource/redaction-10": "^5.0.2", + "@imput/libav.js-remux-cli": "^5.5.6", + "@imput/version-info": "workspace:^", "@sveltejs/adapter-static": "^3.0.2", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@tabler/icons-svelte": "3.6.0", "@types/eslint__js": "^8.42.3", "@types/fluent-ffmpeg": "^2.1.25", "@types/node": "^20.14.10", + "@vitejs/plugin-basic-ssl": "^1.1.0", "compare-versions": "^6.1.0", "dotenv": "^16.0.1", "eslint": "^8.57.0", "glob": "^10.4.5", "mdsvex": "^0.11.2", + "mime": "^4.0.4", "svelte": "^4.2.7", "svelte-check": "^3.6.0", "svelte-preprocess": "^6.0.2", + "sveltekit-i18n": "^2.4.2", + "ts-deepmerge": "^7.0.1", "tslib": "^2.4.1", "turnstile-types": "^1.2.2", "typescript": "^5.4.5", "typescript-eslint": "^7.13.1", "vite": "^5.0.3" - }, - "dependencies": { - "@fontsource-variable/noto-sans-mono": "^5.0.20", - "@fontsource/ibm-plex-mono": "^5.0.13", - "@imput/libav.js-remux-cli": "^5.5.6", - "@imput/version-info": "workspace:^", - "@tabler/icons-svelte": "3.6.0", - "@vitejs/plugin-basic-ssl": "^1.1.0", - "mime": "^4.0.4", - "sveltekit-i18n": "^2.4.2", - "ts-deepmerge": "^7.0.0" } } From ce054e63fc0fbb2d11e3f52b43024952a9aaf361 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Sep 2024 20:23:29 +0600 Subject: [PATCH 46/79] web/settings: improve settings section ids --- web/src/routes/settings/audio/+page.svelte | 5 ++--- web/src/routes/settings/download/+page.svelte | 2 +- web/src/routes/settings/video/+page.svelte | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/web/src/routes/settings/audio/+page.svelte b/web/src/routes/settings/audio/+page.svelte index e2cda6f6..8ea9f34a 100644 --- a/web/src/routes/settings/audio/+page.svelte +++ b/web/src/routes/settings/audio/+page.svelte @@ -9,7 +9,7 @@ import SettingsToggle from "$components/buttons/SettingsToggle.svelte"; - + {#each audioFormatOptions as value} - - + {#each audioBitrateOptions as value} @@ -34,7 +34,7 @@ Date: Wed, 18 Sep 2024 20:30:35 +0600 Subject: [PATCH 47/79] web/PageNav: add fade in animation for subtitle --- web/src/components/subnav/PageNav.svelte | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/web/src/components/subnav/PageNav.svelte b/web/src/components/subnav/PageNav.svelte index c3255c78..ccb2afa1 100644 --- a/web/src/components/subnav/PageNav.svelte +++ b/web/src/components/subnav/PageNav.svelte @@ -73,7 +73,10 @@ {:else} {#if pageSubtitle} -