您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
びにびに
// ==UserScript== // @name GODTMID版b站视频下载 // @namespace http://tampermonkey.net/ // @version 2.9 // @description びにびに // @author GODTMID // @homepage https://github.com/Godtmid // @match https://www.bilibili.com/video/* // @match https://www.bilibili.com/bangumi/* // @match https://www.bilibili.com/medialist/* // @icon http://i0.hdslb.com/bfs/archive/106d36aa2d40ccefab90805da451aca859eea20f.jpg // @require https://lib.baomitu.com/jquery/1.12.4/jquery.min.js // @require https://cdn.staticfile.org/FileSaver.js/2.0.5/FileSaver.min.js // @require https://cdn.bootcdn.net/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js // @grant unsafeWindow // @grant GM_download // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM.addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_notification // @grant GM_openInTab // @grant GM_deleteValue // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // ==/UserScript== (function () { var ydata var p var aid var cid var bvid var ptitle var title var pic var downurl var preurl = document.URL var epInfo var badge var barrageurl var MAXINT = 9007199254740991 var epList var btnflag = false var count = 0 //批量按钮点击事件参数 //线路,质量,弹幕 var indexArr = [0,0,0] var qualityArr = [] var downloadline = ["默认线路","高速线路","其他线路"] var danmuTypeArr = ["无弹幕","ass弹幕","xml弹幕","双弹幕"] var danmuTypeArr1 = ["","ass","xml","all"] var originalInterfaceList = [ {"name":"纯净解析","category":"1","url":"https://z1.m1907.cn/?jx="}, {"name":"高速接口1","category":"1","url":"https://api.sigujx.com/?url="}, {"name":"B站解析1","category":"1","url":"https://vip.parwix.com:4433/player/?url="}, {"name":"B站解析2","category":"1","url":"https://www.cuan.la/m3u8.php?url="}, {"name":"Ckplayer","category":"1","url":"https://www.ckplayer.vip/jiexi/?url="}, {"name":"乐多资源","category":"1","url":"https://api.leduotv.com/wp-api/ifr.php?isDp=1&vid="}, {"name":"ccyjjd","category":"1","url":"https://ckmov.ccyjjd.com/ckmov/?url="}, {"name":"M3U8","category":"1","url":"https://jx.m3u8.tv/jiexi/?url="}, {"name":"BL","category":"1","url":"https://vip.bljiex.com/?v="}, {"name":"Mao解析","category":"1","url":"https://qd.hxys.tv/m3u8.php?url="}, {"name":"盘古","category":"1","url":"https://www.pangujiexi.cc/jiexi.php?url="}, {"name":"SSAMAO","category":"1","url":"https://www.ssamao.com/jx/?url="}, {"name":"无极","category":"1","url":"https://da.wujiys.com/?url="}, {"name":"618G","category":"1","url":"https://jx.618g.com/?url="}, {"name":"ckmov","category":"1","url":"https://www.ckmov.vip/api.php?url="}, {"name":"迪奥","category":"1","url":"https://123.1dior.cn/?url="}, {"name":"福星","category":"1","url":"https://jx.popo520.cn/jiexi/?url="}, {"name":"RDHK","category":"1","url":"https://jx.rdhk.net/?v="}, {"name":"H8","category":"1","url":"https://www.h8jx.com/jiexi.php?url="}, {"name":"解析la","category":"1","url":"https://api.jiexi.la/?url="}, {"name":"久播","category":"1","url":"https://jx.jiubojx.com/vip.php?url="}, {"name":"九八","category":"1","url":"https://jx.youyitv.com/?url="}, {"name":"老板","category":"1","url":"https://vip.laobandq.com/jiexi.php?url="}, {"name":"乐喵","category":"1","url":"https://jx.hao-zsj.cn/vip/?url="}, {"name":"MUTV","category":"1","url":"https://jiexi.janan.net/jiexi/?url="}, {"name":"明日","category":"1","url":"https://jx.yingxiangbao.cn/vip.php?url="}, {"name":"磨菇","category":"1","url":"https://jx.wzslw.cn/?url="}, {"name":"OK","category":"1","url":"https://okjx.cc/?url="}, {"name":"维多","category":"1","url":"https://jx.ivito.cn/?url="}, {"name":"小蒋","category":"1","url":"https://www.kpezp.cn/jlexi.php?url="}, {"name":"小狼","category":"1","url":"https://jx.yaohuaxuan.com/?url="}, {"name":"智能","category":"1","url":"https://vip.kurumit3.top/?v="}, {"name":"星驰","category":"1","url":"https://vip.cjys.top/?url="}, {"name":"星空","category":"1","url":"http://60jx.com/?url="}, {"name":"月亮","category":"1","url":"https://api.yueliangjx.com/?url="}, {"name":"0523","category":"1","url":"https://go.yh0523.cn/y.cy?url="}, {"name":"云端","category":"1","url":"https://jx.ergan.top/?url="}, {"name":"17云","category":"1","url":"https://www.1717yun.com/jx/ty.php?url="}, {"name":"66","category":"1","url":"https://api.3jx.top/vip/?url="}, {"name":"116","category":"1","url":"https://jx.116kan.com/?url="}, {"name":"200","category":"1","url":"https://vip.66parse.club/?url="}, {"name":"云析","category":"1","url":"https://jx.yparse.com/index.php?url="}, {"name":"8090","category":"1","url":"https://www.8090g.cn/?url="}, {"name":"纯净解析","category":"2","url":"https://z1.m1907.cn/?jx="}, {"name":"高速接口1","category":"2","url":"https://jsap.attakids.com/?url="}, {"name":"高速接口2","category":"2","url":"https://api.sigujx.com/?url="}, {"name":"B站解析1","category":"2","url":"https://vip.parwix.com:4433/player/?url="}, {"name":"B站解析2","category":"2","url":"https://www.cuan.la/m3u8.php?url="}, {"name":"Ckplayer","category":"2","url":"https://www.ckplayer.vip/jiexi/?url="}, {"name":"乐多资源","category":"2","url":"https://api.leduotv.com/wp-api/ifr.php?isDp=1&vid="}, {"name":"ccyjjd","category":"2","url":"https://ckmov.ccyjjd.com/ckmov/?url="}, {"name":"M3U8","category":"2","url":"https://jx.m3u8.tv/jiexi/?url="}, {"name":"BL","category":"2","url":"https://vip.bljiex.com/?v="}, {"name":"Mao解析","category":"2","url":"https://qd.hxys.tv/m3u8.php?url="} ]; //批量首次下载规则改名文件标志 var jsonflag = true { var style = ` <style type="text/css"> .blcolor{ background: rgb(0 161 214); border-radius: 2px; font-size: 14px; box-sizing: border-bo; line-height: 27px; height: 27px; width: 40px; color: white; text-align: center; } .blcolor:hover{ border: 1px solid #cc124f; color: deeppink; backgrund:#16b8d6; } .bldownbtn{ z-index: ${MAXINT}; box-sizing: border-box; line-height: 35px; height: 35px; width: 35px; border-radius: 2em; font-size: 10px; align-items: center; justify-content: center; cursor: pointer; color: white; background: rgb(0 161 214); text-align: center; position: fixed; left: 10px; top: 100px; } #showmsg{ z-index: ${MAXINT}; opacity: 0.7; position: fixed; left: 10px; top: 127px; background: azure; font-size: 14px; display:none; max-width: 350px; border-radius: 1em; padding: 5px 10px; } #showmsg bdo{ word-wrap:break-word; color: deeppink; line-height: 1.5; } #showmsg label{ font-size: 15px; float:left; line-height: 1.5; } #showmsg>div{ margin-bottom: 10px; } #showmsg .dlbtn{ float:left; width: 60px; } .clear{ clear:left; } .batchbtn-bangumi{ position: absolute; right: 0px; bottom: 0px; font-size: 4px; background-color: #08ab85cc; z-index: 10; cursor: pointer; border: none; color: white; } .batchbtn:active{ background-color: yellow; } .batchbtn-video-list{ margin-top: -25px; margin-left: 251px; line-height: 1.5; background: rgb(0 161 214); color: white; } .ep-list-progress{ display: block; float: right; height: 42px; line-height: 42px; font-size: 12px; color: #999; margin-right: 7px; font-weight: 1000; cursor: pointer; } </style>` } $(document.head).append(style) initload() timetack() setTimeout(function () { $(".dplayer-video.dplayer-video-current").attr("src") }, 3000) function initload() { setTimeout(function () { loadDownloadBtn() loadBatchComponent() initFont() }, 3000) } function loadDownloadBtn(){//target="_blank" var html = `<div> <div id="bilibilidownload" class="blcolor bldownbtn">下载</div> <div id="showmsg"> <div> <label>图片:</label> <a href="javascript:void(0)" class="blcolor cover">封面下载</a> <a href="javascript:player.screenshot(true)" class="blcolor">截图</a> <a href="javascript:player.screenshot(false)" class="blcolor">GIF截取</a> </div> <div> <label>弹幕下载:</label> <input type="hidden" class="barrage" /> <a href="javascript:void(0)" data-type="xml" class="blcolor barrageType">XML格式</a> <a href="javascript:void(0)" data-type="ass" class="blcolor barrageType">ASS格式</a> </div> <div> <label style="line-height: 3;">本地播放说明:</label> <a href="http://www.downza.cn/soft/184629.html" target="_blank" class="blcolor outerChain">b站本地播放器</a>配合XML格式弹幕食用 <a href="https://potplayer.en.softonic.com" target="_blank" class="blcolor outerChain">Potplayer播放器</a>配合ASS格式弹幕食用 </div> <div class="title"> <label>标题:</label> <bdo></bdo> </div> <div class="subtitle"> <label>当前集:</label> <bdo></bdo> </div> <div class="showtime"> <label>时长:</label> <bdo></bdo> </div> <div class="downloadInfo"> </div> </div> ` $(document.body).append(html) $( "#bilibilidownload" ).draggable({ containment: $(document.body), drag: draggableback }) $('#bilibilidownload').on('click', function () { if($("#showmsg").is(':hidden')){ if($("#showmsg .downloadInfo").html().trim() == ''){ console.clear() if(preurl.startsWith('https://www.bilibili.com/video')){ //getVideoAffectInfo() getVideoData() }else if(preurl.startsWith('https://www.bilibili.com/bangumi')){ getbangumiData() }else if(preurl.startsWith('https://www.bilibili.com/medialist')){ getVideoDataMedialist() } } $(this).text("X") }else{ $(this).text("下载") } $("#showmsg").fadeToggle(1000) }) $(".barrageType").on('click',function(){ const title = $(".barrage").attr("title") const cid = $(".barrage").attr("data-id") const type = $(this).attr("data-type") dmdownload(type,cid,title) }) $(".cover").on('click',function(){ const title = $(this).attr("title") const url = $(this).attr("data-url") const namearr = url.split(".") const suffix = namearr[namearr.length-1] GM_download(url, `${title}.${suffix}`) /*GM_xmlhttpRequest({ method: "GET", url: url, responseType:"blob", onload: function(response) { var blob = new Blob([this.response],{type: "image/jpeg"}) saveAs(blob, `${title}.${suffix}`) } })*/ }) } function draggableback(){ const y = parseInt($("#bilibilidownload").css("top").replace("px",""))+30 const x = parseInt($("#bilibilidownload").css("left").replace("px","")) const wx = document.documentElement.clientWidth const wy = document.documentElement.clientHeight const sx = $("#showmsg").width() let badge1 try{ badge1 = unsafeWindow.__INITIAL_STATE__.epInfo.badge }catch{ badge1 = "" } const sy = badge1==""?419+30:260+30 if(wx-x<=sx&&wy-y<=sy){ $("#showmsg").css("top",(y-sy)+"px").css("left",(x-sx)+"px") }else if(wx-x<=sx){ $("#showmsg").css("top",y+"px").css("left",(x-sx)+"px") }else if(wy-y<=sy){ $("#showmsg").css("top",(y-sy)+"px").css("left",x+"px") }else{ $("#showmsg").css("top",y+"px").css("left",x+"px") } } function dmdownload(type,cid,filename){ GM_xmlhttpRequest({ 'method': 'GET', 'url': `http://comment.bilibili.com/${cid}.xml`, 'onload': function (resp) { const content = resp.responseText.replace(/(?:[\0-\x08\x0B\f\x0E-\x1F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g, "") if(type == "xml"){ const blob = new Blob([content], {type: "application/xml"}) saveAs(blob, `${filename}.xml`) }else if(type == "ass"){ const arr = parseXML(content) const ass = generateASS(setPosition(arr), { 'title': document.title, 'ori': location.href, }); startDownload('\ufeff' + ass, filename + '.ass') }else if(type == "all"){ const blob = new Blob([content], {type: "application/xml"}) saveAs(blob, `${filename}.xml`) const arr = parseXML(content) const ass = generateASS(setPosition(arr), { 'title': document.title, 'ori': location.href, }); startDownload('\ufeff' + ass, filename + '.ass') } //var blob1 = new Blob([ass], {type: "application/xml"}) //saveAs(blob1, `${filename}.ass`) // var i = "https://upos-sz-mirrorcoso1.bilivideo.com/upgcxcode/66/59/246355966/246355966_nb2-1-64.flv?e=ig8euxZM2rNcNbNz7WdVhwdlhbhBhwdVhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1627997482&gen=playurlv2&os=coso1bv&oi=2028683548&trid=202b30558e18460390a9f18201629fa9u&platform=pc&upsig=2f1ba21188852a7fdbc3823af5ff00a2&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=23203114&bvc=vod&nettype=0&orderid=2,3&agrr=0&logo=40000000" // saveAs(i, "dd.flv") // var arr = parseXML(content) // console.log(JSON.stringify(arr)) } }) } function loadBatchComponent(){ var head var exitflag if(preurl.startsWith('https://www.bilibili.com/video')){ head = $("#multi_page .head-left") exitflag = componentvideo() }else if(preurl.startsWith('https://www.bilibili.com/bangumi')){ head = $("#eplist_module .list-title.clearfix") exitflag = componentbangumi() }else if(preurl.startsWith('https://www.bilibili.com/medialist')){ head = $("#playerAuxiliary .player-auxiliary-playlist-bottom") //exitflag = componentMedialist() }else{ return false } if(exitflag){ return false } //批量开关 const batchSwitchEle = ` <div class="ep-list-progress batchSwitch"><span>批量</span></div> ` //下载线路 const linesEle = ` <div class="ep-list-progress lines"> <span>高速线路</span> </div> ` //弹幕 const barrageTypeEle = ` <div class="ep-list-progress barrageType"> <span>无弹幕</span> </div> ` //视频质量 const qualityEle = ` <div class="ep-list-progress qualitys"> </div> ` $(head).append(batchSwitchEle).append(linesEle).append(barrageTypeEle).append(qualityEle) $("#multi_page .head-con").css("padding","7px") $("#eplist_module .list-title.clearfix").css("padding","0 7px") timetaskforquality() //下载按钮点击事件 $(document).on('click','.batchbtn',function(){ //"/bangumi/play/ep391743/" //"/video/BV1es41127PE?p=11" $(this).css("background","gray") let href = $(this).parent().find("a:first").attr("href") if(href.includes("bangumi")){ batchdownloadoneForbangumi(href,this) }else if(href.includes("video")){ batchdownloadoneForvideo(href) }else if(href.includes("medialist")){ batchdownloadoneFormedialist(this) } }) //下载线路选择 $(document).on('click','.lines>span',function(){ batchclickcommon('0',downloadline, this) }) //视频质量选择 $(document).on('click','.qualitys>span',function(){ batchclickcommon('1',qualityArr,this) }) //弹幕类型选择 $(document).on('click','.barrageType>span',function(){ batchclickcommon('2',danmuTypeArr,this) }) } function batchclickcommon(index,valueArr, ele){ ++indexArr[index] >= valueArr.length?indexArr[index]=0:{}; $(ele).html(valueArr[indexArr[index]]) } function componentvideo(){ const batchcom = $("#multi_page .cur-list>ul>li") if(!$(batchcom)[0]){ return true } $("#multi_page .head-left>h3,.cur-page").hide() $('#multi_page .head-left .range-box').hide() //批量下载 const batchbtns = `<button class="batchbtn batchbtn-video batchbtn-video-list part">下载</button>` //批量开关点击事件 $(document).on('click','.batchSwitch',function(){ $("#multi_page .cur-list>ul.list-box>li .duration").toggle() if(!btnflag){ $(this).css("color","#00a1d6") $(batchcom).append(batchbtns) btnflag = true }else{ $(this).css("color","") $(".batchbtn").remove() btnflag = false } }) //选集分布[块分布|列表分布] /*$(document).on('click', '#multi_page .head-left .range-box>i', function(){ if(btnflag){ $(".batchbtn").remove() if($(this).hasClass("van-icon-general_viewmodule")){ $(".cur-list .module-box>li").append(batchbtns) }else{ if($(batchcom)[0]){ $(batchcom).append(batchbtns) } } } })*/ } function componentbangumi(){ if(!$("#eplist_module .list-wrapper>ul>li")[0]|| $("#eplist_module .list-wrapper>ul>li:has(.badge)").length == $("#eplist_module .list-wrapper>ul>li").length){ return true } $("#eplist_module .list-title.clearfix>h4").hide() //批量下载 const batchbtns = `<button class="batchbtn batchbtn-bangumi">下载</button>` const dmbatchbtns = `<button class="batchbtn batchbtn-bangumi dmbtn">弹幕</button>` //批量开关点击事件 $(document).on('click','.batchSwitch>span',function(){ if(!btnflag){ $(this).css("color","#00a1d6") $("#eplist_module .list-wrapper>ul>li.ep-item:not(.badge)").append(batchbtns) $("#eplist_module .list-wrapper>ul>li.ep-item:has(.badge)").append(dmbatchbtns) btnflag = true }else{ $(this).css("color","") $(".batchbtn").remove() btnflag = false } }) //选集分布[块分布|列表分布] $(document).on('click', '.mode-change', function(){ if(btnflag){ $(".batchbtn").remove() $("#eplist_module .list-wrapper>ul>li.ep-item:not(.badge)").append(batchbtns) $("#eplist_module .list-wrapper>ul>li.ep-item:has(.badge)").append(dmbatchbtns) } }) //番剧分集列表滚动事件,渲染因滚动而消失的“下载按钮” $("#eplist_module .list-wrapper").scroll(function(){ if(btnflag){ console.log(1) setTimeout(function () { $("#eplist_module .list-wrapper>ul>li.ep-item:not(:has(>button))").append(batchbtns) $("#eplist_module .list-wrapper>ul>li.ep-item:has(.badge)").append(dmbatchbtns) }, 100) } }) } function componentMedialist(){ let batchcom = $("#playerAuxiliary .player-auxiliary-playlist-item") $("#playerAuxiliary .player-auxiliary-playlist-bottom").empty() //批量下载 const batchbtns = `<button class="batchbtn" style="float:right">下载</button>` //批量开关点击事件 $(document).on('click','.batchSwitch',function(event){ $("#playerAuxiliary .bui-collapse-wrap:eq(1) div").unbind() if(!btnflag){ $(this).css("color","#00a1d6") $(batchcom).append(batchbtns) btnflag = true }else{ $(this).css("color","") $(".batchbtn").remove() btnflag = false } setTimeout(function () { $(".bui-collapse-body:eq(1)").css("height","") }, 500) }) } function timetaskforquality(){ //循环等待视频加载获取视频质量 var qualityinterval = window.setInterval(function () { var tempQualitys if(preurl.startsWith('https://www.bilibili.com/video')){ tempQualitys = $("ul.bui-select-list>li.bui-select-item") }else if(preurl.startsWith('https://www.bilibili.com/bangumi')){ tempQualitys = $("ul.squirtle-select-list.squirtle-quality-select-list.squirtle-dialog>li") } if($(tempQualitys)[0]!=undefined){ $(tempQualitys).each(function(){ let value = $(this).attr("data-value") let name = $(this).text().replace(/\s/g,"").replace("登录即享","") let span = `<span data-value="${value}">${name}</span>` if($(this).hasClass("active")|| $(this).hasClass("bui-select-item-active")){ if(value > 80){ $(".qualitys").html(`<span data-value="64">720P高清</span>`) }else{ $(".qualitys").html(span) } } if(value!='0' && value < 112){ qualityArr.push(span) } }) clearInterval(qualityinterval) } }, 1000) } //定时任务 function timetack(){ const interval = window.setInterval(function () { //页面URL改变重置下载页面 if (preurl != document.URL) { $('#showmsg').hide() $("#showmsg .downloadInfo").empty() preurl = document.URL } //视频网页全屏隐藏下载按钮 if($(".bilibili-player-video-btn.bilibili-player-video-web-fullscreen").hasClass("closed")||$(".squirtle-video-pagefullscreen.squirtle-video-item").hasClass("active")){ $('#bilibilidownload').parent().hide() }else{ $('#bilibilidownload').parent().show() } if($(".dplayer-video.dplayer-video-current")[0] !=undefined){ alert($(".dplayer-video.dplayer-video-current").attr("src")) clearInterval(interval) } }, 1000) } //批量下载,点击单个下载 function batchdownloadoneForbangumi(href,This){ const dmflag = $(This).hasClass("dmbtn")?true:false epList = unsafeWindow.__INITIAL_STATE__.epList let currentid = /\d{1,}/.exec(href)[0] for (let i = 0,len = epList.length; i < len; i++) { if(epList[i].id == currentid){ cid = epList[i].cid aid = epList[i].aid const temptitle = epList[i].titleFormat != "" ? epList[i].titleFormat+":"+epList[i].longTitle : epList[i].longTitle dmdownload(danmuTypeArr1[indexArr[2]],cid,temptitle) dmflag?batchdownloadmodel("bangumi"):{} break } } } function batchdownloadoneForvideo(href){ ydata = unsafeWindow.__INITIAL_STATE__ cid = ydata.videoData.pages[href.split("?p=")[1]-1].cid const part = ydata.videoData.pages[href.split("?p=")[1]-1].part aid = ydata.aid dmdownload(danmuTypeArr1[indexArr[2]],cid,part) batchdownloadmodel("video") } function batchdownloadoneFormedialist(This){ } function batchdownloadmodel(jsontype){ const quality = $(".qualitys>span").attr("data-value") downurl = `https://api.bilibili.com/x/player/playurl?cid=${cid}&avid=${aid}&qn=${quality}` queryDown(downurl, 3) if(jsonflag){ downloadRenameJson(jsontype) jsonflag = false } } //批量下载回调处理 function batchback(result){ const temp = count++ const urls = result.data.durl[0].backup_url urls.push(result.data.durl[0].url) const url = urls[indexArr[0]] const tempa = `<a style="display:none" id="tempadownload${temp}" href="${url}" />` $(document.body).append(tempa) $(`#tempadownload${temp}`)[0].click() setTimeout(function () { $(`#tempadownload${temp}`).remove() }, 200) } //番剧电影下载 function getbangumiData(){ ydata = unsafeWindow.__INITIAL_STATE__ epInfo = ydata.epInfo aid = epInfo.aid cid = epInfo.cid pic = epInfo.cover badge = epInfo.badge p = epInfo.i == undefined ? '1' : epInfo.i + 1 title = $(".media-title").text() downurl = `https://api.bilibili.com/x/player/playurl?cid=${cid}&avid=${aid}` $(".barrage").attr("title",`${title}第${p}集`).attr("data-id",cid) $(".cover").attr("title",title).attr("data-url",pic) $(".title bdo").html(title) $(".subtitle bdo").html(`第${p}集`) if(badge == ''){ queryDown(downurl, 1) }else{ // $("#showmsg .downloadInfo").append(`<b style="color:red;line-height: 3;">版权原因,会员视频无法下载</b>`) const dhtml = ` <a href="javascript:void(0)" id="parseBadge" class="blcolor cover">会员完美解析</a> <label> <a href="javascript:void(0)" id="badgedown" class="blcolor cover" style="display:none">下载</a> <br> <label>说明:解析有点延迟,请拖动一下进度条</label> ` $("#showmsg .downloadInfo").append(dhtml) $("#showmsg .showtime").hide() var playurl $(document).on('click','#showmsg #parseBadge',function(event){ $(this).text("解析中。。。").css("pointer-events","none") GM_xmlhttpRequest({ 'method': 'GET', 'url': `https://vip.jianjians.com/beiyong/?url=`+window.location.href, 'onload': function (result) { if(result.status == 200){ try{ const url = /url = '.*'/.exec(result.responseText)[0].split(`'`)[1] playurl = decodeURIComponent(escape(window.atob(url))).replace("http","https") /*if(playurl){ $('#showmsg #badgedown').attr("href",playurl).show() }*/ console.log(`会员视频链接:${playurl}`) if( $(".twp-mask.twp-static")[0]){ $(".twp-mask.twp-static").empty().append(`<video controls="" autoplay="" style="width:100%;height:100%" name="media"><source src="${playurl}" type="video/mp4"></video>`) } if($("#player_module #bilibili-player video")[0]){ $("#player_module #bilibili-player video").attr("src",playurl) $(".bpx-player-toast-item").hide() } }catch{ $('#showmsg #parseBadge').text("解析出错") } } $('#showmsg #parseBadge').text("会员完美解析").css("pointer-events",'') }, error: function (e) { $('#showmsg #parseBadge').text("解析出错") console.log(e.responseText); } }) }) } } //普通视频下载 function getVideoData() { ydata = unsafeWindow.__INITIAL_STATE__ p = ydata.p aid = ydata.videoData.aid cid = ydata.videoData.pages[p - 1].cid bvid = ydata.videoData.bvid ptitle = ydata.videoData.pages[p - 1].part title = ydata.videoData.title pic = ydata.videoData.pic downurl = `https://api.bilibili.com/x/player/playurl?cid=${cid}&bvid=${bvid}&otype=json&avid=${aid}` //https://api.bilibili.com/x/player/playurl?cid=85793097&bvid=BV1zb411M7NQ&qn=80&type=&otype=json&fourk=1&fnver=0&fnval=80&session=ec9e6190f71492a3899a96cbfc9c581c console.log("****************b站浏览器参数信息*******************") console.log(unsafeWindow.__INITIAL_STATE__) const conmsg = `*******************视频基本信息********************************************************************************* ** bvid:${bvid} ** 封面:${pic} ** 标题:${title} ** 当前P的标题:${ptitle} *******************视频基本信息********************************************************************************* ` console.log(conmsg) $(".barrage").attr("title",title).attr("data-id",cid) $("#showmsg .cover").attr("title",title).attr("data-url",pic) $("#showmsg .title bdo").html(title) if($("#multi_page")[0]!=undefined){ $("#showmsg .subtitle label:first").html(`P${p}:`) $("#showmsg .subtitle bdo").html(`${ptitle}`) }else{ $("#showmsg .subtitle").empty() } queryDown(downurl, 1) } //普通视频下载 function getVideoDataMedialist() { const ele = $(".player-auxiliary-playlist-item-active"); aid = $(ele).attr("data-aid") cid = $(ele).attr("data-cid") bvid = $(ele).attr("data-bvid") title = $("#viewbox_report .video-title").attr("title") downurl = `https://api.bilibili.com/x/player/playurl?cid=${cid}&bvid=${bvid}&otype=json&avid=${aid}` //https://api.bilibili.com/x/player/playurl?cid=85793097&bvid=BV1zb411M7NQ&qn=80&type=&otype=json&fourk=1&fnver=0&fnval=80&session=ec9e6190f71492a3899a96cbfc9c581c console.log("****************b站浏览器参数信息*******************") const conmsg = `*******************视频基本信息********************************************************************************* ** bvid:${bvid} ** 标题:${title} *******************视频基本信息********************************************************************************* ` console.log(conmsg) $(".barrage").attr("title",title).attr("data-id",cid) $("#showmsg .title bdo").html(title) $("#showmsg .subtitle").hide() try{ GM_xmlhttpRequest({ 'method': 'GET', 'url': `https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`, 'onload': function (resp) { pic = JSON.parse(resp.response).data.pic $("#showmsg .cover").attr("title",title).attr("data-url",pic) } })}catch{ $("#showmsg .cover").hide() } queryDown(downurl, 1) } //普通视频下载——首次获取所有可下载视频质量 function firstback(result) { console.log(`*********************************下载接口信息*********************************`) console.log(result) let downarr = result.data.accept_quality.sort((a,b)=>a-b) for (let v = 0,len = downarr.length; v < len; v++) { queryDown(`${downurl}&qn=${downarr[v]}`, 2, downarr[v]) } } //普通视频下载——从首次获取视频质量循环获取下载链接 function back(result, curquality) { const quality = result.data.quality if (quality != curquality) { return } const urls = result.data.durl[0].backup_url urls.push(result.data.durl[0].url) const timelength = result.data.timelength const timestr = getTimeFormat(timelength) const size = result.data.durl[0].size const accept_description = result.data.accept_description[result.data.accept_quality.map(item => item).indexOf(quality)] console.log(`视频信息: ${accept_description} ${(size / 1024 / 1024).toFixed(2)}MB ${timestr}`) $("#showmsg .showtime bdo").html(timestr) let dmsg = ` <div class="${quality}"> <div>下载链接:【${accept_description}】|【${(size / 1024 / 1024).toFixed(2)}MB】</div> <div class="downloadways${quality}"></div> <div> <div class="clear" /> ` $('#showmsg .downloadInfo').append(dmsg) for (let i = 0,len = urls.length; i < len; i++) { console.log(`${downloadline[i]}:${urls[i]}`) $('#showmsg .downloadways'+quality+'').append(`<div class="dlbtn"><a download="${ptitle}" href="${urls[i]}" class="blcolor" target="_blank">${downloadline[i]}</a></div>`) } } //批量改名json function downloadRenameJson(type){ var arr = [] var pages if(type == "video"){ pages = unsafeWindow.__INITIAL_STATE__.videoData.pages for(let i = 0,len = pages.length;i<len;i++){ arr.push({cid:pages[i].cid,name:pages[i].part}) } }else{ var temptitle pages = unsafeWindow.__INITIAL_STATE__.epList for(let i = 0,len = pages.length;i<len;i++){ if(pages[i].badge!=""){ continue } temptitle = pages[i].titleFormat != "" ? pages[i].titleFormat+":"+pages[i].longTitle : pages[i].longTitle arr.push({cid:pages[i].cid,name:temptitle}) } } var str = JSON.stringify(arr) $(document.body).append(`<a id="downloadRenameJson" href='data:text/paint; utf-8,${str}' download="renameJson.txt"></a>`) $("#downloadRenameJson")[0].click() $("#downloadRenameJson").remove() } //时长转换 function getTimeFormat(timelength){ let hour = '0' + parseInt((timelength % (1000 * 60 * 60*60)) / (1000 * 60 * 60)).toString() let minute = parseInt((timelength % (1000 * 60 * 60)) / (1000 * 60)).toString() let second = parseInt((timelength % (1000 * 60)) / 1000).toString() minute = minute.length == 1 ? '0' + minute : minute second = second.length == 1 ? '0' + second : second return `${hour}:${minute}:${second}` } //下载ajax function queryDown(url, type, curquality) { //跨域 $.support.cors = true $.ajax({ type: "GET", //不带请求头,避免跨域预检请求(请求方法:OPTIONS) //contentType: "application/json; charset=utf-8", xhrFields: { withCredentials: true },//跨域 url: url, success: function (result) { if (type == 1) { firstback(result) } else if(type == 2){ back(result, curquality); } else if(type == 3){ batchback(result) } }, error: function (e) { console.log(e.responseText); } }); } //颜色十进制转十六进制 function decimalColorToHTMLcolor(number) { //converts to a integer var intnumber = number - 0; // isolate the colors - really not necessary var red, green, blue; // needed since toString does not zero fill on left var template = "#000000"; // in the MS Windows world RGB colors // are 0xBBGGRR because of the way Intel chips store bytes red = (intnumber&0x0000ff) << 16; green = intnumber&0x00ff00; blue = (intnumber&0xff0000) >>> 16; // mask out each color and reverse the order intnumber = red|green|blue; // toString converts a number to a hexstring var HTMLcolor = intnumber.toString(16); //template adds # for standard HTML #RRGGBB HTMLcolor = template.substring(0,7 - HTMLcolor.length) + HTMLcolor; return HTMLcolor; } //获取视频的用户打赏信息 function getVideoAffectInfo() { //跨域 $.support.cors = true $.ajax({ type: "GET", //contentType: "application/json;charset=UTF-8", url: "https://api.bilibili.com/x/web-interface/archive/stat?bvid=" + bvid, xhrFields: { withCredentials: true },//跨域 success: function (result) { console.log("===用户打赏信息==="); console.log(result) }, error: function (e) { console.log(e.responseText); } }); } //xml弹幕转换ass插件,油猴插件名称bilibili ASS Danmaku Downloader { // 设置项 var config = { 'playResX': 1080, // 屏幕分辨率宽(像素) 'playResY': 810, // 屏幕分辨率高(像素) 'fontlist': [ // 字形(会自动选择最前面一个可用的) 'Microsoft YaHei UI', 'Microsoft YaHei', '文泉驿正黑', 'STHeitiSC', '黑体', ], 'font_size': 1.0, // 字号(比例) 'r2ltime': 12, // 右到左弹幕持续时间(秒) 'fixtime': 4, // 固定弹幕持续时间(秒) 'opacity': 0.7, // 不透明度(比例) 'space': 0, // 弹幕间隔的最小水平距离(像素) 'max_delay': 6, // 最多允许延迟几秒出现弹幕 'bottom': 0, // 底端给字幕保留的空间(像素) 'use_canvas': null, // 是否使用canvas计算文本宽度(布尔值,Linux下的火狐默认否,其他默认是,Firefox bug #561361) 'debug': false, // 打印调试信息 }; var debug = config.debug ? console.log.bind(console) : function () { }; // 将字典中的值填入字符串 var fillStr = function (str) { var dict = Array.apply(Array, arguments); return str.replace(/{{([^}]+)}}/g, function (r, o) { var ret; dict.some(function (i) { return ret = i[o]; }); return ret || ''; }); }; // 将颜色的数值化为十六进制字符串表示 var RRGGBB = function (color) { var t = Number(color).toString(16).toUpperCase(); return (Array(7).join('0') + t).slice(-6); }; // 将可见度转换为透明度 var hexAlpha = function (opacity) { var alpha = Math.round(0xFF * (1 - opacity)).toString(16).toUpperCase(); return Array(3 - alpha.length).join('0') + alpha; }; // 字符串 var funStr = function (fun) { return fun.toString().split(/\r\n|\n|\r/).slice(1, -1).join('\n'); }; // 平方和开根 var hypot = Math.hypot ? Math.hypot.bind(Math) : function () { return Math.sqrt([0].concat(Array.apply(Array, arguments)) .reduce(function (x, y) { return x + y * y; })); }; // 创建下载 var startDownload = function (data, filename) { var blob = new Blob([data], { type: 'application/octet-stream' }); var url = window.URL.createObjectURL(blob); var saveas = document.createElement('a'); saveas.href = url; saveas.style.display = 'none'; document.body.appendChild(saveas); saveas.download = filename; saveas.click(); setTimeout(function () { saveas.parentNode.removeChild(saveas); }, 1000) document.addEventListener('unload', function () { window.URL.revokeObjectURL(url); }); }; // 计算文字宽度 var calcWidth = (function () { // 使用Canvas计算 var calcWidthCanvas = function () { var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); return function (fontname, text, fontsize) { context.font = 'bold ' + fontsize + 'px ' + fontname; return Math.ceil(context.measureText(text).width + config.space); }; } // 使用Div计算 var calcWidthDiv = function () { var d = document.createElement('div'); d.setAttribute('style', [ 'all: unset', 'top: -10000px', 'left: -10000px', 'width: auto', 'height: auto', 'position: absolute', '',].join(' !important; ')); var ld = function () { document.body.parentNode.appendChild(d); } if (!document.body) document.addEventListener('DOMContentLoaded', ld); else ld(); return function (fontname, text, fontsize) { d.textContent = text; d.style.font = 'bold ' + fontsize + 'px ' + fontname; return d.clientWidth + config.space; }; }; // 检查使用哪个测量文字宽度的方法 if (config.use_canvas === null) { if (navigator.platform.match(/linux/i) && !navigator.userAgent.match(/chrome/i)) config.use_canvas = false; } debug('use canvas: %o', config.use_canvas !== false); if (config.use_canvas === false) return calcWidthDiv(); return calcWidthCanvas(); }()); // 选择合适的字体 var choseFont = function (fontlist) { // 检查这个字串的宽度来检查字体是否存在 var sampleText = 'The quick brown fox jumps over the lazy dog' + '7531902468' + ',.!-' + ',。:!' + '天地玄黄' + '則近道矣'; // 和这些字体进行比较 var sampleFont = [ 'monospace', 'sans-serif', 'sans', 'Symbol', 'Arial', 'Comic Sans MS', 'Fixed', 'Terminal', 'Times', 'Times New Roman', '宋体', '黑体', '文泉驿正黑', 'Microsoft YaHei' ]; // 如果被检查的字体和基准字体可以渲染出不同的宽度 // 那么说明被检查的字体总是存在的 var diffFont = function (base, test) { var baseSize = calcWidth(base, sampleText, 72); var testSize = calcWidth(test + ',' + base, sampleText, 72); return baseSize !== testSize; }; var validFont = function (test) { var valid = sampleFont.some(function (base) { return diffFont(base, test); }); debug('font %s: %o', test, valid); return valid; }; // 找一个能用的字体 var f = fontlist[fontlist.length - 1]; fontlist = fontlist.filter(validFont); debug('fontlist: %o', fontlist); return fontlist[0] || f; }; // 从备选的字体中选择一个机器上提供了的字体 var initFont = (function () { var done = false; return function () { if (done) return; done = true; calcWidth = calcWidth.bind(window, config.font = choseFont(config.fontlist) ); }; }()); var generateASS = function (danmaku, info) { var assHeader = fillStr(funStr(function () {/*! ASS弹幕文件文件头 [Script Info] Title: {{title}} Original Script: 根据 {{ori}} 的弹幕信息,由 https://github.com/tiansh/us-danmaku 生成 ScriptType: v4.00+ Collisions: Normal PlayResX: {{playResX}} PlayResY: {{playResY}} Timer: 10.0000 [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding Style: Fix,{{font}},25,&H{{alpha}}FFFFFF,&H{{alpha}}FFFFFF,&H{{alpha}}000000,&H{{alpha}}000000,1,0,0,0,100,100,0,0,1,2,0,2,20,20,2,0 Style: R2L,{{font}},25,&H{{alpha}}FFFFFF,&H{{alpha}}FFFFFF,&H{{alpha}}000000,&H{{alpha}}000000,1,0,0,0,100,100,0,0,1,2,0,2,20,20,2,0 [Events] Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text */}), config, info, {'alpha': hexAlpha(config.opacity) }); // 补齐数字开头的0 var paddingNum = function (num, len) { num = '' + num; while (num.length < len) num = '0' + num; return num; }; // 格式化时间 var formatTime = function (time) { time = 100 * time ^ 0; var l = [[100, 2], [60, 2], [60, 2], [Infinity, 0]].map(function (c) { var r = time % c[0]; time = (time - r) / c[0]; return paddingNum(r, c[1]); }).reverse(); return l.slice(0, -1).join(':') + '.' + l[3]; }; // 格式化特效 var format = (function () { // 适用于所有弹幕 var common = function (line) { var s = ''; var rgb = line.color.split(/(..)/).filter(function (x) { return x; }) .map(function (x) { return parseInt(x, 16); }); // 如果不是白色,要指定弹幕特殊的颜色 if (line.color !== 'FFFFFF') // line.color 是 RRGGBB 格式 s += '\\c&H' + line.color.split(/(..)/).reverse().join(''); // 如果弹幕颜色比较深,用白色的外边框 var dark = rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114 < 0x30; if (dark) s += '\\3c&HFFFFFF'; if (line.size !== 25) s += '\\fs' + line.size; return s; }; // 适用于从右到左弹幕 var r2l = function (line) { return '\\move(' + [ line.poss.x, line.poss.y, line.posd.x, line.posd.y ].join(',') + ')'; }; // 适用于固定位置弹幕 var fix = function (line) { return '\\pos(' + [ line.poss.x, line.poss.y ].join(',') + ')'; }; var withCommon = function (f) { return function (line) { return f(line) + common(line); }; }; return { 'R2L': withCommon(r2l), 'Fix': withCommon(fix), }; }()); // 转义一些字符 var escapeAssText = function (s) { // "{"、"}"字符libass可以转义,但是VSFilter不可以,所以直接用全角补上 return s.replace(/{/g, '{').replace(/}/g, '}').replace(/\r|\n/g, ''); }; // 将一行转换为ASS的事件 var convert2Ass = function (line) { return 'Dialogue: ' + [ 0, formatTime(line.stime), formatTime(line.dtime), line.type, ',20,20,2,,', ].join(',') + '{' + format[line.type](line) + '}' + escapeAssText(line.text); }; return assHeader + danmaku.map(convert2Ass) .filter(function (x) { return x; }) .join('\n'); }; /* 下文字母含义: 0 ||----------------------x----------------------> _____________________c_____________________ = / wc \ 0 | | |--v--| wv | |--v--| | d |--v--| d f |--v--| y |--v--| l f | s _ p | | VIDEO |--v--| |--v--| _ m v | AREA (x ^ y) | v: 弹幕 c: 屏幕 0: 弹幕发送 a: 可行方案 s: 开始出现 f: 出现完全 l: 开始消失 d: 消失完全 p: 上边缘(含) m: 下边缘(不含) w: 宽度 h: 高度 b: 底端保留 t: 时间点 u: 时间段 r: 延迟 并规定 ts := t0s + r tf := wv / (wc + ws) * p + ts tl := ws / (wc + ws) * p + ts td := p + ts */ // 滚动弹幕 var normalDanmaku = (function (wc, hc, b, u, maxr) { return function () { // 初始化屏幕外面是不可用的 var used = [ { 'p': -Infinity, 'm': 0, 'tf': Infinity, 'td': Infinity, 'b': false }, { 'p': hc, 'm': Infinity, 'tf': Infinity, 'td': Infinity, 'b': false }, { 'p': hc - b, 'm': hc, 'tf': Infinity, 'td': Infinity, 'b': true }, ]; // 检查一些可用的位置 var available = function (hv, t0s, t0l, b) { var suggestion = []; // 这些上边缘总之别的块的下边缘 used.forEach(function (i) { if (i.m > hc) return; var p = i.m; var m = p + hv; var tas = t0s; var tal = t0l; // 这些块的左边缘总是这个区域里面最大的边缘 used.forEach(function (j) { if (j.p >= m) return; if (j.m <= p) return; if (j.b && b) return; tas = Math.max(tas, j.tf); tal = Math.max(tal, j.td); }); // 最后作为一种备选留下来 suggestion.push({ 'p': p, 'r': Math.max(tas - t0s, tal - t0l), }); }); // 根据高度排序 suggestion.sort(function (x, y) { return x.p - y.p; }); var mr = maxr; // 又靠右又靠下的选择可以忽略,剩下的返回 suggestion = suggestion.filter(function (i) { if (i.r >= mr) return false; mr = i.r; return true; }); return suggestion; }; // 添加一个被使用的 var use = function (p, m, tf, td) { used.push({ 'p': p, 'm': m, 'tf': tf, 'td': td, 'b': false }); }; // 根据时间同步掉无用的 var syn = function (t0s, t0l) { used = used.filter(function (i) { return i.tf > t0s || i.td > t0l; }); }; // 给所有可能的位置打分,分数是[0, 1)的 var score = function (i) { if (i.r > maxr) return -Infinity; return 1 - hypot(i.r / maxr, i.p / hc) * Math.SQRT1_2; }; // 添加一条 return function (t0s, wv, hv, b) { var t0l = wc / (wv + wc) * u + t0s; syn(t0s, t0l); var al = available(hv, t0s, t0l, b); if (!al.length) return null; var scored = al.map(function (i) { return [score(i), i]; }); var best = scored.reduce(function (x, y) { return x[0] > y[0] ? x : y; })[1]; var ts = t0s + best.r; var tf = wv / (wv + wc) * u + ts; var td = u + ts; use(best.p, best.p + hv, tf, td); return { 'top': best.p, 'time': ts, }; }; }; }(config.playResX, config.playResY, config.bottom, config.r2ltime, config.max_delay)); // 顶部、底部弹幕 var sideDanmaku = (function (hc, b, u, maxr) { return function () { var used = [ { 'p': -Infinity, 'm': 0, 'td': Infinity, 'b': false }, { 'p': hc, 'm': Infinity, 'td': Infinity, 'b': false }, { 'p': hc - b, 'm': hc, 'td': Infinity, 'b': true }, ]; // 查找可用的位置 var fr = function (p, m, t0s, b) { var tas = t0s; used.forEach(function (j) { if (j.p >= m) return; if (j.m <= p) return; if (j.b && b) return; tas = Math.max(tas, j.td); }); return { 'r': tas - t0s, 'p': p, 'm': m }; }; // 顶部 var top = function (hv, t0s, b) { var suggestion = []; used.forEach(function (i) { if (i.m > hc) return; suggestion.push(fr(i.m, i.m + hv, t0s, b)); }); return suggestion; }; // 底部 var bottom = function (hv, t0s, b) { var suggestion = []; used.forEach(function (i) { if (i.p < 0) return; suggestion.push(fr(i.p - hv, i.p, t0s, b)); }); return suggestion; }; var use = function (p, m, td) { used.push({ 'p': p, 'm': m, 'td': td, 'b': false }); }; var syn = function (t0s) { used = used.filter(function (i) { return i.td > t0s; }); }; // 挑选最好的方案:延迟小的优先,位置不重要 var score = function (i, is_top) { if (i.r > maxr) return -Infinity; var f = function (p) { return is_top ? p : (hc - p); }; return 1 - (i.r / maxr * (31/32) + f(i.p) / hc * (1/32)); }; return function (t0s, hv, is_top, b) { syn(t0s); var al = (is_top ? top : bottom)(hv, t0s, b); if (!al.length) return null; var scored = al.map(function (i) { return [score(i, is_top), i]; }); var best = scored.reduce(function (x, y) { return x[0] > y[0] ? x : y; })[1]; use(best.p, best.m, best.r + t0s + u) return { 'top': best.p, 'time': best.r + t0s }; }; }; }(config.playResY, config.bottom, config.fixtime, config.max_delay)); // 为每条弹幕安置位置 var setPosition = function (danmaku) { var normal = normalDanmaku(), side = sideDanmaku(); return danmaku .sort(function (x, y) { return x.time - y.time; }) .map(function (line) { var font_size = Math.round(line.size * config.font_size); var width = calcWidth(line.text, font_size); switch (line.mode) { case 'R2L': return (function () { var pos = normal(line.time, width, font_size, line.bottom); if (!pos) return null; line.type = 'R2L'; line.stime = pos.time; line.poss = { 'x': config.playResX + width / 2, 'y': pos.top + font_size, }; line.posd = { 'x': -width / 2, 'y': pos.top + font_size, }; line.dtime = config.r2ltime + line.stime; return line; }()); case 'TOP': case 'BOTTOM': return (function (isTop) { var pos = side(line.time, font_size, isTop, line.bottom); if (!pos) return null; line.type = 'Fix'; line.stime = pos.time; line.posd = line.poss = { 'x': Math.round(config.playResX / 2), 'y': pos.top + font_size, }; line.dtime = config.fixtime + line.stime; return line; }(line.mode === 'TOP')); default: return null; }; }) .filter(function (l) { return l; }) .sort(function (x, y) { return x.stime - y.stime; }); }; // 获取xml var fetchXML = function (cid, callback) { GM_xmlhttpRequest({ 'method': 'GET', 'url': `http://comment.bilibili.com/${cid}.xml`, 'onload': function (resp) { var content = resp.responseText.replace(/(?:[\0-\x08\x0B\f\x0E-\x1F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g, ""); callback(content); } }); }; var fetchDanmaku = function (cid, callback) { fetchXML(cid, function (content) { callback(parseXML(content)); }); }; var parseXML = function (content) { var data = (new DOMParser()).parseFromString(content, 'text/xml'); return Array.apply(Array, data.querySelectorAll('d')).map(function (line) { var info = line.getAttribute('p').split(','), text = line.textContent; return { 'text': text, 'time': Number(info[0]), 'mode': [undefined, 'R2L', 'R2L', 'R2L', 'BOTTOM', 'TOP'][Number(info[1])], 'size': Number(info[2]), 'color': RRGGBB(parseInt(info[3], 10) & 0xffffff), 'bottom': Number(info[5]) > 0, // 'create': new Date(Number(info[4])), // 'pool': Number(info[5]), // 'sender': String(info[6]), // 'dmid': Number(info[7]), }; }); }; // 获取当前cid /* var getCid = function (callback) { debug('get cid...'); var cid = null, src = null; try { src = document.querySelector('#bofqi iframe, #moviebofqi iframe').src.replace(/^.*\?/, ''); cid = Number(src.match(/cid=(\d+)/)[1]); } catch (e) { } if (!cid) try { src = document.querySelector('#bofqi embed, #moviebofqi embed').getAttribute('flashvars'); cid = Number(src.match(/cid=(\d+)/)[1]); } catch (e) { } if (!cid) try { src = document.querySelector('#bofqi object param[name="flashvars"], #moviebofqi object param[name="flashvars"]').getAttribute('value'); cid = Number(src.match(/cid=(\d+)/)[1]); } catch (e) { } if (cid) setTimeout(callback, 0, cid); else if (src) GM_xmlhttpRequest({ 'method': 'GET', 'url': 'http://interface.bilibili.com/player?' + src, 'onload': function (resp) { try { cid = Number(resp.responseText.match(/<chatid>(\d+)<\/chatid>/)[1]); } catch (e) { } setTimeout(callback, 0, cid || undefined); }, 'onerror': function () { setTimeout(callback, 0); } }); else { setTimeout(getCid, 100, callback); } };*/ // 下载的主程序 /* var mina = function (cid0) { getCid(function (cid) {// cid = cid || cid0; fetchDanmaku(cid, function (danmaku) { var name; try { name = document.querySelector('.viewbox h1, .viewbox h2').textContent; } catch (e) { name = '' + cid; } debug('got xml with %d danmaku', danmaku.length); var ass = generateASS(setPosition(danmaku), { 'title': document.title, 'ori': location.href, }); startDownload('\ufeff' + ass, name + '.ass'); }); }); };*/ // 显示出下载弹幕按钮 /* var showButton = function (count) { GM_addStyle('.arc-toolbar .block.fav { margin-right: 0 } .arc-toolbar .block { padding: 0 18px; }'); var favbar = document.querySelector('.arc-toolbar .block.fav'); var assdown = document.createElement('div'); assdown.innerHTML = '<div id="assdown" class="block ass"><span class="t ass_btn"><i style="display: block; width: 80px; height: 80px; background-position: 0px 0px; background-image: url("data:image/png;base64,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");" class="b-icon b-icon-a b-icon-anim-ass" title="弹幕下载"></i><div class="t-right"><span class="t-right-top">弹幕下载</span><span class="t-right-bottom">' + count + '</span></div></span></div>'; assdown = assdown.firstChild; favbar.parentNode.insertBefore(assdown, favbar.nextSibling); var timer = null, frame = 0; assdown.addEventListener('mouseenter', function () { frame = 0; timer = setTimeout(anim, 0); }); assdown.addEventListener('mouseleave', function () { clearTimeout(timer); timer = null; }); var anim = function () { if (frame === 16) { timer = null; return; } frame++; assdown.querySelector('i').style.backgroundPosition = '-' + (frame * 80) + 'px 0'; setTimeout(anim, 1000 / 16); }; }; // 初始化按钮 var initButton = (function () { var done = false; return function () { debug('init button'); if (!document.querySelector('.arc-toolbar .block.fav')) return; getCid(function (cid) { debug('cid = %o', cid); if (!cid || done) return; else done = true; fetchDanmaku(cid, function (danmaku) { showButton(danmaku.length); document.querySelector('#assdown').addEventListener('click', function (e) { e.preventDefault(); mina(cid); }); }); }); }; }()); */ /* * Common */ // 初始化 /* var init = function () { initFont(); initButton(); }; if (document.body) init(); else window.addEventListener('DOMContentLoaded', init);*/ } })();