mirror of
https://github.com/imputnet/cobalt.git
synced 2025-07-18 19:28:29 +00:00
Merge branch 'main' into main
This commit is contained in:
commit
d336e5bd2e
@ -105,6 +105,18 @@ export const runAPI = (express, app, __dirname) => {
|
|||||||
app.post('/', apiLimiter);
|
app.post('/', apiLimiter);
|
||||||
app.use('/tunnel', apiLimiterStream);
|
app.use('/tunnel', apiLimiterStream);
|
||||||
|
|
||||||
|
app.post('/', (req, res, next) => {
|
||||||
|
if (!acceptRegex.test(req.header('Accept'))) {
|
||||||
|
return fail(res, "error.api.header.accept");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!acceptRegex.test(req.header('Content-Type'))) {
|
||||||
|
return fail(res, "error.api.header.content_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/', (req, res, next) => {
|
app.post('/', (req, res, next) => {
|
||||||
if (!env.turnstileSecret || !env.jwtSecret) {
|
if (!env.turnstileSecret || !env.jwtSecret) {
|
||||||
return next();
|
return next();
|
||||||
@ -128,14 +140,6 @@ export const runAPI = (express, app, __dirname) => {
|
|||||||
return fail(res, "error.api.auth.jwt.invalid");
|
return fail(res, "error.api.auth.jwt.invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!acceptRegex.test(req.header('Accept'))) {
|
|
||||||
return fail(res, "error.api.header.accept");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!acceptRegex.test(req.header('Content-Type'))) {
|
|
||||||
return fail(res, "error.api.header.content_type");
|
|
||||||
}
|
|
||||||
|
|
||||||
req.authorized = true;
|
req.authorized = true;
|
||||||
} catch {
|
} catch {
|
||||||
return fail(res, "error.api.generic");
|
return fail(res, "error.api.generic");
|
||||||
|
@ -65,3 +65,14 @@ export function merge(a, b) {
|
|||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function splitFilenameExtension(filename) {
|
||||||
|
const parts = filename.split('.');
|
||||||
|
const ext = parts.pop();
|
||||||
|
|
||||||
|
if (!parts.length) {
|
||||||
|
return [ ext, "" ]
|
||||||
|
} else {
|
||||||
|
return [ parts.join('.'), ext ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ import createFilename from "./create-filename.js";
|
|||||||
import { createResponse } from "./request.js";
|
import { createResponse } from "./request.js";
|
||||||
import { audioIgnore } from "./service-config.js";
|
import { audioIgnore } from "./service-config.js";
|
||||||
import { createStream } from "../stream/manage.js";
|
import { createStream } from "../stream/manage.js";
|
||||||
|
import { splitFilenameExtension } from "../misc/utils.js";
|
||||||
|
|
||||||
export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disableMetadata, filenameStyle, twitterGif, requestIP, audioBitrate, alwaysProxy }) {
|
export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disableMetadata, filenameStyle, twitterGif, requestIP, audioBitrate, alwaysProxy }) {
|
||||||
let action,
|
let action,
|
||||||
@ -32,10 +33,11 @@ export default function({ r, host, audioFormat, isAudioOnly, isAudioMuted, disab
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action === "muteVideo" && isAudioMuted && !r.filenameAttributes) {
|
if (action === "muteVideo" && isAudioMuted && !r.filenameAttributes) {
|
||||||
const parts = r.filename.split(".");
|
const [ name, ext ] = splitFilenameExtension(r.filename);
|
||||||
const ext = parts.pop();
|
defaultParams.filename = `${name}_mute.${ext}`;
|
||||||
|
} else if (action === "gif") {
|
||||||
defaultParams.filename = `${parts.join(".")}_mute.${ext}`;
|
const [ name ] = splitFilenameExtension(r.filename);
|
||||||
|
defaultParams.filename = `${name}.gif`;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
@ -176,7 +176,7 @@ export const services = {
|
|||||||
Object.values(services).forEach(service => {
|
Object.values(services).forEach(service => {
|
||||||
service.patterns = service.patterns.map(
|
service.patterns = service.patterns.map(
|
||||||
pattern => new UrlPattern(pattern, {
|
pattern => new UrlPattern(pattern, {
|
||||||
segmentValueCharset: UrlPattern.defaultOptions.segmentValueCharset + '@\\.'
|
segmentValueCharset: UrlPattern.defaultOptions.segmentValueCharset + '@\\.:'
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -291,7 +291,7 @@ const convertGif = (streamInfo, res) => {
|
|||||||
const [,,, muxOutput] = process.stdio;
|
const [,,, muxOutput] = process.stdio;
|
||||||
|
|
||||||
res.setHeader('Connection', 'keep-alive');
|
res.setHeader('Connection', 'keep-alive');
|
||||||
res.setHeader('Content-Disposition', contentDisposition(streamInfo.filename.split('.')[0] + ".gif"));
|
res.setHeader('Content-Disposition', contentDisposition(streamInfo.filename));
|
||||||
|
|
||||||
pipe(muxOutput, res, shutdown);
|
pipe(muxOutput, res, shutdown);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { existsSync, unlinkSync, appendFileSync } from "fs";
|
import { existsSync, unlinkSync, appendFileSync } from "fs";
|
||||||
import { createInterface } from "readline";
|
import { createInterface } from "readline";
|
||||||
import { Cyan, Bright } from "./misc/console-text.js";
|
import { Cyan, Bright } from "../misc/console-text.js";
|
||||||
import { loadJSON } from "./misc/load-from-fs.js";
|
import { loadJSON } from "../misc/load-from-fs.js";
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
const { version } = loadJSON("./package.json");
|
const { version } = loadJSON("./package.json");
|
||||||
|
@ -1401,7 +1401,7 @@
|
|||||||
"bsky": [
|
"bsky": [
|
||||||
{
|
{
|
||||||
"name": "horizontal video",
|
"name": "horizontal video",
|
||||||
"url": "https://bsky.app/profile/haileyok.com/post/3l3giwtwp222m",
|
"url": "https://bsky.app/profile/did:plc:oisofpd7lj26yvgiivf3lxsi/post/3l3giwtwp222m",
|
||||||
"params": {},
|
"params": {},
|
||||||
"expected": {
|
"expected": {
|
||||||
"code": 200,
|
"code": 200,
|
||||||
@ -1410,7 +1410,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "horizontal video, recordWithMedia",
|
"name": "horizontal video, recordWithMedia",
|
||||||
"url": "https://bsky.app/profile/juicysteak117.gay/post/3l3wonhk23g2i",
|
"url": "https://bsky.app/profile/did:plc:ywbm3iywnhzep3ckt6efhoh7/post/3l3wonhk23g2i",
|
||||||
"params": {},
|
"params": {},
|
||||||
"expected": {
|
"expected": {
|
||||||
"code": 200,
|
"code": 200,
|
||||||
@ -1419,7 +1419,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vertical video",
|
"name": "vertical video",
|
||||||
"url": "https://bsky.app/profile/haileyok.com/post/3l3jhpomhjk2m",
|
"url": "https://bsky.app/profile/did:plc:oisofpd7lj26yvgiivf3lxsi/post/3l3jhpomhjk2m",
|
||||||
"params": {},
|
"params": {},
|
||||||
"expected": {
|
"expected": {
|
||||||
"code": 200,
|
"code": 200,
|
||||||
@ -1428,7 +1428,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vertical video (muted)",
|
"name": "vertical video (muted)",
|
||||||
"url": "https://bsky.app/profile/haileyok.com/post/3l3jhpomhjk2m",
|
"url": "https://bsky.app/profile/did:plc:oisofpd7lj26yvgiivf3lxsi/post/3l3jhpomhjk2m",
|
||||||
"params": {
|
"params": {
|
||||||
"downloadMode": "mute"
|
"downloadMode": "mute"
|
||||||
},
|
},
|
||||||
@ -1439,7 +1439,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vertical video (audio)",
|
"name": "vertical video (audio)",
|
||||||
"url": "https://bsky.app/profile/haileyok.com/post/3l3jhpomhjk2m",
|
"url": "https://bsky.app/profile/did:plc:oisofpd7lj26yvgiivf3lxsi/post/3l3jhpomhjk2m",
|
||||||
"params": {
|
"params": {
|
||||||
"downloadMode": "audio"
|
"downloadMode": "audio"
|
||||||
},
|
},
|
||||||
@ -1450,7 +1450,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "single image",
|
"name": "single image",
|
||||||
"url": "https://bsky.app/profile/thehardyboycats.bsky.social/post/3l33flpoygt26",
|
"url": "https://bsky.app/profile/did:plc:k4a7d65fcyevbrnntjxh57go/post/3l33flpoygt26",
|
||||||
"params": {},
|
"params": {},
|
||||||
"expected": {
|
"expected": {
|
||||||
"code": 200,
|
"code": 200,
|
||||||
@ -1459,7 +1459,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "several images",
|
"name": "several images",
|
||||||
"url": "https://bsky.app/profile/tracey-m.bsky.social/post/3kzxuxbiul626",
|
"url": "https://bsky.app/profile/did:plc:rai7s6su2sy22ss7skouedl7/post/3kzxuxbiul626",
|
||||||
"params": {},
|
"params": {},
|
||||||
"expected": {
|
"expected": {
|
||||||
"code": 200,
|
"code": 200,
|
||||||
@ -1467,8 +1467,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "deleted post",
|
"name": "deleted post/invalid user",
|
||||||
"url": "https://bsky.app/profile/samuel.bsky.team/post/3l2udah76ch2c",
|
"url": "https://bsky.app/profile/notreal.bsky.team/post/3l2udah76ch2c",
|
||||||
"params": {},
|
"params": {},
|
||||||
"expected": {
|
"expected": {
|
||||||
"code": 400,
|
"code": 400,
|
||||||
|
1
packages/api-client/.gitignore
vendored
Normal file
1
packages/api-client/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
dist
|
@ -47,7 +47,7 @@ and for nerds, we have a giant list of backend changes (that we are also excited
|
|||||||
|
|
||||||
this update allows us to actually innovate and develop new & exciting features. we are no longer held back by the legacy codebase. first feature of such kind is on-device remuxing. go check it out!
|
this update allows us to actually innovate and develop new & exciting features. we are no longer held back by the legacy codebase. first feature of such kind is on-device remuxing. go check it out!
|
||||||
|
|
||||||
oh yeah, we now have 2.5 million monthly users. kind of insane.
|
oh yeah, we now have over 2 million monthly users. kind of insane.
|
||||||
|
|
||||||
we hope you enjoy this update as much as we enjoyed making it. it was a really fun summer project for both of us.
|
we hope you enjoy this update as much as we enjoyed making it. it was a really fun summer project for both of us.
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
"metadata.filename.basic": "basic",
|
"metadata.filename.basic": "basic",
|
||||||
"metadata.filename.pretty": "pretty",
|
"metadata.filename.pretty": "pretty",
|
||||||
"metadata.filename.nerdy": "nerdy",
|
"metadata.filename.nerdy": "nerdy",
|
||||||
"metadata.filename.description": "filename style will only be used for files tunnelled by cobalt. some services don't support filename styles other than classic.",
|
"metadata.filename.description": "filename style will only be used for files tunneled by cobalt. some services don't support filename styles other than classic.",
|
||||||
|
|
||||||
"metadata.filename.preview.video": "Video Title",
|
"metadata.filename.preview.video": "Video Title",
|
||||||
"metadata.filename.preview.audio": "Audio Title - Audio Author",
|
"metadata.filename.preview.audio": "Audio Title - Audio Author",
|
||||||
@ -88,17 +88,17 @@
|
|||||||
"accessibility.motion.description": "disables animations and transitions whenever possible.",
|
"accessibility.motion.description": "disables animations and transitions whenever possible.",
|
||||||
|
|
||||||
"language": "language",
|
"language": "language",
|
||||||
"language.auto.title": "use default browser language",
|
"language.auto.title": "automatic selection",
|
||||||
"language.auto.description": "automatically picks the best language for you. if preferred browser language isn't available, english is used instead.",
|
"language.auto.description": "cobalt will use your browser's default language if translation is available. if not, english will be used instead.",
|
||||||
"language.preferred.title": "preferred language",
|
"language.preferred.title": "preferred language",
|
||||||
"language.preferred.description": "if any text isn’t translated to the preferred language, it will fall back to english.",
|
"language.preferred.description": "this language will be used when automatic selection is disabled. any text that isn't translated will be displayed in english.",
|
||||||
|
|
||||||
"privacy.analytics": "anonymous traffic analytics",
|
"privacy.analytics": "anonymous traffic analytics",
|
||||||
"privacy.analytics.title": "don't contribute to analytics",
|
"privacy.analytics.title": "don't contribute to analytics",
|
||||||
"privacy.analytics.description": "anonymous traffic analytics are needed to get an approximate number of active cobalt users. no identifiable information about you is ever stored. all processed data is anonymized and aggregated.\n\nwe use a self-hosted plausible instance that doesn't use cookies and is fully compliant with GDPR, CCPA, and PECR.",
|
"privacy.analytics.description": "anonymous traffic analytics are needed to get an approximate number of active cobalt users. no identifiable information about you is ever stored. all processed data is anonymized and aggregated.\n\nwe use a self-hosted plausible instance that doesn't use cookies and is fully compliant with GDPR, CCPA, and PECR.",
|
||||||
"privacy.analytics.learnmore": "learn more about plausible's dedication to privacy.",
|
"privacy.analytics.learnmore": "learn more about plausible's dedication to privacy.",
|
||||||
|
|
||||||
"privacy.tunnel": "tunnelling",
|
"privacy.tunnel": "tunneling",
|
||||||
"privacy.tunnel.title": "always tunnel files",
|
"privacy.tunnel.title": "always tunnel files",
|
||||||
"privacy.tunnel.description": "cobalt will hide your ip address, browser info, and bypass local network restrictions. when enabled, files will also have readable filenames that otherwise would be gibberish.",
|
"privacy.tunnel.description": "cobalt will hide your ip address, browser info, and bypass local network restrictions. when enabled, files will also have readable filenames that otherwise would be gibberish.",
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
<div class="action-button-icon">
|
<div class="action-button-icon">
|
||||||
<CopyIcon check={copied} />
|
<CopyIcon check={copied} />
|
||||||
</div>
|
</div>
|
||||||
copy
|
{$t("button.copy")}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{#if device.supports.share}
|
{#if device.supports.share}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import locale from "$lib/i18n/locale";
|
|
||||||
import languages from "$i18n/languages.json";
|
import languages from "$i18n/languages.json";
|
||||||
|
|
||||||
import { t, locales } from "$lib/i18n/translations";
|
import { t, locales } from "$lib/i18n/translations";
|
||||||
@ -10,10 +9,12 @@
|
|||||||
$: currentSetting = $settings.appearance.language;
|
$: currentSetting = $settings.appearance.language;
|
||||||
$: disabled = $settings.appearance.autoLanguage;
|
$: disabled = $settings.appearance.autoLanguage;
|
||||||
|
|
||||||
const updateLocale = (lang: string) => {
|
const updateLocale = (event: Event) => {
|
||||||
|
const target = event.target as HTMLSelectElement;
|
||||||
|
|
||||||
updateSetting({
|
updateSetting({
|
||||||
appearance: {
|
appearance: {
|
||||||
language: lang as keyof typeof languages,
|
language: target.value as keyof typeof languages,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -34,8 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
id="setting-dropdown-appearance-language"
|
id="setting-dropdown-appearance-language"
|
||||||
bind:value={$locale}
|
on:change={updateLocale}
|
||||||
on:change={() => updateLocale($locale)}
|
|
||||||
{disabled}
|
{disabled}
|
||||||
>
|
>
|
||||||
{#each $locales as value}
|
{#each $locales as value}
|
||||||
|
@ -7,7 +7,7 @@ import type {
|
|||||||
LocalizationContent
|
LocalizationContent
|
||||||
} from '$lib/types/i18n';
|
} from '$lib/types/i18n';
|
||||||
|
|
||||||
import languages from '$i18n/languages.json';
|
import _languages from '$i18n/languages.json';
|
||||||
|
|
||||||
const locFiles = import.meta.glob('$i18n/*/**/*.json');
|
const locFiles = import.meta.glob('$i18n/*/**/*.json');
|
||||||
const parsedLocfiles: StructuredLocfileInfo = {};
|
const parsedLocfiles: StructuredLocfileInfo = {};
|
||||||
@ -22,6 +22,8 @@ for (const [path, loader] of Object.entries(locFiles)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultLocale = 'en';
|
const defaultLocale = 'en';
|
||||||
|
const languages: Record<string, string> = _languages;
|
||||||
|
|
||||||
const config: Config<{
|
const config: Config<{
|
||||||
value?: string;
|
value?: string;
|
||||||
formats?: string;
|
formats?: string;
|
||||||
@ -30,6 +32,8 @@ const config: Config<{
|
|||||||
}> = {
|
}> = {
|
||||||
fallbackLocale: defaultLocale,
|
fallbackLocale: defaultLocale,
|
||||||
translations: Object.keys(parsedLocfiles).reduce((obj, lang) => {
|
translations: Object.keys(parsedLocfiles).reduce((obj, lang) => {
|
||||||
|
languages[lang] ??= `${lang} (missing name)`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...obj,
|
...obj,
|
||||||
[lang]: { languages }
|
[lang]: { languages }
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<h3>leading privacy</h3>
|
<h3>leading privacy</h3>
|
||||||
<p>
|
<p>
|
||||||
all requests to backend are anonymous and all tunnels are encrypted.
|
all requests to backend are anonymous and all tunnels are encrypted.
|
||||||
we have a strict zero log policy and don't track <i>anything at all</i>.
|
we have a strict zero log policy and don't track <i>anything</i> about individual people.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
to avoid caching or storing downloaded files, cobalt processes them on-fly, sending processed pieces directly to client.
|
to avoid caching or storing downloaded files, cobalt processes them on-fly, sending processed pieces directly to client.
|
||||||
|
@ -25,10 +25,10 @@
|
|||||||
<section id="saving">
|
<section id="saving">
|
||||||
<h3>saving</h3>
|
<h3>saving</h3>
|
||||||
<p>
|
<p>
|
||||||
when using saving functionality, in some cases cobalt will encrypt & temporarily store information needed for tunnelling. 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.
|
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.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
processed/tunnelled files are never cached anywhere. everything is tunnelled live. cobalt's saving functionality is essentially a fancy proxy service.
|
processed/tunneled files are never cached anywhere. everything is tunneled live. cobalt's saving functionality is essentially a fancy proxy service.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user