BiliGet(获取视频下载地址) 站点源码

决定把bilibili(嗶哩嗶哩) 视频源地址获取原理及源码中的功能改成工具网站,与之前的命令行工具相比有所改进.

DEMO

GitHub(后续更新都会放在这里)

下面的是部分源码:

server.js

#!/bin/env node

var http = require('http'),  
    xml2js = require('xml2js'),
    zlib = require('zlib'),
    fs = require('fs'),
    url = require('url'),
    mime = require('./mime').types;

function regexId(string) {  
    var result = /flashvars="([^"]+)"/.exec(string);
    if(!result) {
        result = /secure,([^"]+)"/.exec(string);
    }
    return result ? result[1] : null;
}

function getId(pageUrl, cb) {  
    var options = url.parse(pageUrl);

    try {
        var req = http.get(options, function(res) {
            var headers = res.headers;

            if(headers['location']) {

                pageUrl = headers['location'];
                options = url.parse(pageUrl);

                var req = http.get(options, function(res) {
                    var gunzip = zlib.createGunzip();

                    res.pipe(gunzip);

                    var html = '';

                    gunzip.on('data', function(data) {
                        html += data;
                    });

                    gunzip.on('end', function() {
                        var id = regexId(html);
                        cb(id);
                    });

                    gunzip.on('error', function(err) {
                        cb();
                    });

                });

            } else {
                var gunzip = zlib.createGunzip();

                res.pipe(gunzip);

                var html = '';

                gunzip.on('data', function(data) {
                    html += data;
                });

                gunzip.on('end', function() {
                    var id = regexId(html);
                    cb(id);
                });

                gunzip.on('error', function(err) {
                    cb();
                });
            }
        });
    } catch(err) {
        cb();
    }
}

function getURL(id, cb) {  
    var pageUrl = 'http://interface.bilibili.tv/playurl?' + id;
    var options = url.parse(pageUrl);

    try {
        var req = http.get(options, function(res) {
            res.setEncoding('utf8');

            var xml_raw = '';
            res.on('data', function(data) {
                xml_raw += data;
            });

            res.on('end', function() {
                var parser = new xml2js.Parser();
                parser.parseString(xml_raw, function(err, result) {
                    if(err) {
                        cb(null);
                    } else {
                        var list_url = [];
                        for(var i in result.video.durl) {
                            list_url.push(result.video.durl[i].url[0]);
                        }
                        cb(list_url);
                    }
                });
            });

            res.on('error', function(err) {
                cb();
            });

        });
    } catch(err) {
        cb();
    }
}

var template = fs.readFileSync('template.html');

var server = new http.Server();

server.on('request', function(req, res) {

    var get = url.parse(req.url, true);
    if(get.pathname == '/') {
        if(get.query.url) {
            getId(get.query.url, function(id) {
                getURL(id, function(list) {
                    if(list) {
                        res.writeHead(200, {
                            'Content-Type': mime['json']
                        });
                        res.end(JSON.stringify(list));
                    } else {
                        res.writeHead(404, {
                            'Content-Type': mime['json']
                        });
                        res.end();
                    }
                });
            });
        } else {
            res.writeHead(200, {
                'Content-Type': 'text/html'
            });
            res.write(template);
            res.end();
        }
    } else {
        var static = /\/static\/([\w]+).([\w]+)/.exec(get.pathname);
        if(static) {
            var file_name = static[1],
                file_type = static[2];
            res.writeHead(200, {
                'Content-Type': mime[file_type],
                'Cache-Control': 'max-age=604800',
            });
            fs.readFile('static/' + file_name + '.' + file_type, 'binary', function(err, data) {
                if(!err) {
                    res.write(data, 'binary');
                }
                res.end();
            });
        } else {
            res.writeHead(404, {
                'Content-Type': 'text/html',
            });
            res.end('Σ( ° △ °|||');
        }
    }

});

server.listen(3001);

process.on('uncaughtException', function(err) {  
    fs.open('exception.log', 'a', 766, function(e, id) {
        fs.write(id, err + '\n', null, 'utf8', function() {
            fs.close(id);
        })
    });
});

mime.js

exports.types = {  
  "css": "text/css",
  "gif": "image/gif",
  "html": "text/html",
  "ico": "image/x-icon",
  "jpeg": "image/jpeg",
  "jpg": "image/jpeg",
  "js": "text/javascript",
  "json": "application/json",
  "pdf": "application/pdf",
  "png": "image/png",
  "svg": "image/svg+xml",
  "swf": "application/x-shockwave-flash",
  "tiff": "image/tiff",
  "txt": "text/plain",
  "wav": "audio/x-wav",
  "wma": "audio/x-ms-wma",
  "wmv": "video/x-ms-wmv",
  "xml": "text/xml"
};

template.html

<!DOCTYPE html>  
<html>  
<head>  
<title>BiliGet</title>  
<meta charset='utf8'/>  
<link href='static/favicon.ico' rel='icon' type='image/x-icon'/>  
<script>  
    var load=document.createElement('img');
    load.src='static/loading.gif';
    function $(id) {
        return document.getElementById(id);
    }

    function center() {
        var ele = $('main');
        var s_width = document.body.scrollWidth;
        var s_height = navigator.userAgent.toString().indexOf('Chrome')==-1?document.documentElement.scrollHeight:document.body.scrollHeight;
        var e_width = ele.offsetWidth;
        var e_height = ele.offsetHeight;
        var left = (s_width - e_width) / 2 + 'px',
            top = (s_height - e_height) / 2 + 'px';
            console.log(s_width,s_height,e_width,e_height,left,top);
        ele.style.left = left;
        ele.style.top = top;
    }

    function show() {
        center();
        var ele = $('main');
        ele.style.opacity = 0;

        (function() {
            ele.style.opacity=parseFloat(ele.style.opacity)+0.1;
            if(parseFloat(ele.style.opacity) < 1) {
                setTimeout(arguments.callee, 80);
            }
        })();
    }

    function ajaxReturn() {
        var url = encodeURIComponent($('url').value);
        var result = $('result');
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {

            if(xhr.readyState == 4) {
                if(xhr.status == 200) {
                    var data = JSON.parse(xhr.responseText);
                    result.innerHTML = '';
                    result.style.textAlign='left';
                    data.forEach(function(e, i, a) {
                        result.innerHTML += '<p><a href="' + e + '">' + e + '</a></p>';
                    });
                } else {
                    result.style.textAlign='center';
                    result.innerHTML = 'Σ( ° △ °|||';
                }
            }
        }
        xhr.open('GET', '?url=' + url, true);
        xhr.send();
        result.style.textAlign='center';
        result.innerHTML='';
        result.appendChild(load);

        return false;
    }
    window.onload = show;
</script>  
<style>  
    body{
        font:14px/20px 'Microsoft YaHei',Arial,Helvetica,sans-serif;
        background:#FFF url('static/bg.jpg') center no-repeat fixed;
    }
    #main{
        padding:50px;
        text-align:center;
        position:fixed;
        background:rgba(255,255,255,0.5);
        width:600px;
        height:400px;
        margin:0 auto;
        border-radius:20px;
        opacity:0;
    }
    #logo{
        opacity:0.8;
        margin:20px;
    }   
    #result{
        margin:10px;
        height:160px;
        overflow-y:auto;
        overflow-x:hidden;
    }
    p{
        margin:0;
        padding:0;
    }
    a{
        color:#36C;
        text-decoration:none;
    }
    input[type='url']{
        border:1px solid #DEDEDE;
        background:rgba(255,255,255,0.8);
        height:30px;
        width:400px;
        border-radius:5px;
    }
    input[type='submit']{
        height:30px;
    }
</style>  
</head>  
<body onresize='center()'>  
    <div id='main'>
        <div id='logo'>
            <img src='static/logo.png' width='400' height='179'/>
        </div>
        <form method='get' action='' onSubmit='return ajaxReturn()'>
            <input type='url' id='url' placeholder='http://www.bilibili.tv/avxxxxxxx' autofocus />
            <input type='submit' value='Get!'/>
        </form>
        <div id='result'></div>
    </div>
</body>  
</html>