see protobufjs https license see protobufjs https license ( function ( ) { try { class Danmaku { static root : any ; static protoSeg : any ; static protoView : any ; constructor ( ) { new Function ( GM . getResourceText ( "protobuf.min.js" ) ) ( ) ; Danmaku . root = ( < any > window ) . protobuf . Root . fromJSON ( API . getModule ( "protobuf.json" ) ) ; Danmaku . protoSeg = Danmaku . root . lookupType ( 'bilibili.DmSegMobileReply' ) ; Danmaku . protoView = Danmaku . root . lookupType ( 'bilibili.DmWebViewReply' ) ; } param danmaku returns toXml ( danmaku ) { let DM = Reflect . has ( danmaku [ 0 ] , "idStr" ) ? this . danmakuFormat ( < danmakuNew [ ] > danmaku ) : < danmaku [ ] > danmaku ; this . sortDmById ( DM , "dmid" ) ; let xml = DM . reduce ( ( s , d ) => { s += `<d p="${ d . stime },${ d . mode },${ d . size },${ d . color },${ d . date },${ d . class },${ d . uid },${ d . dmid }">${ d . text . replace ( /[<">'&]/g , ( a ) => { return { '<' : '&lt;' , '"' : '&quot;' , '>' : '&gt;' , "'" : '&#39;' , '&' : '&amp;' } [ a ] ; } ) . replace ( /(\n|\r\n)/g , "/n" ) }</d>\r\n` ; return s ; } , '<?xml version="1.0" encoding="UTF-8"?><i><chatserver>chat.bilibili.com</chatserver><chatid>' + API . cid + '</chatid><mission>0</mission><maxlimit>99999</maxlimit><state>0</state><real_name>0</real_name><source>e-r</source>\r\n' ) ; xml += "</i>" ; link license see https var regex = /((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/g ; return xml . replace ( regex , '' ) ; } param danmaku param key sortDmById ( danmaku , key ) { let egx = /^\d+$/ ; for ( let i = 0 , d ; i < danmaku . length ; i ++ ) { d = danmaku [ i ] ; if ( ! egx . test ( d [ key ] ) ) throw "请输入数字字符串" ; if ( typeof d [ key ] !== "string" ) d [ key ] = String ( d [ key ] ) ; d [ key ] = d [ key ] . replace ( /^0+/ , "" ) ; } danmaku . sort ( ( a , b ) => this . bigInt ( a [ key ] , b [ key ] ) ? 1 : - 1 ) ; } param num1 param num2 returns bigInt ( num1 , num2 ) { if ( num1 . length > num2 . length ) return true ; else if ( num1 . length < num2 . length ) return false ; else { for ( let i = 0 ; i < num1 . length ; i ++ ) { if ( num1 [ i ] > num2 [ i ] ) return true ; if ( num1 [ i ] < num2 [ i ] ) return false ; } return false ; } } param aid param cid param bas returns async getSegDanmaku ( aid = API . aid , cid = API . cid , bas = false ) { try { aid = aid || API . aid ; cid = cid || API . cid ; if ( ! aid || ! cid ) throw [ "弹幕参数错误！" , "aid：" + aid , "cid：" + cid ] ; let config = await xhr ( { url : API . objUrl ( "https://api.bilibili.com/x/v2/dm/web/view" , { type : String ( 1 ) , oid : String ( cid ) , pid : String ( aid ) } ) , responseType : "arraybuffer" , credentials : true } ) ; config = Danmaku . protoView . decode ( new Uint8Array ( config ) ) ; let pageSize = config . dmSge . pageSize ? config . dmSge . pageSize / 1000 : 360 ; let total = ( ( < any > window ) . player && ( < any > window ) . player . getDuration && ( ( < any > window ) . player . getDuration ( ) / pageSize + 1 ) ) || config . dmSge . total ; let allrequset = [ ] , allDanmaku = [ ] ; if ( API . aid && ( aid != API . aid ) ) total = config . dmSge . total ; if ( ! bas ) { for ( let index = 1 ; index <= total ; index ++ ) { allrequset . push ( xhr ( { url : API . objUrl ( "https://api.bilibili.com/x/v2/dm/web/seg.so" , { type : String ( 1 ) , oid : String ( cid ) , pid : String ( aid ) , segment_index : String ( index ) } ) , responseType : "arraybuffer" , credentials : true } ) ) ; } } if ( config . specialDms . length > 0 ) { for ( let index = 0 ; index < config . specialDms . length ; index ++ ) { allrequset . push ( xhr ( { url : config . specialDms [ index ] . replace ( "http" , "https" ) , responseType : "arraybuffer" , credentials : false } ) ) ; } } let upHighlightDm = [ ] ; if ( config . commandDms . length > 0 ) { for ( let i = 0 ; i < config . commandDms . length ; i ++ ) { let cdm = config . commandDms [ i ] ; if ( cdm . command == "#UP#" ) { cdm . styleClass = "danmaku-up-icon" ; cdm . color = 16777215 ; cdm . pool = 0 ; cdm . fontsize = 25 ; cdm . ctime = new Date ( cdm . mtime ) . getTime ( ) / 1000 ; cdm . mode = 1 ; cdm . midHash = API . crc32 && API . crc32 ( cdm . mid ) ; upHighlightDm . push ( cdm ) ; config . commandDms . splice ( i , 1 ) ; } } if ( API . loadCommandDm && ( < any > API ) . config . commandDm ) API . loadCommandDm ( config . commandDms , aid , cid ) ; } ( await Promise . all ( allrequset ) ) . forEach ( d => { if ( d ) allDanmaku = allDanmaku . concat ( Danmaku . protoSeg . decode ( new Uint8Array ( d ) ) . elems ) ; } ) ; return allDanmaku . concat ( upHighlightDm ) ; } catch ( e ) { toast . error ( "danmaku.js" , e ) ; } } param date param cid returns async getHistoryDanmaku ( date , cid = API . cid ) { if ( ! date || ! API . uid ) return ; cid = cid || API . cid ; let dm = await xhr ( { url : API . objUrl ( "https://api.bilibili.com/x/v2/dm/web/history/seg.so" , { type : String ( 1 ) , oid : String ( cid ) , date : date } ) , responseType : "arraybuffer" , credentials : true } ) ; return this . segDmDecode ( dm ) ; } param xml param append loadLocalDm ( xml , append ) { let doc = new DOMParser ( ) . parseFromString ( xml , "application/xml" ) ; let dm = doc . querySelectorAll ( "d" ) ; if ( dm . length == 0 ) { toast . warning ( "从弹幕文件中没有获取到任何弹幕！" ) ; return ; } let danmaku = [ ] ; let attr , v , mode ; for ( let i = 0 ; i < dm . length ; i ++ ) { v = dm [ i ] ; attr = ( < string > v . getAttribute ( 'p' ) ) . split ( "," ) ; mode = parseInt ( attr [ 1 ] ) ; danmaku [ i ] = { class : parseInt ( attr [ 5 ] ) , color : parseInt ( attr [ 3 ] ) , date : parseInt ( attr [ 4 ] ) , dmid : attr [ 7 ] , mode : mode , size : parseInt ( attr [ 2 ] ) , stime : parseFloat ( attr [ 0 ] ) , text : < string > ( ( mode != 8 && mode != 9 ) ? ( < string > v . textContent ) . replace ( /(\/n|\\n|\n|\r\n)/g , '\n' ) : v . textContent ) , uid : attr [ 6 ] } ; } this . specialEffects ( danmaku ) ; this . sortDmById ( danmaku , "dmid" ) ; param { Array } dm param { Boolean } append if ( ! window . player ?. setDanmaku ) return toast . error ( "刷新弹幕列表失败：播放器内部调用丢失！" ) ; window . player ?. setDanmaku ( danmaku , append ) ; } param dm specialEffects ( dm ) { let textData ; for ( let i = 0 ; i < dm . length ; i ++ ) { textData = dm [ i ] ; if ( textData . text . includes ( '\n' ) ) { textData . class = 1 ; textData . zIndex = textData . stime * 1000 ; if ( ! ( textData . text . includes ( "█" ) || textData . text . includes ( "▂" ) ) ) textData . zIndex = textData . zIndex + 1 ; } } } segDmDecode ( response ) { return Danmaku . protoSeg . decode ( new Uint8Array ( response ) ) . elems ; } param dm param aid returns danmakuFormat ( dm , aid ? ) { aid = aid || API . aid ; let danmaku = dm . map ( function ( v ) { let result = { class : v . pool , color : v . color , date : v . ctime , dmid : v . idStr , mode : v . mode , size : v . fontsize , stime : v . progress / 1000 , text : ( v . mode != 8 && v . mode != 9 ) ? v . content . replace ( /(\/n|\\n|\n|\r\n)/g , '\n' ) : v . content , uid : v . midHash } ; if ( v . action && v . action . startsWith ( "picture:" ) ) result . picture = "//" + v . action . split ( ":" ) [ 1 ] ; if ( v . styleClass !== undefined ) result . AH = v . styleClass ; return result ; } ) ; if ( aid && aid < 400000 ) { this . specialEffects ( danmaku ) ; } this . sortDmById ( danmaku , "dmid" ) ; return danmaku ; } param url async onlineDanmaku ( url ) { try { let obj = await API . urlInputCheck ( url ) ; if ( obj . aid && obj . cid ) { API . getSegDanmaku ( obj . aid , obj . cid ) . then ( d => { d = API . danmakuFormat ( < any [ ] > d , obj . aid ) ; window . player ?. setDanmaku ( d , config . concatDanmaku ) ; API . danmaku = d ; } ) ; } } catch ( e ) { debug . error ( "onlineDanmaku.js" , e ) ; } } } const DM = new Danmaku ( ) ; API . getSegDanmaku = ( aid = API . aid , cid = API . cid , bas = false ) => DM . getSegDanmaku ( < number > aid , < number > cid , bas ) ; API . specialEffects = ( dm ) => DM . specialEffects ( dm ) ; API . sortDmById = ( danmaku , key ) => DM . sortDmById ( danmaku , key ) ; API . toXml = ( danmaku ) => DM . toXml ( danmaku ) ; API . getHistoryDanmaku = ( date , cid ? ) => DM . getHistoryDanmaku ( date , < number > cid ) ; API . loadLocalDm = ( xml , append ) => DM . loadLocalDm ( xml , append ) ; API . segDmDecode = ( response ) => DM . segDmDecode ( response ) ; API . danmakuFormat = ( dm , aid ? ) => DM . danmakuFormat ( dm , aid ) ; API . onlineDanmaku = ( url ) => DM . onlineDanmaku ( url ) ; } catch ( e ) { toast . error ( "danmaku.js" , e ) ; } } ) ( ) ; declare namespace API { param aid param cid param bas returns function getSegDanmaku ( aid ? , cid ? , bas ? ) ; param dm function specialEffects ( dm ) ; param danmaku param key function sortDmById ( danmaku , key ) ; param danmaku returns function toXml ( danmaku ) ; param date param cid returns function getHistoryDanmaku ( date , cid ? ) ; param xml param append function loadLocalDm ( xml , append ) ; param response function segDmDecode ( response ) ; param dm param aid returns function danmakuFormat ( dm , aid ? ) ; param url function onlineDanmaku ( url ) ; } /**
 * 旧版播放器对应的弹幕对象
 */ interface danmaku { class : number ; color : number ; date : number ; dmid : string ; mode : number ; size : number ; stime : number ; text : string ; uid : string ; picture ? : string ; AH ? : string ; zIndex ? : number ; } /**
 * proto弹幕对象
 */ interface danmakuNew { pool : number ; color : number ; ctime : number ; idStr : string ; mode : number ; fontsize : number ; progress : number ; content : string ; midHash : string ; action ? : string ; styleClass ? : string ; } interface Window { player : { param danmaku param append setDanmaku : ( danmaku : danmaku [ ] , append ? : boolean ) => void ; param time offsetDanmaku : ( time : number ) => void ; } ; }