梦汐 发表于 2022-11-9 07:49

【哔哩哔哩】个人空间视频添加播放进度条

本帖最后由 梦汐 于 2022-11-11 10:25 编辑

油猴脚本,功能已完善,直接传送门安装就行:传送门

// ==UserScript==
// @name      哔哩哔哩 - beta
// @namespace   Violentmonkey Scripts
// @match       *://*.bilibili.com/*
// @version   1.0
// @noframes    -
// @author      moxi
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addStyle
// @require   https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.5.1.min.js
// ==/UserScript==
const Match = {
    Init: function () {
      var link = window.location.href
      var rules = this.Rules
      for (keyNmae in rules) {
            var focus = rules //==[正则,[函数]]
            var result = RegExp(focus, "i").exec(link)
            if (result != null) {
                console.log("页面类型", keyNmae, " | ", "使用函数", focus);
                for (let i = 0; i < focus.length; i++) {
                  focus.call(null, { result: result.slice(1,), rules: focus })
                }
            }
      }
    },
    Rules: {//正则
      "Home": [".*://www.bilibili.com(?:$|/$|/\\?.*)", ],//主页
      "Video": [".*://www.bilibili.com/video/(*).*", ],//视频地址
      "Space": [".*://space.bilibili.com/.*", ],//空间主页
    },
    config: {
      "HideHaveRead": true,//隐藏已经阅读过的视频
    }
};
(function () { Match.Init() }());
function RecordLastPlayTime() {
    setInterval(function () {
      var Rresult = RegExp(".*://www.bilibili.com/video/(*).*", "i").exec(window.location.href)
      if (Rresult != null) {
            var node = []
            var element = document.querySelector('.bpx-player-ctrl-time-current')
            if (!!element) {
                node.push(element)
            }//播放进度
            var element = document.querySelector('.bpx-player-ctrl-time-duration')
            if (!!element) {
                node.push(element)
            }//视频时长
            if (node.length == 2) {
                var id = Rresult
                //------------------------------------------------------
                var minute = node.innerHTML.split(":")
                var progress = (minute * 60) + Math.trunc(minute)
                //document.querySelector("bwp-video,video").currentTime
                //------------------------------------------------------
                var minute = node.innerHTML.split(":")
                var duration = (minute * 60) + Math.trunc(minute)
                //document.querySelector("bwp-video,video").duration
                //------------------------------------------------------
                var json = GM_getValue_local("config", id, {})
                if (!json.progress && !json.duration && !json.interaction) {
                  var type = id.toLowerCase == "a" ? "avid" : "bvid";
                  var httpRequest = new XMLHttpRequest();
                  httpRequest.open('GET', `https://api.bilibili.com/x/web-interface/view?${type}=${id}`, false);
                  httpRequest.send(null);
                  var json = JSON.parse(httpRequest.responseText);
                  var duration = json.data.duration
                  var interaction = json.data.rights['is_stein_gate']
                  if (interaction == 1) {
                        //console.log("该视频为互动视频", id, progress, duration);
                        GM_setValue_local("config", id, {
                            interaction: interaction
                        })
                  } else {
                        //console.log("首次记录", id, progress, duration);
                        GM_setValue_local("config", id, {
                            progress: progress,
                            duration: duration
                        })
                  }
                } else {
                  if (!json.interaction) {//不是互动视频才进行记录
                        var disparity = json.duration - duration
                        if (disparity > -5 && disparity < 5) {//这是哔哩哔哩的BUG//同一个视频差距时间不得超过5秒
                            if (progress > json.progress) {//避免被小数覆盖
                              GM_setValue_local("config", id, {
                                    progress: progress,
                                    duration: duration
                              })
                            }
                        }
                  } else {
                        //console.log(id,"该视频为互动视频,无法记录");
                  }
                }
            }
            //console.log(id, { progress: progress, duration: duration });
      }
    }, 1000)
}
function AddPlayProgressUI() {
    ObserveNewElements(".small-item.fakeDanmu-item", function (node) {
      var id = $(node).attr('data-aid')
      var json = GM_getValue_local("config", id, {})
      //console.log("config", id, json.progress, json.duration);
      if (!$.isEmptyObject(json)) {//判断JSON是否为空
            var progress_ui = document.createElement('div');
            var Percent = GetPercent(json.progress, json.duration)
            $(progress_ui).attr('class', 'progress').attr('style', 'top: 0px;position: absolute;width: 100%;height: 4px;background-color: #909090;').html(`<div id="content" style="width: ${Percent}%;height: 100%;background-color: #f00;"></div>`);
            $(node).find('.cover').append(progress_ui);
            if (Match.config["HideHaveRead"]) {
                if (Percent > 95) {
                  $(node).hide();
                }
            }
      }
    })
}
function DanmuAirborne() {//弹幕空降
    const BarrageAirborne = {
      init: function () {
            this.IntervalID = setInterval((function (execution, This) {
                return function () {
                  execution.call(This)
                }
            }(function () {
                var danmaku = this.hBarrage
                var danmaNode = $(".b-danmaku")
                for (let index = 0; index < danmaNode.length; index++) {
                  if (!null) {//danmaku.includes(this.innerText)
                        var result = RegExp("(?:空降|跳伞|伞兵|空)\\D*(\\d{1,}[::]\\d{1,})", "i").exec(danmaNode.innerText)//弹幕是否预设的过滤规则
                        if (result != null && this.status == 0) {//符合则执行
                            console.log(danmaNode.innerText);
                            this.status = 1 //许可
                            danmaku.push(danmaNode.innerText)
                            var minute = result.split(":")//分割
                            if (minute.length == 1) {
                              var minute = result.split(":")
                            }
                            var position = (Math.trunc(minute) * 60) + Math.trunc(minute)//取秒
                            if (position > document.querySelector("bwp-video,video").currentTime) {
                              var node = $('.bpx-player-video-area').append(`<div class="div dtms" style="position: absolute;z-index: 1;left: 50%;top: 50%;transform: translate(-50%, -50%);"><button class="datumoshi" style="padding: 6px 6px;line-height: 18px;border: none;background: #ffffff00;font-size: 18px;color: #fff;text-shadow: 2px 1px 1px #000;">点击空降到${minute}分${minute}秒</button></div>`).find('.datumoshi');

                              $(node).click(CFunction(function (position, node, This) {
                                    document.querySelector("bwp-video,video").currentTime = position
                                    $('.datumoshi').remove()
                                    This.status = 0
                              }, position, node, this))

                              setTimeout(CFunction(function (position, node, This) {
                                    $(node).remove()
                                    if (This.status != 0) {
                                        This.status = 0
                                    }
                              }, null, node, this), 7500)

                              function CFunction(execution, position, node, This) {
                                    return function () {
                                        execution.call(null, position, node, This)
                                    }
                              }
                            }

                        }
                  }
                }
            }, this)), 1000)
      },
      hBarrage: [],
      IntervalID: undefined,
      status: 0,
    }
    BarrageAirborne.init()
}
function MarkingForRead() {
    ObserveNewElements(".play-all-btn:not(.all,.check,.save)", function (node) {
      setTimeout(
            function () {
                $(node).parent().prepend(`<button class="play-all-btn all" style="width: auto;background-color: #ffffff00;padding: 4px;">反选</button><button class="play-all-btn save" style="width: auto;background-color: #ffffff00;padding: 4px;">保存</button>`)
                $(".play-all-btn.all").click(//全选
                  function () {
                        var checkbox = $('.select')
                        if (checkbox.length == 0) {
                            var item = $('.small-item.fakeDanmu-item')
                            for (let index = 0; index < item.length; index++) {
                              $(item).prepend(`<input type="checkbox" class="select" aid="${$(item).attr("data-aid")}" index="${index}" style="position: absolute;z-index: 10;">`)
                            }
                        } else {
                            checkbox.click()
                        }
                  }
                )
                $(".play-all-btn.save").click(//保存
                  function () {
                        var select = []
                        $('.select').each(function () {
                            if (this.checked) {
                              select.push(this.getAttribute('aid'))
                            }
                        })

                        for (let index = 0; index < select.length; index++) {
                            SetForRead(select)
                        }

                  }
                )
            }, 300
      )

    })
}//在UP的投稿列表添加一个全选和保存按钮,选中后按保存会标记为已阅视频
function DeleteSundries() {//删除节点
    var remove = {
      "推荐": ".bili-grid .eva-extension-area",
      "直播": ".bili-grid .live-card-list",
      "动画": ".bili-grid .video-card-list.is-main",
      "番剧": ".bili-grid .bangumi-area",
      "国创": ".bili-grid .guo-chuang-area",
      "综艺": ".bili-grid .variety-area",
      "课堂": ".bili-grid .cheese-card-list",
      "资讯": ".bili-grid .information-area",
      "专栏": ".bili-grid .article-card-list",
      //"跳转": ".banner-link",
      "电影,电视,纪录片": ".bili-grid.movie-card-list",
    }
    for (key in remove) {
      WaitNodeLoad(null, remove, 100, 100, function () {
            if (!this.status) {
                this.parent().remove();
            }
      })
    }
    WaitNodeLoad(null, '.recommended-swipe', 100, 100, function () {
      if (typeof (this.status) == "undefined") {
            console.log("this", this);
            this.innerHTML = `<img style="object-fit: cover;position: absolute;top: 50%;left: 50%;display: block;min-width: 100%;min-height: 100%;transform:translate(-50%,-50%);border: 1px solid #000000;background: url(https://s1.ax1x.com/2022/11/10/zpbUds.jpg) no-repeat center center;background-size: cover;">`
      }
    })
}

function GM_getValue_local(id, key, defaultValue) {
    var data = GM_getValue(id, {});
    return data || defaultValue;
}
function GM_setValue_local(id, key, value) {
    var data = GM_getValue(id, {});
    data = value;
    return GM_setValue(id, data);
}
function ObserveNewElements(selector, response, filter) {//selector,(node,mutation)=>{},filterRepetition=true)
    'use strict';
    if (!!selector) {
      let config = {
            childList: true,
            subtree: true
      };
      const mutationCallback = (mutationsList) => {
            for (let mutation of mutationsList) {
                let type = mutation.type;
                if (type == "childList") {
                  if (mutation.addedNodes.length != 0) {
                        var New_node = []
                        for (let index = 0; index < mutation.addedNodes.length; index++) {
                            var Nodes = $(mutation.addedNodes)
                            for (let Tindex = 0; Tindex < Nodes.length; Tindex++) {
                              if ($(Nodes).is(selector)) {
                                    New_node.push(Nodes)
                              }
                            }
                            Nodes.find(selector).each(
                              function () {
                                    New_node.push(this)
                              }
                            )
                        }
                        if (New_node.length > 0) {
                            setTimeout((function (List) {
                              for (let Tindex = 0; Tindex < List.length; Tindex++) {
                                    if (filter) {
                                        if (List['OpenFilter']) {
                                          console.log("重复", List);
                                          continue;
                                        } else {
                                          List['OpenFilter'] = true
                                        }
                                    }
                                    response.call(mutation, List, mutation)
                              }
                            }(New_node)), 300)
                        }
                  }
                }
            }
      };
      var observe = new MutationObserver(mutationCallback);
      observe.observe(document, config)
      return observe
    }
}
function GetPercent(num, total) {//取百分比
    num = parseFloat(num);
    total = parseFloat(total);
    if (isNaN(num) || isNaN(total)) {
      return 0;
    }
    return total <= 0 ? "0%" : (Math.round(num / total * 10000) / 100.00);
}
function SetForRead(id) {//将视频标记为已阅
    return new Promise(
      function (response) {
            var type = id.toLowerCase == "a" ? "avid" : "bvid";
            var httpRequest = new XMLHttpRequest();
            httpRequest.open('GET', `https://api.bilibili.com/x/web-interface/view?${type}=${id}`, false);
            httpRequest.send(null);
            var json = JSON.parse(httpRequest.responseText);
            var duration = json.data.duration
            var interaction = json.data.rights['is_stein_gate']
            if (interaction == 1) {
                console.log("ForRead >>> 互动视频", id, duration, duration);
                GM_setValue_local("config", id, {
                  interaction: interaction
                })
            } else {
                console.log("ForRead >>> 完成记录", id, duration, duration);
                GM_setValue_local("config", id, {
                  progress: duration,//标记为已阅
                  duration: duration
                })
            }
            return response(id)
      }
    )
}
function WaitNodeLoad(parent, selector, interval, times, callback) {//父节点,选择器,间隔,超时次数,回调
    var _selector = selector, _interval = interval, _times = times
    var _node = !parent ? $(_selector) : $(parent).find(_selector)
    if (_node.length == 0) {//匹配失败
      var number = setInterval(
            function () {
                if (_times <= 0) {
                  callback.call({ "status": false })
                  console.log("关闭", number);
                  return clearInterval(number)
                } else {
                  _times--
                  var node = !parent ? $(_selector) : $(parent).find(_selector)
                  if (node.length != 0) {
                        callback.call(node)
                        return clearInterval(number)
                  }
                }
            }, _interval
      )
    } else {
      return callback.call(_node)
    }
}

山田凉粉 发表于 2022-11-9 08:51

看着不错,可是是纯小白不会用:'(weeqw

补补23456 发表于 2022-11-9 11:47

zoomyou 发表于 2022-11-9 09:09
B站里有些质量高的视频,不仅底端有漂亮的进度条,而且还可以根据视频内容编辑成一段一段的进度,不知道如 ...

分段章节 https://www.bilibili.com/read/cv13543699
必剪客户端可以设置样式

cary_云飞扬 发表于 2022-11-9 07:58

可以搞一波

Break12580 发表于 2022-11-9 08:39

谢谢楼主

fa22 发表于 2022-11-9 08:43

不错的说!感谢了!

bandishui 发表于 2022-11-9 08:43

学习一下, 谢谢分享

士兵许三多 发表于 2022-11-9 08:50

进来学习一下,好东西啊

woaini0454 发表于 2022-11-9 08:50

能不能开发b站电视端带弹幕的应用啊 大佬

15235109295 发表于 2022-11-9 08:58

进来学习一下

cassyjj 发表于 2022-11-9 09:09

学习一下
页: [1] 2 3
查看完整版本: 【哔哩哔哩】个人空间视频添加播放进度条