Spaces:
Sleeping
Sleeping
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" | |
} | |
} | |
} | |