只要 google YouTube 下載 隨便就能找到很多網站讓你能下載影片
不過我不久前就很好奇要怎樣才能取得 yt 影片的真實網站,因此就花了點時間去研究原理
而我最後把它做成一個簡單的 api 伺服器,改一下應該也能當成 npm module 來發布
GitHub: maple3142/ytdl
而這篇文章會簡單的講是怎麼下載 yt 影片的
取得影片資訊
YouTube 官方有個 api 叫 get_video_info 可以取得一些影片的資訊,裡面當然也包括了影片網址
http://www.youtube.com/get_video_info?video_id=VIDEOID&el=embedded&ps=default&eurl=&gl=US&hl=en
裡面的 VIDEOID 是指影片的 id
例如 https://www.youtube.com/watch?v=-tKVN2mAKRI 的 id 是 -tKVN2mAKRI
在裡面會得到一串奇怪的文字,不過它的格式實際上就是 querystring 的格式
像是 key=value&key2=value2 之類的
而在解析出來的物件中會得到許多 key-value pair,其中兩個需要用到的是 url_encoded_fmt_stream_map 和 adaptive_fmts
url_encoded_fmt_stream_map 中是一個使用 , 分隔的字串,其中每個片段又是一個 querystring
格式大概如下:
{
“sp”: “signature”,
“quality”: “hd720”,
“itag”: “22”,
“url”: “https://….”,
“type”: “video/mp4;+codecs=\”avc1.64001F,+mp4a.40.2\””,
“s”: “XXXXXXXXXXXXX”
}
其中有 url,你可能會很直接的以為那個 url 就是真實的的影片網址
不過他在 sp 中沒有 signature 時確實就是真實的影片網址,但是如果有的話你會得到 http code 403
這是因為有 signature 表示這個影片是有加密的,被加密過的字串就是其中的 s,而關於解密部分會在下面講
而我上面說的 adaptive_fmts 的格式其實和 url_encoded_fmt_stream_map 是一樣的,不過它的內容是不一樣的
裡面都是一些沒有聲音的影片和純音樂檔,是代表著分離的影片與音樂
而關於要怎麼 parse 這些資料的範例可以參考: getvid.js
解密 signature
打開任何一個 yt 影片然後打開 devtool(開發人員工具) ,然後找到一個在 player 底下的 base.js
然後在裡面搜尋 signature,在第一個 match 能看到像是下面的函數,其中的 Ty 函數(也有可能是其他名稱)就是解密 signature 的函數了
左下角有個按鈕長 {},滑鼠放上去會顯示 Pretty Print,可以幫你 format 整個 js
在上面設置一個 breakpoint,然後重新整理頁面能在右側的 Scope>Closure 中看到很多東西
在裡面找到 Ty 並展開,然後點擊 [[FunctionLocation]]
devtool
接下來會跳到 Ty 函數的定義,大概長下面這樣
Ty = function(a) {
a = a.split(“”);
Sy.Ug(a, 2);
Sy.ZK(a, 72);
Sy.Ug(a, 2);
Sy.ZK(a, 10);
Sy.Ug(a, 3);
Sy.kJ(a, 60);
return a.join(“”)
}
其中會看到一個東西叫 Sy,雖然它一樣也能在 Scope>Closure 中找到,但是你沒辦法用 [[FunctionLocation]] 跳到那邊
這時候使用 Ctrl+F 搜尋 var Sy 就能很容易的找到 Sy 的定義位置,內容也大概如下
如果名稱不是 Sy,請自己替換
var Sy = {
kJ: function(a, b) {
var c = a[0];
a[0] = a[b % a.length];
a[b % a.length] = c
},
ZK: function(a) {
a.reverse()
},
Ug: function(a, b) {
a.splice(0, b)
}
};
基本上 Ty 函數和 Sy 物件就是整個解密函數的運作原理,可以用它來把前面得到的 signature 解密
然後在 url 後面加上 &signature=解密後的signature 就是真正的網址,可以用瀏覽器直接瀏覽看看
不過這東西並不是只做一次就好,因為這個加密方法還不時會更改,當你發現解密後還是失效時就請重新尋找解密函數
但是我有點懶,所以我就用 js 模擬了我剛做的所有事情,來幫我自動找出解密函數: decsig.js