Open prateek-chaubey opened 4 months ago
Quick fix is to change
let functionName = utils.between(body, `&&(b=a.get("n"))&&(b=`, `(b)`);
to
let functionName = utils.between(body, `&&(b=a.get("n"))&&(b=`, `(b)`) || utils.between(body, `&&(b=String.fromCharCode(110),c=a.get(b))&&(c=`, `(c)`);
At line 58 in lib/sig.js
Thanks it saved my day
I have update the code as described in the thread
let functionName = utils.between(body, &&(b=a.get("n"))&&(b=
, (b)
);
to
let functionName = utils.between(body, &&(b=a.get("n"))&&(b=
, (b)
) || utils.between(body, &&(b=String.fromCharCode(110),c=a.get(b))&&(c=
, (c)
);
At line 58 in lib/sig.js. But still I am getting error with code: 403.
I have update the code as described in the thread
let functionName = utils.between(body,
&&(b=a.get("n"))&&(b=
,(b)
); to let functionName = utils.between(body,&&(b=a.get("n"))&&(b=
,(b)
) || utils.between(body,&&(b=String.fromCharCode(110),c=a.get(b))&&(c=
,(c)
);At line 58 in lib/sig.js. But still I am getting error with code: 403.
That is issue #1295 and I think it is specific to blocking direct watch URL requests.
In my projects I use the innertube API and have not experienced any 403 messages.
Quick fix is to change
let functionName = utils.between(body, `&&(b=a.get("n"))&&(b=`, `(b)`);
tolet functionName = utils.between(body, `&&(b=a.get("n"))&&(b=`, `(b)`) || utils.between(body, `&&(b=String.fromCharCode(110),c=a.get(b))&&(c=`, `(c)`);
At line 58 in lib/sig.js
thank u, bro
Solutions are discuss in #1295
Instead of b=String.fromCharCode(110)
, I now see in the player source, b="nn"[+a.D]
.
Looks like some obfuscation technique. I wonder if this would be better.
let functionName = utils.between(body, 'c=a.get(b))&&(c=', '(c)');
https://github.com/fent/node-ytdl-core/issues/1305#issuecomment-2253373635
Instead of
b=String.fromCharCode(110)
, I now see in the player source,b="nn"[+a.D]
. Looks like some obfuscation technique. I wonder if this would be better.let functionName = utils.between(body, 'c=a.get(b))&&(c=', '(c)');
Again not working
It seems that they keep changing it. Now the expression looks like that:
... b=a.j.n||null)&&(b=oDa[0](b), ...
So, probably some kind of a more semantic approach should be devised.
like this ?
let functionName = utils.between(body, 'b=a.j.n||null)&&(b=oDa[0]', '(b)');
Without the oDa[0]
. But this is still syntactic and if YouTube are going to start changing it periodically from now on, it's going to keep breaking every time.
Please write the full code here again:- @corwin-of-amber
Without the
oDa[0]
. But this is still syntactic and if YouTube are going to start changing it periodically from now on, it's going to keep breaking every time.
Please write the full code here again:- @corwin-of-amber
Without the
oDa[0]
. But this is still syntactic and if YouTube are going to start changing it periodically from now on, it's going to keep breaking every time.
this is working
let functionName = utils.between(body, 'b=a.j.n||null)&&(b=', '(b)');
Hello @StepsOnes This one not working Here is my code Please check it if I am wrong share with correct code.
const extractNCode = () => {
let functionName = utils.between(body, 'b=a.j.n||null)&&(b=', '(b)');
if (functionName.includes('[')) functionName = utils.between(body, var ${functionName.split('[')[0]}=[
, ]
);
if (functionName && functionName.length) {
const functionStart = ${functionName}=function(a)
;
const ndx = body.indexOf(functionStart);
if (ndx >= 0) {
const subBody = body.slice(ndx + functionStart.length);
const functionBody = var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);
;
functions.push(functionBody);
}
}
};
Hello @StepsOnes This one not working Here is my code Please check it if I am wrong share with correct code.
const extractNCode = () => { let functionName = utils.between(body, 'b=a.j.n||null)&&(b=', '(b)'); if (functionName.includes('[')) functionName = utils.between(body,
var ${functionName.split('[')[0]}=[
,]
); if (functionName && functionName.length) { const functionStart =${functionName}=function(a)
; const ndx = body.indexOf(functionStart); if (ndx >= 0) { const subBody = body.slice(ndx + functionStart.length); const functionBody =var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);
; functions.push(functionBody); } } };
It's working for me
const extractNCode = () => {
let functionName = utils.between(body, 'b=a.j.n||null)&&(b=', '(b)');
if (functionName.includes('[')) functionName = utils.between(body, `var ${functionName.split('[')[0]}=[`, `]`);
if (functionName && functionName.length) {
const functionStart = `${functionName}=function(a)`;
const ndx = body.indexOf(functionStart);
if (ndx >= 0) {
const subBody = body.slice(ndx + functionStart.length);
const functionBody = `var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);`;
functions.push(functionBody);
}
}
};
I am checking it. it's not working properly. I think YouTube is asking for a captcha to download and convert video. @StepsOnes
Rather than trying to find the obfuscated n function name from elsewhere in the player code, this looks for the n code function directly.
const extractNCode = () => {
const alphanum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ.$_0123456789';
let functionName = '';
let clue = body.indexOf('enhanced_except');
if (clue < 0) clue = body.indexOf('String.prototype.split.call(a,"")');
if (clue < 0) clue = body.indexOf('Array.prototype.join.call(b,"")');
if (clue > 0) {
let nstart = body.lastIndexOf('=function(a){', clue) - 1;
while (nstart && alphanum.includes(body.charAt(nstart))) {
functionName = body.charAt(nstart) + functionName;
nstart--;
}
}
if (functionName && functionName.length) {
const functionStart = `${functionName}=function(a)`;
const ndx = body.indexOf(functionStart);
if (ndx >= 0) {
const subBody = body.slice(ndx + functionStart.length);
const functionBody = `var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);`;
functions.push(functionBody);
}
}
};
const extractNCode = () => { const alphanum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ.$_0123456789'; let functionName = ''; let clue = body.indexOf('enhanced_except'); if (clue < 0) clue = body.indexOf('String.prototype.split.call(a,"")'); if (clue < 0) clue = body.indexOf('Array.prototype.join.call(b,"")'); if (clue > 0) { let nstart = body.lastIndexOf('=function(a){', clue) - 1; while (nstart && alphanum.includes(body.charAt(nstart))) { functionName = body.charAt(nstart) + functionName; nstart--; } if (functionName && functionName.length) { const functionStart =
${functionName}=function(a)
; const ndx = body.indexOf(functionStart); if (ndx >= 0) { const subBody = body.slice(ndx + functionStart.length); const functionBody =var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);
; functions.push(functionBody); } } };
Can i use this code?
Can i use this code?
Replaces lines 57-69 in lib/sig.js
Doesn't fix 403 issues, though, which is another YouTube barrier entirely.
Can i use this code?
Replaces lines 57-69 in lib/sig.js
Doesn't fix 403 issues, though, which is another YouTube barrier entirely.
Showing 502 bad gateway error
Showing 502 bad gateway error
A bracket was missing. Please try again.
Showing 502 bad gateway error
A bracket was missing. Please try again.
Its working very slow sometime not fetch data. Any better solution?
Its working very slow sometime not fetch data. Any better solution?
YouTube now requires POST requests with a specific byte array payload in order to download longer high quality files. For me, this is still a work in progress. Maybe someone else has found a solution.
But do you have any code-level solution where I can change my API code? Its better than previous code.
it doesn't work again
it doesn't work again
let functionName = utils.between(body, 'b=a.j.n||null)&&(b=', '(b)');
is now required to find
c=a.j[b]||null)&&(c=zDa[0](c);
which it wont. And it will keep breaking time and time again. That is why it is better to locate the function directly. The method I posted above still works.
it doesn't work again
let functionName = utils.between(body, 'b=a.j.n||null)&&(b=', '(b)');
is now required to findc=a.j[b]||null)&&(c=zDa[0](c);
which it wont. And it will keep breaking time and time again. That is why it is better to locate the function directly. The method I posted above still works.
I tried your function, it doesn't work
I tried your function, it doesn't work
Downloaded current version
Replaced the extractNcode function in sig.js
Added console.log(functionName);
before if (functionName && functionName.length) {
When I run the quick example from the project page
const fs = require('fs');
const ytdl = require('./lib/index.js');
ytdl('http://www.youtube.com/watch?v=aqz-KE-bpKQ') .pipe(fs.createWriteStream('video.mp4'));
The output is
C:\Dev\test_nyc>node app
Ema
and in the directory listing:
2024/08/07 13:38 125 829 121 video.mp4
So yes, my function works.
When I use the videoId (4jnaYhnmYlo) in your example:
2024/08/07 13:56 58 553 785 video.mp4
I tried your function, it doesn't work
Downloaded current version Replaced the extractNcode function in sig.js Added
console.log(functionName);
beforeif (functionName && functionName.length) {
When I run the quick example from the project pageconst fs = require('fs'); const ytdl = require('./lib/index.js'); ytdl('http://www.youtube.com/watch?v=aqz-KE-bpKQ') .pipe(fs.createWriteStream('video.mp4'));
The output is
C:\Dev\test_nyc>node app Ema
and in the directory listing:
2024/08/07 13:38 125 829 121 video.mp4
So yes, my function works.
When I use the videoId (4jnaYhnmYlo) in your example:
2024/08/07 13:56 58 553 785 video.mp4
Still not working youtube again fix it
Rather than trying to find the obfuscated n function name from elsewhere in the player code, this looks for the n code function directly.
const extractNCode = () => { const alphanum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ.$_0123456789'; let functionName = ''; let clue = body.indexOf('enhanced_except'); if (clue < 0) clue = body.indexOf('String.prototype.split.call(a,"")'); if (clue < 0) clue = body.indexOf('Array.prototype.join.call(b,"")'); if (clue > 0) { let nstart = body.lastIndexOf('=function(a){', clue) - 1; while (nstart && alphanum.includes(body.charAt(nstart))) { functionName = body.charAt(nstart) + functionName; nstart--; } } if (functionName && functionName.length) { const functionStart = `${functionName}=function(a)`; const ndx = body.indexOf(functionStart); if (ndx >= 0) { const subBody = body.slice(ndx + functionStart.length); const functionBody = `var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);`; functions.push(functionBody); } } };
This work for me, but i modified the functionBody to: var ${functionStart}${utils.cutAfterJS(subBody)};return ${functionName}(ncode);
;
Thanks for the solution.
Rather than trying to find the obfuscated n function name from elsewhere in the player code, this looks for the n code function directly.
const extractNCode = () => { const alphanum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ.$_0123456789'; let functionName = ''; let clue = body.indexOf('enhanced_except'); if (clue < 0) clue = body.indexOf('String.prototype.split.call(a,"")'); if (clue < 0) clue = body.indexOf('Array.prototype.join.call(b,"")'); if (clue > 0) { let nstart = body.lastIndexOf('=function(a){', clue) - 1; while (nstart && alphanum.includes(body.charAt(nstart))) { functionName = body.charAt(nstart) + functionName; nstart--; } } if (functionName && functionName.length) { const functionStart = `${functionName}=function(a)`; const ndx = body.indexOf(functionStart); if (ndx >= 0) { const subBody = body.slice(ndx + functionStart.length); const functionBody = `var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);`; functions.push(functionBody); } } };
This work for me, but i modified the functionBody to:
var ${functionStart}${utils.cutAfterJS(subBody)};return ${functionName}(ncode);
;Thanks for the solution.
Not working i am trying still got an error
Rather than trying to find the obfuscated n function name from elsewhere in the player code, this looks for the n code function directly.
const extractNCode = () => { const alphanum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ.$_0123456789'; let functionName = ''; let clue = body.indexOf('enhanced_except'); if (clue < 0) clue = body.indexOf('String.prototype.split.call(a,"")'); if (clue < 0) clue = body.indexOf('Array.prototype.join.call(b,"")'); if (clue > 0) { let nstart = body.lastIndexOf('=function(a){', clue) - 1; while (nstart && alphanum.includes(body.charAt(nstart))) { functionName = body.charAt(nstart) + functionName; nstart--; } } if (functionName && functionName.length) { const functionStart = `${functionName}=function(a)`; const ndx = body.indexOf(functionStart); if (ndx >= 0) { const subBody = body.slice(ndx + functionStart.length); const functionBody = `var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);`; functions.push(functionBody); } } };
This work for me, but i modified the functionBody to:
var ${functionStart}${utils.cutAfterJS(subBody)};return ${functionName}(ncode);
; Thanks for the solution.Not working i am trying still got an error
What is the error that is presented to you? An 403?
Yes showing 403 error progress bar not working still stuck on progress bar
Here is code which you provided See if i do mistake let me know @JoaoEmanuell
const extractNCode = () => {
const alphanum = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTVUWXYZ.$_0123456789';
let functionName = '';
let clue = body.indexOf('enhanced_except');
if (clue < 0) clue = body.indexOf('String.prototype.split.call(a,"")');
if (clue < 0) clue = body.indexOf('Array.prototype.join.call(b,"")');
if (clue > 0) {
let nstart = body.lastIndexOf('=function(a){', clue) - 1;
while (nstart && alphanum.includes(body.charAt(nstart))) {
functionName = body.charAt(nstart) + functionName;
nstart--;
}
}
if (functionName && functionName.length) {
const functionStart = ${functionName}=function(a)
;
const ndx = body.indexOf(functionStart);
if (ndx >= 0) {
const subBody = body.slice(ndx + functionStart.length);
const functionBody = var ${functionStart}${utils.cutAfterJS(subBody)};return ${functionName}(ncode);
;
functions.push(functionBody);
}
}
};
Please see i got stuck on progress bar
I did notice that sending the Range header (e.g. Range: bytes=0-10485760
) is now mandatory. Without it, 403 is returned. This was not like that before.
I did notice that sending the Range header (e.g.
Range: bytes=0-10485760
) is now mandatory. Without it, 403 is returned. This was not like that before.
So what should we do now? @corwin-of-amber
I am not sure as it does not seem like node-ytdl-core is even maintained. I am trying to trace what https://github.com/yt-dlp/yt-dlp is doing. Their approach seems to be robust, and they have cached ncode functions (probably hosted somewhere).
I really don't understand why this isn't working, but distube works fine.
I really don't understand why this isn't working, but distube works fine.
Yes, these guys are using the player API, which is also what yt-dlp is doing. Probably a more stable approach, so perhaps we should just declare distubejs as the "canonical" YouTube downloader for Node.js.
This library is f*****ed up last 1 month not working. i will not recommended
Its working very slow sometime not fetch data. Any better solution?
YouTube now requires POST requests with a specific byte array payload in order to download longer high quality files. For me, this is still a work in progress. Maybe someone else has found a solution.
I'm currently working on the same thing too
I really don't understand why this isn't working, but distube works fine.
Hmm... Were you able to download and watch the video? If so, please share your code. I tried it few days ago but urls returned 403 error
I really don't understand why this isn't working, but distube works fine.
Hmm... Were you able to download and watch the video? If so, please share your code. I tried it few days ago but urls returned 403 error
My code is a adaptation of react-native-ytdl.
I tested the code in node, but not work (403 error), however the code works in react native, i really don't know why this occurs.
Tip: change the "Logger.debug" for "console.log"
In the future, i go adapt the distube to react native, when the node-ytdl-core doesnt's work anymore.
I really don't understand why this isn't working, but distube works fine.
Hmm... Were you able to download and watch the video? If so, please share your code. I tried it few days ago but urls returned 403 error
My code is a adaptation of react-native-ytdl.
I tested the code in node, but not work (403 error), however the code works in react native, i really don't know why this occurs.
Tip: change the "Logger.debug" for "console.log"
In the future, i go adapt the distube to react native, when the node-ytdl-core doesnt's work anymore.
Okay, good. Are you currently making use of ytdl-core or distube ?
I really don't understand why this isn't working, but distube works fine.
Hmm... Were you able to download and watch the video? If so, please share your code. I tried it few days ago but urls returned 403 error
My code is a adaptation of react-native-ytdl. I tested the code in node, but not work (403 error), however the code works in react native, i really don't know why this occurs. My sig code Tip: change the "Logger.debug" for "console.log" In the future, i go adapt the distube to react native, when the node-ytdl-core doesnt's work anymore.
Okay, good. Are you currently making use of ytdl-core or distube ?
As a base I'm using ytdl-core but a large part of the sig (like the part of extracting functions) was adapted from distube, I combined part of the two to make it work in react native.
Doesn't fix 403 issues, though, which is another YouTube barrier entirely.
One of the variants to bypass 403 is downloading in chunks. I use this variant with distube and its worked for me. As example - videoFile with 260Mb+ called 403, loading with chunks bypasses this limitation/error/etc.
.... in cycle ...
const stream = ytdl(url, { quality: "highestvideo", range: { start: start, end: start + chunkSize - 1 }, });
....
Doesn't fix 403 issues, though, which is another YouTube barrier entirely.
One of the variants to bypass 403 is downloading in chunks. I use this variant with distube and its worked for me. As example - videoFile with 260Mb+ called 403, loading with chunks bypasses this limitation/error/etc.
.... in cycle ... const stream = ytdl(url, { quality: "highestvideo", range: { start: start, end: start + chunkSize - 1 }, }); ....
Are you able to stream the downloadable video url on a browser?
Are you able to stream the downloadable video url on a browser?
Yes, of course.
This a example of code:
index.js
(start with node index.js
) :
const express = require("express");
const ytdl = require("@distube/ytdl-core");
const app = express();
app.get("/video", async (req, res) => {
const url = "https://www.youtube.com/watch?v=ckIQOTduNbI";
const range = req.headers.range;
if (!range) {
return res.status(400).send("Requires Range header");
}
try {
const videoInfo = await ytdl.getInfo(url);
const formats = videoInfo.formats.filter((e) => e.hasVideo && e.contentLength);
const videoFormat = ytdl.chooseFormat(formats, { quality: "highest" });
const videoSize = parseInt(videoFormat.contentLength);
const chunkSize = 10 * 1024 * 1024; // 10MB
const start = Number(range.replace(/\D/g, ""));
const end = Math.min(start + chunkSize - 1, videoSize - 1);
const contentLength = end - start + 1;
const headers = {
"Content-Range": `bytes ${start}-${end}/${videoSize}`,
"Accept-Ranges": "bytes",
"Content-Length": contentLength,
"Content-Type": "video/mp4",
};
res.writeHead(206, headers);
const videoStream = ytdl(url, {
quality: "highest",
filter: (e) => e.hasVideo && e.contentLength,
range: { start, end },
});
videoStream.on("error", (err) => console.error(err));
videoStream.on("progress", (chunkLength, downloaded, total) => {
console.log(`${chunkLength} / ${((downloaded / total) * 100).toFixed(2)}% loaded`);
});
videoStream.pipe(res).on("finish", () => {
console.log("Streaming finished");
});
} catch (error) {
console.error("Error fetching video:", error);
res.status(500).send("Error fetching video");
}
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
index.html
:
<!DOCTYPE html>
<html lang="uk">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Video Stream</title>
<style>
.centered {
margin-left: auto;
margin-right: auto;
margin-top: 1em;
width: 720px;
}
</style>
</head>
<body>
<div class="centered">
<video id="videoPlayer" width="720" controls autoplay>
<source src="http://localhost:3000/video" type="video/mp4" />
Your browser does not support the video tag. Please try again with a modern browser.
</video>
</div>
</body>
</html>
This code works great. However, keep in mind that the audio might be separate.
Are you able to stream the downloadable video url on a browser?
Yes, of course.
This a example of code:
index.js
(start withnode index.js
) :const express = require("express"); const ytdl = require("@distube/ytdl-core"); const app = express(); app.get("/video", async (req, res) => { const url = "https://www.youtube.com/watch?v=ckIQOTduNbI"; const range = req.headers.range; if (!range) { return res.status(400).send("Requires Range header"); } try { const videoInfo = await ytdl.getInfo(url); const formats = videoInfo.formats.filter((e) => e.hasVideo && e.contentLength); const videoFormat = ytdl.chooseFormat(formats, { quality: "highest" }); const videoSize = parseInt(videoFormat.contentLength); const chunkSize = 10 * 1024 * 1024; // 10MB const start = Number(range.replace(/\D/g, "")); const end = Math.min(start + chunkSize - 1, videoSize - 1); const contentLength = end - start + 1; const headers = { "Content-Range": `bytes ${start}-${end}/${videoSize}`, "Accept-Ranges": "bytes", "Content-Length": contentLength, "Content-Type": "video/mp4", }; res.writeHead(206, headers); const videoStream = ytdl(url, { quality: "highest", filter: (e) => e.hasVideo && e.contentLength, range: { start, end }, }); videoStream.on("error", (err) => console.error(err)); videoStream.on("progress", (chunkLength, downloaded, total) => { console.log(`${chunkLength} / ${((downloaded / total) * 100).toFixed(2)}% loaded`); }); videoStream.pipe(res).on("finish", () => { console.log("Streaming finished"); }); } catch (error) { console.error("Error fetching video:", error); res.status(500).send("Error fetching video"); } }); app.listen(3000, () => { console.log("Server is running on port 3000"); });
index.html
:<!DOCTYPE html> <html lang="uk"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Video Stream</title> <style> .centered { margin-left: auto; margin-right: auto; margin-top: 1em; width: 720px; } </style> </head> <body> <div class="centered"> <video id="videoPlayer" width="720" controls autoplay> <source src="http://localhost:3000/video" type="video/mp4" /> Your browser does not support the video tag. Please try again with a modern browser. </video> </div> </body> </html>
This code works great. However, keep in mind that the audio might be separate.
Wow, it's working perfectly now. Now, if i want to download audio and video, i'll repeat same process then merge it with ffmpeg
if i want to download audio and video, i'll repeat same process then merge it with ffmpeg
Yes, it will make the code a bit more complex, but that's exactly how you need to proceed.
Since last evening the ytdl isn't working, it's showing all the formats and video info , but the the video urls aren't working , i checked the code and it seems like after YouTube got updated , the ncode extraction isn't working.