精华内容
下载资源
问答
  • 原本也有想过可以利用现有的弹幕姬做个插件来解决的,但无奈不会C#,所以只能自己研究b站弹幕协议。 后来有写过一个C++版本的,不过有一些小问题,这在后文中会提到。 开码 一丶利用 POST 方式获取 B 站直播弹幕 ...

    前言

    关于这个小项目的由来。
    最开始是想要利用b站的弹幕进行一些互动之类的。原本也有想过可以利用现有的弹幕姬做个插件来解决的,但无奈不会C#,所以只能自己研究b站的弹幕协议。
    后来有写过一个C++版本的,不过有一些小问题,这在后文中会提到。

    开码

    一丶利用 POST 方式获取 B 站直播弹幕

    参考:【python】b站直播弹幕获取
    首先,随便打开一个b站的直播页面,按F12打开控制台,点进“网络(Network)”标签,刷新一下,然后审计一下里面的内容,可以找到“gethistory”这个文件里面就是我们要的弹幕了。
    打开的直播页面
    实际上,仔细观察便不难发现,请求 gethistory 的时候返回的是请求时最近的10条历史弹幕,不过根据这些就可以写出来一个简易的弹幕姬了。具体做法就是每隔一定的时间请求一次,然后与上次的请求做对比。不同的部分就是这段时间新发的弹幕了,这样就可以对弹幕进行一些操作了。
    我们点进“headers”标签:
    headers标签
    有了这些我们就可以开写一个弹幕姬了。
    虽然headers很乱,不过实际上我们在请求弹幕的时候并不需要这么多headers,具体哪些headers是必要的可以用实验试出来,不过具体过程和结果我就直接略去了。最后的代码可以参考:
    B站直播弹幕爬取
    或我自己写的C++版本:
    【笔记/学习】c++实现b站弹幕姬
    (代码有点长而且不是本文的重点这里就不放了)
    注:之前的时候获取弹幕的URL是:https://api.live.bilibili.com/ajax/msg,不过我写这篇文再去复现的时候发现这个URL已经没了,经过观察发现变成了 https://api.live.bilibili.com/xlive/web-room/v1/dM/gethistory 截至写文时二者都能用。不过这些都不是重点了。

    二丶利用 WebSocket 获取 B 站弹幕

    前文利用 POST 的方式获取B站弹幕。这种方法虽然简单,但也有不方便的地方。我设的时间间隔为3s,但如果3s内发送的弹幕数量超过了10条,这种方法就会丢失一部分的弹幕,而如果简单的减小时间间隔,不仅会占用更多的网络资源,如果太过频繁的话还可能会被封IP。
    而 HTTP 请求的这种缺陷也正好就是 WebSocket 的出现所为了解决的问题。事实上,我们在看B站直播的时候正是通过 WebSocket 的方式与服务器通信的。
    让我们继续打开 F12 :
    b站弹幕的websocket
    这个 sub 就是与弹幕服务器通信的 WebSocket 啦。
    点进 Message 标签,会看见一大堆东西。不过我们并不需要自己去研究这个通信协议,在 Github 上已经有了B站的API可以直接使用。
    弹幕WS协议
    API文档使用 JavaScript 写的,不过这并不妨碍我们移植一个 Python 版本的。
    由 API 可知,我们与服务器进行通信所发送的数据大多是 json 的数据,偶尔还会有 zlib 数据。所以我们自然需要导入这两个包。我们与服务器使用 WebSocket 进行通信,但原生 Python 并不能直接发送 WebSocket,我们自然也不可能使用 socket 去造轮子。不过好在已经有很多好用的 WebSocket 的库可供我们使用了。
    目前常用的 WebSocket 库有: websocket-client, websockets, aiowebsocket 三个。其中 websocket-client 是同步的,因为我们在收弹幕的同时还得要发送心跳包才能不被服务器断开连接,使用异步io会方便一些。所以不用他。另外两个我也都试过。感觉上 aiowebsocket 更稳定一些,所以这里我们使用这个库。
    安装:

    pip install aiowebsocket
    

    除了 aiowebsocket 要安装外,其他的库都是 python 自带的,直接导入就行了。自然不要忘了用了异步操作要加上 asyncio 库哦。

    import asyncio
    import zlib
    from aiowebsocket.converses import AioWebSocket
    import json
    

    之后我们写好入口函数:

    if __name__ == '__main__':
        remote = 'wss://broadcastlv.chat.bilibili.com:2245/sub'
        try:
            asyncio.get_event_loop().run_until_complete(startup(remote))
        except KeyboardInterrupt as exc:
            pring('Quit.')
    

    remote 自然就是API中弹幕服务器的地址了。然后是startup()

    roomid = '5322'
    
    data_raw='000000{headerLen}0010000100000007000000017b22726f6f6d6964223a{roomid}7d'
    data_raw=data_raw.format(headerLen=hex(27+len(roomid))[2:], roomid=''.join(map(lambda x:hex(ord(x))[2:],list(roomid))))
    
    async def startup(url):
        async with AioWebSocket(url) as aws:
            converse = aws.manipulator
            
            await converse.send(bytes.fromhex(data_raw))
            tasks=[receDM(converse), sendHeartBeat(converse)]
            await asyncio.wait(tasks)
    

    在连接到弹幕服务器后必须先发一个数据包写出进入的房间,否则连接会被断开。这里我是直接从浏览器抄的。数据包中的包长度必须要正确,所以这里要计算一下包长度。
    然后这里先把接受弹幕的 receDM() 和发送心跳包的 sendHeartBeat 先写好。接下来是这两个函数:

    hb = '00000010001000010000000200000001'
    async def sendHeartBeat(websocket):
        while True:
            await asyncio.sleep(30)
            await websocket.send(bytes.fromhex(hb))
            print('[Notice] Sent HeartBeat.')
    
    async def receDM(websocket):
        while True:
            recv_text = await websocket.receive()
            printDM(recv_text)
    

    B站的弹幕服务器是如果70秒没有心跳就断开连接,这里是30s发送一次。因为只需要发一个没有内容的数据包就行了,所以这里也是直接从浏览器抄的。。
    对于接收到的数据包的处理比较复杂,这里我们单独写一个函数来处理它。
    首先,由API我们可以看到每个数据包的头部是怎样的:

    位置 0-3 4-5 6-7 8-11 12-15 16-
    说明 数据包长度 数据包头部长度 协议版本 操作类型 数据包头部长度 数据包内容

    不过这些内容我们并不都需要用到。
    下面上代码,具体说明在注释中写了:

    # 将数据包传入:
    def printDM(data):
        # 获取数据包的长度,版本和操作类型
        packetLen = int(data[:4].hex(),16)
        ver = int(data[6:8].hex(),16)
        op = int(data[8:12].hex(),16)
    
        # 有的时候可能会两个数据包连在一起发过来,所以利用前面的数据包长度判断,
        if(len(data)>packetLen):
            printDM(data[packetLen:])
            data=data[:packetLen]
    
        # 有时会发送过来 zlib 压缩的数据包,这个时候要去解压。
        if(ver == 2):
            data = zlib.decompress(data)
            printDM(data)
            print('db3')
            return
        
        # ver 为1的时候为进入房间后或心跳包服务器的回应。op 为3的时候为房间的人气值。
        if(ver == 1):
            if(op == 3):
                print('[RENQI]  {}'.format(int(data[16:].hex(),16)))
            return
    
        # ver 不为2也不为1目前就只能是0了,也就是普通的 json 数据。
        # op 为5意味着这是通知消息,cmd 基本就那几个了。
        if(op==5):
            try:
                jd = json.loads(data[16:].decode('utf-8', errors='ignore'))
                if(jd['cmd']=='DANMU_MSG'):
                    print('[DANMU] ', jd['info'][2][1], ': ', jd['info'][1])
                elif(jd['cmd']=='SEND_GIFT'):
                    print('[GITT]',jd['data']['uname'], ' ', jd['data']['action'], ' ', jd['data']['num'], 'x', jd['data']['giftName'])
                elif(jd['cmd']=='LIVE'):
                    print('[Notice] LIVE Start!')
                elif(jd['cmd']=='PREPARING'):
                    print('[Notice] LIVE Ended!')
                else:
                    print('[OTHER] ', jd['cmd'])
            except Exception as e:
                pass
    

    这样,一个简单的弹幕姬就完成了!

    三丶试试搞一些其他的事情吧!

    至此,我们已经有了一个可以在控制台输出弹幕内容的弹幕姬了。不过,这并不是结束,有了这个我们就可以利用弹幕搞事情了(
    先放个简单的功能:

    把弹幕保存至本地

    先到如下时间:

    import time
    

    然后我们把前面的print()函数改掉:

    def log(typ, body):
    	with open('D:/danmu.txt','a') as fd:
    		fd.write(time.strftime("[%H:%M:%S] ", time.localtime()))
            fd.write(body+'\n')
    

    这样就可以爬取弹幕到本地了。

    利用SAPI朗读弹幕

    利用 SAPI 朗读需要导入相应的包:

    import win32com.client
    

    然后改写log函数:

    def log(typ, body):
    		speak = win32com.client.Dispatch("SAPI.SpVoice")
            #创建发声对象
            speak.Speak(body)
            #使用发生对象读取文字
    	with open('D:/danmu.txt','a') as fd:
    		fd.write(time.strftime("[%H:%M:%S] ", time.localtime()))
            fd.write(body+'\n')
    

    来使python自动朗读弹幕。
    一般 Windows 都可以直接使用,不能用的话再上网查吧。。

    利用聊天机器人实现自动聊天

    首先打开b站一个直播间,发条弹幕截下包:
    测试弹幕
    照着参考,可以大致写出一份发送弹幕的python脚本

    import requests
    import time
    form_data = {
        'color': '65532',
    	'fontsize': '25',
    	'mode': '1',
    	'msg': 'test',
    	'rnd': int(time.time()),
    	'roomid': '1136753',
    	'csrf_token': 'cce335cbfa5bfd292a049b813175bd12',
    	'csrf': 'cce335cbfa5bfd292a049b813175bd12'
    }
    # 设置cookie值帮助我们在发送弹幕的时候,服务器识别我们的身份
    cookie = { '你的cookie(上图红色部分)' }
    res = requests.post('https://api.live.bilibili.com/msg/send', cookies=cookie, data=form_data)
    print (res.status_code)
    

    上面的 csrfcsrf_token 在使用的时候最好也换成自己的。
    然后可以去注册图灵机器人/思知机器人等API(不推荐图灵,之前还好,后来一去看感觉有点贵),申请到 appid。
    具体可以参考我以前写的这篇文章
    最后代码差不多是这样:

    def talk(msg):
        form_data = {
            'color': '65532',
            'fontsize': '25',
            'mode': '1',
            'msg': 'test',
            'rnd': int(time.time()),
            'roomid': roomid,
            'csrf_token': 'cce335cbfa5bfd292a049b813175bd12',
            'csrf': 'cce335cbfa5bfd292a049b813175bd12'
        }
        cookie = { '你的cookie(上图红色部分)' }
        payload = {
            'appid': '你的appid',
            'userid': '1234',
        }
        payload['spoken'] = msg
        res1 = requests.get("https://api.ownthink.com/bot", params=payload)
        form_data['msg'] = res1.json()['data']['info']['text']
        res2 = requests.post('https://api.live.bilibili.com/msg/send', cookies=cookie, data=form_data)
    

    好啦,去直播间发条弹幕看看效果:
    效果
    注意使用的时候加上限制不要对机器人的回复再去回复就行了

    。。。

    后记

    嗯。。。暂时就先写这么多吧,还有什么要补充的以后再说吧。。
    源码什么的之后再传吧。。

    展开全文
  • 本文介绍了一个简单的自动发弹幕的例子,其实用插件做爬虫也是可行的。插件本地存储可以用 localStorage 先把爬取的资源链接存好,然后做个页面用表格展示,表格另存到本地,再用 python 程序直接访问链接。 自动发...

    感觉网上关于插件开发的文章不多,其实插件开发很简单,也很方便。比起 selenium 解析网页,用 javascript 代码直接嵌入浏览器要方便很多。本文介绍了一个简单的自动发弹幕的例子,其实用插件做爬虫也是可行的。插件本地存储可以用 localStorage 先把爬取的资源链接存好,然后做个页面用表格展示,表格另存到本地,再用 python 程序直接访问链接。

    自动发弹幕这个例子只需要三个文件,manifest.json ,background.js ,Bw.png (插件图标)。

    manifest.json 文件代码:

    {
      "manifest_version": 2,
      "name": "弹幕助手",
      "description": "自动填充弹幕",
      "version": "1.0",
      "permissions": [
        "tabs", "http://*/plus/flink_add.php", "https://*/plus/flink_add.php"
      ],
      "background": {
        "scripts": ["background.js"]
      },
      "browser_action": {
          "default_title": "自动填充弹幕",
          "default_icon": "Bw.png" 
      }
      
    }

    background.js 文件代码:

    // Copyright (c) 2011 The Chromium Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
     
    // Called when the user clicks on the browser action.
    
    var sleep = function(time) {
        var startTime = new Date().getTime() + parseInt(time, 10);
        while(new Date().getTime() < startTime) {}
    };
     
    function checkURL(url){
        console.log('URL:'+url);
        var flag = false;
        if(typeof url == "undefined" || null == url)
            url = window.location.href; 
    	// var regex = /.*\:\/\/.*\/form.php/;
    	var regex = /https\:\/\/.*bilibili\.com.*/;
        var match = url.match(regex);
        if(typeof match != "undefined" && null != match)
            flag = true;
        return flag;
    }
    
    
    function autoAddValue()
    {  
    	chrome.tabs.executeScript(null,
    		// 直接修改 input 的 value 值可能会引发问题。因为浏览器会认为程序自动输入的值是输入框的默认值,认为用户并没有输入文字!
    		{code:" var input = document.querySelectorAll('input.bilibili-player-video-danmaku-input')[0]; \
    				var playbutton = document.querySelectorAll('div.bilibili-player-video-state')[0];\
    				var ClickDiv = document.querySelectorAll('div.bilibili-player-video-btn-send')[0];  \
    				\
    				function fireClick(node){ \
    					if (document.createEvent) {\
    						var evt_m = document.createEvent('MouseEvents');\
    						evt_m.initMouseEvent('click', true, false);\
    						node.dispatchEvent(evt_m);    \
    					} else if(document.createEventObject) {\
    						node.fireEvent('onclick') ;    \
    					} else if (typeof node.onclick == 'function') {\
    						node.onclick();    \
    					}\
    				}\
    				function inputValue(dom, st) {\
    					var evt = new InputEvent('input', {\
    					  inputType: 'insertText',\
    					  data: st,\
    					  dataTransfer: null,\
    					  isComposing: false\
    					});\
    					dom.value = st;\
    					dom.dispatchEvent(evt);\
    				}\
    				\
    				fireClick(playbutton);\
    				inputValue(input, '这个视频我看了好多遍了,讲得好。');\
    				function bullet(){\
    					setInterval(function(){ \
    						fireClick(ClickDiv);  \
    						console.log(\"点击一下\");\
    					}, 9000);\
    				}\
    				setTimeout(\"bullet()\", 5000 );\
            "}); 
    }
    
    var countUpdate = 1;
     
    chrome.browserAction.onClicked.addListener(function(tabId,changeInfo,tab){ 
    	countUpdate = 1; 
    });
    
    chrome.tabs.onCreated.addListener(function(tabId,changeInfo,tab){ 
    
    	if(checkURL(tab.url)){
    		countUpdate = 1;
    	}	 
    })  
    
    chrome.tabs.onUpdated.addListener( function(tabId,changeInfo,tab){ 
    
    	if(countUpdate==2){
    		sleep(5000);
    		if(checkURL(tab.url))
    		{
    			autoAddValue();
    			chrome.tabs.executeScript({
    				code: 'document.body.style.backgroundColor="#f2d649";console.log('+countUpdate.toString()+');'   // 黄色
    			});
    		}
    		else
    		{
    			console.log('不是bilibili网站!');   // 这个无法显示在页面上,因为不是同一个页面。
    			chrome.tabs.executeScript({
    				code: 'document.body.style.backgroundColor="#f24182"'    // 红色
    			});
    		}
    	}    
    	countUpdate = countUpdate + 1;
    });
    
    // https://www.bilibili.com/video/BV1Ht411y73G?p=4
    
    

    background.js 就是从浏览器启动一直在后台运行的东西。这个文件的功能是,如果网页是B站播放页面就把背景改为黄色,否则红色。在弹幕输入框输入“这视频很好.....”这句话,然后每9s发送一次。注意,输入文本和点击都必须发送事件,input 直接修改 value 属性是没用的。

    把三个文件放在一个文件夹下,打开浏览器扩展页的开发者模式,把文件夹拖入扩展页,安装完成。

    如果想了解插件还有什么其他的能力,访问  Chrome插件(Extensions)开发攻略 。

    展开全文
  • 自助b 站弹幕服务器连接器主程序负责保持与弹幕服务器的连接和解析弹幕数据,其他功能由插件提供。 自动加载plugins 文件夹下的js 文件,插件导出一个类,所有插件在程序启动时进行初始化,传入一个ebus,ebus 目前...
  • 准备工作 下载安装python https://www.python.org/ 下载python并安装,本文章基于python 3.8.1 idea安装python插件 idea找到settings,进入并搜索python插件安装。...B站弹幕爬虫 ...至于B站弹幕爬虫,我们...

    准备工作

    1. 下载安装python
    https://www.python.org/ 下载python并安装,本文章基于python 3.8.1
    
    1. idea安装python插件
      1
      idea找到settings,进入并搜索python插件安装。

    2. 创建python工程
      2
      直接点next就行。

    B站弹幕爬虫

    3
    新建python文件,就可以直接写python代码并且运行了
    至于B站弹幕爬虫,我们首先随意打开一个B站视频,浏览器按F12打开开发者工具,点击network,然后刷新视频页面进行抓包。
    4
    找到它,这个地址是每个视频的弹幕文件地址
    5
    然后使用requests库进行get请求,re库去掉标签,jieba库对所有弹幕生成的字符串进行词语分割,然后利用wordcloud库生成词云图片。

    # 爬取B站弹幕
    from wordcloud import WordCloud
    import requests, re, jieba, os
    
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
    }
    
    # https://api.bilibili.com/x/v1/dm/list.so?oid=110428362
    
    def danmu(url):
        response = requests.get(url, headers=headers)
        html_doc = response.content.decode('utf-8')
        format = re.compile("<d.*?>(.*?)</d>")
        danMu = format.findall(html_doc)
        cut_list = jieba.lcut(" ".join(danMu))
        new_str = " ".join(cut_list)
        word_cloud = WordCloud(font_path="msyh.ttc").generate(new_str)
        word_cloud.to_file('d:\pic.png')
    
    if __name__ == '__main__':
        s = input("输入要爬取的弹幕地址:")
        danmu(s.strip())
        # try:
        #     danmu(s)
        # except Exception:
        #     print(Exception)
        os.startfile('d:\pic.png')
    

    最后根据弹幕生成词云:
    6

    展开全文
  • 功能快捷键 撤销:Ctrl/Command + Z 重做:Ctrl/Command + Y 加粗:Ctrl/Command + B 斜体:Ctrl/Command + I 标题:Ctrl/Command + Shift + H 无序列表:Ctrl/Command + Shift + U 有序列表:Ctrl/Command + Shift...

    1.首先打开外国的著名网址(高科技软件(doge_))

    https://www.tampermonkey.net/
    在这里插入图片描述

    2.点击如图所示

    在这里插入图片描述

    3.安装后chrome浏览器会出现tampermonkey的图标

    4.复制以下代码在下面第四项中添加新脚本

    在这里插入图片描述

    5.粘贴覆盖在下方代码1开头到15行的代码。(总之就是覆盖去掉之前的代码就行了)

    在这里插入图片描述

    // ==UserScript==
    // @name       视频网HTML5播放小工具
    // @description 三大功能 。启用HTML5播放;万能网页全屏;添加快捷键:快进、快退、暂停/播放、音量、下一集、切换(网页)全屏、上下帧、播放速度。支持视频站点:油管、TED、优.土、QQ、B站、西瓜视频、爱奇艺、A站、PPTV、芒果TV、咪咕视频、新浪、微博、网易[娱乐、云课堂、新闻]、搜狐、风行、百度云视频等;直播:斗鱼、YY、虎牙、龙珠、战旗。可增加自定义站点
    // @homepage   https://bbs.kafan.cn/thread-2093014-1-1.html
    // @include    https://*.qq.com/*
    // @exclude    https://user.qzone.qq.com/*
    // @include    https://www.weiyun.com/video_*
    // @include    https://v.youku.com/v_show/id_*
    // @include    https://vku.youku.com/live/*
    // @include    https://video.tudou.com/v/*
    // @include    https://www.iqiyi.com/*
    // @include    https://www.bilibili.com/*
    // @include    https://www.ixigua.com/*
    // @include    http://www.le.com/ptv/vplay/*
    // @include    https://www.le.com/ptv/vplay/*
    // @include    https://www.acfun.cn/*
    // @include    http://v.pptv.com/show/*
    // @include    https://v.pptv.com/show/*
    // @include    http://www.miguvideo.com/*
    // @include    https://www.miguvideo.com/*
    // @include    https://tv.sohu.com/*
    // @include    https://film.sohu.com/album/*
    // @include    https://www.mgtv.com/*
    // @include    http://m.fun.tv/*
    // @include    https://m.fun.tv/*
    // @include    http://www.fun.tv/vplay/*
    // @include    https://www.fun.tv/vplay/*
    // @version    1.6.9
    // @include    https://*.163.com/*
    // @include    https://*.icourse163.org/*
    // @include    https://*.sina.com.cn/*
    // @include    https://video.sina.cn/*
    // @include    https://k.sina.cn/*
    // @include    https://weibo.com/*
    // @include    https://*.weibo.com/*
    // @include    https://pan.baidu.com/*
    // @include    https://yun.baidu.com/*
    // @include    http://v.ifeng.com/*
    // @include    https://v.ifeng.com/*
    // @include    http://news.mtime.com/*
    // @include    http://video.mtime.com/*
    // @GM_info
    // @include    https://www.youtube.com/watch?v=*
    // @include    https://www.ted.com/talks/*
    // @noframes
    // @include    https://www.yy.com/*
    // @include    https://v.huya.com/play/*
    // @include    https://www.huya.com/*
    // @include    https://v.douyu.com/*
    // @include    https://www.douyu.com/*
    // @include    http://star.longzhu.com/*
    // @include    https://star.longzhu.com/*
    // @include    https://www.zhanqi.tv/*
    // @run-at     document-start
    // @include    https://www.yunbtv.com/vodplay/*
    // @include    *://www.dililitv.com/*
    // @include    *://www.dynamicpuer.com/tv-play-*
    // @include    http://www.kalidm.com/bangumi/v_*
    // @grant      unsafeWindow
    // @grant      GM_addStyle
    // @grant      GM_registerMenuCommand
    // @grant      GM_setValue
    // @grant      GM_getValue
    // @namespace  https://greasyfork.org/users/7036
    // ==/UserScript==
    
    'use strict';
    const w = unsafeWindow || window;
    const { host, pathname: path } = location;
    const d = document, find = [].find;
    let v, _fp, _fs, by; // document.body
    const observeOpt = {childList : true, subtree : true};
    const noopFn = () => {};
    const q = (css, p = d) => p.querySelector(css);
    const delElem = e => e.remove();
    const $$ = function(c, cb = delElem, doc = d) {
    	if (!c || !c.length) return;
    	if (typeof c === 'string') c = doc.querySelectorAll(c);
    	if (!cb) return c;
    	for (let e of c) if (e && cb(e)=== !1) break;
    };
    const gmFuncOfCheckMenu = (title, saveName, defaultVal = true) => {
    	const r = GM_getValue(saveName, defaultVal);
    	if (r) title = '√  '+ title;
    	GM_registerMenuCommand(title, () => {
    		GM_setValue(saveName, !r);
    		location.reload();
    	});
    	return r;
    };
    const r1 = (regp, s) => regp.test(s) && RegExp.$1;
    const log = console.log.bind(console, '%c脚本[%s] 反馈:%s\n%s', 'color:#c3c;font-size:1.5em',
    	GM_info.script.name, GM_info.script.homepage);
    const sleep = ms => new Promise(resolve => { setTimeout(resolve, ms) });
    const getStyle = (o, s) => {
    	if (o.style[s]) return o.style[s];
    	if (getComputedStyle) {
    		const x = getComputedStyle(o, '');
    		s = s.replace(/([A-Z])/g,'-$1').toLowerCase();
    		return x && x.getPropertyValue(s);
    	}
    };
    const doClick = e => {
    	if (typeof e === 'string') e = q(e);
    	if (e) { e.click ? e.click() : e.dispatchEvent(new MouseEvent('click')) };
    };
    const clickDualButton = btn => { // 2合1 按钮
    	!btn.nextSibling || getStyle(btn, 'display') !== 'none' ? doClick(btn) : doClick(btn.nextSibling);
    };
    const intervalQuery = (cb, condition, stop = true) => {
    	const fn = typeof condition === 'string' ? q.bind(null, condition) : condition;
    	const t = setInterval(() => {
    		const r = fn();
    		if (r) {
    			stop && clearInterval(t);
    			cb(r);
    		}
    	}, 300);
    };
    const goNextMV = () => {
    	const m = path.match(/^(.+)(\d+)(\D*)$/);
    	const d = +m[2] + 1;
    	location.assign(m[1] + d + m[3]);
    };
    const firefoxVer = r1(/Firefox\/(\d+)/, navigator.userAgent);
    const isEdge = / Edge?\//.test(navigator.userAgent);
    const fakeUA = ua => Object.defineProperty(navigator, 'userAgent', {
    	value: ua,
    	writable: false,
    	configurable: false,
    	enumerable: true
    });
    const getMainDomain = host => {
    	const a = host.split('.');
    	let i = a.length - 2;
    	if (/^(com|tv|net|org|gov|edu)$/.test(a[i])) i--;
    	return a[i];
    };
    const ua_chrome = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3626.121 Safari/537.36';
    
    class FullScreen {
    	constructor(e) {
    		let fn = d.exitFullscreen || d.webkitExitFullscreen || d.mozCancelFullScreen || d.msExitFullscreen || noopFn;
    		this.exit = fn.bind(d);
    		fn = e.requestFullscreen || e.webkitRequestFullScreen || e.mozRequestFullScreen || e.msRequestFullScreen || noopFn;
    		this.enter = fn.bind(e);
    	}
    
    	static isFull() {
    		return !!(d.fullscreen || d.webkitIsFullScreen || d.mozFullScreen ||
    		d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement);
    	}
    
    	toggle() {
    		FullScreen.isFull() ? this.exit() : this.enter();
    	}
    }
    
    //万能网页全屏, 参考了:https://github.com/gooyie/ykh5p
    class FullPage {
    	constructor(video, onSwitch) {
    		this._video = video;
    		this._isFull = !1;
    		this._onSwitch = onSwitch;
    		this._checkContainer();
    		GM_addStyle(
    			`.gm-fp-body .gm-fp-zTop {
    				position: relative !important;
    				z-index: 2147483647 !important;
    			}
    			.gm-fp-wrapper, .gm-fp-body{ overflow:hidden !important; }
    			.gm-fp-wrapper .gm-fp-innerBox {
    				width: 100% !important;
    				height: 100% !important;
    			}
    			.gm-fp-wrapper {
    				display: block !important;
    				position: fixed !important;
    				width: 100% !important;
    				height: 100% !important;
    				top: 0 !important;
    				left: 0 !important;
    				background: #000 !important;
    				z-index: 2147483647 !important;
    			}`
    		);
    	}
    
    	getPlayerContainer(video) {
    		let e = video, p = e.parentNode;
    		const { clientWidth: wid, clientHeight: h } = e;
    		do {
    			e.classList.add('gm-fp-innerBox');
    			e = p;
    			p = e.parentNode;
    		} while (e.nodeName == 'DIV' && p.clientWidth-wid < 5 && p.clientHeight-h < 5);
    		//e 为返回值,在此之后不能变了
    		while (p !== by) {
    			p.classList.add('gm-fp-zTop');
    			p = p.parentNode;
    		}
    		return e;
    	}
    
    	_checkContainer() {
    		this._container = this._container || this.getPlayerContainer(this._video);
    	}
    
    	get container() {
    		this._checkContainer();
    		return this._container;
    	}
    
    	static isFull(e) {
    		return w.innerWidth - e.clientWidth < 5 && w.innerHeight - e.clientHeight < 5;
    	}
    
    	toggle() {
    		const cb = this._onSwitch;
    		if (!this._isFull && cb) cb(true);
    		by.classList.toggle('gm-fp-body');
    		this.container.classList.toggle('gm-fp-wrapper');
    		this._isFull = !this._isFull;
    		if (!this._isFull && cb) setTimeout(cb, 199, !1);
    	}
    }
    
    const u = getMainDomain(host);
    const events = {
    	on(name, fn) {
    		this[name] = fn;
    	}
    };
    const app = {
    	isLive: !1,
    	disableSpace: !1,
    	multipleV: !1, //多视频页面
    	isNumURL: !1, //网址数字分集
    	checkMV() {
    		if (!v || !v.offsetWidth) v = this.findMV();
    		return v;
    	},
    	checkDPlayer() {
    		if (this.dpShell) return true;
    		if (this.dpShell = v.closest('.dplayer')) {
    			this.btnFP = q('.dplayer-full-in-icon > span', this.dpShell);
    			this.btnFS = q('.dplayer-full-icon', this.dpShell);
    			this.btnPlay = this.btnNext = _fs = _fp = null;
    			if (this.nextCSS) this.btnNext = q(this.nextCSS);
    			this.disableSpace = !1;
    			this.dpShell.closest('body > *').classList.add('gm-dp-zTop');
    			this.dpShell.addEventListener('dblclick', ev => {
    				this.btnFP.click();
    			}, true);
    		}
    		return !!this.dpShell;
    	},
    	hotKey(e) {
    		if (e.ctrlKey || e.altKey || e.target.contentEditable=='true' ||
    			/INPUT|TEXTAREA|SELECT/.test(e.target.nodeName)) return;
    		if (e.shiftKey && ![32,13,37,39].includes(e.keyCode)) return;
    		if (this.isLive && [37,39,78,88,67,90].includes(e.keyCode)) return;
    		if (this.extPlayer && this.extPlayer.contains(e.target) && [32,37,39].includes(e.keyCode)) return;
    		if (!this.checkMV()) return;
    		!this.checkDPlayer() && this.checkUI();
    		if (events.keydown && events.keydown(e)) return;
    		let n;
    		switch (e.keyCode) {
    		case 32: //space
    			if (e.shiftKey) {
    				v.scrollIntoView();
    				v.focus();
    			} else {
    				if (this.disableSpace && by != e.target) return;
    				if (this.btnPlay) clickDualButton(this.btnPlay);
    				else v.paused ? v.play() : v.pause();
    			}
    			e.preventDefault();
    			break;
    		case 37: n = e.shiftKey ? -20 : -5; //left  快退5秒,shift加速
    		case 39: //right
    			n = n || (e.shiftKey ? 20 : 5); //快进5秒,shift加速
    			v.currentTime += n;
    			break;
    		case 78: // N 下一首
    			if (this.btnNext) doClick(this.btnNext);
    			else if (this.isNumURL) goNextMV();
    			break;
    		case 38: n = 0.1; //加音量
    		case 40: //降音量
    			n = n || -0.1;
    			n += v.volume;
    			n = + n.toFixed(1);
    			if (0 <= n && n <= 1) v.volume = n;
    			e.preventDefault();
    			break;
    		case 13: //回车键。 全屏
    			if (e.shiftKey) {
    				_fp ? _fp.toggle() : clickDualButton(this.btnFP);
    			} else {
    				_fs ? _fs.toggle() : clickDualButton(this.btnFS);
    			}
    			break;
    		case 27: //esc
    			if (this.dpShell) return;
    			if (FullScreen.isFull()) {
    				_fs ? _fs.exit() : clickDualButton(this.btnFS);
    			} else if (FullPage.isFull(v)) {
    				_fp ? _fp.toggle() : clickDualButton(this.btnFP);
    			}
    			break;
    		case 67: n = 0.1; //按键C:加速播放 +0.1
    		case 88: //按键X:减速播放 -0.1
    			n = n || -0.1;
    			n += v.playbackRate;
    			n = + n.toFixed(2);
    			if (0 < n && n <= 16) v.playbackRate = n;
    			break;
    		case 90: //按键Z:正常速度播放
    			v.playbackRate = 1;
    			break;
    		case 70: n = 0.03; //按键F:下一帧
    		case 68: //按键D:上一帧
    			n = n || -0.03;
    			if (!v.paused) v.pause();
    			v.currentTime += n;
    			break;
    		default: return;
    		}
    		e.stopPropagation();
    	},
    	checkUI() {
    		if (this.webfullCSS && !this.btnFP) this.btnFP = q(this.webfullCSS);
    		if (this.btnFP) _fp = null;
    		else if (!_fp) _fp = new FullPage(v, this.switchFP);
    
    		if (this.fullCSS && !this.btnFS) this.btnFS = q(this.fullCSS);
    		if (this.btnFS) _fs = null;
    		else if (!_fs) _fs = new FullScreen(v);
    
    		if (this.nextCSS && !this.btnNext) this.btnNext = q(this.nextCSS);
    		if (this.playCSS && !this.btnPlay) this.btnPlay = q(this.playCSS);
    	},
    	switchFP(toFull) {
    		if (toFull) {
    			for (let e of this.vSet) this.viewObserver.unobserve(e);
    		} else {
    			for (let e of this.vList) this.viewObserver.observe(e);
    		}
    	},
    	onGrowVList() {
    		if (this.vList.length == this.vCount) return;
    		if (this.viewObserver) {
    			for (let e of this.vList) {
    				if (!this.vSet.has(e)) this.viewObserver.observe(e);
    			}
    		} else {
    			const config = {
    				rootMargin: '0px',
    				threshold: 0.9
    			};
    			this.viewObserver = new IntersectionObserver(this.onIntersection.bind(this), config);
    			for (let e of this.vList) this.viewObserver.observe(e);
    		}
    		this.vSet = new Set(this.vList);
    		this.vCount = this.vList.length;
    	},
    	onIntersection(entries) {
    		if (this.vList.length < 2) return;
    		const entry = find.call(entries, k => k.isIntersecting);
    		if (!entry || v == entry.target) return;
    		v = entry.target;
    		_fs = new FullScreen(v);
    		_fp = new FullPage(v, this.switchFP);
    		events.switchMV && events.switchMV();
    	},
    	bindEvent() {
    		by = d.body;
    		log('bind event\n', v);
    		events.foundMV && events.foundMV();
    		if (!this.isLive) {
    			const onCanplay = ev => {
    				v.removeEventListener('canplay', onCanplay);
    				v.playbackRate = localStorage.mvPlayRate || 1;
    				v.addEventListener('ratechange', ev => {
    					localStorage.mvPlayRate = v.playbackRate;
    				});
    			};
    			v.addEventListener('canplay', onCanplay);
    		}
    		const fn = ev => {
    			events.canplay && events.canplay();
    			v.removeEventListener('canplaythrough', fn);
    		};
    		if (v.readyState > 3) fn();
    		else v.addEventListener('canplaythrough', fn);
    		by.addEventListener('keydown', this.hotKey);
    		if (this.extPlayerCSS) {
    			const x = this.extPlayer = v.closest(this.extPlayerCSS);
    			x && x.addEventListener('keydown', this.hotKey);
    		}
    
    		if (this.multipleV) {
    			new MutationObserver(this.onGrowVList.bind(this)).observe(by, observeOpt);
    			this.vCount = 0;
    			this.onGrowVList();
    		}
    		this.checkDPlayer();
    	},
    	init() {
    		this.hotKey = this.hotKey.bind(this);
    		this.switchFP = this.multipleV ? this.switchFP.bind(this) : null;
    		this.vList = d.getElementsByTagName('video');
    		const fn = e => this.cssMV ? e.matches(this.cssMV) : e.offsetWidth > 9;
    		this.findMV = find.bind(this.vList, fn);
    		intervalQuery(e => {
    			v = e;
    			$$(this.adsCSS);
    			this.bindEvent();
    		}, this.findMV);
    	}
    };
    
    let router = {
    	ted() {
    		app.fullCSS = 'button[title="Enter Fullscreen"]';
    		app.playCSS = 'button[title="play video"]';
    		if (!gmFuncOfCheckMenu('TED强制高清', 'ted_forceHD')) return;
    		const getHDSource = async () => {
    			const pn = r1(/^(\/talks\/\w+)/, path);
    			const resp = await fetch(pn + '/metadata.json');
    			const data = await resp.json();
    			return data.talks[0].downloads.nativeDownloads.high;
    		};
    		const check = async (rs) => {
    			if (!v.src || v.src.startsWith('http')) return;
    			$$(app.vList, e => { e.removeAttribute('src') }); // 取消多余的媒体资源请求
    			try {
    				v.src = await getHDSource();
    			} catch(ex) {
    				console.error(ex);
    			}
    		};
    		events.on('foundMV', () => {
    			new MutationObserver(check).observe(v, {
    				attributes: true,
    				attributeFilter: ['src']
    			});
    			check();
    		});
    	},
    	youtube() {
    		app.playCSS = 'button.ytp-play-button';
    		app.fullCSS = 'button.ytp-fullscreen-button';
    	},
    	qq() {
    		app.nextCSS = '.txp_btn_next';
    		app.webfullCSS = '.txp_btn_fake';
    		app.fullCSS = '.txp_btn_fullscreen';
    		app.extPlayerCSS = '#mod_player';
    	},
    	youku() {
    		if (host.startsWith('vku.')) {
    			events.on('canplay', () => {
    				app.isLive = !q('.spv_progress');
    			});
    			app.fullCSS = '.live_icon_full';
    		} else {
    			const switchQuality = gmFuncOfCheckMenu('自动切换最高清晰度', 'yk_switchQuality', !1);
    			const fn = () => {
    				w.$('.settings-item.quality-item').remove('[data-val=download]')
    					.removeClass('disable youku_vip_pay_btn login-canuse')
    					.children('span').remove();
    				switchQuality && w.$('.quality-item:first').click();
    			};
    			events.on('canplay', fn);
    			events.on('foundMV',() => {
    				by.addEventListener('keyup', e => e.stopPropagation());
    			});
    			GM_registerMenuCommand('解除清晰度选择限制', fn);
    			app.webfullCSS = '.control-webfullscreen-icon';
    			app.fullCSS = '.control-fullscreen-icon';
    			app.nextCSS = 'span.icon-next';
    		}
    	},
    	bilibili() {
    		app.nextCSS = '.bilibili-player-video-btn-next';
    		app.webfullCSS = '.bilibili-player-video-web-fullscreen';
    		app.fullCSS = '.bilibili-player-video-btn-fullscreen';
    		app.extPlayerCSS = '#playerWrap';
    		const danmu = gmFuncOfCheckMenu('弹幕', 'bili_danmu');
    		const danmuCSS = '.bilibili-player-video-danmaku-switch input';
    		events.on('foundMV', () => {
    			intervalQuery(e => {if (e.checked != danmu) e.click()}, danmuCSS);
    		});
    
    		const fName= path.startsWith('/bangumi/') ? 'replaceState' : 'pushState';
    		const rawFn = history[fName]; //二方法不触发onpopstate事件
    		history[fName] = function(...args) {
    			rawFn.apply(this, args);
    			events.foundMV();
    			app.btnNext = app.btnFP = app.btnFS = null;
    		};
    	},
    	pptv() {
    		app.fullCSS = '.w-zoom-container > div';
    		app.webfullCSS = '.w-expand-container > div';
    		app.nextCSS = '.w-next';
    	},
    	mgtv() {
    		app.fullCSS = 'mango-screen';
    		app.webfullCSS = 'mango-webscreen > a';
    		app.nextCSS = 'mango-control-playnext-btn';
    	},
    	ixigua() {
    		app.fullCSS = '.xgplayer-fullscreen';
    		app.webfullCSS = '.xgplayer-cssfullscreen';
    		app.nextCSS = '.xgplayer-playNext';
    		events.on('foundMV', () => {
    			v.addEventListener('keydown', app.hotKey);
    		});
    	},
    	miguvideo() {
    		app.playCSS = '.play-btn';
    		app.fullCSS = '.zoom-btn';
    		app.webfullCSS = '.page-zoom-btn';
    		app.nextCSS = '.next-btn';
    	},
    	weibo() {
    		app.multipleV = path.startsWith('/u/');
    	},
    	baidu() {
    		events.on('keydown', e => {
    			if (!w.videojs) return;
    			let n, p = w.videojs.getPlayers("video-player").html5player.tech_;
    			switch (e.keyCode) {
    			case 67: n = 0.1;
    			case 88:
    				n = n || -0.1;
    				n += p.playbackRate().toFixed(2);
    				if (0 < n && n <= 16) p.setPlaybackRate(+n);
    				return true;
    			case 90:
    				p.setPlaybackRate(1);
    				return true;
    			}
    		});
    		app.extPlayerCSS = '.video-content';
    	},
    	acfun() {
    		app.nextCSS = '.btn-next-part .control-btn';
    		app.webfullCSS = '.fullscreen-web';
    		app.fullCSS = '.fullscreen-screen';
    	},
    	['163']() {
    		app.multipleV = host.startsWith('news.');
    		GM_addStyle('div.video,video{max-height: 100% !important;}');
    		return host.split('.').length > 3;
    	},
    	sohu() {
    		app.nextCSS = 'li.on[data-vid]+li a';
    		app.fullCSS = '.x-fullscreen-btn';
    		app.webfullCSS = '.x-pagefs-btn';
    	},
    	fun() {
    		app.nextCSS = '.btn-item.btn-next';
    	},
    	kalidm() {
    		app.nextCSS = `a[href="${path}"]+a`;
    		events.on('foundMV', () => {
    			v.closest('#player').removeEventListener('dblclick', test);
    		});
    		GM_addStyle('.dplayer-loaded{ background-color:orange !important; }');
    	}
    };
    app.disableSpace = /^(youtube|ixigua|qq|pptv|fun)$/.test(u);
    
    if (!router[u]) { //直播站点
    	router = {
    		douyu() {
    			const inRoom = host.startsWith('www.');
    			events.on('foundMV', () => {
    				if (inRoom && path != '/') {
    					intervalQuery(doClick, '.roomSmallPlayerFloatLayout-closeBtn');
    					q('#js-player-aside-state').checked = true;
    				}
    			});
    			app.cssMV = '[src^=blob]';
    			app.playCSS = inRoom ? 'div[class|=play]' : '.play-a473b6';
    			app.webfullCSS = inRoom ? 'div[class|=wfs]' : '.pagefull1-cd359b';
    			if (inRoom) app.fullCSS = 'div[class|=fs]'; // '.windowfull1-228321'
    			app.adsCSS = 'a[href*="wan.douyu.com"]';
    		},
    		yy() {
    			app.isLive = !path.startsWith('/x/');
    			if (app.isLive) {
    				app.fullCSS = '.yc__fullscreen-btn';
    				app.webfullCSS = '.yc__cinema-mode-btn';
    				app.playCSS = '.yc__play-btn';
    			}
    		},
    		huya() {
    			if (firefoxVer && firefoxVer < 57) return true;
    			app.webfullCSS = '.player-fullpage-btn';
    			app.fullCSS = '.player-fullscreen-btn';
    			app.playCSS = '#player-btn';
    			app.adsCSS = '#player-subscribe-wap,#wrap-income';
    			intervalQuery(doClick, '.login-tips-close');
    			localStorage['sidebar/ads'] = '{}';
    			localStorage['sidebar/state'] = 0;
    			localStorage.TT_ROOM_SHIELD_CFG_0_ = '{"10000":1,"20001":1,"20002":1,"20003":1,"30000":1}';
    		},
    		longzhu() {
    			app.fullCSS = 'a.ya-screen-btn';
    		},
    		zhanqi() {
    			localStorage.lastPlayer = 'h5';
    			app.fullCSS = '.video-fullscreen';
    		}
    	};
    	if (router[u]) {
    		app.isLive = app.isLive || !host.startsWith('v.');
    		(!w.chrome || isEdge) && fakeUA(ua_chrome);
    	}
    }
    
    Reflect.defineProperty(navigator, 'plugins', {
    	get() { return { length: 0 } }
    });
    GM_registerMenuCommand('脚本功能快捷键表' , alert.bind(w,
    `左右方向键:快退、快进5秒; +shift: 20秒
    上下方向键:音量调节
    空格键:暂停/播放; +shift: 定位播放器窗口
    N:播放下一集
    回车键:切换全屏; +shift: 切换网页全屏
    ESC:退出(网页)全屏
    C:加速0.1倍播放       X:减速0.1倍播放       Z:正常速度播放
    D:上一帧        F:下一帧`
    ));
    if (!router[u] || !router[u]()) app.init();
    if (!router[u] && !app.isNumURL) app.isNumURL = /\W\d+(\.[a-z]{3,8})?$/.test(path);
    

    6.然后左上角点击保存。

    在这里插入图片描述

    7.就会出现下面的一行有着视频网站LOGO的东西。然后,它是自动启动。

    在这里插入图片描述
    **

    完成了,然后在视频网站试用一下吧!

    **在这里插入图片描述

    展开全文
  • 这是一个基于nodejs的终端B站弹幕脚本 HXD们能不能帮忙点个star呀 怎么用? 理论上你是需要使用npm安装依赖的,分别是fetch,xml2js,sleep 然后就可以用nodejs *.js,然后输入BV号就可以运行了 为啥做这个 是因为无聊...
  • 和朋友合作用d3可视化b站弹幕,一方面学习巩固,另外也是通过可视化大致了解下视频是否精彩,再决定看不看[捂脸] 使用方式 通过bookmarklet使用,有点像chrome插件但更方面一点~。地址:bilibili可视化没用过的进去...
  • NPlayer 是由 Typescript 加 Sass 编写,无任何第三方运行时依赖,Gzip 大小只有 21KB,兼容 IE11,支持 ...它还拥有插件系统,弹幕功能 就是使用插件形式提供。该播放器可以接入任何 流媒体,如 hls、dash 和 flv 等。
  • 现同时支持斗鱼、虎牙、Mildom、b站和ytb 请在当地法律限制和直播平台规范内使用,不要拿来做于直播内容无关的事!!!!! 使用方法:打开直播间页面后复制v3.0.js文件代码至浏览器控制台(F12 console) 你的页面...
  • 精仿B站播放器外加弹幕库源码 简单介绍 # 插件功能:弹幕后台、前置广告、暂停广告、会员去广告,记忆回放,自动下一集 # 插件支持:.m3u8、.mp4、.flv 等常见视频格式,注意:不支持 MP4 H265 格式的视频 # 插件...
  • pakku 立志于改善哔哩哔哩弹幕视频的观看体验,可以合并B站视频中绝大多数刷屏弹幕,让您免受各种带节奏弹幕的刷屏之苦。左侧的截图就展示了 pakku 实现的惊人效果。 内容近似且时间差在一定时间以内的多个刷屏弹幕...
  • 精仿B站播放器外加弹幕库源码 插件功能:弹幕后台、前置广告、暂停广告、会员去广告,记忆回放,自动下一集 插件支持:.m3u8、.mp4、.flv 等常见视频格式,注意:不支持 MP4 H265 格式的视频 插件兼容:电脑、手机端...
  • 有浏览器就行,无需爬虫,无需安装其他任何软件或者插件。 在任意b站直播间,打开浏览器开发者工具(推荐...1, 从弹幕中随机抽出 js 代码如下 var danmus = $(".chat-history-list .chat-items .chat-item.danmaku.
  • 通过Bilibili Helper可以简单方便的修改B站Flash播放器为最新的HTML5网页播放器,同时还可以绕过B站的区域限制看仅限部分地区可看的新番、拥有关注动态通知推送、视频及弹幕下载、反向查询弹幕发送者B站帐户等功能。...
  •   大家好,我是小编南风吹,每天推荐一个小工具/源码,装满你的收藏夹,让你轻松...它还拥有插件系统,弹幕功能就是使用插件形式提供。该播放器可以接入任何流媒体,如hls、dash和flv等。 开源协议   使用 MIT 开源
  • 教你怎么用you-get精确下载B站视频及弹幕1、安装相关环境及插件2、安装you-get3、使用you-get进行下载视频 1、安装相关环境及插件 安装Pythone 3 ,去官网下载可执行的安装包 点击下载...
  • 这是一个用来在B站视频进度条上方创建显示弹幕热度的Chrome插件,以弹幕数量-时间的直方图显示,在高能处(定义为短时间内有大量弹幕出现的时间点)有明显的峰值,可以用来直观地看视频中的热点,也可以拿来作为空降...
  • 本案例是使用WeX5制作的app,界面模仿b站(哔哩哔哩弹幕视频网)客户端。 功能介绍: ①模仿b站app的首页展现、话题/游戏中心等页面展现、多栏目侧滑查看、左侧边栏、搜索栏等。 ②视频详细信息展现,示意性分享与...
  • 先来思考一个问题,B站一个视频的弹幕最多会有多少? 比较多的会有2000条吧,这么多数据,B站肯定是不会直接把弹幕和这个视频绑在一起的。 也就是说,有一个视频地址为https://www.bilibili.com/video/av67946325,...
  • 我的弹幕助手不会自动去褥辣条了,重新设置之后直播间页面也会不停的刷新(刷新频率设置了0,应该是关闭的) <p><strong>重现bug 说明您在进行了怎样的操作后出现了bug 1. 如上 2. 如上 <p>...
  • 先来思考一个问题,B站一个视频的弹幕最多会有多少? 比较多的会有2000条吧,这么多数据,B站肯定是不会直接把弹幕和这个视频绑在一起的。 也就是说,有一个视频地址为 https://www.bilibili.com/vide..., 你...
  • 安装有SwitchyOmega且开着未加载资源计数功能, 当Pakku下载图标停止闪烁时B站播放器有很大几率显示弹幕池加载失败, 同时SwitchyOmega显示在*.bilibili.com</code>未加载的资源+1 </li><li> 同时安装有哔哩哔哩...
  • 在打开一个B站的视频后,扩展会从html页面中解析出弹幕文件的ID,然后构造弹幕文件的url,向B站服务器发送请求,从而获取到弹幕文件,软色情视频的分类算法和 一样的,只是将 python 的代码移植成了 JavaScript的。...
  • B站全名哔哩哔哩,域名bilibili.com ,名字源于《魔法禁书目录》中 御坂美琴 的昵称,所以B站动漫出现 bilibili译制的字样时, 会有很多弹幕刷出 御坂美琴译制, 由于“御坂美琴”这个动漫角色在二次元世界的江湖地位...
  • B站直播互动及数据收集机器人 注意,本项目仍然在活跃开发中,进行并发开发时请注意注意将来的破坏性植入 说明 本项目为一个Nodejs新手的开发作品,若您对代码有任何建议或意见,欢迎提交问题,我会虚心学习各位的...
  • Bilibili 直播姬-crx插件

    2021-04-06 16:46:14
    配合弹幕接口功能,可以做出更自定义更丰富的弹幕互动直播场景,例如弹幕点歌、弹幕点电影和语音朗读弹幕这些小玩意,甚至还可以弹幕写文章或者弹幕玩页游等等,全都能在你的网页用js实现。为什么还需要Node.js呢,...
  • 为此花费许久尝试了搜到的各种办法(关插件、硬件加速、弹幕渲染等),都未奏效。后来自己摸索,一条条查修改的配置,终于找到了问题所在。 以下为失败信息: 视频显示 加载视频内容[失败] MEDIA SEGMENT 下载错误 ...
  • <div><p> ... 您所使用的软件或服务(...但是能看到弹幕。 试了其它b站视频也是如此。情况如上图所示。 求助! 您所处的国家或地区:AU</p><p>该提问来源于开源项目:uku/Unblock-Youku</p></div>
  • B站播放器提供快捷键以及调整默认状态的功能,如关闭弹幕、截图、画中画等。 这是一个功能精简、轻量型的哔哩哔哩(HTML5)播放器扩展。1. 播放器设置此扩展会默认关闭哔哩哔哩(Bilibili)弹幕,但可以在扩展选项中...

空空如也

空空如也

1 2 3
收藏数 49
精华内容 19
关键字:

b站弹幕插件