notes
[ru / en]

How to download mp4 from YouTube in the browser

2026.02.07

We're digging through YouTube's code to get links to the original mp4s.

For those who want to get started quickly, the instructions are below.

Details

If you open YouTube on your desktop, the video is loaded in parts by the webm stream.

On the video page (specifically the video), in ytInitialPlayerResponse.streamingData.formats, there is a data array that includes a link to a low-quality mp4 file in 360p. She doesn't work.

But it uses the URL stream from ytInitialPlayerResponse.streamingData.serverAbrStreamingUrl.

There is a list of all available formats (webm, mp4) in ytInitialPlayerResponse.streamingData.adaptiveFormats, but there is no URL.

All links in adaptiveFormats appear as soon as you visit a video page from your mobile. If you don't know, DevTools allows you to emulate different devices (change screen size and set a different UserAgent).

In older videos, the url is in the url field, but in relatively newer ones, it is in the signatureCipher field, which has the following format (separated by an ampersand):

s=EJE4JURJJKKLHFM75uutnjuyuyu8899oiooyioyGy-iyoAc6t6IIYOYOyoo...\nsp=sig\nurl=https://rr3.googlevideo.com/videoplayback?expire=...
  • s - sign for url
  • sp - it contains the name of the signature parameter, which is sig
  • url - URL

Adding &sig=<s> to url doesn't help because there's some manipulation in the signature:

  • a pair of characters is swapped
  • a couple of characters are cut out
  • reverse string

Since developers can change the method with a single finger, there's no point in using these algorithms. They won't last long. I tested this while writing this note.

The main idea is to simply discard all webm files, keep the mp4 files, and feed them to the player.

But I still want to make fewer movements, and switching between desktop and mobile modes is tiring. What should I do?

If you try to share a video, it opens an iframe with the url https://www.youtube.com/embed/<video_id>. And there is a POST request to https://www.youtube.com/youtubei/v1/player, which contains all the links to everything.

This is the most important set of data that YouTube needs to provide video data:

{\n\tvideoId: <video_id>,\n\tcontext: {\n\t\tclient: <client_info>,\n\t\tthirdParty: {\n\t\t\t"embedUrl": "https://www.youtube.com/watch?v=" + <video_id>,\n\t\t},\n\t},\n\tplaybackContext: {\n\t\tcontentPlaybackContext: {\n\t\t\tsignatureTimestamp: 20486\n\t\t}\n\t}\n}
  • video_id - the id of the video that is in the v parameter in the url. logical, yes.
  • client_info - client data (about us), located in ytcfg.data_.INNERTUBE_CONTEXT.client

We make a request, but there are no links. Wonders. You just need to change client_info.clientName to WEB_EMBEDDED_PLAYER, which is initially WEB.

I have the links, now all I have to do is feed the mp4 to the player.

First we filter:

if (e.mimeType.startsWith('video/mp4')) {\n\tmp4.push(e);\n}

Updating the player config

window.ytplayer.config = {args: {raw_player_response: <new_video_info>}};

Reset the player area #ytd-player > #container

player.innerHTML = '';

We create a player with new data using the following steps:

createPlayer = window.yt && window.yt.player && window.yt.player.Application \n\t&& (window.yt.player.Application.createAlternate || window.yt.player.Application.create)

All player configurations are available in ytcfg.data_.WEB_PLAYER_CONTEXT_CONFIGS, the one you need is WEB_PLAYER_CONTEXT_CONFIG_ID_KEVLAR_WATCH:

createPlayer(\n\tplayer,\n\twindow.ytplayer.config,\n\tytcfg.data_.WEB_PLAYER_CONTEXT_CONFIGS.WEB_PLAYER_CONTEXT_CONFIG_ID_KEVLAR_WATCH\n);

Instruction

Attention When using the mp4 player, quality switching does not work. To get each link, you need to recreate the player with the desired quality.

  • On the video page (the v parameter is present in the address), press ctrl+shift+i and we get to the DevTools console
  • Paste this code into the console, and a download link to the mp4 will appear in the upper left corner of the player
(function () {\n\tlet video_id = (new URLSearchParams(location.search)).get('v');\n\tif (!video_id)\n\t\tthrow new Error('video_id not found');\n\n\tlet client = ytcfg.data_.INNERTUBE_CONTEXT.client;\n\tclient.clientName = 'WEB_EMBEDDED_PLAYER';\n\n\tlet data = {\n\t\tvideoId: video_id,\n\t\tcontext: {\n\t\t\tclient: ytcfg.data_.INNERTUBE_CONTEXT.client,\n\t\t\tthirdParty: {\n\t\t\t\t'embedUrl': 'https://www.youtube.com/watch?v=' + video_id,\n\t\t\t},\n\t\t},\n\t\tplaybackContext: {\n\t\t\tcontentPlaybackContext: {\n\t\t\t\tsignatureTimestamp: 20486\n\t\t\t}\n\t\t}\n\t};\n\n\tfetch('/youtubei/v1/player', {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t},\n\t\tbody: JSON.stringify(data)\n\t}).then(function (response) {\n\t\tif (!response.ok)\n\t\t\tthrow new Error(`HTTP error: ${response.status}`);\n\n\t\treturn response.json();\n\t}).then(function (data) {\n\n\t\tlet mp4 = [];\n\t\tdata.streamingData.adaptiveFormats.forEach(e => {\n\t\t\tif (e.mimeType.startsWith('video/mp4')) {\n\t\t\t\tmp4.push(e);\n\t\t\t}\n\t\t});\n\t\tif (!mp4.length)\n\t\t\tthrow new Error('mp4 links not found');\n\n\t\tdata.streamingData.adaptiveFormats = mp4;\n\t\tdata.streamingData.formats = mp4;\n\t\twindow.ytplayer.config = {\n\t\t\targs: {\n\t\t\t\traw_player_response: data\n\t\t\t}\n\t\t};\n\n\t\tlet player = document.querySelector('#ytd-player > #container');\n\t\tplayer.innerText = '';\n\n\t\tlet createPlayer = window.yt && window.yt.player\n\t\t\t&& window.yt.player.Application\n\t\t\t&& (window.yt.player.Application.createAlternate || window.yt.player.Application.create);\n\n\t\tcreatePlayer(\n\t\t\tplayer,\n\t\t\twindow.ytplayer.config,\n\t\t\tytcfg.data_.WEB_PLAYER_CONTEXT_CONFIGS.WEB_PLAYER_CONTEXT_CONFIG_ID_KEVLAR_WATCH\n\t\t);\n\n\t\tlet link = document.createElement('a');\n\t\tlink.setAttribute('target', '_blank');\n\t\tlink.style.cssText = `\n\t\t\tposition: absolute;\n\t\t\tz-index: 1;\n\t\t\tbackground: #fff;\n\t\t\tcolor: #000;\n\t\t`;\n\t\tlink.innerText = 'download';\n\n\t\tlet video = document.querySelector('video');\n\t\tplayer.previousElementSibling && player.previousElementSibling.remove();\n\t\tif (video.getAttribute('src').startsWith('https://')) {\n\t\t\tlink.setAttribute('href', video.getAttribute('src'));\n\t\t\tplayer.before(link);\n\t\t}\n\n\t}).catch(error => {\n\t\tconsole.error('Fetch error:', error.message);\n\t});\n\n})();

Результат

mp4 ссылка в Youtube
more topics: download, js