Ben há 11 meses atrás
pai
commit
d5d7cfb288
6 ficheiros alterados com 10057 adições e 141 exclusões
  1. 8644 0
      js/base.js
  2. 3 1
      js/extract_function.js
  3. 156 122
      js/info.js
  4. 1 1
      js/main.swift
  5. 17 17
      js/test.js
  6. 1236 0
      js/yt-dlp.html

Diff do ficheiro suprimidas por serem muito extensas
+ 8644 - 0
js/base.js


+ 3 - 1
js/extract_function.js

@@ -29,5 +29,7 @@ extractFunction = (jsCode, regexp) => {
 const fileContent = fs.readFileSync('base.js');
 
 const result = extractFunction(fileContent.toString(), /([a-zA-Z0-9]+)=function\([a-zA-Z0-9]+\)\{a=a\.split\(""\).*};/)
-
 console.log(result('adoidjaijdioawjiodjaaa'))
+
+const result2 = extractFunction(fileContent.toString(), /([a-zA-Z0-9]+)=function\([a-zA-Z0-9]+\)\{var b=a\.split\(""\)[\s\S]*?};/)
+console.log(result2);

+ 156 - 122
js/info.js

@@ -120,8 +120,8 @@ getStringBetween = (string, needleStart, needleEnd, offsetStart = 0, offsetEnd =
     return string.substring(x + needleStart.length + offsetEnd, y + offsetStart);
 }
 
-getDecipherFunction = (jsCode) => {
-    const match = jsCode.match(/([a-zA-Z0-9]+)=function\([a-zA-Z0-9]+\)\{a=a\.split\(""\).*};/)
+findFunction = (jsCode, regexp) => {
+    const match = jsCode.match(regexp)
     if (!match && match.length <= 1) {
         return null;
     }
@@ -147,34 +147,68 @@ getDecipherFunction = (jsCode) => {
 };
 
 const cache = {};
-extractJSSignatureFunction = async (baseJsUrl, platform) => {
-    const cacheKey = `js:${baseJsUrl}`;
+fetchBaseJSContent = async (baseJsUrl, platform) => {
+    const cacheKey = `jsContent:${baseJsUrl}`;
     if (cache[cacheKey]) {
-        console.log(`from cache JSSignatureFunction: ${baseJsUrl}`);
+        console.log(`from cache: ${baseJsUrl}`);
         return cache[cacheKey];
     }
     console.log(`extract baseUrl: ${baseJsUrl}`);
     const baseContentResp = 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',
     }, platform);
-    const {data, headers} = baseContentResp;
-    const decipher = getDecipherFunction(data);
-    if (decipher) {
-        cache[cacheKey] = decipher;
-    }
-    return decipher;
+    const {data, _} = baseContentResp;
+    cache[cacheKey] = data;
+    return data;
+}
+
+extractJSSignatureFunction = async (baseJsUrl, platform) => {
+    const baseJsContent = await fetchBaseJSContent(baseJsUrl, platform);
+    return findFunction(baseJsContent, /([a-zA-Z0-9]+)=function\([a-zA-Z0-9]+\)\{a=a\.split\(""\).*};/);
+}
+extractNJSFunction = async (baseJsUrl, platform) => {
+    const baseJsContent = await fetchBaseJSContent(baseJsUrl, platform);
+    return findFunction(baseJsContent, /([a-zA-Z0-9]+)=function\([a-zA-Z0-9]+\)\{var b=a\.split\(""\)[\s\S]*?};/);
 }
 
-getUrlFromSignature = async (signatureCipher, baseJsUrl, platform) => {
-    const decipher = await extractJSSignatureFunction(baseJsUrl, platform);
+signUrl = async (signatureCipher, baseJsUrl, platfrom) => {
     const searchParams = {}
     for (const item of signatureCipher.split('&')) {
         const [key, value] = item.split('=');
         searchParams[decodeURIComponent(key)] = decodeURIComponent(value);
     }
     const [url, signature, sp] = [searchParams['url'], searchParams['s'], searchParams['sp']];
+    const decipher = await extractJSSignatureFunction(baseJsUrl, platfrom);
+    if (!decipher) {
+        return null;
+    }
     console.log(`signatureCipher=${signatureCipher}, url=${url}, signature=${signature}, sp=${sp}`)
-    return `${url}&${sp}=${decipher(signature)}`;
+    let newUrl = `${url}&${sp}=${decipher(signature)}`;
+
+    function replaceUrlParam(url, paramName, paramValue) {
+        let pattern = new RegExp(`([?&])${paramName}=.*?(&|$)`, 'i');
+        let newUrl = url.replace(pattern, `$1${paramName}=${paramValue}$2`);
+
+        if (newUrl === url && url.indexOf('?') === -1) {
+            newUrl += `?${paramName}=${paramValue}`;
+        } else if (newUrl === url) {
+            newUrl += `&${paramName}=${paramValue}`;
+        }
+        return newUrl;
+    }
+
+    for (const item of url.split('&')) {
+        const [key, value] = item.split('=');
+        searchParams[decodeURIComponent(key)] = decodeURIComponent(value);
+    }
+
+    const nFunction = await extractNJSFunction(baseJsUrl, platfrom);
+    const n = searchParams['n']
+    if (n && nFunction) {
+        const newN = nFunction(n);
+        return replaceUrlParam(newUrl, 'n', newN);
+    }
+    return newUrl;
 }
 
 detail = async (url, platform) => {
@@ -219,113 +253,113 @@ detail = async (url, platform) => {
         originFormats = originFormats.concat(currentFormats);
         console.log(`after html, format size:${originFormats.length}`);
 
-        // android
-        try {
-            const apiKey = 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w'
-            const apiUrl = `https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`;
-            const apiResp = await request('POST', apiUrl, JSON.stringify({
-                "context": {
-                    "client": {
-                        "clientName": "ANDROID",
-                        "clientVersion": "19.09.37",
-                        "androidSdkVersion": 30,
-                        'userAgent': 'com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip',
-                        "hl": "en",
-                        "timeZone": "UTC",
-                        "utcOffsetMinutes": 0
-                    }
-                },
-                'videoId': url.replace('https://www.youtube.com/watch?v=', ''),
-                "params": "CgIIAQ==",
-                "playbackContext": {
-                    "contentPlaybackContext": {
-                        "html5Preference": "HTML5_PREF_WANTS"
-                    }
-                },
-                "contentCheckOk": true,
-                "racyCheckOk": true
-            }), {
-                'Host': 'www.youtube.com',
-                'Connection': 'keep-alive',
-                'User-Agent': 'com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip',
-                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
-                'Accept-Language': 'en-us,en;q=0.5',
-                'Sec-Fetch-Mode': 'navigate',
-                'X-YouTube-Client-Name': '3',
-                'X-YouTube-Client-Version': '19.09.37',
-                'Origin': 'https://www.youtube.com',
-                'Accept-Encoding': 'gzip, deflate, br',
-                'Cookie': parseSetCookie(htmlHeaders),
-                'Content-Type': 'application/json'
-            }, platform);
-            let {data: apiData, _} = apiResp;
-            console.log(`android api result: ${JSON.stringify(apiResp)}`);
-            const res = JSON.parse(apiData);
-            const currentFormats = [];
-            for (const format of [].concat(res["streamingData"]["formats"]).concat(res["streamingData"]["adaptiveFormats"])) {
-                if (format) {
-                    format["from"] = "android"
-                    currentFormats.push(format);
-                }
-            }
-            originFormats = originFormats.concat(currentFormats);
-        } catch (e) {
-            console.log(`can not found format android api error: ${e}`);
-        }
-        console.log(`after android api, format size:${originFormats.length}`);
-        // ios
-        try {
-            const apiKey = 'AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc'
-            const apiUrl = `https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`;
-            let apiResp = await request('POST', apiUrl, JSON.stringify({
-                "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)',
-                        "hl": "en",
-                        "timeZone": "UTC",
-                        "utcOffsetMinutes": 0
-                    }
-                },
-                'videoId': url.replace('https://www.youtube.com/watch?v=', ''),
-                "playbackContext": {
-                    "contentPlaybackContext": {
-                        "html5Preference": "HTML5_PREF_WANTS"
-                    }
-                },
-                "contentCheckOk": true,
-                "racyCheckOk": true
-            }), {
-                'Host': 'www.youtube.com',
-                'Connection': 'keep-alive',
-                'User-Agent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
-                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
-                'Accept-Language': 'en-us,en;q=0.5',
-                'Sec-Fetch-Mode': 'navigate',
-                'X-YouTube-Client-Name': '5',
-                'X-YouTube-Client-Version': '19.09.3',
-                'Origin': 'https://www.youtube.com',
-                'Accept-Encoding': 'gzip, deflate, br',
-                'Cookie': parseSetCookie(htmlHeaders),
-                'Content-Type': 'application/json'
-            }, platform);
-            let {data: apiData, _} = apiResp;
-            console.log(`ios api result: ${JSON.stringify(apiResp)}`);
-            const res = JSON.parse(apiData);
-            const currentFormats = [];
-            for (const format of [].concat(res["streamingData"]["formats"]).concat(res["streamingData"]["adaptiveFormats"])) {
-                if (format) {
-                    format["from"] = "ios"
-                    currentFormats.push(format);
-                }
-            }
-            originFormats = originFormats.concat(currentFormats);
-        } catch (e) {
-            console.log(`can not found format ios api error: ${e}`);
-        }
-        console.log(`after ios api, format size:${originFormats.length}`);
+        // // android
+        // try {
+        //     const apiKey = 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w'
+        //     const apiUrl = `https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`;
+        //     const apiResp = await request('POST', apiUrl, JSON.stringify({
+        //         "context": {
+        //             "client": {
+        //                 "clientName": "ANDROID",
+        //                 "clientVersion": "19.09.37",
+        //                 "androidSdkVersion": 30,
+        //                 'userAgent': 'com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip',
+        //                 "hl": "en",
+        //                 "timeZone": "UTC",
+        //                 "utcOffsetMinutes": 0
+        //             }
+        //         },
+        //         'videoId': url.replace('https://www.youtube.com/watch?v=', ''),
+        //         "params": "CgIIAQ==",
+        //         "playbackContext": {
+        //             "contentPlaybackContext": {
+        //                 "html5Preference": "HTML5_PREF_WANTS"
+        //             }
+        //         },
+        //         "contentCheckOk": true,
+        //         "racyCheckOk": true
+        //     }), {
+        //         'Host': 'www.youtube.com',
+        //         'Connection': 'keep-alive',
+        //         'User-Agent': 'com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip',
+        //         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+        //         'Accept-Language': 'en-us,en;q=0.5',
+        //         'Sec-Fetch-Mode': 'navigate',
+        //         'X-YouTube-Client-Name': '3',
+        //         'X-YouTube-Client-Version': '19.09.37',
+        //         'Origin': 'https://www.youtube.com',
+        //         'Accept-Encoding': 'gzip, deflate, br',
+        //         'Cookie': parseSetCookie(htmlHeaders),
+        //         'Content-Type': 'application/json'
+        //     }, platform);
+        //     let {data: apiData, _} = apiResp;
+        //     console.log(`android api result: ${JSON.stringify(apiResp)}`);
+        //     const res = JSON.parse(apiData);
+        //     const currentFormats = [];
+        //     for (const format of [].concat(res["streamingData"]["formats"]).concat(res["streamingData"]["adaptiveFormats"])) {
+        //         if (format) {
+        //             format["from"] = "android"
+        //             currentFormats.push(format);
+        //         }
+        //     }
+        //     originFormats = originFormats.concat(currentFormats);
+        // } catch (e) {
+        //     console.log(`can not found format android api error: ${e}`);
+        // }
+        // console.log(`after android api, format size:${originFormats.length}`);
+        // // ios
+        // try {
+        //     const apiKey = 'AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc'
+        //     const apiUrl = `https://www.youtube.com/youtubei/v1/player?key=${apiKey}&prettyPrint=false`;
+        //     let apiResp = await request('POST', apiUrl, JSON.stringify({
+        //         "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)',
+        //                 "hl": "en",
+        //                 "timeZone": "UTC",
+        //                 "utcOffsetMinutes": 0
+        //             }
+        //         },
+        //         'videoId': url.replace('https://www.youtube.com/watch?v=', ''),
+        //         "playbackContext": {
+        //             "contentPlaybackContext": {
+        //                 "html5Preference": "HTML5_PREF_WANTS"
+        //             }
+        //         },
+        //         "contentCheckOk": true,
+        //         "racyCheckOk": true
+        //     }), {
+        //         'Host': 'www.youtube.com',
+        //         'Connection': 'keep-alive',
+        //         'User-Agent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
+        //         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+        //         'Accept-Language': 'en-us,en;q=0.5',
+        //         'Sec-Fetch-Mode': 'navigate',
+        //         'X-YouTube-Client-Name': '5',
+        //         'X-YouTube-Client-Version': '19.09.3',
+        //         'Origin': 'https://www.youtube.com',
+        //         'Accept-Encoding': 'gzip, deflate, br',
+        //         'Cookie': parseSetCookie(htmlHeaders),
+        //         'Content-Type': 'application/json'
+        //     }, platform);
+        //     let {data: apiData, _} = apiResp;
+        //     console.log(`ios api result: ${JSON.stringify(apiResp)}`);
+        //     const res = JSON.parse(apiData);
+        //     const currentFormats = [];
+        //     for (const format of [].concat(res["streamingData"]["formats"]).concat(res["streamingData"]["adaptiveFormats"])) {
+        //         if (format) {
+        //             format["from"] = "ios"
+        //             currentFormats.push(format);
+        //         }
+        //     }
+        //     originFormats = originFormats.concat(currentFormats);
+        // } catch (e) {
+        //     console.log(`can not found format ios api error: ${e}`);
+        // }
+        // console.log(`after ios api, format size:${originFormats.length}`);
 
         const baseJsUrl = `https://www.youtube.com${JSON.parse(html.match(/set\(({.+?})\);/)[1])["PLAYER_JS_URL"]}`
         let formatIds = [];
@@ -334,7 +368,7 @@ detail = async (url, platform) => {
             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);
+                    format["url"] = await signUrl(format["signatureCipher"], baseJsUrl, platform);
                 }
                 if (format["url"]) {
                     const {vcodec, acodec} = parseCodecs(format)

+ 1 - 1
js/main.swift

@@ -133,7 +133,7 @@ func testSearch(keyword: String, ctx: JSContext) -> Void {
 
 let ctx = createJSContext()
 
-if let url = URL(string: "file:///Users/ben/Desktop/app/be/be-ytb/js/info.js") {
+if let url = URL(string: "https://s3.amazonaws.com/justlistenmusic4560.com/bundle") {
     downloadJSFile(url: url) { result in
         switch result {
         case .success(let jsString):

+ 17 - 17
js/test.js

@@ -1,23 +1,23 @@
-// detail(`https://www.youtube.com/watch?v=yBjnGz4S8FU`, 'WEB')
-//     .then(res => {
-//         console.log(res);
-//     })
-//     .catch(e => {
-//         console.log(e);
-//     })
-
-search("周 杰伦", null, "WEB")
+detail(`https://www.youtube.com/watch?v=yBjnGz4S8FU`, 'WEB')
     .then(res => {
         console.log(res);
-
-        search("周 杰伦", res["data"]["next"], "WEB")
-            .then(res => {
-                console.log(res);
-            })
-            .catch(e => {
-                console.log(e);
-            })
     })
     .catch(e => {
         console.log(e);
     })
+
+// search("周 杰伦", null, "WEB")
+//     .then(res => {
+//         console.log(res);
+//
+//         search("周 杰伦", res["data"]["next"], "WEB")
+//             .then(res => {
+//                 console.log(res);
+//             })
+//             .catch(e => {
+//                 console.log(e);
+//             })
+//     })
+//     .catch(e => {
+//         console.log(e);
+//     })

Diff do ficheiro suprimidas por serem muito extensas
+ 1236 - 0
js/yt-dlp.html


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff