api/vimeo: add transcript-only downloads

This commit is contained in:
lmdev 2026-09-04 00:37:52 -04:00
parent bd9cddeab7
commit 396087d186
5 changed files with 55 additions and 4 deletions

View file

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

View file

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

View file

@ -2,6 +2,10 @@ import HLS from "hls-parser";
import { env } from "../../config.js";
import { merge } from '../../misc/utils.js';
import { getCookie } from "../cookie/manager.js";
import {
createCaptionResponse,
matchesCaptionLanguage,
} from "../helpers/captions.js";
const resolutionMatch = {
"3840": 2160,
@ -76,6 +80,15 @@ const compareQuality = (rendition, requestedQuality) => {
return Math.abs(quality - requestedQuality);
}
const getPlayerConfig = (id, password) => {
const url = new URL(`https://player.vimeo.com/video/${id}/config`);
if (password) url.searchParams.set("h", password);
return fetch(url)
.then(r => r.json())
.catch(() => {});
}
const getDirectLink = async (data, quality, subtitleLang) => {
if (!data.files) return;
@ -102,7 +115,9 @@ const getDirectLink = async (data, quality, subtitleLang) => {
subtitles = config.request.text_tracks.find(
t => t.lang.startsWith(subtitleLang)
);
subtitles = new URL(subtitles.url, "https://player.vimeo.com/").toString();
if (subtitles) {
subtitles = new URL(subtitles.url, "https://player.vimeo.com/").toString();
}
}
}
@ -183,6 +198,26 @@ const getHLS = async (configURL, obj) => {
}
export default async function(obj) {
if (obj.isCaptionOnly) {
const config = await getPlayerConfig(obj.id, obj.password);
const track = config?.request?.text_tracks?.find(track =>
track.url
&& matchesCaptionLanguage(track.lang, obj.captionLanguage)
);
if (!track) return { error: "fetch.empty" };
return createCaptionResponse({
url: new URL(track.url, "https://player.vimeo.com/").toString(),
format: obj.captionFormat,
language: track.lang,
service: "vimeo",
id: obj.id,
title: config.video?.title,
author: config.video?.owner?.name,
source: `https://vimeo.com/${obj.id}`,
});
}
let quality = obj.quality === "max" ? 9000 : Number(obj.quality);
if (quality < 240) quality = 240;
if (!quality || obj.isAudioOnly) quality = 9000;

View file

@ -21,6 +21,19 @@
"status": "redirect"
}
},
{
"name": "standalone English transcript",
"url": "https://vimeo.com/76979871",
"params": {
"downloadMode": "captions",
"captionFormat": "txt",
"captionLanguage": "en"
},
"expected": {
"code": 200,
"status": "tunnel"
}
},
{
"name": "1080p dash parcel",
"url": "https://vimeo.com/967252742",
@ -63,4 +76,4 @@
"status": "redirect"
}
}
]
]