merge: 10.7.3 from main

This commit is contained in:
wukko 2025-02-11 16:18:30 +06:00
commit c31c484894
No known key found for this signature in database
GPG key ID: 3E30B3F26C7B4AA2
14 changed files with 63 additions and 31 deletions

View file

@ -31,3 +31,6 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- run: pnpm i --frozen-lockfile && node api/src/util/test run-tests-for ${{ matrix.service }} - run: pnpm i --frozen-lockfile && node api/src/util/test run-tests-for ${{ matrix.service }}
env:
API_EXTERNAL_PROXY: ${{ secrets.API_EXTERNAL_PROXY }}
TEST_IGNORE_SERVICES: "none"

View file

@ -1,7 +1,7 @@
{ {
"name": "@imput/cobalt-api", "name": "@imput/cobalt-api",
"description": "save what you love", "description": "save what you love",
"version": "10.7.2", "version": "10.7.3",
"author": "imput", "author": "imput",
"exports": "./src/cobalt.js", "exports": "./src/cobalt.js",
"type": "module", "type": "module",

View file

@ -23,6 +23,10 @@ export async function runTest(url, params, expect) {
if (expect.status !== result.body.status) { if (expect.status !== result.body.status) {
const detail = `${expect.status} (expected) != ${result.body.status} (actual)`; const detail = `${expect.status} (expected) != ${result.body.status} (actual)`;
error.push(`status mismatch: ${detail}`); error.push(`status mismatch: ${detail}`);
if (result.body.status === 'error') {
error.push(`error code: ${result.body?.error?.code}`);
}
} }
if (expect.errorCode && expect.errorCode !== result.body?.error?.code) { if (expect.errorCode && expect.errorCode !== result.body?.error?.code) {

View file

@ -305,12 +305,12 @@ export default function instagram(obj) {
if (sidecar) { if (sidecar) {
const picker = sidecar.edges.filter(e => e.node?.display_url) const picker = sidecar.edges.filter(e => e.node?.display_url)
.map((e, i) => { .map((e, i) => {
const type = e.node?.is_video ? "video" : "photo"; const type = e.node?.is_video && e.node?.video_url ? "video" : "photo";
let url; let url;
if (type === 'video') { if (type === "video") {
url = e.node?.video_url; url = e.node?.video_url;
} else if (type === 'photo') { } else if (type === "photo") {
url = e.node?.display_url; url = e.node?.display_url;
} }

View file

@ -44,7 +44,7 @@ export default async function(o) {
let fileMetadata = { let fileMetadata = {
title: videoData.movie.title.trim(), title: videoData.movie.title.trim(),
author: (videoData.author?.name || videoData.compilationTitle).trim(), author: (videoData.author?.name || videoData.compilationTitle)?.trim(),
} }
if (bestVideo) return { if (bestVideo) return {

View file

@ -98,6 +98,14 @@ function aliasURL(url) {
if (url.hostname === 'xhslink.com' && parts.length === 3) { if (url.hostname === 'xhslink.com' && parts.length === 3) {
url = new URL(`https://www.xiaohongshu.com/a/${parts[2]}`); url = new URL(`https://www.xiaohongshu.com/a/${parts[2]}`);
} }
break;
case "loom":
const idPart = parts[parts.length - 1];
if (idPart.length > 32) {
url.pathname = `/share/${idPart.slice(-32)}`;
}
break;
} }
return url; return url;

View file

@ -4,6 +4,7 @@ import { env } from "../config.js";
import { runTest } from "../misc/run-test.js"; import { runTest } from "../misc/run-test.js";
import { loadJSON } from "../misc/load-from-fs.js"; import { loadJSON } from "../misc/load-from-fs.js";
import { Red, Bright } from "../misc/console-text.js"; import { Red, Bright } from "../misc/console-text.js";
import { setGlobalDispatcher, ProxyAgent } from "undici";
import { randomizeCiphers } from "../misc/randomize-ciphers.js"; import { randomizeCiphers } from "../misc/randomize-ciphers.js";
import { services } from "../processing/service-config.js"; import { services } from "../processing/service-config.js";
@ -13,7 +14,11 @@ const getTests = (service) => loadJSON(getTestPath(service));
// services that are known to frequently fail due to external // services that are known to frequently fail due to external
// factors (e.g. rate limiting) // factors (e.g. rate limiting)
const finnicky = new Set(['bilibili', 'instagram', 'facebook', 'youtube', 'vk', 'twitter', 'reddit']); const finnicky = new Set(
typeof process.env.TEST_IGNORE_SERVICES === 'string'
? process.env.TEST_IGNORE_SERVICES.split(',')
: ['bilibili', 'instagram', 'facebook', 'youtube', 'vk', 'twitter', 'reddit']
);
const runTestsFor = async (service) => { const runTestsFor = async (service) => {
const tests = getTests(service); const tests = getTests(service);
@ -64,6 +69,14 @@ const printHeader = (service, padLen) => {
console.log(service + '='.repeat(50)); console.log(service + '='.repeat(50));
} }
if (env.externalProxy) {
setGlobalDispatcher(new ProxyAgent(env.externalProxy));
}
env.streamLifespan = 10000;
env.apiURL = 'http://x/';
randomizeCiphers();
const action = process.argv[2]; const action = process.argv[2];
switch (action) { switch (action) {
case "get-services": case "get-services":
@ -86,9 +99,6 @@ switch (action) {
break; break;
case "run-tests-for": case "run-tests-for":
env.streamLifespan = 10000;
env.apiURL = 'http://x/';
randomizeCiphers();
try { try {
const { softFails } = await runTestsFor(process.argv[3]); const { softFails } = await runTestsFor(process.argv[3]);
@ -104,10 +114,6 @@ switch (action) {
const maxHeaderLen = Object.keys(services).reduce((n, v) => v.length > n ? v.length : n, 0); const maxHeaderLen = Object.keys(services).reduce((n, v) => v.length > n ? v.length : n, 0);
const failCounters = {}; const failCounters = {};
env.streamLifespan = 10000;
env.apiURL = 'http://x/';
randomizeCiphers();
for (const service in services) { for (const service in services) {
printHeader(service, maxHeaderLen); printHeader(service, maxHeaderLen);
const { fails, softFails } = await runTestsFor(service); const { fails, softFails } = await runTestsFor(service);

View file

@ -29,7 +29,6 @@
{ {
"name": "shortlink video", "name": "shortlink video",
"url": "https://fb.watch/r1K6XHMfGT/", "url": "https://fb.watch/r1K6XHMfGT/",
"canFail": true,
"params": {}, "params": {},
"expected": { "expected": {
"code": 200, "code": 200,
@ -39,7 +38,6 @@
{ {
"name": "reel video", "name": "reel video",
"url": "https://web.facebook.com/reel/730293269054758", "url": "https://web.facebook.com/reel/730293269054758",
"canFail": true,
"params": {}, "params": {},
"expected": { "expected": {
"code": 200, "code": 200,

View file

@ -102,9 +102,8 @@
}, },
{ {
"name": "retweeted video", "name": "retweeted video",
"url": "https://twitter.com/uwukko/status/1696901469633421344", "url": "https://twitter.com/schlizzawg/status/1869017025055793405",
"params": {}, "params": {},
"canFail": true,
"expected": { "expected": {
"code": 200, "code": 200,
"status": "redirect" "status": "redirect"
@ -145,7 +144,7 @@
"params": {}, "params": {},
"expected": { "expected": {
"code": 200, "code": 200,
"status": "redirect" "status": "tunnel"
} }
}, },
{ {
@ -203,11 +202,11 @@
}, },
{ {
"name": "bookmarked photo", "name": "bookmarked photo",
"url": "https://twitter.com/i/bookmarks?post_id=1837430141179289876", "url": "https://twitter.com/i/bookmarks?post_id=1887450602164396149",
"params": {}, "params": {},
"expected": { "expected": {
"code": 200, "code": 200,
"status": "redirect" "status": "tunnel"
} }
} }
] ]

View file

@ -48,7 +48,6 @@
{ {
"name": "short link, wrong id", "name": "short link, wrong id",
"url": "https://xhslink.com/a/aaaaaa", "url": "https://xhslink.com/a/aaaaaa",
"canFail": true,
"params": {}, "params": {},
"expected": { "expected": {
"code": 400, "code": 400,

View file

@ -1,6 +1,6 @@
{ {
"name": "@imput/cobalt-web", "name": "@imput/cobalt-web",
"version": "10.6", "version": "10.7.3",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {

View file

@ -50,7 +50,9 @@
<div class="picker-body"> <div class="picker-body">
{#if items} {#if items}
{#each items as item, i} {#each items as item, i}
<PickerItem {item} number={i + 1} /> {#if item?.url}
<PickerItem {item} number={i + 1} />
{/if}
{/each} {/each}
{/if} {/if}
</div> </div>

View file

@ -14,18 +14,28 @@
export let number: number; export let number: number;
let imageLoaded = false; let imageLoaded = false;
const isTunnel = new URL(item.url).pathname === "/tunnel";
let validUrl = false;
try {
new URL(item.url);
validUrl = true;
} catch {}
const isTunnel = validUrl && new URL(item.url).pathname === "/tunnel";
$: itemType = item.type ?? "photo"; $: itemType = item.type ?? "photo";
</script> </script>
<button <button
class="picker-item" class="picker-item"
on:click={() => on:click={() => {
downloadFile({ if (validUrl) {
url: item.url, downloadFile({
urlType: isTunnel ? "tunnel" : "redirect", url: item.url,
})} urlType: isTunnel ? "tunnel" : "redirect",
});
}
}}
> >
<div class="picker-type"> <div class="picker-type">
{#if itemType === "video"} {#if itemType === "video"}

View file

@ -72,6 +72,9 @@ export const youtubeLanguages = [
"ur", "ur",
"uz", "uz",
"vi", "vi",
"zh",
"zh-Hans",
"zh-Hant",
"zh-CN", "zh-CN",
"zh-HK", "zh-HK",
"zh-TW", "zh-TW",