js实现bilibili弹幕列表随视频播放滚动

2024-04-17 04:32

本文主要是介绍js实现bilibili弹幕列表随视频播放滚动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

以下是一些写脚ben时获得的数据记录,最后面是脚ben代码。

抓包加上查资料拿到的弹幕数据接口:

  • "https://api.bilibili.com/x/v1/dm/list.so?oid=" + cid 返回数据类型为XML(旧接口)。
  • https://api.bilibili.com/x/v2/dm/web/seg.so?oid=" + cid + "&type=1&segment_index=1 返回二进制数据(新接口)

二进制数据处理比较麻烦,我就用了XML的接口。

cid是视频的id,也叫oid,不是AV号,获取方式有两种:

  • 在控制台输入cid获取当前视频的cid
  • 通过方法:window.__INITIAL_STATE__.videoData.pages

接口测试时获取的数据如下:
在这里插入图片描述
弹幕的返回数据含义如下:

<d p="1611.18000,1,25,16777215,1631635549,0,ab0d3321,54811347826209280,10">来看八爷</d>

有9个参数,经过查询并且测试获得了部分参数的含义:

  • 参数1: 1611.18000 弹幕出现的时间,以秒数为单位
  • 参数2: 1 弹幕的模式,1-3 滚动弹幕,4 底端弹幕,5顶端弹幕,6 逆向弹幕,7 精准定位,8 高级弹幕
  • 参数3: 25 字号 (12非常小,16特小,18小,25中,36大,45很大,64特别大)
  • 参数4: 16777215 字体的颜色,这串数字是十进制表示
  • 参数5: 1631635549 unix时间戳
  • 参数6: 0 弹幕池类型,0普通池,1字幕池,2特殊池 【目前特殊池为高级弹幕专用】
  • 参数7: ab0d3321 发送者的Hash,用于“屏蔽此弹幕的发送者”功能
  • 参数8: 54811347826209280 弹幕在弹幕数据库中rowID 用于“历史弹幕”功能。
  • 参数9: 10 未知,貌似是新增的,查阅的资料里都没有这个参数

这里只有第一个参数用得到,剩下的参数会在我的另一个修改视频弹幕的脚ben里用到。

最终效果如下:
在这里插入图片描述

代码如下:

// 允许跨域请求,防止浏览器拦截
var meta = document.createElement("meta");
meta.httpEquiv = "Access-Control-Allow-Origin";
meta.content = "*";
$("head")[0].appendChild(meta);// 初始化若干变量
var page_loc = location.href;
var page_start = page_loc.indexOf("?p=");
var page_p;// 获取视频cid
if (page_start == -1 || page_start == 1) page_p = 0;
else page_p = page_loc.slice(page_start + 3) - 1;
var page_cid = window.__INITIAL_STATE__.videoData.pages[page_p].cid;// 发起请求,拿到弹幕数据
$.ajax({url: "https://api.bilibili.com/x/v1/dm/list.so?oid=" + page_cid,type: "GET",dataType: "XML",success: function (xml) {// 处理返回数据data_deal(xml.all);},error: function (err) {console.log(err);alert("弹幕列表滚动脚本失效,可尝试更新最新版本脚本解决问题!\n如果有一定编程基础可以打开控制台查看报错信息。");}
});// 存储数据
function data_deal(data) {var data_list = new Array(); // 存信息var data_arr = new Array(); // 存内容var data_length = data.length;for (var i = 8; i < data_length; i++) {var data_array = data[i].attributes.p.value.split(",");$.each(data_array, function (i) {data_array[i] = parseFloat(data_array[i]); // 转换类型,便于排序})data_list.push(data_array);data_arr.push(data[i].innerHTML);}// 执行排序data_sort(data_list, 0, data_list.length - 1, data_arr);// 修改弹幕列表$(".bui-collapse-header").bind("click", function () {var clearInter = setInterval(function () {if ($(".player-auxiliary-danmaku-load-status").css("display") == "none") {list_change(data_list, data_arr);clearInterval(clearInter);}}, 100);});
}// 数据排序(快排算法)
function data_sort(data_list, l, r, data_arr) {if (l < r) {var i = l,j = r,temp = data_list[l],t1, t2, t3;while (i < j) {while (i < j && data_list[j][0] >= temp[0]) j--;while (i < j && data_list[i][0] <= temp[0]) i++;if (i < j) {t1 = data_list[i];data_list[i] = data_list[j];data_list[j] = t1;t2 = data_arr[i];data_arr[i] = data_arr[j];data_arr[j] = t2;}}data_list[l] = data_list[i];data_list[i] = temp;t3 = data_arr[l];data_arr[l] = data_arr[i];data_arr[i] = t3;data_sort(data_list, l, i - 1, data_arr);data_sort(data_list, i + 1, r, data_arr);}
}// 时间格式转换(分转秒)
function time_m_s(a) {if (a.length == 2) return a[0] * 60 + a[1];else return a[0] * 3600 + a[1] * 60 + a[2];
}// 时间格式转换(秒转分)
function time_s_m(a) {a = Math.floor(a);var m = Math.floor(a / 60);var s = (a - Math.floor(a / 60) * 60);if (m < 0) m = "00";else if (m < 10) m = "0" + String(m);if (s < 10) s = "0" + String(s);return m + ":" + s;
}// 发送日期格式转换
function data_s_d(a) {var date = new Date(a * 1000);var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';var D = date.getDate();if (D < 10) D = "0" + String(D) + ' ';else D = D + ' ';var h = date.getHours();if (h < 10) h = "0" + String(h) + ":";else if (h == 0) h = "00" + ":";else h = h + ":";var m = date.getMinutes();if (m < 10) m = "0" + String(m);else if (m == 0) m = "00";return M + D + h + m;
}// 获取当前视频播放秒数
function get_now_s() {var time_now = $('.bilibili-player-video-time-now')[0].innerHTML.split(":");$.each(time_now, function (i) {time_now[i] = parseInt(time_now[i]);})return time_m_s(time_now);
}function list_change(data_list, data_arr) {// 修改页面控制$(".player-auxiliary-danmaku-btn-time").removeAttr("orderby");$(".player-auxiliary-danmaku-btn-danmaku").removeAttr("orderby");$(".player-auxiliary-danmaku-btn-date").removeAttr("orderby");$(".player-auxiliary-danmaku-function > .player-auxiliary-danmaku-btn-danmaku")[0].innerHTML = "滚动弹幕(共" + data_arr.length + "条)";// 弹幕排序list_sort(data_list, data_arr);
}function list_sort(data_list, data_arr) {// 破坏初始列表结构$(".player-auxiliary-danmaku-wrap > div > ul").empty();$(".player-auxiliary-danmaku-wrap > .player-auxiliary-danmaku-contaner").removeClass("player-auxiliary-danmaku-contaner player-auxiliary-bscrollbar");$(".player-auxiliary-danmaku-wrap").off();$(".player-auxiliary-danmaku-wrap > div > ul").off();// 清除BFCvar div = document.createElement("div");var nothing = document.createTextNode(".");div.appendChild(nothing);$(".player-auxiliary-danmaku-wrap > div > ul")[0].appendChild(div);$(div).addClass("BFC-nothing");$(".BFC-nothing").css("opacity", "0");// 构造有序列表$(data_arr).each(function (i, v) {var li = document.createElement("li");var span1 = document.createElement("span");var span2 = document.createElement("span");var span3 = document.createElement("span");var span4 = document.createElement("span");var text1 = document.createTextNode(time_s_m(data_list[i][0]));var text2 = document.createTextNode(v);var text3 = document.createTextNode(data_s_d(data_list[i][4]));var text4 = document.createTextNode(">");span1.appendChild(text1);span2.appendChild(text2);span3.appendChild(text3);span4.appendChild(text4);$(li).attr("dmno", i);$(span2).attr("title", v);$(li).addClass("danmaku-info-row");$(span1).addClass("danmaku-info-time");$(span2).addClass("danmaku-info-danmaku");$(span3).addClass("danmaku-info-date");$(span4).addClass("danmaku-info-animate");li.appendChild(span1);li.appendChild(span2);li.appendChild(span3);li.appendChild(span4);$(".player-auxiliary-danmaku-wrap > div > ul")[0].appendChild(li);$('.danmaku-info-animate').css({'position': 'absolute','width': '15px','height': '100%','padding': '0','left': '0px','font-size': '15px','color': 'rgb(1,185,245)','transition': '1s ease-in-out'});});// 播放动画(监听视频播放进度)var isChange = $(".bilibili-player-video-time-now")[0].innerHTML;player_animate(data_list, get_now_s());setInterval(function () {if (isChange != $(".bilibili-player-video-time-now")[0].innerHTML) {player_animate(data_list);  // 动画播放isChange = $(".bilibili-player-video-time-now")[0].innerHTML;}}, 1000);
}// 播放动画
function player_animate(data_list) {var time_now_s = get_now_s();var length = data_list.length;var begin = -1, end = -1;for (var i = 0; i < length; i++) {if (Math.floor(data_list[i][0]) < time_now_s) continue;if (Math.floor(data_list[i][0]) > time_now_s) break;if (begin != -1) end++;if (begin == -1) begin = end = i;}if (end == -1) return;var li_height = parseFloat($(".player-auxiliary-danmaku-wrap > div > ul > li").css("height").replace("px", ""));var li_width = parseFloat($(".player-auxiliary-danmaku-wrap > div > ul > li").css("width").replace("px", ""));for (var i = begin; i <= end; i++) {if (length > 15 && i > 11) $(".player-auxiliary-danmaku-wrap > div > ul > li:first").css("margin-top", "-" + (i - 11) * li_height + "px");  // 列表滚动$($('.player-auxiliary-danmaku-wrap > div > ul > li')[i]).find(".danmaku-info-animate").css("left", li_width);  // 播放动画}
}

如果有兴趣了解更多相关内容,欢迎来访我的个人网站:eyes++的个人空间

如果对该类脚本感兴趣的话,欢迎来到我的github看看,我开源了一个script仓库,里面有很多我写的和正在写的脚本,大佬们有兴趣可以一起贡献代码,仓库地址

------------------分割线(2021/10/7)-------------------
之前那个脚本效率太低了,所以没有投入使用,我给它优化了一下逻辑,现在可以投入使用了,但是我实在是不会用js注入动画,所以这次的我取消了动画。。。。。。有知道的大佬可以教教我,下载地址:bilibili弹幕列表实时滚动播放

代码如下:

// ==UserScript==
// @name         bilibili弹幕列表实时滚动播放
// @namespace    https://eyesblog.gitee.io
// @version      1.0
// @description  在B站看视频时弹幕一多就会遮住内容,可我又想看视频又想看弹幕,就写了这个脚本,安装后右侧的弹幕列表会随视频播放而滚动,并且会有弹幕出现提示
// @author       eyes++
// @match        https://www.bilibili.com/video/*
// @require      https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js
// @grant        none
// ==/UserScript==(function () {'use strict';// 允许跨域请求,防止浏览器拦截let meta = document.createElement("meta");meta.httpEquiv = "Access-Control-Allow-Origin";meta.content = "*";$("head")[0].appendChild(meta);// 初始化若干变量let page_loc = location.href;let page_start = page_loc.indexOf("?p=");let page_p;// 获取视频cidif (page_start == -1 || page_start == 1) page_p = 0;else page_p = page_loc.slice(page_start + 3) - 1;let page_cid = window.__INITIAL_STATE__.videoData.pages[page_p].cid;// 发起请求,拿到弹幕数据$.ajax({url: "https://api.bilibili.com/x/v1/dm/list.so?oid=" + page_cid,type: "GET",dataType: "XML",success: function (xml) {// 处理返回数据data_deal(xml.all);},error: function (err) {console.log("弹幕列表滚动脚本报错:", err);alert("弹幕列表滚动脚本失效,可尝试更新最新版本脚本解决问题!\n如果有一定编程基础可以打开控制台查看报错信息。");}});// 存储数据let data_deal = data => {let data_list = new Array(); // 存时间信息let data_arr = new Array(); // 存内容let data_length = data.length;for (var i = 8; i < data_length; i++) {let data_array = data[i].attributes.p.value.split(",");data_array.splice(5, 4);data_array.splice(1, 3);$.each(data_array, function (i) {data_array[i] = parseFloat(data_array[i]); // 转换类型,便于排序})data_list.push(data_array);data_arr.push(data[i].innerHTML);}// 执行排序data_sort(data_list, 0, data_length - 9, data_arr);// 修改弹幕列表let clear = setInterval(() => {if ($(".bui-collapse-header")[0]) { // 等待弹幕列表加载出来clearInterval(clear);$(".bui-collapse-header").click(() => {let clearInter = setInterval(function () {if ($(".player-auxiliary-danmaku-load-status").css("display") == "none") { // 等待弹幕数据加载出来clearInterval(clearInter);list_change(data_list, data_arr); // 然后开始修改列表}}, 100);})}}, 100)}// 数据排序(快排算法)let data_sort = (data_list, l, r, data_arr) => {if (l < r) {let i = l,j = r,temp = data_list[l],t1, t2, t3;while (i < j) {while (i < j && data_list[j][0] >= temp[0]) j--;while (i < j && data_list[i][0] <= temp[0]) i++;if (i < j) {t1 = data_list[i];data_list[i] = data_list[j];data_list[j] = t1;t2 = data_arr[i];data_arr[i] = data_arr[j];data_arr[j] = t2;}}data_list[l] = data_list[i];data_list[i] = temp;t3 = data_arr[l];data_arr[l] = data_arr[i];data_arr[i] = t3;data_sort(data_list, l, i - 1, data_arr);data_sort(data_list, i + 1, r, data_arr);}}// 查找let danmaku_search = (arr, time) => {try {arr.forEach((v, i) => {if (v[0] - time >= 0 && v[0] - time < 1) throw new Error(String(i));if (v[0] - time > 1) throw new Error(i);});} catch (e) {return e.message;}return -1;}// 时间格式转换(分转秒)let time_m_s = a => {if (a.length == 2) return a[0] * 60 + a[1];else return a[0] * 3600 + a[1] * 60 + a[2];}// 时间格式转换(秒转分)let time_s_m = a => {a = Math.floor(a);let m = Math.floor(a / 60);let s = (a - Math.floor(a / 60) * 60);if (m < 0) m = "00";else if (m < 10) m = "0" + String(m);if (s < 10) s = "0" + String(s);return m + ":" + s;}// 发送日期格式转换let data_s_d = a => {let date = new Date(a * 1000);let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';let D = date.getDate();if (D < 10) D = "0" + String(D) + ' ';else D = D + ' ';let h = date.getHours();if (h < 10) h = "0" + String(h) + ":";else if (h == 0) h = "00" + ":";else h = h + ":";let m = date.getMinutes();if (m < 10) m = "0" + String(m);else if (m == 0) m = "00";return M + D + h + m;}// 获取当前视频播放秒数let get_now_s = () => {let time_now = $('.bilibili-player-video-time-now')[0].innerHTML.split(":");$.each(time_now, function (i) {time_now[i] = parseInt(time_now[i]);})return time_m_s(time_now);}let list_change = (data_list, data_arr) => {// 修改页面控制$(".player-auxiliary-danmaku-btn-time").removeAttr("orderby");$(".player-auxiliary-danmaku-btn-danmaku").removeAttr("orderby");$(".player-auxiliary-danmaku-btn-date").removeAttr("orderby");$(".player-auxiliary-danmaku-function > .player-auxiliary-danmaku-btn-danmaku")[0].innerHTML = "滚动弹幕(共" + data_arr.length + "条)";// 破坏初始列表结构$(".player-auxiliary-danmaku-wrap > .player-auxiliary-danmaku-contaner").removeClass("player-auxiliary-danmaku-contaner player-auxiliary-bscrollbar");$(".player-auxiliary-danmaku-wrap").off();$(".player-auxiliary-danmaku-wrap > div > ul").off();// 监听视频播放let danmaku_length = $(".player-auxiliary-danmaku-wrap > div > ul > li").length; // 获取当前展示的弹幕数量let li_width = $(".player-auxiliary-danmaku-wrap > div > ul > li").css("width"); // 列表宽度let isChange = $(".bilibili-player-video-time-now")[0].innerHTML;let data_length = data_list.length;// 开场进行一次排序if (parseFloat($(".player-auxiliary-danmaku-wrap").css("height").replace("px", "")) >= parseFloat($(".player-auxiliary-danmaku-wrap > div > ul").css("height").replace("px", ""))) {setInterval(() => {if (isChange != $(".bilibili-player-video-time-now")[0].innerHTML) {let now_i = danmaku_search(data_list, get_now_s()); // 获取即将播放的弹幕if (now_i != -1) list_structure(data_list, data_arr, 0, danmaku_length - 1, now_i, li_width);isChange = $(".bilibili-player-video-time-now")[0].innerHTML;}}, 1000);} else {setInterval(() => {if (isChange != $(".bilibili-player-video-time-now")[0].innerHTML) {// 获取列表首尾let now_i = danmaku_search(data_list, get_now_s());// 判断是否需要更新if (now_i != -1 && parseInt(now_i) + danmaku_length - 15 >= data_length) list_structure(data_list, data_arr, data_length - danmaku_length, data_length - 1);else if (now_i != -1) list_structure(data_list, data_arr, parseInt(now_i) - 7, parseInt(now_i) + danmaku_length - 8);isChange = $(".bilibili-player-video-time-now")[0].innerHTML;}}, 1000);}// 开启与停止滚动控制}// 构建弹幕列表let list_structure = (data_list, data_arr, start, end) => {$(".player-auxiliary-danmaku-wrap > div > ul").empty(); // 清除先前的弹幕,重新构建for (let i = start; i <= end; i++) {let li = document.createElement("li");let span1 = document.createElement("span");let span2 = document.createElement("span");let span3 = document.createElement("span");let text1 = document.createTextNode(time_s_m(data_list[i][0]));let text2 = document.createTextNode(data_arr[i]);let text3 = document.createTextNode(data_s_d(data_list[i][1]));span1.appendChild(text1);span2.appendChild(text2);span3.appendChild(text3);$(span1).addClass("danmaku-info-time");$(span2).addClass("danmaku-info-danmaku");$(span3).addClass("danmaku-info-date");li.appendChild(span1);li.appendChild(span2);li.appendChild(span3);$(li).addClass("danmaku-info-row");$(".player-auxiliary-danmaku-wrap > div > ul")[0].appendChild(li);}}
})();

安装脚本后只要打开右边的弹幕列表,弹幕就会随视频播放而滚动了。
在这里插入图片描述

这篇关于js实现bilibili弹幕列表随视频播放滚动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/910782

相关文章

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核