diff --git a/.gitignore b/.gitignore index a9f07b7..2cec14d 100644 --- a/.gitignore +++ b/.gitignore @@ -140,4 +140,5 @@ fabric.properties # End of https://www.gitignore.io/api/node,webstorm,visualstudiocode .DS_Store -.parcel-cache \ No newline at end of file +.parcel-cache +.vscode diff --git a/dist/index.js b/dist/index.js index 51f25cc..95738ff 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32,14 +32,18 @@ const $4fa36e821943b400$var$buildDecoder = async (watchHtml)=>{ let decodeFunction = decodeFunctionMatches[0]; let varNameMatches = decodeFunction.match(/\.split\(\"\"\);([a-zA-Z0-9]+)\./); if (!varNameMatches) return null; - let varDeclaresMatches = jsFileContent.match(new RegExp(`(var ${varNameMatches[1]}={[\\s\\S]+}};)[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.prototype`)); - if (!varDeclaresMatches) return null; + let varStartIndex = jsFileContent.indexOf(`var ${varNameMatches[1]}={`); + if (varStartIndex < 0) return null; + let varEndIndex = jsFileContent.indexOf("}};", varStartIndex); + if (varEndIndex < 0) return null; + let varDeclares = jsFileContent.substring(varStartIndex, varEndIndex + 3); + if (!varDeclares) return null; return function(signatureCipher) { let params = new URLSearchParams(signatureCipher); let { s: signature , sp: signatureParam = "signature" , url: url } = Object.fromEntries(params); let decodedSignature = new Function(` "use strict"; - ${varDeclaresMatches[1]} + ${varDeclares} return (${decodeFunction})("${signature}"); `)(); return `${url}&${signatureParam}=${encodeURIComponent(decodedSignature)}`; diff --git a/dist/index.js.map b/dist/index.js.map index ee12770..0bde75b 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"mappings":"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","sources":["src/index.js"],"sourcesContent":["const axios = require('axios');\nconst M3U8FileParser = require('m3u8-file-parser');\nconst m3u8Parser = new M3U8FileParser();\n\nconst getRemoteFile = async (url) => {\n try {\n let { data } = await axios.get(url);\n return data;\n } catch (e) {\n return null;\n }\n}\n\nconst resolvePlayerResponse = (watchHtml) => {\n if (!watchHtml) {\n return '';\n }\n\n let matches = watchHtml.match(/ytInitialPlayerResponse = (.*)}}};/);\n return matches ? matches[1] + '}}}' : '';\n}\n\nconst resoleM3U8Link = (watchHtml) => {\n if (!watchHtml) {\n return null;\n }\n\n let matches = watchHtml.match(/hlsManifestUrl\":\"(.*\\/file\\/index\\.m3u8)/)\n return matches ? matches[1] : null;\n}\n\nconst buildDecoder = async (watchHtml) => {\n if (!watchHtml) {\n return null;\n }\n\n let jsFileUrlMatches = watchHtml.match(/\\/s\\/player\\/[A-Za-z0-9]+\\/[A-Za-z0-9_.]+\\/[A-Za-z0-9_]+\\/base\\.js/);\n\n if (!jsFileUrlMatches) {\n return null;\n }\n\n let jsFileContent = await getRemoteFile(`https://www.youtube.com${jsFileUrlMatches[0]}`);\n\n let decodeFunctionMatches = jsFileContent.match(/function.*\\.split\\(\\\"\\\"\\).*\\.join\\(\\\"\\\"\\)}/);\n\n if (!decodeFunctionMatches) {\n return null;\n }\n\n let decodeFunction = decodeFunctionMatches[0];\n\n let varNameMatches = decodeFunction.match(/\\.split\\(\\\"\\\"\\);([a-zA-Z0-9]+)\\./);\n\n if (!varNameMatches) {\n return null;\n }\n\n let varDeclaresMatches = jsFileContent.match(new RegExp(`(var ${varNameMatches[1]}={[\\\\s\\\\S]+}};)[a-zA-Z0-9]+\\\\.[a-zA-Z0-9]+\\\\.prototype`));\n\n if (!varDeclaresMatches) {\n return null;\n }\n\n return function (signatureCipher) {\n let params = new URLSearchParams(signatureCipher);\n let { s: signature, sp: signatureParam = 'signature', url } = Object.fromEntries(params);\n let decodedSignature = new Function(`\n \"use strict\";\n ${varDeclaresMatches[1]}\n return (${decodeFunction})(\"${signature}\");\n `)();\n\n return `${url}&${signatureParam}=${encodeURIComponent(decodedSignature)}`;\n }\n}\n\nconst getInfo = async ({ url, throwOnError = false }) => {\n\n let videoId = getVideoId({ url });\n\n if (!videoId) return false;\n\n let ytApi = 'https://www.youtube.com/watch';\n\n try {\n const response = await axios.get(ytApi, {\n params: { v: videoId }\n });\n\n if (!response || response.status != 200 || !response.data) {\n const error = new Error('Cannot get youtube video response')\n error.response = response;\n throw error;\n }\n\n let ytInitialPlayerResponse = resolvePlayerResponse(response.data);\n let parsedResponse = JSON.parse(ytInitialPlayerResponse);\n let streamingData = parsedResponse.streamingData || {};\n\n let formats = (streamingData.formats || []).concat(streamingData.adaptiveFormats || []);\n\n let isEncryptedVideo = !!formats.find(it => !!it.signatureCipher);\n\n if (isEncryptedVideo) {\n let decoder = await buildDecoder(response.data);\n\n if (decoder) {\n formats = formats.map(it => {\n if (it.url || !it.signatureCipher) {\n return it;\n }\n\n it.url = decoder(it.signatureCipher);\n delete it.signatureCipher;\n return it;\n });\n }\n }\n\n let result = {\n videoDetails: parsedResponse.videoDetails || {},\n formats: formats.filter(format => format.url)\n };\n\n if (result.videoDetails.isLiveContent) {\n try {\n let m3u8Link = resoleM3U8Link(response.data);\n if (m3u8Link) {\n let m3u8FileContent = await getRemoteFile(m3u8Link);\n\n m3u8Parser.read(m3u8FileContent);\n\n result.liveData = {\n manifestUrl: m3u8Link,\n data: m3u8Parser.getResult()\n };\n\n m3u8Parser.reset();\n }\n } catch (e) {\n if (throwOnError) {\n throw e;\n }\n }\n }\n\n return result;\n } catch (e) {\n if (throwOnError) {\n throw e;\n }\n\n return false;\n }\n};\n\nconst getVideoId = ({ url }) => {\n let opts = { fuzzy: true };\n\n if (/youtu\\.?be/.test(url)) {\n\n // Look first for known patterns\n let i;\n let patterns = [\n /youtu\\.be\\/([^#\\&\\?]{11})/, // youtu.be/\n /\\?v=([^#\\&\\?]{11})/, // ?v=\n /\\&v=([^#\\&\\?]{11})/, // &v=\n /embed\\/([^#\\&\\?]{11})/, // embed/\n /\\/v\\/([^#\\&\\?]{11})/ // /v/\n ];\n\n // If any pattern matches, return the ID\n for (i = 0; i < patterns.length; ++i) {\n if (patterns[i].test(url)) {\n return patterns[i].exec(url)[1];\n }\n }\n\n if (opts.fuzzy) {\n // If that fails, break it apart by certain characters and look\n // for the 11 character key\n let tokens = url.split(/[\\/\\&\\?=#\\.\\s]/g);\n for (i = 0; i < tokens.length; ++i) {\n if (/^[^#\\&\\?]{11}$/.test(tokens[i])) {\n return tokens[i];\n }\n }\n }\n }\n\n return null;\n};\n\nmodule.exports = {\n getInfo,\n getVideoId\n};\n"],"names":[],"version":3,"file":"index.js.map"} \ No newline at end of file +{"mappings":"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","sources":["src/index.js"],"sourcesContent":["const axios = require('axios');\nconst M3U8FileParser = require('m3u8-file-parser');\nconst m3u8Parser = new M3U8FileParser();\n\nconst getRemoteFile = async (url) => {\n try {\n let { data } = await axios.get(url);\n return data;\n } catch (e) {\n return null;\n }\n}\n\nconst resolvePlayerResponse = (watchHtml) => {\n if (!watchHtml) {\n return '';\n }\n\n let matches = watchHtml.match(/ytInitialPlayerResponse = (.*)}}};/);\n return matches ? matches[1] + '}}}' : '';\n}\n\nconst resoleM3U8Link = (watchHtml) => {\n if (!watchHtml) {\n return null;\n }\n\n let matches = watchHtml.match(/hlsManifestUrl\":\"(.*\\/file\\/index\\.m3u8)/)\n return matches ? matches[1] : null;\n}\n\nconst buildDecoder = async (watchHtml) => {\n if (!watchHtml) {\n return null;\n }\n\n let jsFileUrlMatches = watchHtml.match(/\\/s\\/player\\/[A-Za-z0-9]+\\/[A-Za-z0-9_.]+\\/[A-Za-z0-9_]+\\/base\\.js/);\n\n if (!jsFileUrlMatches) {\n return null;\n }\n\n let jsFileContent = await getRemoteFile(`https://www.youtube.com${jsFileUrlMatches[0]}`);\n\n let decodeFunctionMatches = jsFileContent.match(/function.*\\.split\\(\\\"\\\"\\).*\\.join\\(\\\"\\\"\\)}/);\n\n if (!decodeFunctionMatches) {\n return null;\n }\n\n let decodeFunction = decodeFunctionMatches[0];\n\n let varNameMatches = decodeFunction.match(/\\.split\\(\\\"\\\"\\);([a-zA-Z0-9]+)\\./);\n\n if (!varNameMatches) {\n return null;\n }\n\n let varStartIndex = jsFileContent.indexOf(`var ${varNameMatches[1]}={`);\n if (varStartIndex < 0) {\n return null;\n }\n let varEndIndex = jsFileContent.indexOf('}};', varStartIndex);\n if (varEndIndex < 0) {\n return null\n }\n\n let varDeclares = jsFileContent.substring(varStartIndex, varEndIndex + 3);\n\n if (!varDeclares) {\n return null;\n }\n\n return function (signatureCipher) {\n let params = new URLSearchParams(signatureCipher);\n let { s: signature, sp: signatureParam = 'signature', url } = Object.fromEntries(params);\n let decodedSignature = new Function(`\n \"use strict\";\n ${varDeclares}\n return (${decodeFunction})(\"${signature}\");\n `)();\n\n return `${url}&${signatureParam}=${encodeURIComponent(decodedSignature)}`;\n }\n}\n\nconst getInfo = async ({ url, throwOnError = false }) => {\n\n let videoId = getVideoId({ url });\n\n if (!videoId) return false;\n\n let ytApi = 'https://www.youtube.com/watch';\n\n try {\n const response = await axios.get(ytApi, {\n params: { v: videoId }\n });\n\n if (!response || response.status != 200 || !response.data) {\n const error = new Error('Cannot get youtube video response')\n error.response = response;\n throw error;\n }\n\n let ytInitialPlayerResponse = resolvePlayerResponse(response.data);\n let parsedResponse = JSON.parse(ytInitialPlayerResponse);\n let streamingData = parsedResponse.streamingData || {};\n\n let formats = (streamingData.formats || []).concat(streamingData.adaptiveFormats || []);\n\n let isEncryptedVideo = !!formats.find(it => !!it.signatureCipher);\n\n if (isEncryptedVideo) {\n let decoder = await buildDecoder(response.data);\n\n if (decoder) {\n formats = formats.map(it => {\n if (it.url || !it.signatureCipher) {\n return it;\n }\n\n it.url = decoder(it.signatureCipher);\n delete it.signatureCipher;\n return it;\n });\n }\n }\n\n let result = {\n videoDetails: parsedResponse.videoDetails || {},\n formats: formats.filter(format => format.url)\n };\n\n if (result.videoDetails.isLiveContent) {\n try {\n let m3u8Link = resoleM3U8Link(response.data);\n if (m3u8Link) {\n let m3u8FileContent = await getRemoteFile(m3u8Link);\n\n m3u8Parser.read(m3u8FileContent);\n\n result.liveData = {\n manifestUrl: m3u8Link,\n data: m3u8Parser.getResult()\n };\n\n m3u8Parser.reset();\n }\n } catch (e) {\n if (throwOnError) {\n throw e;\n }\n }\n }\n\n return result;\n } catch (e) {\n if (throwOnError) {\n throw e;\n }\n\n return false;\n }\n};\n\nconst getVideoId = ({ url }) => {\n let opts = { fuzzy: true };\n\n if (/youtu\\.?be/.test(url)) {\n\n // Look first for known patterns\n let i;\n let patterns = [\n /youtu\\.be\\/([^#\\&\\?]{11})/, // youtu.be/\n /\\?v=([^#\\&\\?]{11})/, // ?v=\n /\\&v=([^#\\&\\?]{11})/, // &v=\n /embed\\/([^#\\&\\?]{11})/, // embed/\n /\\/v\\/([^#\\&\\?]{11})/ // /v/\n ];\n\n // If any pattern matches, return the ID\n for (i = 0; i < patterns.length; ++i) {\n if (patterns[i].test(url)) {\n return patterns[i].exec(url)[1];\n }\n }\n\n if (opts.fuzzy) {\n // If that fails, break it apart by certain characters and look\n // for the 11 character key\n let tokens = url.split(/[\\/\\&\\?=#\\.\\s]/g);\n for (i = 0; i < tokens.length; ++i) {\n if (/^[^#\\&\\?]{11}$/.test(tokens[i])) {\n return tokens[i];\n }\n }\n }\n }\n\n return null;\n};\n\nmodule.exports = {\n getInfo,\n getVideoId\n};\n"],"names":[],"version":3,"file":"index.js.map"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 79ec5a7..6a8cab0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "youtube-stream-url", - "version": "2.4.1", + "version": "2.4.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "youtube-stream-url", - "version": "2.4.1", + "version": "2.4.2", "license": "MIT", "dependencies": { "axios": "^1.1.3", @@ -1555,9 +1555,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001429", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001429.tgz", - "integrity": "sha512-511ThLu1hF+5RRRt0zYCf2U2yRr9GPF6m5y90SBCWsvSoYoW7yAGlv/elyPaNfvGCkp6kj/KFZWU0BMA69Prsg==", + "version": "1.0.30001546", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz", + "integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==", "dev": true, "funding": [ { @@ -1567,6 +1567,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -2027,9 +2031,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -2565,9 +2569,9 @@ ] }, "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -3759,9 +3763,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001429", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001429.tgz", - "integrity": "sha512-511ThLu1hF+5RRRt0zYCf2U2yRr9GPF6m5y90SBCWsvSoYoW7yAGlv/elyPaNfvGCkp6kj/KFZWU0BMA69Prsg==", + "version": "1.0.30001546", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz", + "integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==", "dev": true }, "chalk": { @@ -4061,9 +4065,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "lightningcss": { @@ -4402,9 +4406,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "source-map": { diff --git a/package.json b/package.json index 68ae1d1..50b2baf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "youtube-stream-url", - "version": "2.4.1", + "version": "2.4.2", "description": "Get stream url from youtube link", "source": "src/index.js", "main": "dist/index.js", @@ -28,5 +28,8 @@ }, "devDependencies": { "parcel": "^2.7.0" + }, + "volta": { + "node": "18.18.0" } -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index 9015af9..21ce0a0 100644 --- a/src/index.js +++ b/src/index.js @@ -56,9 +56,18 @@ const buildDecoder = async (watchHtml) => { return null; } - let varDeclaresMatches = jsFileContent.match(new RegExp(`(var ${varNameMatches[1]}={[\\s\\S]+}};)[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.prototype`)); + let varStartIndex = jsFileContent.indexOf(`var ${varNameMatches[1]}={`); + if (varStartIndex < 0) { + return null; + } + let varEndIndex = jsFileContent.indexOf('}};', varStartIndex); + if (varEndIndex < 0) { + return null + } + + let varDeclares = jsFileContent.substring(varStartIndex, varEndIndex + 3); - if (!varDeclaresMatches) { + if (!varDeclares) { return null; } @@ -67,7 +76,7 @@ const buildDecoder = async (watchHtml) => { let { s: signature, sp: signatureParam = 'signature', url } = Object.fromEntries(params); let decodedSignature = new Function(` "use strict"; - ${varDeclaresMatches[1]} + ${varDeclares} return (${decodeFunction})("${signature}"); `)();