diff --git a/api/src/processing/services/youtube.js b/api/src/processing/services/youtube.js index cf83fbd..d1e28cd 100644 --- a/api/src/processing/services/youtube.js +++ b/api/src/processing/services/youtube.js @@ -1,10 +1,10 @@ -import { fetch } from "undici"; +import { fetch } from 'undici'; -import { Innertube, Session } from "youtubei.js"; +import { Innertube, Session } from 'youtubei.js'; -import { env } from "../../config.js"; -import { cleanString } from "../../misc/utils.js"; -import { getCookie, updateCookieValues } from "../cookie/manager.js"; +import { env } from '../../config.js'; +import { cleanString } from '../../misc/utils.js'; +import { getCookie, updateCookieValues } from '../cookie/manager.js'; const PLAYER_REFRESH_PERIOD = 1000 * 60 * 15; // ms @@ -12,30 +12,29 @@ let innertube, lastRefreshedAt; const codecMatch = { h264: { - videoCodec: "avc1", - audioCodec: "mp4a", - container: "mp4" + videoCodec: 'avc1', + audioCodec: 'mp4a', + container: 'mp4', }, av1: { - videoCodec: "av01", - audioCodec: "opus", - container: "webm" + videoCodec: 'av01', + audioCodec: 'opus', + container: 'webm', }, vp9: { - videoCodec: "vp9", - audioCodec: "opus", - container: "webm" - } -} + videoCodec: 'vp9', + audioCodec: 'opus', + container: 'webm', + }, +}; const transformSessionData = (cookie) => { - if (!cookie) - return; + if (!cookie) return; const values = { ...cookie.values() }; - const REQUIRED_VALUES = [ 'access_token', 'refresh_token' ]; + const REQUIRED_VALUES = ['access_token', 'refresh_token']; - if (REQUIRED_VALUES.some(x => typeof values[x] !== 'string')) { + if (REQUIRED_VALUES.some((x) => typeof values[x] !== 'string')) { return; } @@ -47,13 +46,14 @@ const transformSessionData = (cookie) => { } return values; -} +}; const cloneInnertube = async (customFetch) => { - const shouldRefreshPlayer = lastRefreshedAt + PLAYER_REFRESH_PERIOD < new Date(); + const shouldRefreshPlayer = + lastRefreshedAt + PLAYER_REFRESH_PERIOD < new Date(); if (!innertube || shouldRefreshPlayer) { innertube = await Innertube.create({ - fetch: customFetch + fetch: customFetch, }); lastRefreshedAt = +new Date(); } @@ -66,7 +66,7 @@ const cloneInnertube = async (customFetch) => { innertube.session.player, undefined, customFetch ?? innertube.session.http.fetch, - innertube.session.cache + innertube.session.cache, ); const cookie = getCookie('youtube_oauth'); @@ -90,142 +90,154 @@ const cloneInnertube = async (customFetch) => { updateCookieValues(cookie, { ...session.oauth.client_id, ...session.oauth.oauth2_tokens, - expiry_date: newExpiry.toISOString() + expiry_date: newExpiry.toISOString(), }); } } const yt = new Innertube(session); return yt; -} +}; -export default async function(o) { +export default async function (o) { let yt; try { - yt = await cloneInnertube( - (input, init) => fetch(input, { + yt = await cloneInnertube((input, init) => + fetch(input, { ...init, - dispatcher: o.dispatcher - }) + dispatcher: o.dispatcher, + }), ); - } catch(e) { - if (e.message?.endsWith("decipher algorithm")) { - return { error: "youtube.decipher" } - } else if (e.message?.includes("refresh access token")) { - return { error: "youtube.token_expired" } + } catch (e) { + if (e.message?.endsWith('decipher algorithm')) { + return { error: 'youtube.decipher' }; + } else if (e.message?.includes('refresh access token')) { + return { error: 'youtube.token_expired' }; } else throw e; } - const quality = o.quality === "max" ? "9000" : o.quality; + const quality = o.quality === 'max' ? '9000' : o.quality; - let info, isDubbed, - format = o.format || "h264"; + let info, + isDubbed, + format = o.format || 'h264'; function qual(i) { if (!i.quality_label) { return; } - return i.quality_label.split('p')[0].split('s')[0] + return i.quality_label.split('p')[0].split('s')[0]; } try { - info = await yt.getBasicInfo(o.id, yt.session.logged_in ? 'ANDROID' : 'IOS'); - } catch(e) { - if (e?.info?.reason === "This video is private") { - return { error: "content.video.private" }; - } else if (e?.message === "This video is unavailable") { - return { error: "content.video.unavailable" }; + info = await yt.getBasicInfo( + o.id, + yt.session.logged_in ? 'ANDROID' : 'IOS', + ); + } catch (e) { + if (e?.info?.reason === 'This video is private') { + return { error: 'content.video.private' }; + } else if (e?.message === 'This video is unavailable') { + return { error: 'content.video.unavailable' }; } else { - return { error: "fetch.fail" }; + return { error: 'fetch.fail' }; } } - if (!info) return { error: "fetch.fail" }; + if (!info) return { error: 'fetch.fail' }; const playability = info.playability_status; const basicInfo = info.basic_info; - if (playability.status === "LOGIN_REQUIRED") { - if (playability.reason.endsWith("bot")) { - return { error: "youtube.login" } + if (playability.status === 'LOGIN_REQUIRED') { + if (playability.reason.endsWith('bot')) { + return { error: 'youtube.login' }; } - if (playability.reason.endsWith("age")) { - return { error: "content.video.age" } + if (playability.reason.endsWith('age')) { + return { error: 'content.video.age' }; } - if (playability?.error_screen?.reason?.text === "Private video") { - return { error: "content.video.private" } + if (playability?.error_screen?.reason?.text === 'Private video') { + return { error: 'content.video.private' }; } } - if (playability.status === "UNPLAYABLE") { - if (playability?.reason?.endsWith("request limit.")) { - return { error: "fetch.rate" } + if (playability.status === 'UNPLAYABLE') { + if (playability?.reason?.endsWith('request limit.')) { + return { error: 'fetch.rate' }; } - if (playability?.error_screen?.subreason?.text?.endsWith("in your country")) { - return { error: "content.video.region" } + if ( + playability?.error_screen?.subreason?.text?.endsWith( + 'in your country', + ) + ) { + return { error: 'content.video.region' }; } - if (playability?.error_screen?.reason?.text === "Private video") { - return { error: "content.video.private" } + if (playability?.error_screen?.reason?.text === 'Private video') { + return { error: 'content.video.private' }; } } - if (playability.status !== "OK") { - return { error: "content.video.unavailable" }; + if (playability.status !== 'OK') { + return { error: 'content.video.unavailable' }; } if (basicInfo.is_live) { - return { error: "content.video.live" }; + return { error: 'content.video.live' }; } // return a critical error if returned video is "Video Not Available" // or a similar stub by youtube if (basicInfo.id !== o.id) { return { - error: "fetch.fail", - critical: true - } + error: 'fetch.fail', + critical: true, + }; } const filterByCodec = (formats) => formats - .filter(e => - e.mime_type.includes(codecMatch[format].videoCodec) - || e.mime_type.includes(codecMatch[format].audioCodec) - ) - .sort((a, b) => Number(b.bitrate) - Number(a.bitrate)); + .filter( + (e) => + e.mime_type.includes(codecMatch[format].videoCodec) || + e.mime_type.includes(codecMatch[format].audioCodec), + ) + .sort((a, b) => Number(b.bitrate) - Number(a.bitrate)); let adaptive_formats = filterByCodec(info.streaming_data.adaptive_formats); - if (adaptive_formats.length === 0 && format === "vp9") { - format = "h264" - adaptive_formats = filterByCodec(info.streaming_data.adaptive_formats) + if (adaptive_formats.length === 0 && format === 'vp9') { + format = 'h264'; + adaptive_formats = filterByCodec(info.streaming_data.adaptive_formats); } let bestQuality; - const bestVideo = adaptive_formats.find(i => i.has_video && i.content_length); - const hasAudio = adaptive_formats.find(i => i.has_audio && i.content_length); + const bestVideo = adaptive_formats.find( + (i) => i.has_video && i.content_length, + ); + const hasAudio = adaptive_formats.find( + (i) => i.has_audio && i.content_length, + ); if (bestVideo) bestQuality = qual(bestVideo); if ((!bestQuality && !o.isAudioOnly) || !hasAudio) - return { error: "youtube.codec" }; + return { error: 'youtube.codec' }; if (basicInfo.duration > env.durationLimit) - return { error: "content.too_long" }; + return { error: 'content.too_long' }; - const checkBestAudio = (i) => (i.has_audio && !i.has_video); + const checkBestAudio = (i) => i.has_audio && !i.has_video; - let audio = adaptive_formats.find(i => - checkBestAudio(i) && i.is_original + let audio = adaptive_formats.find( + (i) => checkBestAudio(i) && i.is_original, ); if (o.dubLang) { - let dubbedAudio = adaptive_formats.find(i => - checkBestAudio(i) - && i.language === o.dubLang - && i.audio_track - ) + let dubbedAudio = adaptive_formats.find( + (i) => + checkBestAudio(i) && i.language === o.dubLang && i.audio_track, + ); if (dubbedAudio && !dubbedAudio?.audio_track?.audio_is_default) { audio = dubbedAudio; @@ -234,52 +246,62 @@ export default async function(o) { } if (!audio) { - audio = adaptive_formats.find(i => checkBestAudio(i)); + audio = adaptive_formats.find((i) => checkBestAudio(i)); } let fileMetadata = { title: cleanString(basicInfo.title.trim()), - artist: cleanString(basicInfo.author.replace("- Topic", "").trim()), - } + artist: cleanString(basicInfo.author.replace('- Topic', '').trim()), + }; - if (basicInfo?.short_description?.startsWith("Provided to YouTube by")) { - let descItems = basicInfo.short_description.split("\n\n"); + if (basicInfo?.short_description?.startsWith('Provided to YouTube by')) { + let descItems = basicInfo.short_description.split('\n\n'); fileMetadata.album = descItems[2]; fileMetadata.copyright = descItems[3]; - if (descItems[4].startsWith("Released on:")) { - fileMetadata.date = descItems[4].replace("Released on: ", '').trim() + if (descItems[4].startsWith('Released on:')) { + fileMetadata.date = descItems[4] + .replace('Released on: ', '') + .trim(); } } let filenameAttributes = { - service: "youtube", + service: 'youtube', id: o.id, title: fileMetadata.title, author: fileMetadata.artist, - youtubeDubName: isDubbed ? o.dubLang : false - } + youtubeDubName: isDubbed ? o.dubLang : false, + }; - if (audio && o.isAudioOnly) return { - type: "audio", - isAudioOnly: true, - urls: audio.decipher(yt.session.player), - filenameAttributes: filenameAttributes, - fileMetadata: fileMetadata, - bestAudio: format === "h264" ? "m4a" : "opus" - } - - const matchingQuality = Number(quality) > Number(bestQuality) ? bestQuality : quality, - checkSingle = i => - qual(i) === matchingQuality && i.mime_type.includes(codecMatch[format].videoCodec), - checkRender = i => + if (audio && o.isAudioOnly) + return { + type: 'audio', + isAudioOnly: true, + urls: audio.decipher(yt.session.player), + filenameAttributes: filenameAttributes, + fileMetadata: fileMetadata, + bestAudio: format === 'h264' ? 'm4a' : 'opus', + }; + + const matchingQuality = + Number(quality) > Number(bestQuality) ? bestQuality : quality, + checkSingle = (i) => + qual(i) === matchingQuality && + i.mime_type.includes(codecMatch[format].videoCodec), + checkRender = (i) => qual(i) === matchingQuality && i.has_video && !i.has_audio; let match, type, urls; // prefer good premuxed videos if available - if (!o.isAudioOnly && !o.isAudioMuted && format === "h264" && bestVideo.fps <= 30) { + if ( + !o.isAudioOnly && + !o.isAudioMuted && + format === 'h264' && + bestVideo.fps <= 30 + ) { match = info.streaming_data.formats.find(checkSingle); - type = "proxy"; + type = 'proxy'; urls = match?.decipher(yt.session.player); } @@ -287,11 +309,11 @@ export default async function(o) { if (!match && video && audio) { match = video; - type = "merge"; + type = 'merge'; urls = [ video.decipher(yt.session.player), - audio.decipher(yt.session.player) - ] + audio.decipher(yt.session.player), + ]; } if (match) { @@ -303,9 +325,9 @@ export default async function(o) { type, urls, filenameAttributes, - fileMetadata - } + fileMetadata, + }; } - return { error: "fetch.fail" } + return { error: 'fetch.fail' }; } diff --git a/config.json b/config.json new file mode 100644 index 0000000..e5b36f2 --- /dev/null +++ b/config.json @@ -0,0 +1,16 @@ +{ + "apps": [ + { + "name": "downloader-api", + "cwd": "api", + "script": "node", + "args": "src/thvideodl.js", + "env": { + "API_URL": "https://dl.hep.gg/", + "API_PORT": 9000, + "API_NAME": "Team Hydra Downloader", + "COOKIE_PATH": "cookies.json" + } + } + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d31627..2f9dedb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,6 +119,9 @@ importers: '@fontsource/redaction-10': specifier: ^5.0.2 version: 5.0.2 + '@sveltejs/adapter-node': + specifier: ^5.2.2 + version: 5.2.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))) '@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))) @@ -564,6 +567,42 @@ packages: '@polka/url@1.0.0-next.25': resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + '@rollup/plugin-commonjs@26.0.1': + resolution: {integrity: sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@15.2.3': + resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.0': + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.19.2': resolution: {integrity: sha512-OHflWINKtoCFSpm/WmuQaWW4jeX+3Qt3XQDepkkiFTsoxFc5BpF3Z5aDxFZgBqRjO6ATP5+b1iilp4kGIZVWlA==} cpu: [arm] @@ -644,6 +683,11 @@ packages: cpu: [x64] os: [win32] + '@sveltejs/adapter-node@5.2.2': + resolution: {integrity: sha512-BCX4zP0cf86TXpmvLQTnnT/tp7P12UMezf+5LwljP1MJC1fFzn9XOXpAHQCyP+pyHGy2K7p5gY0LyLcZFAL02w==} + peerDependencies: + '@sveltejs/kit': ^2.4.0 + '@sveltejs/adapter-static@3.0.2': resolution: {integrity: sha512-/EBFydZDwfwFfFEuF1vzUseBoRziwKP7AoHAwv+Ot3M084sE/HTVBHf9mCmXfdM9ijprY5YEugZjleflncX5fQ==} peerDependencies: @@ -716,6 +760,9 @@ packages: '@types/pug@2.0.10': resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/unist@2.0.10': resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} @@ -875,6 +922,10 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + bundle-require@5.0.0: resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -926,6 +977,9 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} @@ -1234,6 +1288,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -1450,6 +1507,14 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} + is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1462,6 +1527,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1470,6 +1538,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.2: resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} @@ -1724,6 +1795,9 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} @@ -1833,6 +1907,10 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1974,6 +2052,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + svelte-check@3.8.5: resolution: {integrity: sha512-3OGGgr9+bJ/+1nbPgsvulkLC48xBsqsgtc8Wam281H4G9F5v3mYGa2bHRsPuwHC5brKl4AxJH95QF73kmfihGQ==} hasBin: true @@ -2527,6 +2609,42 @@ snapshots: '@polka/url@1.0.0-next.25': {} + '@rollup/plugin-commonjs@26.0.1(rollup@4.19.2)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.19.2) + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 10.4.5 + is-reference: 1.2.1 + magic-string: 0.30.11 + optionalDependencies: + rollup: 4.19.2 + + '@rollup/plugin-json@6.1.0(rollup@4.19.2)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.19.2) + optionalDependencies: + rollup: 4.19.2 + + '@rollup/plugin-node-resolve@15.2.3(rollup@4.19.2)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.19.2) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-builtin-module: 3.2.1 + is-module: 1.0.0 + resolve: 1.22.8 + optionalDependencies: + rollup: 4.19.2 + + '@rollup/pluginutils@5.1.0(rollup@4.19.2)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.19.2 + '@rollup/rollup-android-arm-eabi@4.19.2': optional: true @@ -2575,6 +2693,14 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.19.2': optional: true + '@sveltejs/adapter-node@5.2.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)))': + dependencies: + '@rollup/plugin-commonjs': 26.0.1(rollup@4.19.2) + '@rollup/plugin-json': 6.1.0(rollup@4.19.2) + '@rollup/plugin-node-resolve': 15.2.3(rollup@4.19.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)) + rollup: 4.19.2 + '@sveltejs/adapter-static@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)))': dependencies: '@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)) @@ -2660,6 +2786,8 @@ snapshots: '@types/pug@2.0.10': {} + '@types/resolve@1.20.2': {} + '@types/unist@2.0.10': {} '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)': @@ -2840,6 +2968,8 @@ snapshots: buffer-from@1.1.2: {} + builtin-modules@3.3.0: {} + bundle-require@5.0.0(esbuild@0.23.0): dependencies: esbuild: 0.23.0 @@ -2896,6 +3026,8 @@ snapshots: commander@4.1.1: {} + commondir@1.0.1: {} + compare-versions@6.1.1: {} concat-map@0.0.1: {} @@ -3204,6 +3336,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.5 @@ -3475,6 +3609,14 @@ snapshots: dependencies: binary-extensions: 2.3.0 + is-builtin-module@3.2.1: + dependencies: + builtin-modules: 3.3.0 + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -3483,10 +3625,16 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-module@1.0.0: {} + is-number@7.0.0: {} is-path-inside@3.0.3: {} + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.5 + is-reference@3.0.2: dependencies: '@types/estree': 1.0.5 @@ -3688,6 +3836,8 @@ snapshots: path-key@3.1.1: {} + path-parse@1.0.7: {} + path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 @@ -3769,6 +3919,12 @@ snapshots: resolve-from@5.0.0: {} + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + reusify@1.0.4: {} rimraf@2.7.1: @@ -3948,6 +4104,8 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + svelte-check@3.8.5(postcss@8.4.40)(svelte@4.2.18): dependencies: '@jridgewell/trace-mapping': 0.3.25 diff --git a/web/changelogs/1.0.md b/web/changelogs/1.0.md new file mode 100644 index 0000000..accf407 --- /dev/null +++ b/web/changelogs/1.0.md @@ -0,0 +1,6 @@ +--- +title: 'Team Hydra Video Downloader 1.0' +date: 'September 16th, 2024' +--- + +This is the first release of the Team Hydra Video Downloader. diff --git a/web/package.json b/web/package.json index b53f3ac..db0fd0b 100644 --- a/web/package.json +++ b/web/package.json @@ -22,6 +22,7 @@ "devDependencies": { "@eslint/js": "^9.5.0", "@fontsource/redaction-10": "^5.0.2", + "@sveltejs/adapter-node": "^5.2.2", "@sveltejs/adapter-static": "^3.0.2", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", diff --git a/web/src/app.html b/web/src/app.html index b60acb3..2c877be 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -1,35 +1,46 @@ - + - - - - - + + + + + %sveltekit.head% - - - + + + - - + + - - + + - - - -
- %sveltekit.body% -
+ +
%sveltekit.body%
diff --git a/web/src/components/donate/DonateBanner.svelte b/web/src/components/donate/DonateBanner.svelte index b9b4a95..6b17684 100644 --- a/web/src/components/donate/DonateBanner.svelte +++ b/web/src/components/donate/DonateBanner.svelte @@ -1,20 +1,17 @@ @@ -94,7 +93,6 @@ } #banner-right { - transform: translate(12px, 44px); display: flex; align-items: center; position: absolute; @@ -102,15 +100,6 @@ bottom: 0; } - #imput-logo { - display: flex; - } - - #imput-logo :global(svg) { - width: 48px; - height: 42px; - } - #banner-left { display: flex; flex-direction: column; @@ -131,7 +120,7 @@ } #banner-title.redaction { - font-family: "Redaction 10", serif; + font-family: 'Redaction 10', serif; font-smooth: never; -webkit-font-smoothing: none; } @@ -141,6 +130,8 @@ opacity: 0.4; line-height: 1.5; font-size: 16px; + /* No line wrapping */ + white-space: normal; } #banner-background-animation { diff --git a/web/src/components/donate/DonateOptionsCard.svelte b/web/src/components/donate/DonateOptionsCard.svelte index 66bfe02..236ab18 100644 --- a/web/src/components/donate/DonateOptionsCard.svelte +++ b/web/src/components/donate/DonateOptionsCard.svelte @@ -1,26 +1,26 @@ @@ -75,44 +78,19 @@ - - -
- {#each Object.entries(PRESET_DONATION_AMOUNTS) as [ amount, component ]} - - - - - - {/each}
@@ -126,26 +104,26 @@ max="10000" step=".01" required - placeholder={$t("donate.card.custom")} + placeholder={$t('donate.card.custom')} bind:this={customInput} bind:value={customInputValue} on:input={() => (customFocused = true)} on:focus={() => (customFocused = true)} on:blur={() => (customFocused = false)} - on:keydown={(e) => e.key === "Enter" && sendCustom()} + on:keydown={(e) => e.key === 'Enter' && sendCustom()} />
diff --git a/web/src/components/misc/Meowbalt.svelte b/web/src/components/misc/Meowbalt.svelte index 26ad14b..7c5905e 100644 --- a/web/src/components/misc/Meowbalt.svelte +++ b/web/src/components/misc/Meowbalt.svelte @@ -1,35 +1,24 @@ diff --git a/web/src/components/save/SupportedServices.svelte b/web/src/components/save/SupportedServices.svelte index 263e067..c7ecbbf 100644 --- a/web/src/components/save/SupportedServices.svelte +++ b/web/src/components/save/SupportedServices.svelte @@ -1,9 +1,9 @@ @@ -30,12 +32,12 @@ await loadInfo(); } }} - aria-label={$t(`save.services.title_${expanded ? "hide" : "show"}`)} + aria-label={$t(`save.services.title_${expanded ? 'hide' : 'show'}`)} >
- {$t("save.services.title")} + {$t('save.services.title')}
@@ -48,14 +50,14 @@ {#each { length: 17 } as _} {/each} {/if}
- {$t("save.services.disclaimer")} + {$t('save.services.disclaimer')}
diff --git a/web/src/components/save/buttons/DownloadButton.svelte b/web/src/components/save/buttons/DownloadButton.svelte index 08800e8..aab85b0 100644 --- a/web/src/components/save/buttons/DownloadButton.svelte +++ b/web/src/components/save/buttons/DownloadButton.svelte @@ -60,6 +60,7 @@ export const download = async (link: string) => { changeDownloadButton("think"); + console.log("Downloading", link); const response = await API.request(link); diff --git a/web/src/components/sidebar/CobaltLogo.svelte b/web/src/components/sidebar/CobaltLogo.svelte index 25ef81b..e2c7918 100644 --- a/web/src/components/sidebar/CobaltLogo.svelte +++ b/web/src/components/sidebar/CobaltLogo.svelte @@ -3,7 +3,7 @@ diff --git a/web/src/routes/about/credits/+page.svelte b/web/src/routes/about/credits/+page.svelte deleted file mode 100644 index a914827..0000000 --- a/web/src/routes/about/credits/+page.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - -
-
-

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. - 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. -

-

- cobalt frontend is - source first - and licensed under - CC-BY-NC-SA 4.0. - we decided to use this license to stop grifters from profiting off our work & from creating malicious clones that deceive people and hurt our public identity. -

-

- we rely on many open source libraries, create & distribute our own. - you can see the full list of dependencies on github. -

-
-
diff --git a/web/src/routes/about/general/+page.svelte b/web/src/routes/about/general/+page.svelte deleted file mode 100644 index a7ecacd..0000000 --- a/web/src/routes/about/general/+page.svelte +++ /dev/null @@ -1,87 +0,0 @@ - - -
-
-

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. -

-

- to avoid caching or storing downloaded files, cobalt processes them on-fly, sending processed pieces directly to client. - this technology is used when your request needs additional processing, such as when source service stores video & audio in separate files. -

-

- for even higher level of protection, you can ask cobalt to always tunnel everything. - 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. -

-

- main processing instances are hosted on several dedicated servers in several countries, - to reduce latency and distribute the traffic. -

-

- we constantly improve our infrastructure along with our long-standing partner, - royalehosting.net! - 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! -

-

- we believe that the future of the internet is open, - which is why cobalt is - - source first - - and - - easily self-hostable. - - - you can check the source code & contribute to cobalt - 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. -

-
- -
-

on-device processing

-

- new features, such as remuxing, 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/src/routes/about/privacy/+page.svelte b/web/src/routes/about/privacy/+page.svelte deleted file mode 100644 index 28bdba5..0000000 --- a/web/src/routes/about/privacy/+page.svelte +++ /dev/null @@ -1,81 +0,0 @@ - - -
-
-

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 - 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. -

- -

- - {$t("settings.privacy.analytics.learnmore")} - -

- -

- 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. - -

-
-
diff --git a/web/src/routes/about/terms/+page.svelte b/web/src/routes/about/terms/+page.svelte deleted file mode 100644 index 016e453..0000000 --- a/web/src/routes/about/terms/+page.svelte +++ /dev/null @@ -1,37 +0,0 @@ -
-
-
-

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. -

-
- -
-

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 -

-
-
diff --git a/web/src/routes/donate/+page.svelte b/web/src/routes/donate/+page.svelte index beb4b85..a8ce79c 100644 --- a/web/src/routes/donate/+page.svelte +++ b/web/src/routes/donate/+page.svelte @@ -14,11 +14,11 @@ - {$t("tabs.donate")} ~ {$t("general.cobalt")} + Donate ~ THVDL @@ -28,27 +28,6 @@
- -
- -
-

{$t("donate.body.motivation")}

-

{$t("donate.body.keep_going")}

-
- -
-
- -

{$t("donate.alternative.title")}

-
-
- {#each Object.entries(donate.crypto) as [name, address]} - - {/each} - {#each Object.entries(donate.other) as [name, address]} - - {/each} -
diff --git a/web/src/routes/remux/+page.svelte b/web/src/routes/remux/+page.svelte index 21c5bbe..cff1c0a 100644 --- a/web/src/routes/remux/+page.svelte +++ b/web/src/routes/remux/+page.svelte @@ -195,10 +195,10 @@ - {$t("tabs.remux")} ~ {$t("general.cobalt")} + Remux - THVDL diff --git a/web/src/routes/settings/+layout.svelte b/web/src/routes/settings/+layout.svelte index cbcd528..42fff99 100644 --- a/web/src/routes/settings/+layout.svelte +++ b/web/src/routes/settings/+layout.svelte @@ -1,101 +1,70 @@ - - - - - - - - - - - - {#if $settings.advanced.debug} - - - - {/if} - diff --git a/web/src/routes/settings/appearance/+page.svelte b/web/src/routes/settings/appearance/+page.svelte index b1067be..4bcfc0c 100644 --- a/web/src/routes/settings/appearance/+page.svelte +++ b/web/src/routes/settings/appearance/+page.svelte @@ -1,17 +1,17 @@ - - + + {#each themeOptions as value} - - - - - - diff --git a/web/src/routes/updates/+page.svelte b/web/src/routes/updates/+page.svelte index 4786af3..24d5ede 100644 --- a/web/src/routes/updates/+page.svelte +++ b/web/src/routes/updates/+page.svelte @@ -1,16 +1,16 @@ - - {$t("tabs.updates")} ~ {$t("general.cobalt")} - - + Updates ~ THVDL + @@ -96,20 +91,17 @@ {#if prev} {/if}
{#await changelog.page} {#key changelog.version} - + {/key} {:then page} - {prev || ""} + {prev || ''} {/if} {#if next} @@ -134,9 +128,9 @@ on:click={loadNext} on:focus={preloadNext} on:mousemove={preloadNext} - aria-label={$t("updates.button.next", { value: next })} + aria-label={$t('updates.button.next', { value: next })} > - {next || ""} + {next || ''} {/if} @@ -148,9 +142,9 @@ on:click={loadNext} on:focus={preloadNext} on:mousemove={preloadNext} - aria-label={$t("updates.button.next", { value: next })} + aria-label={$t('updates.button.next', { value: next })} > - {next || ""} + {next || ''} {/if} diff --git a/web/static/favicon.png b/web/static/favicon.png index 4bb87e5..2c1cb87 100644 Binary files a/web/static/favicon.png and b/web/static/favicon.png differ diff --git a/web/static/logos/hydralogo.png b/web/static/logos/hydralogo.png new file mode 100644 index 0000000..2c1cb87 Binary files /dev/null and b/web/static/logos/hydralogo.png differ diff --git a/web/static/manifest.json b/web/static/manifest.json deleted file mode 100644 index ea35be2..0000000 --- a/web/static/manifest.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "name": "cobalt", - "short_name": "cobalt", - "start_url": "/", - "icons": [ - { - "src": "/icons/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/icons/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "/icons/generic.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "any" - }, - { - "src": "/icons/maskable/48.png", - "sizes": "48x48", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "/icons/maskable/72.png", - "sizes": "72x72", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "/icons/maskable/96.png", - "sizes": "96x96", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "/icons/maskable/128.png", - "sizes": "128x128", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "/icons/maskable/192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "/icons/maskable/384.png", - "sizes": "384x384", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "/icons/maskable/512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ], - "share_target": { - "method": "GET", - "action": "/", - "enctype": "application/x-www-form-urlencoded", - "params": { - "text": "u", - "url": "u" - } - }, - "theme_color": "#000000", - "background_color": "#000000", - "display": "standalone" -} diff --git a/web/static/meowbalt/error.png b/web/static/meowbalt/error.png deleted file mode 100644 index f20c1d6..0000000 Binary files a/web/static/meowbalt/error.png and /dev/null differ diff --git a/web/static/meowbalt/fast.png b/web/static/meowbalt/fast.png deleted file mode 100644 index 94d0384..0000000 Binary files a/web/static/meowbalt/fast.png and /dev/null differ diff --git a/web/static/meowbalt/question.png b/web/static/meowbalt/question.png deleted file mode 100644 index 0d6a51c..0000000 Binary files a/web/static/meowbalt/question.png and /dev/null differ diff --git a/web/static/meowbalt/smile.png b/web/static/meowbalt/smile.png deleted file mode 100644 index cd59da3..0000000 Binary files a/web/static/meowbalt/smile.png and /dev/null differ diff --git a/web/static/meowbalt/think.png b/web/static/meowbalt/think.png deleted file mode 100644 index 2b06f0c..0000000 Binary files a/web/static/meowbalt/think.png and /dev/null differ diff --git a/web/svelte.config.js b/web/svelte.config.js index b86956e..c8e6281 100644 --- a/web/svelte.config.js +++ b/web/svelte.config.js @@ -8,27 +8,27 @@ import { sveltePreprocess } from 'svelte-preprocess'; const config = { // Consult https://kit.svelte.dev/docs/integrations#preprocessors // for more information about preprocessors - extensions: [".svelte", ".md"], + extensions: ['.svelte', '.md'], preprocess: [ { - name: "strip-announcer", + name: 'strip-announcer', markup: ({ content: code }) => { code = code.replace( /
/, - '' + '', ); - return { code } - } + return { code }; + }, }, sveltePreprocess(), mdsvex({ extensions: ['.md'], layout: join( dirname(fileURLToPath(import.meta.url)), - '/src/components/changelog/ChangelogEntryWrapper.svelte' - ) - }) + '/src/components/changelog/ChangelogEntryWrapper.svelte', + ), + }), ], kit: { adapter: adapter({ @@ -38,22 +38,22 @@ const config = { assets: 'build', fallback: '404.html', precompress: false, - strict: true + strict: true, }), env: { - publicPrefix: 'WEB_' + publicPrefix: 'WEB_', }, version: { - pollInterval: 60000 + pollInterval: 60000, }, paths: { - relative: false + relative: false, }, alias: { $components: 'src/components', $i18n: 'i18n', - } - } + }, + }, }; export default config;