Browse Source

mod: info

Ben 1 year ago
parent
commit
2bd5aeca08
3 changed files with 8768 additions and 123 deletions
  1. 8639 0
      js/base.js
  2. 128 122
      js/info.js
  3. 1 1
      test.py

File diff suppressed because it is too large
+ 8639 - 0
js/base.js


+ 128 - 122
js/info.js

@@ -54,10 +54,10 @@ request = async (method, url, data = null, headers = {}, platform) => {
     if (platform === "WEB") {
         url = url.replace("https://www.youtube.com", "http://127.0.0.1");
     }
-    console.log(`请求url:${url}`)
-    console.log(`请求data:${data}`)
-    console.log(`请求method:${method}`)
-    console.log(`请求headers:${JSON.stringify((headers))}`)
+    console.log(`request url:${url}`)
+    console.log(`request data:${data}`)
+    console.log(`request method:${method}`)
+    console.log(`request headers:${JSON.stringify((headers))}`)
     if (platform === "WEB") {
         return fetch(url, {
             "method": method,
@@ -95,16 +95,15 @@ getDecipherFunction = (string) => {
 
 const cache = {};
 extractJSSignatureFunction = async (baseJsUrl, platform) => {
-    console.log(`解析baseUrl: ${baseJsUrl}`);
+    console.log(`extract baseUrl: ${baseJsUrl}`);
     const cacheKey = `js:${baseJsUrl}`;
     if (cache[cacheKey]) {
-        console.log(`从缓存中获取JSSignatureFunction: ${baseJsUrl}`);
+        console.log(`from cache JSSignatureFunction: ${baseJsUrl}`);
         return cache[cacheKey];
     }
-    const headers = {
+    const baseContent = await request('GET', baseJsUrl, null, {
         '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',
-    }
-    const baseContent = await request('GET', baseJsUrl, null, headers, platform);
+    }, platform);
     const decipher = getDecipherFunction(baseContent);
     if (decipher) {
         cache[cacheKey] = decipher;
@@ -133,8 +132,8 @@ detail = async (url, platform) => {
         let regex = /var ytInitialPlayerResponse\s*=\s*({.*?});/;
         let match = html.match(regex);
         if (!match || !match.length) {
-            console.log("解释失败: 无法找到json");
-            throw new Error('JSON not found.');
+            console.log("can not found JSON: ytInitialPlayerResponse");
+            throw new Error('JSON not found: ytInitialPlayerResponse');
         }
         const ytInitialPlayerResponse = JSON.parse(match[1]);
         console.log(ytInitialPlayerResponse);
@@ -149,116 +148,125 @@ detail = async (url, platform) => {
             })
         }
 
-        const baseJsUrl = `https://www.youtube.com${JSON.parse(html.match(/set\(({.+?})\);/)[1])["PLAYER_JS_URL"]}`
-        const formats = [];
+        let originFormats = [];
+        // android
         try {
-            let regex = /ytcfg\.set\s*\(\s*({.+?})\s*\)\s*;/
-            let match = html.match(regex);
-            if (match != null && match.length === 2) {
-                const masterYtConfig = JSON.parse(match[1]);
-                const apiKey = masterYtConfig['INNERTUBE_API_KEY'] || 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39'
-                const data = {
-                    "context": {
-                        "client": {
-                            "clientName": "ANDROID",
-                            "clientVersion": "19.09.37",
-                            "androidSdkVersion": 30,
-                            "userAgent": "com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip",
-                        }
-                    },
-                    'videoId': url.replace('https://www.youtube.com/watch?v=', ''),
-                    "playbackContext": {
-                        "contentPlaybackContext": {
-                            "html5Preference": "HTML5_PREF_WANTS"
-                        }
-                    },
-                    "params": "CgIIAQ==",
-                    "contentCheckOk": true,
-                    "racyCheckOk": true
-                }
-                const apiUrl = `https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`;
-                let res = await request('POST', apiUrl, JSON.stringify(data), {
-                    'X-YouTube-Client-Name': '5',
-                    'X-YouTube-Client-Version': '19.09.3',
-                    'User-Agent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
-                    'Content-Type': 'application/json'
-                }, platform);
-                console.log(`api结果: ${res}`);
-                res = JSON.parse(res);
-                console.log(res);
-                for (let format of [].concat(res["streamingData"]["formats"]).concat(res["streamingData"]["adaptiveFormats"])) {
-                    if (format) {
-                        console.log(`current format: ${JSON.stringify(format)}`);
-                        if (!format["url"]) {
-                            format["url"] = await getUrlFromSignature(format["signatureCipher"], baseJsUrl, platform);
-                        }
-                        if (format["url"]) {
-                            const {vcodec, acodec} = parseCodecs(format)
-                            if (vcodec && acodec) {
-                                formats.push({
-                                    "width": format["width"] + "",
-                                    "height": format["height"] + "",
-                                    "type": format["mimeType"],
-                                    "quality": format["quality"],
-                                    "itag": format["itag"],
-                                    "fps": format["fps"] + "",
-                                    "bitrate": format["bitrate"] + "",
-                                    "url": format["url"],
-                                    "ext": "mp4",
-                                    "vcodec": vcodec,
-                                    "acodec": acodec,
-                                    "vbr": "0",
-                                    "abr": "0",
-                                    "container": "mp4_dash"
-                                })
-                            }
-                        }
+            const apiKey = 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39'
+            const data = {
+                "context": {
+                    "client": {
+                        "clientName": "ANDROID",
+                        "clientVersion": "19.09.37",
+                        "androidSdkVersion": 30,
+                        "userAgent": "com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip",
                     }
-                }
+                },
+                'videoId': url.replace('https://www.youtube.com/watch?v=', ''),
+                "playbackContext": {
+                    "contentPlaybackContext": {
+                        "html5Preference": "HTML5_PREF_WANTS"
+                    }
+                },
+                "params": "CgIIAQ==",
+                "contentCheckOk": true,
+                "racyCheckOk": true
             }
+            const apiUrl = `https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`;
+            let apiResp = await request('POST', apiUrl, JSON.stringify(data), {
+                'X-YouTube-Client-Name': '5',
+                'X-YouTube-Client-Version': '19.09.3',
+                'User-Agent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
+                'Content-Type': 'application/json'
+            }, platform);
+            console.log(`android api result: ${JSON.stringify(apiResp)}`);
+            const res = JSON.parse(apiResp);
+            originFormats = originFormats.concat([].concat(res["streamingData"]["formats"]).concat(res["streamingData"]["adaptiveFormats"]));
         } catch (e) {
-            console.log(`无法从api中解析format,并且报错了: ${e}`);
+            console.log(`can not found format android api error: ${e}`);
         }
-        if (formats.length === 0) {
-            for (let format of [].concat(ytInitialPlayerResponse["streamingData"]["formats"]).concat(ytInitialPlayerResponse["streamingData"]["adaptiveFormats"])) {
-                console.log(`current format: ${JSON.stringify(format)}`);
-                if (format) {
-                    if (!format["url"]) {
-                        format["url"] = await getUrlFromSignature(format["signatureCipher"], baseJsUrl, platform);
+        console.log(`after android api, format size:${originFormats.length}`);
+        // ios
+        try {
+            const apiKey = 'AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc'
+            const data = {
+                "context": {
+                    "client": {
+                        'clientName': 'IOS',
+                        'clientVersion': '19.09.3',
+                        'deviceModel': 'iPhone14,3',
+                        'userAgent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)'
                     }
-                    if (format["url"]) {
-                        const {vcodec, acodec} = parseCodecs(format)
-                        if (vcodec && acodec) {
-                            formats.push({
-                                "width": format["width"] + "",
-                                "height": format["height"] + "",
-                                "type": format["mimeType"],
-                                "quality": format["quality"],
-                                "itag": format["itag"],
-                                "fps": format["fps"] + "",
-                                "bitrate": format["bitrate"] + "",
-                                "url": format["url"],
-                                "ext": "mp4",
-                                "vcodec": vcodec,
-                                "acodec": acodec,
-                                "vbr": "0",
-                                "abr": "0",
-                                "container": "mp4_dash"
-                            })
-                        }
+                },
+                'videoId': url.replace('https://www.youtube.com/watch?v=', ''),
+                "playbackContext": {
+                    "contentPlaybackContext": {
+                        "html5Preference": "HTML5_PREF_WANTS"
+                    }
+                },
+                "contentCheckOk": true,
+                "racyCheckOk": true
+            }
+            const apiUrl = `https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`;
+            let apiResp = await request('POST', apiUrl, JSON.stringify(data), {
+                'X-YouTube-Client-Name': '5',
+                'X-YouTube-Client-Version': '19.09.3',
+                'User-Agent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
+                'Content-Type': 'application/json'
+            }, platform);
+            console.log(`ios api result: ${JSON.stringify(apiResp)}`);
+            const res = JSON.parse(apiResp);
+            originFormats = originFormats.concat([].concat(res["streamingData"]["formats"]).concat(res["streamingData"]["adaptiveFormats"]));
+        } catch (e) {
+            console.log(`can not found format ios api error: ${e}`);
+        }
+        console.log(`after android api, format size:${originFormats.length}`);
+
+        originFormats = originFormats.concat(ytInitialPlayerResponse["streamingData"]["formats"]).concat(ytInitialPlayerResponse["streamingData"]["adaptiveFormats"]);
+        console.log(`after html, format size:${originFormats.length}`);
+
+        const baseJsUrl = `https://www.youtube.com${JSON.parse(html.match(/set\(({.+?})\);/)[1])["PLAYER_JS_URL"]}`
+        let formatIds = [];
+        const formats = [];
+        for (let format of originFormats) {
+            console.log(`current format: ${JSON.stringify(format)}`);
+            if (format && formatIds.indexOf(format['itag']) === -1) {
+                if (!format["url"]) {
+                    format["url"] = await getUrlFromSignature(format["signatureCipher"], baseJsUrl, platform);
+                }
+                if (format["url"]) {
+                    const {vcodec, acodec} = parseCodecs(format)
+                    if (vcodec && acodec) {
+                        formats.push({
+                            "width": format["width"] + "",
+                            "height": format["height"] + "",
+                            "type": format["mimeType"],
+                            "quality": format["quality"],
+                            "itag": format["itag"],
+                            "fps": format["fps"] + "",
+                            "bitrate": format["bitrate"] + "",
+                            "url": format["url"],
+                            "ext": "mp4",
+                            "vcodec": vcodec,
+                            "acodec": acodec,
+                            "vbr": "0",
+                            "abr": "0",
+                            "container": "mp4_dash"
+                        })
+                        formatIds.push(format["itag"]);
                     }
                 }
             }
         }
 
-        match = html.match(/var ytInitialData\s*=\s*({.*?});/);
+        const ytInitialDataMatch = html.match(/var ytInitialData\s*=\s*({.*?});/);
         const recommendInfo = [];
-        if (match && match.length === 2) {
-            const ytInitialData = JSON.parse(match[1]);
+        if (ytInitialDataMatch && ytInitialDataMatch.length === 2) {
+            const ytInitialData = JSON.parse(ytInitialDataMatch[1]);
+            console.log(`ytInitialData: ${JSON.stringify(ytInitialData)}`);
             for (const item of ytInitialData["contents"]?.["twoColumnWatchNextResults"]?.["secondaryResults"]?.["secondaryResults"]?.["results"] || []) {
                 if (item["compactVideoRenderer"]) {
                     const recommendVideo = item["compactVideoRenderer"];
-                    console.log(`推荐视频: ${JSON.stringify(recommendVideo)}`);
+                    console.log(`recommend video: ${JSON.stringify(recommendVideo)}`);
                     if (recommendVideo["videoId"]) {
                         recommendInfo.push({
                             "type": "gridVideoRenderer",
@@ -274,8 +282,6 @@ detail = async (url, platform) => {
                     }
                 }
             }
-        } else {
-            console.log(`解析失败,无法找到 ytInitialData,无法获取推荐视频`);
         }
 
         const videoDetails = {
@@ -303,14 +309,14 @@ detail = async (url, platform) => {
             },
             "id": "MusicDetailViewModel_detail_url"
         }
-        console.log(`解析结果: ${JSON.stringify(ret)}`);
+        console.log(`detail result: ${JSON.stringify(ret)}`);
         return ret;
     } catch (e) {
         const ret = {
             "code": -1,
             "msg": e.toString()
         }
-        console.log(`解析失败: ${JSON.stringify(ret)}`);
+        console.log(`detail result error: ${JSON.stringify(ret)}`);
         console.log(e);
         return ret;
     }
@@ -318,8 +324,8 @@ detail = async (url, platform) => {
 
 search = async (keyword, next, platform) => {
     try {
-        console.log(`接受到搜索请求 keyword: ${keyword}`);
-        console.log(`接收到搜索请求 next: ${next}`);
+        console.log(`search keyword: ${keyword}`);
+        console.log(`search next: ${next}`);
         if (next) {
             const nextObject = JSON.parse(next);
             const key = nextObject["key"];
@@ -337,7 +343,7 @@ search = async (keyword, next, platform) => {
             const videos = [];
             for (const item of res["onResponseReceivedCommands"][0]["appendContinuationItemsAction"]["continuationItems"][0]["itemSectionRenderer"]["contents"]) {
                 const video = item["videoRenderer"];
-                console.log(`搜索结果: ${JSON.stringify(video)}`);
+                console.log(`search result video: ${JSON.stringify(video)}`);
                 if (video && video["videoId"]) {
                     videos.push({
                         "type": "videoWithContextRenderer",
@@ -361,12 +367,12 @@ search = async (keyword, next, platform) => {
                     "data": videos,
                     "next": JSON.stringify({
                         "key": nextObject["key"],
-                        "continuation": res["onResponseReceivedCommands"][0]["appendContinuationItemsAction"]["continuationItems"][1]["continuationItemRenderer"]["continuationEndpoint"]["continuationCommand"]["token"],
+                        "continuation": res["onResponseReceivedCommands"]?.[0]?.["appendContinuationItemsAction"]?.["continuationItems"]?.[1]?.["continuationItemRenderer"]?.["continuationEndpoint"]?.["continuationCommand"]?.["token"],
                     }),
                 },
                 "id": "MusicSearchResultViewModel_search_result"
             }
-            console.log(`携带next搜索结果成功: ${JSON.stringify(ret)}`);
+            console.log(`[next] search result: ${JSON.stringify(ret)}`);
             return ret;
         } else {
             let url = `https://www.youtube.com/results?q=${encodeURIComponent(keyword)}&sp=EgIQAQ%253D%253D`;
@@ -376,16 +382,16 @@ search = async (keyword, next, platform) => {
             let regex = /var ytInitialData\s*=\s*({.*?});/;
             let match = html.match(regex);
             if (!match || !match.length) {
-                console.log("搜索失败:无法找到ytInitialData");
-                throw new Error('JSON not found.');
+                console.log("can not found ytInitialData");
+                throw new Error('JSON not found: ytInitialData');
             }
 
             const ytInitialDataResp = JSON.parse(match[1]);
             const videos = [];
-            for (const item of ytInitialDataResp["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"]) {
+            for (const item of ytInitialDataResp["contents"]?.["twoColumnSearchResultsRenderer"]?.["primaryContents"]?.["sectionListRenderer"]?.["contents"]?.[0]?.["itemSectionRenderer"]?.["contents"]) {
                 if (item["videoRenderer"]) {
                     const video = item["videoRenderer"];
-                    console.log(`搜索结果: ${JSON.stringify(video)}`);
+                    console.log(`search result video: ${JSON.stringify(video)}`);
                     if (video && video["videoId"]) {
                         videos.push({
                             "type": "videoWithContextRenderer",
@@ -412,7 +418,7 @@ search = async (keyword, next, platform) => {
                     .split(",")[0]
                     .split('"')[2];
             }
-            next["continuation"] = ytInitialDataResp["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"]["sectionListRenderer"]["contents"][1]["continuationItemRenderer"]["continuationEndpoint"]["continuationCommand"]["token"]
+            next["continuation"] = ytInitialDataResp["contents"]?.["twoColumnSearchResultsRenderer"]?.["primaryContents"]?.["sectionListRenderer"]?.["contents"]?.[1]?.["continuationItemRenderer"]?.["continuationEndpoint"]?.["continuationCommand"]?.["token"]
 
             const ret = {
                 "code": 200,
@@ -423,7 +429,7 @@ search = async (keyword, next, platform) => {
                 },
                 "id": "MusicSearchResultViewModel_search_result"
             }
-            console.log(`未携带next搜索结果成功: ${JSON.stringify(ret)}`);
+            console.log(`unnext search result: ${JSON.stringify(ret)}`);
             return ret;
         }
     } catch (e) {
@@ -431,7 +437,7 @@ search = async (keyword, next, platform) => {
             "code": -1,
             "msg": e.toString()
         }
-        console.log(`搜索失败: ${JSON.stringify(ret)}`);
+        console.log(`search result error: ${JSON.stringify(ret)}`);
         return ret;
     }
 }

+ 1 - 1
test.py

@@ -8,7 +8,7 @@ with yt_dlp.YoutubeDL({
     'proxy': 'socks://127.0.0.1:8889',
     'nocheckcertificate': True
 }) as ydl:
-    info = ydl.extract_info("https://www.youtube.com/watch?v=S9bCLPwzSC0", download=False)
+    info = ydl.extract_info("https://www.youtube.com/watch?v=WCtJ_EZQQBM", download=False)
     formats = []
     for item in info["formats"]:
         if item.get("resolution") != "audio only" and item.get("url") and item.get("acodec") and item.get(

Some files were not shown because too many files changed in this diff