api/vk: add transcript-only downloads

This commit is contained in:
lmdev 2026-09-04 00:35:04 -04:00
parent c6da1e4556
commit bd9cddeab7
5 changed files with 52 additions and 3 deletions

View file

@ -36,7 +36,7 @@ if the desired service isn't supported yet, feel free to create an appropriate i
| twitch clips | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| twitter/x | ✅ | ✅ | ✅ | ❌ | ➖ | ➖ |
| vimeo | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| vk videos & clips | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ |
| vk videos & clips | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| youtube | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| emoji | meaning |

View file

@ -103,6 +103,9 @@ export default async function({ host, patternMatch, params, authType }) {
accessKey: patternMatch.accessKey,
quality: params.videoQuality,
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", "youtube"]);
export const captionOnlyServices = new Set(["loom", "rutube", "vk", "youtube"]);
export const hlsExceptions = new Set(["dailymotion", "vimeo", "rutube", "bsky", "youtube"]);
export const services = {

View file

@ -1,4 +1,8 @@
import { env } from "../../config.js";
import {
createCaptionResponse,
matchesCaptionLanguage,
} from "../helpers/captions.js";
const resolutions = ["2160", "1440", "1080", "720", "480", "360", "240", "144"];
@ -80,7 +84,16 @@ const getVideo = async (ownerId, videoId, accessKey) => {
return video;
}
export default async function ({ ownerId, videoId, accessKey, quality, subtitleLang }) {
export default async function ({
ownerId,
videoId,
accessKey,
quality,
subtitleLang,
isCaptionOnly,
captionLanguage,
captionFormat,
}) {
const token = await getToken();
if (!token) return { error: "fetch.fail" };
@ -103,6 +116,26 @@ export default async function ({ ownerId, videoId, accessKey, quality, subtitleL
return { error: "content.video.unavailable" };
}
if (isCaptionOnly) {
const subtitle = video.subtitles?.find(subtitle =>
subtitle.title?.endsWith(".vtt")
&& matchesCaptionLanguage(subtitle.lang, captionLanguage)
);
if (!subtitle) return { error: "fetch.empty" };
const id = `${ownerId}_${videoId}${accessKey ? `_${accessKey}` : ''}`;
return createCaptionResponse({
url: subtitle.url,
format: captionFormat,
language: subtitle.lang,
service: "vk",
id,
title: video.title?.trim(),
source: `https://vk.com/video${id}`,
headers: { "user-agent": vkClientAgent },
});
}
if (!video.files || !video.duration) {
return { error: "fetch.fail" };
}

View file

@ -61,6 +61,19 @@
"status": "tunnel"
}
},
{
"name": "standalone transcript",
"url": "https://vkvideo.ru/video-26006257_456245538",
"params": {
"downloadMode": "captions",
"captionFormat": "txt",
"captionLanguage": "ru"
},
"expected": {
"code": 200,
"status": "tunnel"
}
},
{
"name": "ancient video (fallback to 240p)",
"url": "https://vk.com/video-1959_28496479",