api/tiktok: add transcript-only downloads

This commit is contained in:
lmdev 2026-09-04 00:39:24 -04:00
parent 396087d186
commit b585d371b3
5 changed files with 50 additions and 3 deletions

View file

@ -31,7 +31,7 @@ if the desired service isn't supported yet, feel free to create an appropriate i
| snapchat | ✅ | ✅ | ✅ | ❌ | ➖ | ➖ |
| soundcloud | ➖ | ✅ | ➖ | ❌ | ✅ | ✅ |
| streamable | ✅ | ✅ | ✅ | ❌ | ➖ | ➖ |
| tiktok | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| tiktok | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| tumblr | ✅ | ✅ | ✅ | ❌ | ➖ | ➖ |
| twitch clips | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| twitter/x | ✅ | ✅ | ✅ | ❌ | ➖ | ➖ |

View file

@ -167,6 +167,9 @@ export default async function({ host, patternMatch, params, authType }) {
h265: params.allowH265,
alwaysProxy: params.alwaysProxy,
subtitleLang,
isCaptionOnly,
captionLanguage: params.captionLanguage || subtitleLang,
captionFormat: params.captionFormat,
});
break;

View file

@ -1,7 +1,7 @@
import UrlPattern from "url-pattern";
export const audioIgnore = new Set(["vk", "ok", "loom"]);
export const captionOnlyServices = new Set(["loom", "rutube", "vimeo", "vk", "youtube"]);
export const captionOnlyServices = new Set(["loom", "rutube", "tiktok", "vimeo", "vk", "youtube"]);
export const hlsExceptions = new Set(["dailymotion", "vimeo", "rutube", "bsky", "youtube"]);
export const services = {

View file

@ -5,6 +5,10 @@ import { genericUserAgent } from "../../config.js";
import { updateCookie } from "../cookie/manager.js";
import { createStream } from "../../stream/manage.js";
import { convertLanguageCode } from "../../misc/language-codes.js";
import {
createCaptionResponse,
matchesCaptionLanguage,
} from "../helpers/captions.js";
const shortDomain = "https://vt.tiktok.com/";
@ -72,6 +76,33 @@ export default async function(obj) {
return { error: "fetch.empty" };
}
if (obj.isCaptionOnly) {
const requestedLanguage = convertLanguageCode(obj.captionLanguage)
|| obj.captionLanguage;
const subtitle = detail.video?.subtitleInfos?.find(subtitle =>
subtitle.Format === "webvtt"
&& matchesCaptionLanguage(subtitle.LanguageCodeName, requestedLanguage)
);
if (!subtitle) return { error: "fetch.empty" };
const language = convertLanguageCode(subtitle.LanguageCodeName)
|| subtitle.LanguageCodeName;
return createCaptionResponse({
url: subtitle.Url,
format: obj.captionFormat,
language,
service: "tiktok",
id: postId,
title: detail.desc?.trim(),
author: detail.author.uniqueId,
source: `https://www.tiktok.com/@${detail.author.uniqueId}/video/${postId}`,
headers: {
cookie: cookie.toString(),
"user-agent": genericUserAgent,
},
});
}
let video, videoFilename, audioFilename, audio, images,
filenameBase = `tiktok_${detail.author?.uniqueId}_${postId}`,
bestAudio; // will get defaulted to m4a later on in match-action

View file

@ -8,6 +8,19 @@
"status": "tunnel"
}
},
{
"name": "standalone English transcript",
"url": "https://www.tiktok.com/@tedtoks/video/7168241231892319531",
"params": {
"downloadMode": "captions",
"captionFormat": "txt",
"captionLanguage": "en"
},
"expected": {
"code": 200,
"status": "tunnel"
}
},
{
"name": "images",
"url": "https://www.tiktok.com/@matryoshk4/video/7231234675476532526",
@ -44,4 +57,4 @@
"status": "error"
}
}
]
]