This commit is contained in:
Vladimir Barinov 2025-07-12 13:01:33 -05:00 committed by GitHub
commit 8fcaa4a71a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 144 additions and 1 deletions

View File

@ -191,6 +191,7 @@ export default function({
case "streamable": case "streamable":
case "snapchat": case "snapchat":
case "twitch": case "twitch":
case "ntv":
responseType = "redirect"; responseType = "redirect";
break; break;
} }

View File

@ -30,6 +30,7 @@ import facebook from "./services/facebook.js";
import bluesky from "./services/bluesky.js"; import bluesky from "./services/bluesky.js";
import xiaohongshu from "./services/xiaohongshu.js"; import xiaohongshu from "./services/xiaohongshu.js";
import newgrounds from "./services/newgrounds.js"; import newgrounds from "./services/newgrounds.js";
import ntv from "./services/ntv.js";
let freebind; let freebind;
@ -231,6 +232,15 @@ export default async function({ host, patternMatch, params, authType }) {
r = await dailymotion(patternMatch); r = await dailymotion(patternMatch);
break; break;
case "ntv":
r = await ntv({
name: patternMatch.name ?? null,
showid: patternMatch.showid ?? null,
videoid: patternMatch.videoid,
quality: params.videoQuality
});
break;
case "snapchat": case "snapchat":
r = await snapchat({ r = await snapchat({
...patternMatch, ...patternMatch,

View File

@ -1,6 +1,6 @@
import UrlPattern from "url-pattern"; import UrlPattern from "url-pattern";
export const audioIgnore = new Set(["vk", "ok", "loom"]); export const audioIgnore = new Set(["vk", "ok", "loom", "ntv"]);
export const hlsExceptions = new Set(["dailymotion", "vimeo", "rutube", "bsky", "youtube"]); export const hlsExceptions = new Set(["dailymotion", "vimeo", "rutube", "bsky", "youtube"]);
export const services = { export const services = {
@ -219,6 +219,13 @@ export const services = {
"v/:id" "v/:id"
], ],
subdomains: ["music", "m"], subdomains: ["music", "m"],
},
ntv: {
patterns: [
"peredacha/:name/m:showid/o:videoid",
"video/:videoid",
],
tld: "ru"
} }
} }

View File

@ -82,4 +82,8 @@ export const testers = {
"newgrounds": pattern => "newgrounds": pattern =>
pattern.id?.length <= 12 || pattern.audioId?.length <= 12, pattern.id?.length <= 12 || pattern.audioId?.length <= 12,
"ntv": pattern =>
pattern.videoid.length <= 32 ||
(pattern.name.length <= 64 && pattern.showid.length <= 32 && pattern.videoid.length <= 32),
} }

View File

@ -0,0 +1,101 @@
import { env } from "../../config.js";
const ntvApi = 'https://www.ntv.ru/api/player/?id=';
// the most generic user agent
export const clientAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0";
const getVideo = async (videoID) => {
const video = await fetch(`${ntvApi}${videoID}`, {
method: "GET",
headers: {
"user-agent": clientAgent,
}
})
.then(r => {
if (r.status === 200) {
return r.json();
}
});
return video;
}
export default async function (obj) {
let video = null;
if (obj.name && obj.name.length > 0) {
const fetchID = await fetch(`https://www.ntv.ru/peredacha/${obj.name}/m${obj.showid}/o${obj.videoid}`, {
method: "GET",
headers: {
"user-agent": clientAgent,
}
})
.then(r => {
if (r.status === 200) {
return r.text();
}
});
let figuredVideoId = null;
const match = String(fetchID).match(/<meta\s+property="og:video:url"\s+content="https:\/\/www\.ntv\.ru\/embed\/(\d+)\/?"/i);
if (match && match[1]) {
figuredVideoId = match[1];
} else {
return { error: "fetch.fail" };
}
video = await getVideo(figuredVideoId);
} else {
video = await getVideo(obj.videoid);
}
if (!video) {
return { error: "fetch.empty" };
}
if (!video.playback || !video.totaltime) {
return { error: "fetch.fail" };
}
if (video.totaltime > env.durationLimit) {
return { error: "content.too_long" };
}
const userQuality = obj.quality === "max" ? "1080" : obj.quality;
let url = null;
// for hls there's 144p, 360p, 720p and 1080p available, but for mp4 only 360p and 1080p
// we'll use mp4 to simplify things
switch (userQuality) {
case "1080":
url = video.playback.hd_video;
break;
case "720":
case "480":
case "360":
url = video.playback.video;
break;
default:
// 360 by default
url = video.playback.video;
}
if (!url) return { error: "fetch.fail" };
const fileMetadata = {
title: video.description.trim(),
// despite the param name, it actually contains the video title with the name of the show
}
return {
urls: url,
fileMetadata,
filenameAttributes: {
service: "ntv",
id: `${obj.videoid}`,
title: fileMetadata.title,
extension: "mp4"
}
}
}

View File

@ -0,0 +1,20 @@
[
{
"name": "tv show video",
"url": "https://www.ntv.ru/peredacha/dacha_otvet/m18960/o806491",
"params": {},
"expected": {
"code": 200,
"status": "redirect"
}
},
{
"name": "newsfeed video",
"url": "https://www.ntv.ru/video/2483380",
"params": {},
"expected": {
"code": 200,
"status": "redirect"
}
}
]