How to get direct url for youtube videos

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)

Thanks in advance!

Answers


As of today 15/06/2012 is really simple; but you have to be watch for future changes. Here it is (in 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")
}

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.

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.

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

{
  "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"
  }
}

Why do you want to reinvent the wheel? use Rapidleech script!

Btw, if you still need to reinvent the wheel, here is source-code of the Youtube plugin of Rapidleech (it's highly readable:


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);
        }
    }

Need Your Help

Where does this value come from in this Sudoku solver implementation in MIPS asm

assembly mips sudoku

I'm interested in this block of code, from https://github.com/delucas/sudoku-project/blob/master/sudoku-assembler-mips/sudokiller.s#L158

$post->post_parent behaves different on page then in functions.php

php wordpress parent breadcrumbs

I created/modified a function to display breadcrumbs on pages on WordPress. The modified version makes use of #post->post_parent to get the parent of a page in order to have a full breadcrumb trail...