Javascript 如何获取 YouTube 视频的直接网址
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8402457/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get direct url for youtube videos
提问by Feng Huo
All the current tutorials on making your own youtube video downloader are outdated. The whole token insertion from video info does not work, and neither does using the javascript method. Does anyone know of a way to do this currently without just downloading someone else's programs? I just want to know the method to go about so I can program my own (for fun :D)
当前有关制作自己的 youtube 视频下载器的所有教程都已过时。视频信息中的整个令牌插入不起作用,使用 javascript 方法也不起作用。有没有人知道目前无需下载其他人的程序即可执行此操作的方法?我只想知道要进行的方法,以便我可以编写自己的程序(为了好玩:D)
Thanks in advance!
提前致谢!
回答by Geuis
As of 1/1/2018, the old technique described below doesn't seem to work reliably. Youtube's in-page variable structure seems to have changed and I haven't been able to figure out the updated version.
截至 2018 年 1 月 1 日,下面描述的旧技术似乎不能可靠地工作。Youtube 的页内变量结构似乎发生了变化,我一直无法弄清楚更新的版本。
Here's an updated (June, 2017) version that works directly from the dev tools console available in any browser. Currently should work for any Youtube media type, including up to 4k if its available.
这是一个更新的(2017 年 6 月)版本,可直接从任何浏览器中可用的开发工具控制台运行。目前应该适用于任何 Youtube 媒体类型,包括最多 4k(如果可用)。
https://gist.github.com/geuis/8b1b2ea57d7f9a9ae22f80d4fbf5b97f
https://gist.github.com/geuis/8b1b2ea57d7f9a9ae22f80d4fbf5b97f
// ES6 version
const videoUrls = ytplayer.config.args.adaptive_fmts
.split(',')
.map(item => item
.split('&')
.reduce((prev, curr) => (curr = curr.split('='),
Object.assign(prev, {[curr[0]]: decodeURIComponent(curr[1])})
), {})
)
.reduce((prev, curr) => Object.assign(prev, {
[curr.quality_label || curr.type]: curr
}), {});
console.log(videoUrls);
// ES5 version
var videoUrls = ytplayer.config.args.adaptive_fmts
.split(',')
.map(function (item) {
return item
.split('&')
.reduce(function (prev, curr) {
curr = curr.split('=');
return Object.assign(prev, {[curr[0]]: decodeURIComponent(curr[1])})
}, {});
})
.reduce(function (prev, curr) {
return Object.assign(prev, {
[curr.quality_label || curr.type]: curr
});
}, {});
console.log(videoUrls);
Sample output for https://www.youtube.com/watch?v=9bZkp7q19f0
https://www.youtube.com/watch?v=9bZkp7q19f0 的示例输出
{
"1080p": {
"itag": "248",
"xtags": "",
"lmt": "1440215955569849",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=248&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "video/webm;+codecs=\"vp9\"",
"bitrate": "1733724",
"index": "243-1977",
"size": "1920x1080",
"projection_type": "1",
"fps": "30",
"clen": "31192903",
"init": "0-242",
"quality_label": "1080p"
},
"720p": {
"itag": "247",
"xtags": "",
"lmt": "1440215905109639",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=247&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "video/webm;+codecs=\"vp9\"",
"bitrate": "726076",
"index": "243-1933",
"size": "1280x720",
"projection_type": "1",
"fps": "30",
"clen": "15801933",
"init": "0-242",
"quality_label": "720p"
},
"480p": {
"itag": "244",
"xtags": "",
"lmt": "1440215890236689",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=244&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "video/webm;+codecs=\"vp9\"",
"bitrate": "396541",
"index": "243-1933",
"size": "854x480",
"projection_type": "1",
"fps": "30",
"clen": "7928237",
"init": "0-242",
"quality_label": "480p"
},
"360p": {
"itag": "243",
"xtags": "",
"lmt": "1440215888783441",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=243&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "video/webm;+codecs=\"vp9\"",
"bitrate": "223695",
"index": "243-1933",
"size": "640x360",
"projection_type": "1",
"fps": "30",
"clen": "5127362",
"init": "0-242",
"quality_label": "360p"
},
"240p": {
"itag": "242",
"xtags": "",
"lmt": "1440215900971640",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=242&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "video/webm;+codecs=\"vp9\"",
"bitrate": "113952",
"index": "242-1931",
"size": "426x240",
"projection_type": "1",
"fps": "30",
"clen": "2597162",
"init": "0-241",
"quality_label": "240p"
},
"144p": {
"itag": "278",
"xtags": "",
"lmt": "1440215900119192",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=278&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "video/webm;+codecs=\"vp9\"",
"bitrate": "60303",
"index": "242-1930",
"size": "256x144",
"projection_type": "1",
"fps": "15",
"clen": "1798744",
"init": "0-241",
"quality_label": "144p"
},
"audio/mp4;+codecs=\"mp4a.40.2\"": {
"bitrate": "128266",
"itag": "140",
"xtags": "",
"lmt": "1440578358539132",
"index": "592-1271",
"clen": "8482615",
"projection_type": "1",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=140&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "audio/mp4;+codecs=\"mp4a.40.2\"",
"init": "0-591"
},
"audio/webm;+codecs=\"vorbis\"": {
"bitrate": "118499",
"itag": "171",
"xtags": "",
"lmt": "1440215938192462",
"index": "4452-5366",
"clen": "6383456",
"projection_type": "1",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=171&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "audio/webm;+codecs=\"vorbis\"",
"init": "0-4451"
},
"audio/webm;+codecs=\"opus\"": {
"bitrate": "154966",
"itag": "251",
"xtags": "",
"lmt": "1440215889283443",
"index": "272-1186",
"clen": "9526605",
"projection_type": "1",
"url": "https://r16---sn-n4v7kn7r.c.youtube.com/videoplayback?itag=251&keepalive=ye…2Cmm%2Cmn%2Cms%2Cmv%2Cpl%2Crequiressl%2Csc%2Csource%2Cexpire&mt=1498245944",
"type": "audio/webm;+codecs=\"opus\"",
"init": "0-271"
}
}
回答by Ivan Castellanos
As of today 15/06/2012 is really simple; but you have to be watch for future changes. Here it is (in Javascript)
截至今天 15/06/2012 真的很简单;但你必须注意未来的变化。在这里(在 Javascript 中)
try{
var urls = document.body.innerHTML.match(/"url_encoded_fmt_stream_map": "url=([^"]+)/)[1]
urls = decodeURIComponent(urls).replace(/\u0026/g,'&')
urls = urls.replace(/&quality.+?(?=,url)/g,'');;
urls = urls.split(',url=')
// urls is an array of all the possible qualities
// To download one you could use something like:
// document.location = urls[0]
// The first one is usually in the highest quality available
} catch(e){
console.error("Youtube may have changed its API")
}
回答by Sepehr
Why do you want to reinvent the wheel? use Rapidleech script!
为什么要重新发明轮子?使用 Rapidleech 脚本!
Btw, if you still need to reinvent the wheel, here is source-code of the Youtube plugin of Rapidleech (it's highly readable:
顺便说一句,如果您仍然需要重新发明轮子,这里是 Rapidleech 的 Youtube 插件的源代码(它具有高度可读性:
class youtube_com extends DownloadClass {
/*
Some blah blah about the age verification and erroneous URLs
$fmt is quality/format number and is an integer
*/
public function Download($link) {
$this->fmts = array(38,37,22,45,35,44,34,43,18,5,17);
$yt_fmt = empty($_REQUEST['yt_fmt']) ? '' : $_REQUEST['yt_fmt'];
$this->fmturlmaps = $this->GetVideosArr($fmt_url_maps);
if (empty($yt_fmt) && !isset($_GET["audl"])) return $this->QSelector($link);
elseif (isset($_REQUEST['ytube_mp4']) && $_REQUEST['ytube_mp4'] == 'on' && !empty($yt_fmt)) {
//look for and download the highest quality we can find?
if ($yt_fmt == 'highest') {
foreach ($this->fmts as $fmt) {
if (array_key_exists($fmt, $this->fmturlmaps)) {
$furl = $this->fmturlmaps[$fmt];
break;
}
}
} else { //get the format the user specified (making sure it actually exists)
if (!$furl = $this->fmturlmaps[$yt_fmt]) html_error ('Specified video format not found');
$fmt = $yt_fmt;
}
} else { //just get the one Youtube plays by default (in some cases it could also be the highest quality format)
$fmt = key($this->fmturlmaps);
$furl = $this->fmturlmaps[$fmt];
}
if (preg_match ('%^5|34|35$%', $fmt)) $ext = '.flv';
elseif (preg_match ('%^17$%', $fmt)) $ext = '.3gp';
elseif (preg_match ('%^18|22|37|38$%', $fmt)) $ext = '.mp4';
elseif (preg_match ('%^43|44|45$%', $fmt)) $ext = '.webm';
else $ext = '.flv';
if (!preg_match('#<title>(.*)\s+-\sYouTube[\r|\n|\t|\s]*</title>#Us', $this->page, $title)) html_error('No video title found! Download halted.');
if (!preg_match ('/video_id=(.+?)(\\|"|&|(\\u0026))/', $this->page, $video_id)) html_error('Video id not found.');
$FileName = str_replace (Array ("\", "/", ":", "*", "?", "\"", "<", ">", "|"), "_", html_entity_decode(trim($title[1]), ENT_QUOTES)) . "-[{$video_id[1]}][f$fmt]$ext";
if (stristr($furl, '|')) {
$u_arr = explode('|', $furl);
$furl = preg_replace('#://([^/]+)#', "://".$u_arr[2], $u_arr[0]);
}
if (isset($_REQUEST['ytdirect']) && $_REQUEST['ytdirect'] == 'on')
{
echo "<br /><br /><h4><a style='color:yellow' href='" . urldecode($furl) . "'>Click here or copy the link to your download manager to download</a></h4>";
echo "<input name='dlurl' style='width: 1000px; border: 1px solid #55AAFF; background-color: #FFFFFF; padding:3px' value='" . urldecode($furl) . "' onclick='javascript:this.select();' readonly></input>";
}
else
{
$this->RedirectDownload (urldecode($furl), $FileName, $this->cookie, 0, 0, $FileName);
}
}