星期五, 7月 20, 2012

[Youtube][JavaScript] How to get Youtube Streaming URL, subtitle and resolution.

[ 2012/9/27更新 ]

  Youtube 在擷取影音串流多將原本的 sig參數改為 signature,但是在取得 Video Information的網址並沒有跟著修改,我參照了 youtube-dl的改法將 parse的規則改了一下,應該就過囉.只是不知道何時會再修改就是了.
記錄在下方這個commit.
https://github.com/kvzhuang/video-parser/commit/1d2c6cb7b5de852951d1eaf8153a6e0ee984409a


  最近因為一些需求在研究youtube的影音串流網址,其目的就是有一個 youtube影片的URL,最後得到此影片真實的影音串流.

  分做三個部分:取得youtube的原始影音串流,取得字幕檔,處理解析度.


影音串流:
  首先是先研究 Python的 youtube-dl ,目的是傳入一個 youtube的影片網址然後取得其真實的影音串流,思考方式是設置一個 API Server,當有使用者送出 Request後,透過 youtube-dl取得影片的資訊,可以選擇格式下載檔案( FLV,  MP4..包括 srt字幕檔案),或是取得影音串流 URL並且回傳給使用者,功能相當齊全.

  然而有一個問題就是跨國之間 youtube的影音串流網址不同,如果我們使用一個 API Server架在台灣,若其他國家來跟我們詢問,透過的是我們網段取得的影音串流網址,再轉給其他國家將會無法使用. 

  所以把目標轉到 JavaScript,這個網路上有很多人做,主要就是透過 Bookmark(或是Greasemonkey),在使用者端執行 Script Injection.做法有很多,如 Vexe'd有介紹用我的最愛下載 YouTube 影片.如果要更複雜一點的就如下方的 Script,可以把它貼到bookmark裡面,在看影片時候執行即可以下載影片.


javascript:(function() {
    function get_filetype(fmt_id) {
        switch(fmt_id) {
            case '5':
            case '6':
            case '34':
            case '35':
                return 'FLV';
            case '18':
            case '22':
            case '37':
            case '38':
            case '82':
            case '83':
            case '84':
            case '85':
                return 'MP4';
            case '43':
            case '44':
            case '45':
            case '46':
            case '100':
            case '101':
            case '102':
                return 'WEBM';
            default:
                return 'unknown';
        }
    }
    function get_video_url(fmt_stream_map, 
                           chosen_fmt_id, 
                           avail_fmt_ids) {
        fmt_stream_map=unescape(fmt_stream_map);
        if(avail_fmt_ids.indexOf(chosen_fmt_id)===0)
            return 


fmt_stream_map.substring(4,
                         fmt_stream_map.indexOf('&quality='));

        if(avail_fmt_ids.indexOf(chosen_fmt_id)===avail_fmt_ids.length-1)
            return 


fmt_stream_map.substring(fmt_stream_map.lastIndexOf('http'),
                         fmt_stream_map.lastIndexOf('&quality='));
        var pattern=new RegExp('&itag='+avail_fmt_ids[avail_fmt_ids.indexOf(chosen_fmt_id)-1]+',url=(.+?)&quality=');
        return fmt_stream_map.match(pattern)[1];
    }
    (function main() {
        var args=yt.playerConfig['args'];
        var fmt_quality_list='';
        var chosen_fmt_id=0;
        var fmt_info=args['fmt_list'].split(',');
        var fmt_resolution='';
        var avail_fmt_ids=new Array(fmt_info.length);
        for(var i in fmt_info) {
            avail_fmt_ids[i]=fmt_info[i].split('/')[0];
            fmt_resolution=fmt_info[i].split('/')[1];
            fmt_quality_list+=(avail_fmt_ids[i]+' = '+fmt_resolution+' ('+get_filetype(avail_fmt_ids[i])+')\n');
        }
        loop: while(true) {
            chosen_fmt_id=prompt('Please enter a format id.\n'+fmt_quality_list, avail_fmt_ids[0]);
            if(chosen_fmt_id===null) {
                return;
            }
            for(var i in avail_fmt_ids) {
                if(chosen_fmt_id===avail_fmt_ids[i]) {
                    break loop;
                }
            }
        }

        window.open(get_video_url(args['url_encoded_fmt_stream_map'], chosen_fmt_id, avail_fmt_ids)+'&title='+document.getElementsByName('title')[0].content);
    }());
}());



  主要原理使用 Script Injection的概念透過 youtube當中的 JavaScript object: yt ,來取得所有的資訊.列出幾個比較常用的關鍵字:url_encoded_fmt_stream_map(原始影音串流URL),fmt_list(影片支援解析度列表).

字幕檔Subtitle:

  Youtube把字幕檔放在他們家的 video.google.com,所以當有 Video ID跟確認有此語言的代碼,即可取得字幕檔.不過格式為 Google設計的XML檔案,要直接使用或許要做一些手腳.
範例連結:http://video.google.com/timedtext?lang=en&v=j3avqi1zObE
  以下擷取 youtube-dl的轉換 xml->srt的操作.
def _closed_captions_xml_to_srt(self, xml_string):
srt = ''
texts = re.findall(r'([^<]+)', xml_string, re.MULTILINE)
# TODO parse xml instead of regex
for n, (start, dur_tag, dur, caption) in enumerate(texts):
if not dur: dur = '4'
start = float(start)
end = start + float(dur)
start = "%02i:%02i:%02i,%03i" %(start/(60*60), start/60%60, start%60, start%1*1000)
end = "%02i:%02i:%02i,%03i" %(end/(60*60), end/60%60, end%60, end%1*1000)
caption = unescapeHTML(caption)
caption = unescapeHTML(caption) # double cycle, intentional
srt += str(n+1) + '\n'
srt += start + ' --> ' + end + '\n'
srt += caption + '\n\n'
return srt 



影片格式 以及解析度:

  Youtube也提供許多種影片格式,我們回到上面的 Script Injection Code可以知道,yt.playerConfig裏頭的  args['fmt_list']有此影片提供的格式.並且參照一次搞懂十種YouTube格式.整理出各fmt參數相對應的影片格式跟解析度.

  舉例來說,如果我要取得720P的MP4影音串流,我就要設定我的影音串流URL的ittag為22就會有不同的影音串流.

(PS.我試了很多次直接在 youtube url裏頭設定fmt=22, fmt=45似乎已經不能設定影音格式了?)

沒有留言: