Greasy Fork is available in English.

MyBilibili

b站主页面视频推荐挂载脚本

// ==UserScript==
// @name         MyBilibili
// @namespace    http://tampermonkey.net/
// @version      0.3.7
// @description  b站主页面视频推荐挂载脚本
// @author       N-cat
// @match        https://api.bilibili.com/x/web-interface/dynamic/region?ps=*&rid=*
// @match        *://www.bilibili.com/
// @match        *://www.bilibili.com/video/*
// @icon         https://www.bilibili.com/favicon.ico?v=1
// @grant        GM_xmlhttpRequest
// @connect      *
// ==/UserScript==

(function() {
    'use strict';
    // api及参数说明来自 https://zhuanlan.zhihu.com/p/210779665
    const style = `<style>
    .add-main{
        margin:auto;
        width:1100px;
    }
    .add-div{
        width: 206px;
        height: 206px;
        display:inline-block;
        padding:7px 7px 7px 7px;
        box-sizing:content-box;
        position:relative;
    }
    .add-a{
        color: #212121;
        margin: 10px 0 8px;
        height: 40px;
        text-decoration: none;
        width: 206px;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        font-size: 14px;
        line-height: 20px;
        overflow: hidden;
        text-overflow: ellipsis;
        transition: color .3s;
    }
    .add-a:hover{
        color: #00a1d6;
        transition: color .3s;
    }
    .add-img{
        width: 206px;
        height: 116px;
        border-radius: 2px;
    }
    .add-author{
        color:#999;
        text-decoration: none;
        transition: color .3s;
        font-size: 12px;
    }
    .add-author:hover{
        color: #00a1d6;
        transition: color .3s;
    }
    .re{
        position:fixed;
        right:80px;
        bottom:80px;
        background-color:#00a1d6;
        color:white;
        height:50px;
        width:50px;
        border-radius: 10px;
        font-size:15px;
        border:solid 3px #FA5A57;
        cursor:pointer;
        outline: none;
    }
    .top{
        position:fixed;
        right:80px;
        bottom:150px;
        background-color:#00a1d6;
        color:white;
        height:50px;
        width:50px;
        border-radius: 10px;
        font-size:15px;
        border:solid 3px #FA5A57;
        cursor:pointer;
        outline: none;
    }
    .add-bds{
        position: absolute;
        height: 22px;
        width: 206px;
        top: 101px;
        color: white;
        font-size: 14px;
        background-color: rgba(0,0,0,.6);
        // text-align: center;
        border-radius: 2px;
    }
    .jindu{
        position:fixed;
        right:80px;
        bottom:40px;
        background-color:#00a1d6;
        width: 80px;
        color:white;
        border-radius: 10px;
        height: 20px;
        border:solid 3px #FA5A57;
        outline: none;
        cursor:default;
        text-align: center;
        font-size: 10px;
    }
    </style>`;
    var ps = 50; // 页数
    var rid = 1; // 类型(1:综合)
    // var cookie = document.cookie;
    // console.log(cookie)
    var data
    // api删除原数据
    var pre = document.getElementsByTagName('pre')
    if(pre.length != 0){
        pre[0].remove();
    }
    // 主站隐藏原数据(旧)
    var olddiv = document.getElementsByClassName('b-wrap');
    for(let i in olddiv){
        if(i > 1){
            olddiv[i].style.display = "none";
        }
    }
    olddiv = document.getElementsByClassName('international-footer');
    if(olddiv.length != 0){
        olddiv[0].style.display = "none";
    }
    // 主站隐藏原数据(新)
    var newdiv = document.getElementsByClassName('bili-layout');
    if(newdiv.length != 0){
        newdiv[0].style.display = "none";
    }
    // 请求推荐api
    function getvideo(){
        GM_xmlhttpRequest({
            url:"https://api.bilibili.com/x/web-interface/dynamic/region?ps=" + ps + "&rid=" + rid,
            // url:"https://api.bilibili.com/x/web-interface/popular?ps=50&pn=10",
            method:"get",
            // cookie:cookie,
            onload:function(xhr){
                data = JSON.parse(xhr.response)
                console.log(data.data.archives)
                // 添加元素
                let div = document.createElement("div");
                div.classList.add('add-main');
                for(let i of data.data.archives){
                    //div.innerHTML += '<div class="add-div">'
                    //div.innerHTML += '<a href="https://www.bilibili.com/video/' + i.bvid + '" target="_blank"><img class="add-img" src="' + i.pic + '" /></a><br>'
                    //div.innerHTML += '<a class="add-a" href="https://www.bilibili.com/video/' + i.bvid + '" target="_blank">' + i.title + '</a></div>';
                    // 视频总div
                    let id = document.createElement("div");
                    div.appendChild(id);
                    id.classList.add('add-div');
                    // 图片链接
                    let a1 = document.createElement("a");
                    id.appendChild(a1);
                    a1.innerHTML = '<img class="add-img" src="' + i.pic + '" />'
                    a1.setAttribute('href',"https://www.bilibili.com/video/" + i.bvid );
                    a1.setAttribute('target',"_blank");
                    // 文字链接
                    let a2 = document.createElement("a");
                    id.appendChild(a2);
                    a2.classList.add('add-a');
                    a2.setAttribute('href',"https://www.bilibili.com/video/" + i.bvid );
                    a2.setAttribute('target',"_blank");
                    // 文字
                    let txt = document.createTextNode(i.title);
                    a2.appendChild(txt);
                    // 作者
                    let a3 = document.createElement("a");
                    id.appendChild(a3);
                    a3.classList.add('add-author');
                    a3.setAttribute('href',"https://space.bilibili.com/" + i.owner.mid );
                    a3.setAttribute('target',"_blank");
                    let author = document.createTextNode(i.owner.name);
                    a3.appendChild(author);
                    // 播放量,点赞,时常
                    let bds = document.createElement("div");
                    bds.classList.add('add-bds');
                    let b = i.stat.view;
                    if(i.stat.view >= 10000){
                        b = (i.stat.view/10000).toFixed(1) + '万'
                    }
                    let d = i.stat.like;
                    if(i.stat.like >= 10000){
                        d = (i.stat.like/10000).toFixed(1) + '万'
                    }
                    let s = i.duration;
                    if(i.duration >= 3600){
                        let mm = parseInt((i.duration%3600)/60);
                        if(mm < 10){
                            mm = '0' + mm;
                        }
                        let ss = i.duration%60;
                        if(ss < 10){
                            ss = '0' + ss;
                        }
                        s = parseInt(i.duration/3600) + ':' + mm + ':' + ss;
                    } else if(i.duration <= 3600 && i.duration >= 60){
                        let mm = parseInt(i.duration/60);
                        if(mm < 10){
                            mm = '0' + mm;
                        }
                        let ss = i.duration%60;
                        if(ss < 10){
                            ss = '0' + ss;
                        }
                        s = mm + ':' + ss;
                    } else {
                        let ss = i.duration%60;
                        if(ss < 10){
                            ss = '0' + ss;
                        }
                        s = '00:' + ss
                    }
                    bds.innerHTML = '<span style="padding-left:10px;float:left;line-height:22px"> ▶ ' + b + ' ❤ ' + d + '</span><span style="padding-right:10px;float:right;line-height:22px">' + s + '</span>';
                    id.appendChild(bds);
                }
                div.innerHTML += style;
                // 设置添加位置
                var header = document.getElementsByClassName('bili-layout');
                if(header.length != 0){
                    header[0].parentNode.insertBefore(div, header[0]); // 新
                } else {
                    document.body.append(div); // 旧
                }
            }
        })
    }
    function getQueryVariable(variable){
        var query = window.location.search.substring(1);
        var vars = query.split("&");
        for (var i=0;i<vars.length;i++) {
            var pair = vars[i].split("=");
            if(pair[0] == variable){return pair[1];}
        }
        return(false);
    }
    // 进度条
    let jindu = document.createElement("input");
    jindu.classList.add('jindu');
    jindu.setAttribute("value", "下载进度条");
    jindu.setAttribute("readonly", "true");
    document.body.append(jindu);
    // 请求事件监听(每0.5s输出一次)
    var ms= 500;
    var lastClick = Date.now() - ms;
    function downloadProgress(event) {
    // 如果lengthComputable属性的值是false,那么意味着总字节数是未知并且total的值为零
        if ((event.lengthComputable && Date.now() - lastClick >= ms) || event.loaded == event.total) {
            let progress = event.loaded / event.total * 100;
            jindu.setAttribute("value", '已下载 ' + progress.toFixed(0) + '%');
            console.log('已下载 ' + progress.toFixed(0) + '%');
            // 更新时间
            lastClick = Date.now();
        }
    }
    getvideo();
    var url = window.location.href;
    var btn = document.createElement("input");
    if(url.split('/')[3] == 'video'){
        // 下载按钮
        btn.setAttribute("type", "button");
        btn.setAttribute("value", "下载");
        btn.classList.add('re');
        document.body.append(btn);
        btn.onclick = function(){
            var bv = url.split('/')[4].split('?')[0];
            var page = 1;
            if(getQueryVariable('p')){
                page = getQueryVariable('p');
            }
            // alert(bv);
            GM_xmlhttpRequest({
                url:'https://api.bilibili.com/x/web-interface/view/detail?bvid='+bv,
                method:"get",
                // cookie:cookie,
                onload:function(xhr){
                    data = JSON.parse(xhr.response)
                    console.log(data.data.View);
                    var name = data.data.View.title;
                    var cid = data.data.View.pages[page-1].cid;
                    var part = data.data.View.pages[page-1].part;
                    GM_xmlhttpRequest({
                        url:'https://api.bilibili.com/x/player/playurl?bvid='+bv+'&cid='+cid+'&qn=116',
                        method:"get",
                        // cookie:cookie,
                        onload:function(xhr){
                            data = JSON.parse(xhr.response)
                            console.log(data);
                            var vurl = data.data.durl[0].url;
                            // 复制视频名称到剪切板
                            const input = document.createElement('input');
                            document.body.appendChild(input);
                            input.setAttribute('value', name);
                            input.select(); // 选取文本域的内容
                            if (document.execCommand('copy')) {
                                document.execCommand('copy');
                                console.log('复制成功');
                            }
                            document.body.removeChild(input);
                            // 确认下载
                            var con = confirm('确认后视频开始下载,请耐心等待(暂无进度条)\n下载视频:'+name+'\n(P'+page+' '+part+')');
                            if(con == true){
                                //
                                // 文件流可以自定义文件名,但是反馈太慢了,暂不采用
                                var x = new XMLHttpRequest()
                                // 设置xhr监听函数
                                x.addEventListener('progress', downloadProgress);
                                x.open("GET", vurl, true)
                                x.responseType = 'blob'
                                x.onload=function(e) {
                                    //会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。
                                    var url = window.URL.createObjectURL(x.response)
                                    var a = document.createElement('a')
                                    a.href = url
                                    a.download = name+'.flv';
                                    a.click()
                                }
                                x.send()
                                //
                                // 普通新页面下载
                                //window.open(vurl,'_self');
                            }
                        }
                    });
                }
            });
        }
    }else{
        // 刷新按钮
        btn.setAttribute("type", "button");
        btn.setAttribute("value", "刷新");
        btn.classList.add('re');
        document.body.append(btn);
        btn.onclick = function(){
            document.body.scrollTop = document.documentElement.scrollTop = 0;
            var dd = document.getElementsByClassName('add-main');
            for(let j of dd){
                j.style.display = "none";
            }
            getvideo(); // 请求后刷新也会请求一次,这里待优化
        }
    }
    // 顶部按钮
    var top = document.createElement("input");
    top.setAttribute("type", "button");
    top.setAttribute("value", "顶部");
    top.classList.add('top');
    document.body.append(top);
    top.onclick = function(){
        document.body.scrollTop = document.documentElement.scrollTop = 0;
    }
    // 下拉刷新
    let timeout = null;
    window.onscroll = function() {
      const scrollH = document.documentElement.scrollHeight;// 文档的完整高度
      const scrollT = document.documentElement.scrollTop || document.body.scrollTop; // 当前滚动条的垂直偏移
      const screenH = window.screen.height; // 屏幕可视高度
      if ((scrollH - scrollT - screenH) < 500) { // 只是一个相对值,可以让页面再接近底面的时候就开始请求
          timeout && clearTimeout(timeout); // 判断timeout是否在执行
          timeout = setTimeout(() => {
              console.log('下拉刷新')
              getvideo();
          }, 3000); // api在3s内请求返回重复内容
      }
};
})();