mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-25 08:58:23 +03:00
Merge branch 'master' of https://github.com/ajayyy/SponsorBlockServer into more-coverage
This commit is contained in:
59
src/utils/getVideoDetails.ts
Normal file
59
src/utils/getVideoDetails.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { config } from "../config";
|
||||
import { innerTubeVideoDetails } from "../types/innerTubeApi.model";
|
||||
import { APIVideoData } from "../types/youtubeApi.model";
|
||||
import { YouTubeAPI } from "../utils/youtubeApi";
|
||||
import { getPlayerData } from "../utils/innerTubeAPI";
|
||||
|
||||
export interface videoDetails {
|
||||
videoId: string,
|
||||
duration: number,
|
||||
authorId: string,
|
||||
authorName: string,
|
||||
title: string,
|
||||
published: number,
|
||||
thumbnails: {
|
||||
url: string,
|
||||
width: number,
|
||||
height: number,
|
||||
}[]
|
||||
}
|
||||
|
||||
const convertFromInnerTube = (input: innerTubeVideoDetails): videoDetails => ({
|
||||
videoId: input.videoId,
|
||||
duration: Number(input.lengthSeconds),
|
||||
authorId: input.channelId,
|
||||
authorName: input.author,
|
||||
title: input.title,
|
||||
published: new Date(input.publishDate).getTime()/1000,
|
||||
thumbnails: input.thumbnail.thumbnails
|
||||
});
|
||||
|
||||
const convertFromNewLeaf = (input: APIVideoData): videoDetails => ({
|
||||
videoId: input.videoId,
|
||||
duration: input.lengthSeconds,
|
||||
authorId: input.authorId,
|
||||
authorName: input.author,
|
||||
title: input.title,
|
||||
published: input.published,
|
||||
thumbnails: input.videoThumbnails
|
||||
});
|
||||
|
||||
async function newLeafWrapper(videoId: string, ignoreCache: boolean) {
|
||||
const result = await YouTubeAPI.listVideos(videoId, ignoreCache);
|
||||
return result?.data ?? Promise.reject();
|
||||
}
|
||||
|
||||
export function getVideoDetails(videoId: string, ignoreCache = false): Promise<videoDetails> {
|
||||
if (!config.newLeafURLs) {
|
||||
return getPlayerData(videoId, ignoreCache)
|
||||
.then(data => convertFromInnerTube(data));
|
||||
}
|
||||
return Promise.any([
|
||||
newLeafWrapper(videoId, ignoreCache)
|
||||
.then(videoData => convertFromNewLeaf(videoData)),
|
||||
getPlayerData(videoId, ignoreCache)
|
||||
.then(data => convertFromInnerTube(data))
|
||||
]).catch(() => {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
58
src/utils/innerTubeAPI.ts
Normal file
58
src/utils/innerTubeAPI.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import axios from "axios";
|
||||
import { Logger } from "./logger";
|
||||
import { innerTubeVideoDetails } from "../types/innerTubeApi.model";
|
||||
import DiskCache from "./diskCache";
|
||||
|
||||
async function getFromITube (videoID: string): Promise<innerTubeVideoDetails> {
|
||||
// start subrequest
|
||||
const url = "https://www.youtube.com/youtubei/v1/player";
|
||||
const data = {
|
||||
context: {
|
||||
client: {
|
||||
clientName: "WEB",
|
||||
clientVersion: "2.20211129.09.00"
|
||||
}
|
||||
},
|
||||
videoId: videoID
|
||||
};
|
||||
const result = await axios.post(url, data, {
|
||||
timeout: 3500
|
||||
});
|
||||
if (result.status === 200) {
|
||||
return result.data.videoDetails;
|
||||
} else {
|
||||
return Promise.reject(result.status);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPlayerData (videoID: string, ignoreCache = false): Promise<innerTubeVideoDetails> {
|
||||
if (!videoID || videoID.length !== 11 || videoID.includes(".")) {
|
||||
return Promise.reject("Invalid video ID");
|
||||
}
|
||||
|
||||
const cacheKey = `yt.itube.video.${videoID}`;
|
||||
if (!ignoreCache) { // try fetching from cache
|
||||
try {
|
||||
const data = await DiskCache.get(cacheKey);
|
||||
if (data) {
|
||||
Logger.debug(`InnerTube API: cache used for video information: ${videoID}`);
|
||||
return data as innerTubeVideoDetails;
|
||||
}
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
try {
|
||||
const data = await getFromITube(videoID)
|
||||
.catch(err => {
|
||||
Logger.warn(`InnerTube API Error for ${videoID}: ${err}`);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
DiskCache.set(cacheKey, data)
|
||||
.then(() => Logger.debug(`InnerTube API: video information cache set for: ${videoID}`))
|
||||
.catch((err: any) => Logger.warn(err));
|
||||
return data;
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,13 @@
|
||||
import redis from "../utils/redis";
|
||||
import { tempVIPKey } from "../utils/redisKeys";
|
||||
import { HashedUserID } from "../types/user.model";
|
||||
import { YouTubeAPI } from "../utils/youtubeApi";
|
||||
import { APIVideoInfo } from "../types/youtubeApi.model";
|
||||
import { VideoID } from "../types/segments.model";
|
||||
import { config } from "../config";
|
||||
import { Logger } from "./logger";
|
||||
|
||||
function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<APIVideoInfo> {
|
||||
return config.newLeafURLs ? YouTubeAPI.listVideos(videoID, ignoreCache) : null;
|
||||
}
|
||||
import { getVideoDetails } from "./getVideoDetails";
|
||||
|
||||
export const isUserTempVIP = async (hashedUserID: HashedUserID, videoID: VideoID): Promise<boolean> => {
|
||||
const apiVideoInfo = await getYouTubeVideoInfo(videoID);
|
||||
const channelID = apiVideoInfo?.data?.authorId;
|
||||
const apiVideoDetails = await getVideoDetails(videoID);
|
||||
const channelID = apiVideoDetails?.authorId;
|
||||
try {
|
||||
const reply = await redis.get(tempVIPKey(hashedUserID));
|
||||
return reply && reply == channelID;
|
||||
|
||||
@@ -52,6 +52,5 @@ export class YouTubeAPI {
|
||||
}
|
||||
}
|
||||
|
||||
export function getMaxResThumbnail(apiInfo: APIVideoData): string | void {
|
||||
return apiInfo?.videoThumbnails?.find((elem) => elem.quality === "maxres")?.second__originalUrl;
|
||||
}
|
||||
export const getMaxResThumbnail = (videoID: string): string =>
|
||||
`https://i.ytimg.com/vi/${videoID}/maxresdefault.jpg`;
|
||||
Reference in New Issue
Block a user