const request = async (method, url, data = null, headers = {}) => { return new Promise(function (resolve, reject) { const xhr = new XMLHttpRequest(); xhr.open(method, url); // 设置请求头 Object.keys(headers).forEach(function (key) { xhr.setRequestHeader(key, headers[key]); }); xhr.onload = function () { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.responseText); } else { reject(new Error('Request failed with status: ' + xhr.status)); } }; xhr.onerror = function () { reject(new Error('Request failed')); }; xhr.send(data); }); } getVideoDetail = async (url) => { const headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Safari/537.36', } return request('GET', url, null, headers) .then(res => { let regex = /var ytInitialPlayerResponse\s*=\s*({.*?});/; let match = res.match(regex); if (!match || !match.length) { throw new Error('JSON not found.'); } const ytInitialPlayerResponse = JSON.parse(match[1]); console.log(ytInitialPlayerResponse); const originVideoDetails = ytInitialPlayerResponse["videoDetails"]; const thumbnails = [] for (const item of originVideoDetails["thumbnail"]["thumbnails"]) { thumbnails.push({ "url": item["url"], "width": item["width"] + "", "height": item["height"] + "" }) } const formats = [] for (const item of ytInitialPlayerResponse["streamingData"]["formats"].concat(ytInitialPlayerResponse["streamingData"]["adaptiveFormats"])) { if (item && item["signatureCipher"] && item["mimeType"]) { let urlRegex = /url=([^&]+)/; let match = item["signatureCipher"].match(urlRegex); if (!match) { continue; } const encodedUrl = match[1]; const decodedUrl = decodeURIComponent(encodedUrl); formats.push({ "width": item["width"] + "", "height": item["height"] + "", "type": item["mimeType"], "quality": item["quality"], "itag": item["itag"], "fps": item["fps"] + "", "bitrate": item["bitrate"] + "", "url": decodedUrl, "ext": "mp4", "vcodec": item["mimeType"], "acodec": item["mimeType"], "vbr": "0", "abr": "0", "container": "mp4_dash" }) } } regex = /var ytInitialData\s*=\s*({.*?});/; match = res.match(regex); if (!match || !match.length) { throw new Error('JSON not found.'); } if (!match || !match.length) { throw new Error('JSON not found.'); } const ytInitialData = JSON.parse(match[1]); console.log(ytInitialData); const recommendInfo = []; for (const item of ytInitialData["contents"]["twoColumnWatchNextResults"]["secondaryResults"]["secondaryResults"]["results"]) { if (item["compactVideoRenderer"]) { const recommendVideo = item["compactVideoRenderer"]; recommendInfo.push({ "type": "gridVideoRenderer", "videoId": recommendVideo["videoId"], "title": recommendVideo["title"]["simpleText"], "thumbnails": recommendVideo["thumbnail"]["thumbnails"], "channelName": recommendVideo["longBylineText"]["runs"][0]["text"], "publishedTimeText": recommendVideo["publishedTimeText"]["simpleText"], "viewCountText": recommendVideo["viewCountText"]["simpleText"], "shortViewCountText": recommendVideo["shortViewCountText"]["simpleText"], "lengthText": recommendVideo["lengthText"]["simpleText"] }) } } const videoDetails = { "isLiveContent": originVideoDetails["isLiveContent"], "title": originVideoDetails["title"], "thumbnails": thumbnails, "description": originVideoDetails["shortDescription"], "lengthSeconds": originVideoDetails["lengthSeconds"], "viewCount": originVideoDetails["viewCount"], "keywords": originVideoDetails["keywords"], "author": originVideoDetails["author"], "channelID": originVideoDetails["channelId"], "recommendInfo": recommendInfo, "channelURL": `https://www.youtube.com/channel/${originVideoDetails["channelId"]}`, "videoId": originVideoDetails["videoId"] } return { "code": 200, "msg": "", "data": { "videoDetails": videoDetails, "streamingData": { "formats": formats } }, "id": "MusicDetailViewModel_detail_url" } }) }