2021-04-01 15:13:35 +00:00
< template >
2022-02-22 06:13:36 +00:00
< div
ref = "container"
data - shaka - player - container
class = "w-full max-h-screen flex justify-center"
: class = "{ 'player-container': !isEmbed }"
>
< video ref = "videoEl" class = "w-full" data -shaka -player :autoplay ="shouldAutoPlay" :loop ="selectedAutoLoop" / >
2023-02-02 15:02:04 +00:00
< button
v - if = "inSegment"
class = "skip-segment-button"
type = "button"
: aria - label = "$t('actions.skip_segment')"
aria - pressed = "false"
@ click = "onClickSkipSegment"
>
< span v -t = " ' actions.skip_segment ' " / >
< i class = "material-icons-round" > skip _next < / i >
< / button >
2021-04-01 15:13:35 +00:00
< / div >
< / template >
< script >
2023-01-25 23:44:07 +00:00
import "shaka-player/dist/controls.css" ;
2021-04-01 15:13:35 +00:00
const shaka = import ( "shaka-player/dist/shaka-player.ui.js" ) ;
2022-10-19 22:02:00 +00:00
if ( ! window . muxjs ) {
import ( "mux.js" ) . then ( muxjs => {
window . muxjs = muxjs ;
} ) ;
}
2022-01-23 19:08:33 +00:00
const hotkeys = import ( "hotkeys-js" ) ;
2021-04-01 15:13:35 +00:00
export default {
2021-05-06 17:30:02 +00:00
props : {
2021-10-08 18:52:51 +00:00
video : {
type : Object ,
default : ( ) => {
return { } ;
} ,
} ,
2022-04-08 21:29:50 +00:00
playlist : {
type : Object ,
default : null ,
} ,
index : {
type : Number ,
default : - 1 ,
} ,
2021-10-08 18:52:51 +00:00
sponsors : {
type : Object ,
default : ( ) => {
return { } ;
} ,
} ,
2021-05-06 17:30:02 +00:00
selectedAutoPlay : Boolean ,
2021-07-04 18:42:10 +00:00
selectedAutoLoop : Boolean ,
2021-08-04 05:13:22 +00:00
isEmbed : Boolean ,
2021-05-06 17:30:02 +00:00
} ,
2022-07-19 16:29:03 +00:00
emits : [ "timeupdate" ] ,
2021-07-07 14:18:09 +00:00
data ( ) {
return {
2021-10-27 00:15:37 +00:00
lastUpdate : new Date ( ) . getTime ( ) ,
2022-01-13 22:41:53 +00:00
initialSeekComplete : false ,
2022-04-08 21:29:50 +00:00
destroying : false ,
2023-02-02 15:02:04 +00:00
inSegment : false ,
2021-07-07 14:18:09 +00:00
} ;
} ,
2021-05-06 17:30:02 +00:00
computed : {
2021-07-04 18:26:02 +00:00
shouldAutoPlay : _this => {
2021-10-28 17:35:48 +00:00
return _this . getPreferenceBoolean ( "playerAutoPlay" , true ) && ! _this . isEmbed ;
2021-05-06 17:30:02 +00:00
} ,
2021-07-21 10:48:59 +00:00
preferredVideoCodecs : _this => {
var preferredVideoCodecs = [ ] ;
2022-06-06 04:55:01 +00:00
const enabledCodecs = _this . getPreferenceString ( "enabledCodecs" , "vp9,avc" ) . split ( "," ) ;
2021-07-21 10:48:59 +00:00
2021-08-27 07:33:55 +00:00
if (
_this . $refs . videoEl . canPlayType ( 'video/mp4; codecs="av01.0.08M.08"' ) !== "" &&
enabledCodecs . includes ( "av1" )
)
2021-07-21 10:48:59 +00:00
preferredVideoCodecs . push ( "av01" ) ;
2021-08-27 07:33:55 +00:00
if ( _this . $refs . videoEl . canPlayType ( 'video/webm; codecs="vp9"' ) !== "" && enabledCodecs . includes ( "vp9" ) )
preferredVideoCodecs . push ( "vp9" ) ;
if (
_this . $refs . videoEl . canPlayType ( 'video/mp4; codecs="avc1.4d401f"' ) !== "" &&
enabledCodecs . includes ( "avc" )
)
2021-07-21 10:48:59 +00:00
preferredVideoCodecs . push ( "avc1" ) ;
return preferredVideoCodecs ;
} ,
2021-05-06 17:30:02 +00:00
} ,
2021-07-09 20:55:09 +00:00
mounted ( ) {
2022-05-23 20:18:20 +00:00
if ( ! this . $shaka ) this . shakaPromise = shaka . then ( shaka => shaka . default ) . then ( shaka => ( this . $shaka = shaka ) ) ;
2022-01-23 19:08:33 +00:00
if ( ! this . $hotkeys )
this . hotkeysPromise = hotkeys . then ( mod => mod . default ) . then ( hotkeys => ( this . $hotkeys = hotkeys ) ) ;
2021-07-09 20:55:09 +00:00
} ,
2021-10-08 18:52:51 +00:00
activated ( ) {
2022-05-23 20:18:20 +00:00
this . destroying = false ;
2022-06-26 14:15:03 +00:00
this . sponsors ? . segments ? . forEach ( segment => ( segment . skipped = false ) ) ;
2022-01-23 19:08:33 +00:00
this . hotkeysPromise . then ( ( ) => {
var self = this ;
this . $hotkeys (
2023-02-01 15:37:43 +00:00
"f,m,j,k,l,c,space,up,down,left,right,0,1,2,3,4,5,6,7,8,9,shift+n,shift+,,shift+.,return" ,
2022-01-23 19:08:33 +00:00
function ( e , handler ) {
const videoEl = self . $refs . videoEl ;
switch ( handler . key ) {
case "f" :
self . $ui . getControls ( ) . toggleFullScreen ( ) ;
e . preventDefault ( ) ;
break ;
case "m" :
videoEl . muted = ! videoEl . muted ;
e . preventDefault ( ) ;
break ;
case "j" :
videoEl . currentTime = Math . max ( videoEl . currentTime - 15 , 0 ) ;
e . preventDefault ( ) ;
break ;
case "l" :
videoEl . currentTime = videoEl . currentTime + 15 ;
e . preventDefault ( ) ;
break ;
case "c" :
self . $player . setTextTrackVisibility ( ! self . $player . isTextTrackVisible ( ) ) ;
e . preventDefault ( ) ;
break ;
case "k" :
case "space" :
if ( videoEl . paused ) videoEl . play ( ) ;
else videoEl . pause ( ) ;
e . preventDefault ( ) ;
break ;
case "up" :
videoEl . volume = Math . min ( videoEl . volume + 0.05 , 1 ) ;
e . preventDefault ( ) ;
break ;
case "down" :
videoEl . volume = Math . max ( videoEl . volume - 0.05 , 0 ) ;
e . preventDefault ( ) ;
break ;
case "left" :
videoEl . currentTime = Math . max ( videoEl . currentTime - 5 , 0 ) ;
e . preventDefault ( ) ;
break ;
case "right" :
videoEl . currentTime = videoEl . currentTime + 5 ;
e . preventDefault ( ) ;
break ;
case "0" :
videoEl . currentTime = 0 ;
e . preventDefault ( ) ;
break ;
case "1" :
videoEl . currentTime = videoEl . duration * 0.1 ;
e . preventDefault ( ) ;
break ;
case "2" :
videoEl . currentTime = videoEl . duration * 0.2 ;
e . preventDefault ( ) ;
break ;
case "3" :
videoEl . currentTime = videoEl . duration * 0.3 ;
e . preventDefault ( ) ;
break ;
case "4" :
videoEl . currentTime = videoEl . duration * 0.4 ;
e . preventDefault ( ) ;
break ;
case "5" :
videoEl . currentTime = videoEl . duration * 0.5 ;
e . preventDefault ( ) ;
break ;
case "6" :
videoEl . currentTime = videoEl . duration * 0.6 ;
e . preventDefault ( ) ;
break ;
case "7" :
videoEl . currentTime = videoEl . duration * 0.7 ;
e . preventDefault ( ) ;
break ;
case "8" :
videoEl . currentTime = videoEl . duration * 0.8 ;
e . preventDefault ( ) ;
break ;
case "9" :
videoEl . currentTime = videoEl . duration * 0.9 ;
e . preventDefault ( ) ;
break ;
2022-08-06 10:03:31 +00:00
case "shift+n" :
self . navigateNext ( ) ;
e . preventDefault ( ) ;
break ;
2022-01-23 19:08:33 +00:00
case "shift+," :
self . $player . trickPlay ( Math . max ( videoEl . playbackRate - 0.25 , 0.25 ) ) ;
break ;
case "shift+." :
self . $player . trickPlay ( Math . min ( videoEl . playbackRate + 0.25 , 2 ) ) ;
break ;
2023-02-01 15:37:43 +00:00
case "return" :
self . skipSegment ( videoEl ) ;
break ;
2022-01-23 19:08:33 +00:00
}
} ,
) ;
} ) ;
2021-10-08 18:52:51 +00:00
} ,
deactivated ( ) {
2022-04-08 21:29:50 +00:00
this . destroying = true ;
2022-01-23 19:08:33 +00:00
this . destroy ( true ) ;
2021-10-08 18:52:51 +00:00
} ,
unmounted ( ) {
2022-04-08 21:29:50 +00:00
this . destroying = true ;
2022-01-23 19:08:33 +00:00
this . destroy ( true ) ;
2021-10-08 18:52:51 +00:00
} ,
2021-05-06 17:30:02 +00:00
methods : {
2021-09-05 13:12:27 +00:00
async loadVideo ( ) {
2021-06-09 21:21:35 +00:00
const component = this ;
2021-05-06 17:30:02 +00:00
const videoEl = this . $refs . videoEl ;
2021-04-01 15:13:35 +00:00
2021-05-06 17:30:02 +00:00
videoEl . setAttribute ( "poster" , this . video . thumbnailUrl ) ;
2021-04-01 15:13:35 +00:00
2022-03-16 18:04:01 +00:00
const time = this . $route . query . t ? ? this . $route . query . start ;
if ( time ) {
2022-01-12 04:52:15 +00:00
let start = 0 ;
if ( /^[\d]*$/g . test ( time ) ) {
start = time ;
} else {
const hours = /([\d]*)h/gi . exec ( time ) ? . [ 1 ] ;
const minutes = /([\d]*)m/gi . exec ( time ) ? . [ 1 ] ;
const seconds = /([\d]*)s/gi . exec ( time ) ? . [ 1 ] ;
if ( hours ) {
start += parseInt ( hours ) * 60 * 60 ;
}
if ( minutes ) {
start += parseInt ( minutes ) * 60 ;
}
if ( seconds ) {
start += parseInt ( seconds ) ;
}
}
videoEl . currentTime = start ;
2022-01-13 22:41:53 +00:00
this . initialSeekComplete = true ;
2023-01-13 13:40:12 +00:00
} else if ( window . db && this . getPreferenceBoolean ( "watchHistory" , false ) ) {
2021-10-27 00:15:37 +00:00
var tx = window . db . transaction ( "watch_history" , "readonly" ) ;
var store = tx . objectStore ( "watch_history" ) ;
2021-11-11 08:16:00 +00:00
var request = store . get ( this . video . id ) ;
2022-01-01 14:53:55 +00:00
request . onsuccess = function ( event ) {
2021-10-27 00:15:37 +00:00
var video = event . target . result ;
2022-08-06 10:13:41 +00:00
const currentTime = video ? . currentTime ;
if ( currentTime ) {
if ( currentTime < component . video . duration * 0.9 ) {
videoEl . currentTime = currentTime ;
}
2021-10-27 00:15:37 +00:00
}
} ;
2022-01-13 22:41:53 +00:00
tx . oncomplete = ( ) => {
this . initialSeekComplete = true ;
} ;
} else {
this . initialSeekComplete = true ;
2021-10-27 00:15:37 +00:00
}
2021-04-01 15:13:35 +00:00
2021-10-06 14:33:52 +00:00
const noPrevPlayer = ! this . $player ;
2021-05-06 17:30:02 +00:00
var streams = [ ] ;
streams . push ( ... this . video . audioStreams ) ;
streams . push ( ... this . video . videoStreams ) ;
2021-07-07 19:34:46 +00:00
const MseSupport = window . MediaSource !== undefined ;
2021-09-02 13:46:27 +00:00
const lbry = this . getPreferenceBoolean ( "disableLBRY" , false )
? null
: this . video . videoStreams . filter ( stream => stream . quality === "LBRY" ) [ 0 ] ;
2021-07-28 08:02:23 +00:00
2021-06-09 21:21:35 +00:00
var uri ;
2021-09-05 13:12:27 +00:00
var mime ;
2021-06-09 21:21:35 +00:00
2021-08-30 15:48:08 +00:00
if ( this . video . livestream ) {
2021-06-09 21:21:35 +00:00
uri = this . video . hls ;
2021-09-05 13:12:27 +00:00
mime = "application/x-mpegURL" ;
2021-08-30 15:48:08 +00:00
} else if ( this . video . audioStreams . length > 0 && ! lbry && MseSupport ) {
2021-08-15 19:54:34 +00:00
if ( ! this . video . dash ) {
2023-03-02 14:18:53 +00:00
const dash = ( await import ( "../utils/DashUtils.js" ) ) . generate _dash _file _from _formats (
streams ,
this . video . duration ,
) ;
2021-06-09 21:21:35 +00:00
2021-08-15 19:54:34 +00:00
uri = "data:application/dash+xml;charset=utf-8;base64," + btoa ( dash ) ;
2022-12-02 21:11:22 +00:00
} else {
const url = new URL ( this . video . dash ) ;
url . searchParams . set ( "rewrite" , false ) ;
uri = url . toString ( ) ;
}
2021-09-05 13:12:27 +00:00
mime = "application/dash+xml" ;
2021-07-28 08:02:23 +00:00
} else if ( lbry ) {
uri = lbry . url ;
2021-09-02 13:46:27 +00:00
if ( this . getPreferenceBoolean ( "proxyLBRY" , false ) ) {
const url = new URL ( uri ) ;
2021-11-24 17:36:29 +00:00
const proxyURL = new URL ( this . video . proxyUrl ) ;
let proxyPath = proxyURL . pathname ;
if ( proxyPath . lastIndexOf ( "/" ) === proxyPath . length - 1 ) {
proxyPath = proxyPath . substring ( 0 , proxyPath . length - 1 ) ;
}
2021-09-02 13:46:27 +00:00
url . searchParams . set ( "host" , url . host ) ;
2021-11-24 17:36:29 +00:00
url . protocol = proxyURL . protocol ;
url . host = proxyURL . host ;
url . pathname = proxyPath + url . pathname ;
2021-09-02 13:46:27 +00:00
uri = url . toString ( ) ;
}
2021-09-05 13:12:27 +00:00
const contentType = await fetch ( uri , {
method : "HEAD" ,
2022-05-04 09:19:13 +00:00
} ) . then ( response => {
uri = response . url ;
2022-05-05 18:02:25 +00:00
return response . headers . get ( "Content-Type" ) ;
2022-05-04 09:19:13 +00:00
} ) ;
2021-09-05 13:12:27 +00:00
mime = contentType ;
2021-11-07 18:40:42 +00:00
} else if ( this . video . hls ) {
uri = this . video . hls ;
mime = "application/x-mpegURL" ;
2021-06-18 13:20:41 +00:00
} else {
2021-07-10 19:59:21 +00:00
uri = this . video . videoStreams . filter ( stream => stream . codec == null ) . slice ( - 1 ) [ 0 ] . url ;
2021-09-05 13:12:27 +00:00
mime = "video/mp4" ;
2021-06-09 21:21:35 +00:00
}
2021-05-06 17:30:02 +00:00
if ( noPrevPlayer )
2021-07-09 20:55:09 +00:00
this . shakaPromise . then ( ( ) => {
2022-04-08 21:29:50 +00:00
if ( this . destroying ) return ;
2022-05-23 20:18:20 +00:00
this . $shaka . polyfill . installAll ( ) ;
2021-07-09 20:55:09 +00:00
2022-05-23 20:18:20 +00:00
const localPlayer = new this . $shaka . Player ( videoEl ) ;
2021-11-24 17:36:29 +00:00
const proxyURL = new URL ( component . video . proxyUrl ) ;
let proxyPath = proxyURL . pathname ;
if ( proxyPath . lastIndexOf ( "/" ) === proxyPath . length - 1 ) {
proxyPath = proxyPath . substring ( 0 , proxyPath . length - 1 ) ;
}
2021-07-09 20:55:09 +00:00
localPlayer . getNetworkingEngine ( ) . registerRequestFilter ( ( _type , request ) => {
const uri = request . uris [ 0 ] ;
var url = new URL ( uri ) ;
2021-09-05 13:12:27 +00:00
const headers = request . headers ;
if (
url . host . endsWith ( ".googlevideo.com" ) ||
( url . host . endsWith ( ".lbryplayer.xyz" ) &&
( component . getPreferenceBoolean ( "proxyLBRY" , false ) || headers . Range ) )
) {
2021-07-09 20:55:09 +00:00
url . searchParams . set ( "host" , url . host ) ;
2021-11-24 17:36:29 +00:00
url . protocol = proxyURL . protocol ;
url . host = proxyURL . host ;
url . pathname = proxyPath + url . pathname ;
2021-07-09 20:55:09 +00:00
request . uris [ 0 ] = url . toString ( ) ;
}
2021-11-24 17:36:29 +00:00
if ( url . pathname === proxyPath + "/videoplayback" ) {
2021-08-25 20:32:56 +00:00
if ( headers . Range ) {
url . searchParams . set ( "range" , headers . Range . split ( "=" ) [ 1 ] ) ;
request . headers = { } ;
request . uris [ 0 ] = url . toString ( ) ;
}
}
2021-07-09 20:55:09 +00:00
} ) ;
2021-06-09 21:21:35 +00:00
2021-07-09 20:55:09 +00:00
localPlayer . configure (
"streaming.bufferingGoal" ,
Math . max ( this . getPreferenceNumber ( "bufferGoal" , 10 ) , 10 ) ,
) ;
2021-06-22 10:54:20 +00:00
2022-05-23 20:18:20 +00:00
this . setPlayerAttrs ( localPlayer , videoEl , uri , mime , this . $shaka ) ;
2021-07-09 20:55:09 +00:00
} ) ;
2022-05-23 20:18:20 +00:00
else this . setPlayerAttrs ( this . $player , videoEl , uri , mime , this . $shaka ) ;
2021-05-06 17:30:02 +00:00
if ( noPrevPlayer ) {
videoEl . addEventListener ( "timeupdate" , ( ) => {
2021-10-27 00:15:37 +00:00
const time = videoEl . currentTime ;
2022-07-19 16:29:03 +00:00
this . $emit ( "timeupdate" , time ) ;
2021-10-27 00:15:37 +00:00
this . updateProgressDatabase ( time ) ;
2021-05-06 17:30:02 +00:00
if ( this . sponsors && this . sponsors . segments ) {
2023-02-01 15:37:43 +00:00
const segment = this . findCurrentSegment ( time ) ;
2023-02-02 15:02:04 +00:00
this . inSegment = ! ! segment ;
2023-02-01 15:37:43 +00:00
if ( segment ? . autoskip && ( ! segment . skipped || this . selectedAutoLoop ) ) {
this . skipSegment ( videoEl , segment ) ;
}
2021-05-06 17:30:02 +00:00
}
} ) ;
videoEl . addEventListener ( "volumechange" , ( ) => {
2022-11-16 18:51:56 +00:00
this . setPreference ( "volume" , videoEl . volume , true ) ;
2021-05-06 17:30:02 +00:00
} ) ;
2022-01-16 08:43:24 +00:00
videoEl . addEventListener ( "ratechange" , e => {
const rate = videoEl . playbackRate ;
if ( rate > 0 && ! isNaN ( videoEl . duration ) && ! isNaN ( videoEl . duration - e . timeStamp / 1000 ) )
2022-11-16 18:51:56 +00:00
this . setPreference ( "rate" , rate , true ) ;
2021-10-08 16:38:01 +00:00
} ) ;
2021-10-03 19:18:04 +00:00
2021-05-06 17:30:02 +00:00
videoEl . addEventListener ( "ended" , ( ) => {
2022-08-06 10:03:31 +00:00
if (
! this . selectedAutoLoop &&
this . selectedAutoPlay &&
( this . playlist ? . relatedStreams ? . length > 0 || this . video . relatedStreams . length > 0 )
) {
this . navigateNext ( ) ;
2021-06-24 18:39:22 +00:00
}
2021-05-06 17:30:02 +00:00
} ) ;
}
//TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12
} ,
2023-02-01 15:37:43 +00:00
findCurrentSegment ( time ) {
return this . sponsors ? . segments ? . find ( s => time >= s . segment [ 0 ] && time < s . segment [ 1 ] ) ;
} ,
2023-02-02 15:02:04 +00:00
onClickSkipSegment ( ) {
const videoEl = this . $refs . videoEl ;
this . skipSegment ( videoEl ) ;
} ,
2023-02-01 15:37:43 +00:00
skipSegment ( videoEl , segment ) {
const time = videoEl . currentTime ;
if ( ! segment ) segment = this . findCurrentSegment ( time ) ;
if ( ! segment ) return ;
console . log ( "Skipped segment at " + time ) ;
videoEl . currentTime = segment . segment [ 1 ] ;
segment . skipped = true ;
} ,
2021-09-05 13:12:27 +00:00
setPlayerAttrs ( localPlayer , videoEl , uri , mime , shaka ) {
2021-11-11 08:16:00 +00:00
const url = "/watch?v=" + this . video . id ;
2021-10-06 14:33:52 +00:00
if ( ! this . $ui ) {
2022-01-22 23:10:05 +00:00
this . destroy ( ) ;
2021-11-11 08:16:00 +00:00
const OpenButton = class extends shaka . ui . Element {
constructor ( parent , controls ) {
super ( parent , controls ) ;
this . newTabButton _ = document . createElement ( "button" ) ;
this . newTabButton _ . classList . add ( "shaka-cast-button" ) ;
this . newTabButton _ . classList . add ( "shaka-tooltip" ) ;
this . newTabButton _ . ariaPressed = "false" ;
this . newTabIcon _ = document . createElement ( "i" ) ;
this . newTabIcon _ . classList . add ( "material-icons-round" ) ;
this . newTabIcon _ . textContent = "launch" ;
this . newTabButton _ . appendChild ( this . newTabIcon _ ) ;
const label = document . createElement ( "label" ) ;
label . classList . add ( "shaka-overflow-button-label" ) ;
label . classList . add ( "shaka-overflow-menu-only" ) ;
this . newTabNameSpan _ = document . createElement ( "span" ) ;
this . newTabNameSpan _ . innerText = "Open in new tab" ;
label . appendChild ( this . newTabNameSpan _ ) ;
this . newTabButton _ . appendChild ( label ) ;
this . parent . appendChild ( this . newTabButton _ ) ;
this . eventManager . listen ( this . newTabButton _ , "click" , ( ) => {
this . video . pause ( ) ;
window . open ( url ) ;
} ) ;
}
} ;
OpenButton . Factory = class {
create ( rootElement , controls ) {
return new OpenButton ( rootElement , controls ) ;
}
} ;
shaka . ui . OverflowMenu . registerElement ( "open_new_tab" , new OpenButton . Factory ( ) ) ;
2021-10-06 14:33:52 +00:00
this . $ui = new shaka . ui . Overlay ( localPlayer , this . $refs . container , videoEl ) ;
2021-06-07 19:22:29 +00:00
2022-11-15 20:58:30 +00:00
const overflowMenuButtons = [
"quality" ,
"language" ,
"captions" ,
"picture_in_picture" ,
"playback_rate" ,
"airplay" ,
] ;
2021-11-11 08:16:00 +00:00
if ( this . isEmbed ) {
overflowMenuButtons . push ( "open_new_tab" ) ;
}
2021-06-07 19:22:29 +00:00
const config = {
2021-11-11 08:16:00 +00:00
overflowMenuButtons : overflowMenuButtons ,
2021-06-07 19:22:29 +00:00
seekBarColors : {
base : "rgba(255, 255, 255, 0.3)" ,
buffered : "rgba(255, 255, 255, 0.54)" ,
played : "rgb(255, 0, 0)" ,
} ,
} ;
2021-10-06 14:33:52 +00:00
this . $ui . configure ( config ) ;
2021-06-07 19:22:29 +00:00
}
2022-06-06 02:18:47 +00:00
this . updateMarkers ( ) ;
2023-01-21 22:50:56 +00:00
const event = new Event ( "playerInit" ) ;
window . dispatchEvent ( event ) ;
2021-10-06 14:33:52 +00:00
const player = this . $ui . getControls ( ) . getPlayer ( ) ;
2021-06-07 19:22:29 +00:00
2021-10-06 14:33:52 +00:00
this . $player = player ;
2021-06-07 19:22:29 +00:00
2021-07-03 19:24:09 +00:00
const disableVideo = this . getPreferenceBoolean ( "listen" , false ) && ! this . video . livestream ;
2021-07-15 08:41:36 +00:00
2021-10-06 14:33:52 +00:00
this . $player . configure ( {
2021-07-21 10:48:59 +00:00
preferredVideoCodecs : this . preferredVideoCodecs ,
2021-07-15 08:41:36 +00:00
preferredAudioCodecs : [ "opus" , "mp4a" ] ,
manifest : {
disableVideo : disableVideo ,
} ,
} ) ;
2021-06-07 20:35:45 +00:00
2021-07-04 18:26:02 +00:00
const quality = this . getPreferenceNumber ( "quality" , 0 ) ;
2021-07-15 08:41:36 +00:00
const qualityConds =
quality > 0 && ( this . video . audioStreams . length > 0 || this . video . livestream ) && ! disableVideo ;
2021-10-06 14:33:52 +00:00
if ( qualityConds ) this . $player . configure ( "abr.enabled" , false ) ;
2021-06-21 20:03:11 +00:00
2021-09-05 13:12:27 +00:00
player . load ( uri , 0 , mime ) . then ( ( ) => {
2022-12-06 17:31:34 +00:00
const isSafari = window . navigator ? . vendor ? . includes ( "Apple" ) ;
if ( ! isSafari ) {
// Set the audio language
const prefLang = this . getPreferenceString ( "hl" , "en" ) . substr ( 0 , 2 ) ;
var lang = "en" ;
for ( var l in player . getAudioLanguages ( ) ) {
if ( l == prefLang ) {
lang = l ;
return ;
}
2022-11-15 21:16:19 +00:00
}
2022-12-06 17:31:34 +00:00
player . selectAudioLanguage ( lang ) ;
2022-11-15 21:16:19 +00:00
}
2022-11-15 20:58:30 +00:00
2021-06-21 20:03:11 +00:00
if ( qualityConds ) {
var leastDiff = Number . MAX _VALUE ;
var bestStream = null ;
2022-05-08 15:25:20 +00:00
var bestAudio = 0 ;
2022-11-15 21:16:19 +00:00
const tracks = player
. getVariantTracks ( )
. filter ( track => track . language == lang || track . language == "und" ) ;
2022-05-08 15:25:20 +00:00
// Choose the best audio stream
2022-08-14 18:37:07 +00:00
if ( quality >= 480 )
2022-11-15 21:16:19 +00:00
tracks . forEach ( track => {
2022-05-08 15:25:20 +00:00
const audioBandwidth = track . audioBandwidth ;
if ( audioBandwidth > bestAudio ) bestAudio = audioBandwidth ;
} ) ;
// Find best matching stream based on resolution and bitrate
2022-11-15 21:16:19 +00:00
tracks
2021-06-21 20:03:11 +00:00
. sort ( ( a , b ) => a . bandwidth - b . bandwidth )
. forEach ( stream => {
2022-05-08 15:25:20 +00:00
if ( stream . audioBandwidth < bestAudio ) return ;
2021-06-21 20:03:11 +00:00
const diff = Math . abs ( quality - stream . height ) ;
if ( diff < leastDiff ) {
leastDiff = diff ;
bestStream = stream ;
}
} ) ;
2022-05-08 15:25:20 +00:00
2021-06-21 20:03:11 +00:00
player . selectVariantTrack ( bestStream ) ;
}
2021-05-06 17:30:02 +00:00
this . video . subtitles . map ( subtitle => {
player . addTextTrackAsync (
subtitle . url ,
subtitle . code ,
2023-01-13 13:32:43 +00:00
"subtitles" ,
2021-05-06 17:30:02 +00:00
subtitle . mimeType ,
null ,
subtitle . name ,
) ;
} ) ;
2021-07-04 18:26:02 +00:00
videoEl . volume = this . getPreferenceNumber ( "volume" , 1 ) ;
2022-01-16 08:43:24 +00:00
const rate = this . getPreferenceNumber ( "rate" , 1 ) ;
2022-10-16 12:49:56 +00:00
videoEl . playbackRate = rate ;
videoEl . defaultPlaybackRate = rate ;
2021-05-06 17:30:02 +00:00
} ) ;
2023-01-27 17:13:20 +00:00
// expand the player to fullscreen when the fullscreen query equals true
if ( this . $route . query . fullscreen === "true" && ! this . $ui . getControls ( ) . isFullScreenEnabled ( ) )
this . $ui . getControls ( ) . toggleFullScreen ( ) ;
2021-05-06 17:30:02 +00:00
} ,
2021-10-27 00:15:37 +00:00
async updateProgressDatabase ( time ) {
// debounce
if ( new Date ( ) . getTime ( ) - this . lastUpdate < 500 ) return ;
this . lastUpdate = new Date ( ) . getTime ( ) ;
2022-01-13 22:41:53 +00:00
if ( ! this . initialSeekComplete || ! this . video . id || ! window . db ) return ;
2021-10-27 00:15:37 +00:00
var tx = window . db . transaction ( "watch_history" , "readwrite" ) ;
var store = tx . objectStore ( "watch_history" ) ;
2021-11-11 08:16:00 +00:00
var request = store . get ( this . video . id ) ;
2022-01-01 14:53:55 +00:00
request . onsuccess = function ( event ) {
2021-10-27 00:15:37 +00:00
var video = event . target . result ;
if ( video ) {
video . currentTime = time ;
store . put ( video ) ;
}
} ;
} ,
2022-01-13 04:52:14 +00:00
seek ( time ) {
if ( this . $refs . videoEl ) {
this . $refs . videoEl . currentTime = time ;
}
} ,
2022-08-06 10:03:31 +00:00
navigateNext ( ) {
const params = this . $route . query ;
let url = this . playlist ? . relatedStreams ? . [ this . index ] ? . url ? ? this . video . relatedStreams [ 0 ] . url ;
const searchParams = new URLSearchParams ( ) ;
for ( var param in params )
switch ( param ) {
case "v" :
case "t" :
break ;
case "index" :
if ( this . index < this . playlist . relatedStreams . length ) searchParams . set ( "index" , this . index + 1 ) ;
break ;
case "list" :
if ( this . index < this . playlist . relatedStreams . length ) searchParams . set ( "list" , params . list ) ;
break ;
default :
searchParams . set ( param , params [ param ] ) ;
break ;
}
2023-01-27 17:13:20 +00:00
// save the fullscreen state
searchParams . set ( "fullscreen" , this . $ui . getControls ( ) . isFullScreenEnabled ( ) ) ;
2022-08-06 10:03:31 +00:00
const paramStr = searchParams . toString ( ) ;
if ( paramStr . length > 0 ) url += "&" + paramStr ;
this . $router . push ( url ) ;
} ,
2022-06-06 02:18:47 +00:00
updateMarkers ( ) {
const markers = this . $refs . container . querySelector ( ".shaka-ad-markers" ) ;
const array = [ "to right" ] ;
2022-06-06 02:41:13 +00:00
this . sponsors ? . segments ? . forEach ( segment => {
2022-06-06 02:18:47 +00:00
const start = ( segment . segment [ 0 ] / this . video . duration ) * 100 ;
const end = ( segment . segment [ 1 ] / this . video . duration ) * 100 ;
var color ;
switch ( segment . category ) {
case "sponsor" :
color = "#00d400" ;
break ;
case "selfpromo" :
color = "#ffff00" ;
break ;
case "interaction" :
color = "#cc00ff" ;
break ;
case "poi_highlight" :
color = "#ff1684" ;
break ;
case "intro" :
color = "#00ffff" ;
break ;
case "outro" :
color = "#0202ed" ;
break ;
case "preview" :
color = "#008fd6" ;
break ;
case "filler" :
color = "#7300FF" ;
break ;
case "music_offtopic" :
color = "#ff9900" ;
break ;
default :
color = "white" ;
}
array . push ( ` transparent ${ start } % ` ) ;
array . push ( ` ${ color } ${ start } % ` ) ;
array . push ( ` ${ color } ${ end } % ` ) ;
array . push ( ` transparent ${ end } % ` ) ;
} ) ;
if ( array . length <= 1 ) {
return ;
}
2022-06-27 04:26:47 +00:00
if ( markers ) markers . style . background = ` linear-gradient( ${ array . join ( "," ) } ) ` ;
2022-06-06 02:18:47 +00:00
} ,
2022-01-23 19:08:33 +00:00
destroy ( hotkeys ) {
2021-10-06 14:33:52 +00:00
if ( this . $ui ) {
this . $ui . destroy ( ) ;
this . $ui = undefined ;
this . $player = undefined ;
2021-09-05 20:53:59 +00:00
}
2021-10-06 14:33:52 +00:00
if ( this . $player ) {
this . $player . destroy ( ) ;
this . $player = undefined ;
2021-09-05 20:53:59 +00:00
}
2022-06-26 14:15:03 +00:00
if ( hotkeys ) this . $hotkeys ? . unbind ( ) ;
this . $refs . container ? . querySelectorAll ( "div" ) . forEach ( node => node . remove ( ) ) ;
2021-09-05 20:53:59 +00:00
} ,
2021-05-06 17:30:02 +00:00
} ,
2022-06-06 02:18:47 +00:00
watch : {
sponsors ( ) {
2023-02-02 13:25:45 +00:00
const skipOptions = this . getPreferenceJSON ( "skipOptions" , { } ) ;
2023-02-01 15:37:43 +00:00
this . sponsors ? . segments ? . forEach ( segment => {
const option = skipOptions [ segment . category ] ;
segment . autoskip = option === undefined || option === "auto" ;
} ) ;
2022-06-06 02:18:47 +00:00
if ( this . getPreferenceBoolean ( "showMarkers" , true ) ) {
this . shakaPromise . then ( ( ) => {
this . updateMarkers ( ) ;
} ) ;
}
} ,
} ,
2021-04-01 15:13:35 +00:00
} ;
< / script >
2021-10-08 18:52:51 +00:00
< style >
2022-01-22 23:09:29 +00:00
. player - container {
@ apply max - h - 75 vh min - h - 64 bg - black ;
}
2022-01-31 04:34:27 +00:00
. shaka - video - container . material - icons - round {
@ apply ! text - xl ;
}
. shaka - current - time {
@ apply ! text - base ;
2021-10-08 18:52:51 +00:00
}
. shaka - video - container : - webkit - full - screen {
max - height : none ! important ;
}
2022-07-23 16:15:40 +00:00
/* captions style */
. shaka - text - wrapper * {
text - align : left ! important ;
}
. shaka - text - wrapper > span > span {
background - color : transparent ! important ;
}
/* apply to all spans that don't include multiple other spans to avoid the style being applied to the text container too when the subtitles are two lines */
. shaka - text - wrapper > span > span * : first - child : last - child {
background - color : rgba ( 0 , 0 , 0 , 0.6 ) ! important ;
padding : 0.09 em 0 ;
}
2023-02-02 15:02:04 +00:00
. skip - segment - button {
/* position button above player overlay */
z - index : 1000 ;
position : absolute ;
transform : translate ( 0 , - 50 % ) ;
top : 50 % ;
right : 0 ;
background - color : rgb ( 0 0 0 / 0.5 ) ;
border : 2 px rgba ( 255 , 255 , 255 , 0.75 ) solid ;
border - right : 0 ;
border - radius : 0.75 em ;
border - top - right - radius : 0 ;
border - bottom - right - radius : 0 ;
padding : 0.5 em ;
/* center text vertically */
display : flex ;
align - items : center ;
justify - content : center ;
2023-02-08 07:17:18 +00:00
color : # fff ;
2023-02-02 15:02:04 +00:00
line - height : 1.5 em ;
}
. skip - segment - button . material - icons - round {
font - size : 1.6 em ! important ;
line - height : inherit ! important ;
}
2021-10-08 18:52:51 +00:00
< / style >