File size: 4,076 Bytes
2f527a4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
import { env } from "../../config.js";
const resolutions = ["2160", "1440", "1080", "720", "480", "360", "240", "144"];
const oauthUrl = "https://oauth.vk.com/oauth/get_anonym_token";
const apiUrl = "https://api.vk.com/method";
const clientId = "51552953";
const clientSecret = "qgr0yWwXCrsxA1jnRtRX";
// used in stream/shared.js for accessing media files
export const vkClientAgent = "com.vk.vkvideo.prod/822 (iPhone, iOS 16.7.7, iPhone10,4, Scale/2.0) SAK/1.119";
const cachedToken = {
token: "",
expiry: 0,
device_id: "",
};
const getToken = async () => {
if (cachedToken.expiry - 10 > Math.floor(new Date().getTime() / 1000)) {
return cachedToken.token;
}
const randomDeviceId = crypto.randomUUID().toUpperCase();
const anonymOauth = new URL(oauthUrl);
anonymOauth.searchParams.set("client_id", clientId);
anonymOauth.searchParams.set("client_secret", clientSecret);
anonymOauth.searchParams.set("device_id", randomDeviceId);
const oauthResponse = await fetch(anonymOauth.toString(), {
headers: {
"user-agent": vkClientAgent,
}
}).then(r => {
if (r.status === 200) {
return r.json();
}
});
if (!oauthResponse) return;
if (oauthResponse?.token && oauthResponse?.expired_at && typeof oauthResponse?.expired_at === "number") {
cachedToken.token = oauthResponse.token;
cachedToken.expiry = oauthResponse.expired_at;
cachedToken.device_id = randomDeviceId;
}
if (!cachedToken.token) return;
return cachedToken.token;
}
const getVideo = async (ownerId, videoId, accessKey) => {
const video = await fetch(`${apiUrl}/video.get`, {
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded; charset=utf-8",
"user-agent": vkClientAgent,
},
body: new URLSearchParams({
anonymous_token: cachedToken.token,
device_id: cachedToken.device_id,
lang: "en",
v: "5.244",
videos: `${ownerId}_${videoId}${accessKey ? `_${accessKey}` : ''}`
}).toString()
})
.then(r => {
if (r.status === 200) {
return r.json();
}
});
return video;
}
export default async function ({ ownerId, videoId, accessKey, quality }) {
const token = await getToken();
if (!token) return { error: "fetch.fail" };
const videoGet = await getVideo(ownerId, videoId, accessKey);
if (!videoGet || !videoGet.response || videoGet.response.items.length !== 1) {
return { error: "fetch.empty" };
}
const video = videoGet.response.items[0];
if (video.restriction) {
const title = video.restriction.title;
if (title.endsWith("country") || title.endsWith("region.")) {
return { error: "content.video.region" };
}
if (title === "Processing video") {
return { error: "fetch.empty" };
}
return { error: "content.video.unavailable" };
}
if (!video.files || !video.duration) {
return { error: "fetch.fail" };
}
if (video.duration > env.durationLimit) {
return { error: "content.too_long" };
}
const userQuality = quality === "max" ? resolutions[0] : quality;
let pickedQuality;
for (const resolution of resolutions) {
if (video.files[`mp4_${resolution}`] && +resolution <= +userQuality) {
pickedQuality = resolution;
break
}
}
const url = video.files[`mp4_${pickedQuality}`];
if (!url) return { error: "fetch.fail" };
const fileMetadata = {
title: video.title.trim(),
}
return {
urls: url,
fileMetadata,
filenameAttributes: {
service: "vk",
id: `${ownerId}_${videoId}${accessKey ? `_${accessKey}` : ''}`,
title: fileMetadata.title,
resolution: `${pickedQuality}p`,
qualityLabel: `${pickedQuality}p`,
extension: "mp4"
}
}
}
|