/**
 * 本模块负责实现旧版播放器载入本地视频及弹幕功能
 */ /**
 * 本模块负责实现旧版播放器载入本地视频及弹幕功能
 */ ( function ( ) { class LocalMedia { data : { xml : File [ ] ; json : File [ ] ; mp4 : File [ ] ; } = { xml : [ ] , json : [ ] , mp4 : [ ] } ; offset : number = 0 ; keyboard : boolean = false ; /**
         * 读取文件地址
         */ change ( files ) { const file = files ; if ( file . length === 0 ) { return toast . warning ( "请选择本地视频或弹幕文件！" , "视频：.mp4（且符合浏览器支持的编码）" , "弹幕：.xml, .json" ) ; } this . data = { xml : [ ] , json : [ ] , mp4 : [ ] } ; this . data = Array . from ( file ) . reduce ( ( d , i ) => { /\.xml$/ . test ( i . name ) && d . xml . push ( i ) ; /\.json$/ . test ( i . name ) && d . json . push ( i ) ; /\.mp4$/ . test ( i . name ) && d . mp4 . push ( i ) ; return d ; } , this . data ) ; if ( ! this . data . xml [ 0 ] && ! this . data . json [ 0 ] && ! this . data . mp4 [ 0 ] ) { return toast . warning ( "未能识别到任何有效文件信息 →_→" ) ; } this . video ( ) ; this . danmaku ( ) ; } param file readFile ( file ) { return new Promise ( ( resolve , reject ) => { if ( ! file ) reject ( toast . error ( '无效文件路径！' ) ) ; const reader = new FileReader ( ) ; reader . readAsText ( file , 'utf-8' ) ; reader . onload = ( ) => { resolve ( reader . result ) ; } ; reader . onerror = ( ) => { reject ( toast . error ( '读取文件出错，请重试！' ) ) ; } ; } ) ; } /**
         * 载入弹幕
         */ async danmaku ( ) { if ( ! API . loadLocalDm ) { return toast . error ( "载入本地弹幕失败：本地弹幕组件丢失！" ) ; } if ( ! this . data . xml [ 0 ] && ! this . data . json [ 0 ] ) return ; this . data . xml . forEach ( async ( d , i ) => { let data = await this . readFile ( d ) ; toast ( "本地弹幕：" + d . name , "载入模式：" + ( ( i || config . concatDanmaku ) ? "与当前弹幕合并" : "替换当前弹幕" ) ) ; API . loadLocalDm ( data , Boolean ( i ) || config . concatDanmaku ) ; } ) ; this . data . json . forEach ( async ( d , i ) => { let data = JSON . parse ( await this . readFile ( d ) ) || [ ] ; toast ( "本地弹幕：" + d . name , "载入模式：" + ( ( this . data . xml [ 0 ] || i || config . concatDanmaku ) ? "与当前弹幕合并" : "替换当前弹幕" ) ) ; window . player ?. setDanmaku ( data , < any > this . data . xml [ 0 ] || Boolean ( i ) || config . concatDanmaku ) ; } ) ; API . bofqiMessage ( ) ; this . offset = 0 ; if ( ! window . player ?. offsetDanmaku ) return toast . error ( "绑定键盘事件失败：弹幕偏移组件丢失！" ) ; else { toast ( "已绑定键盘事件" , "可以通过键盘 , 和 . 两个键（即上标为 < 和 > 的两个键）提前或延后弹幕偏移，频度1秒/次" ) ; if ( ! this . keyboard ) { this . keyboard = true ; document . addEventListener ( "keydown" , ( ev ) => { switch ( ev . key ) { case "," : window . player . offsetDanmaku ( - 1 ) ; this . offset -- ; API . bofqiMessage ( [ "弹幕偏移：" , `${ this . offset } 秒` ] ) ; break ; case "." : window . player . offsetDanmaku ( 1 ) ; this . offset ++ ; API . bofqiMessage ( [ "弹幕偏移：" , `${ this . offset } 秒` ] ) ; break ; default : break ; } } ) ; } } } /**
         * 载入视频
         */ video ( ) { if ( this . data . mp4 [ 0 ] ) { toast . warning ( "载入本地视频中..." , "请无视控制台大量报错！" ) ; let video = < HTMLVideoElement > document . querySelector ( "#bilibiliPlayer > div.bilibili-player-area.video-state-pause > div.bilibili-player-video-wrap > div.bilibili-player-video > video" ) ; video . src = URL . createObjectURL ( this . data . mp4 [ 0 ] ) ; toast . success ( "本地视频：" + this . data . mp4 [ 0 ] . name ) ; ( < HTMLDivElement > document . querySelector ( ".bilibili-player-video-time-total" ) ) . textContent = this . time ( video . duration ) ; } } param time returns time ( time ) { time = Number ( time ) || 0 ; let s = time % 60 ; let m = ( time - s ) / 60 ; s = ( Array ( 2 ) . join ( '0' ) + s ) . slice ( - 2 ) ; m = m < 10 ? ( Array ( 2 ) . join ( '0' ) + m ) . slice ( - 2 ) : m ; return `${ m }:${ s }` ; } } const localMedia = new LocalMedia ( ) ; API . localMedia = ( files ) => { try { localMedia . change ( files ) ; } catch ( e ) { toast . error ( "localMedia.js" , e ) ; } } ; } ) ( ) ; declare namespace API { param files function localMedia ( files ) ; }