jellyfin-roku/components/JFVideo.bs
2024-07-30 21:27:46 -04:00

316 lines
10 KiB
Plaintext

import "pkg:/source/utils/misc.bs"
import "pkg:/source/utils/config.bs"
sub init()
m.playbackTimer = m.top.findNode("playbackTimer")
m.bufferCheckTimer = m.top.findNode("bufferCheckTimer")
m.top.observeField("state", "onState")
m.top.observeField("content", "onContentChange")
m.playbackTimer.observeField("fire", "ReportPlayback")
m.bufferPercentage = 0 ' Track whether content is being loaded
m.playReported = false
m.top.transcodeReasons = []
m.bufferCheckTimer.duration = 30
if m.global.session.user.settings["ui.design.hideclock"] = true
clockNode = findNodeBySubtype(m.top, "clock")
if clockNode[0] <> invalid then clockNode[0].parent.removeChild(clockNode[0].node)
end if
'Play Next Episode button
m.nextEpisodeButton = m.top.findNode("nextEpisode")
m.nextEpisodeButton.text = tr("Next Episode")
m.nextEpisodeButton.setFocus(false)
m.nextupbuttonseconds = m.global.session.user.settings["playback.nextupbuttonseconds"].ToInt()
m.showNextEpisodeButtonAnimation = m.top.findNode("showNextEpisodeButton")
m.hideNextEpisodeButtonAnimation = m.top.findNode("hideNextEpisodeButton")
m.checkedForNextEpisode = false
m.getNextEpisodeTask = createObject("roSGNode", "GetNextEpisodeTask")
m.getNextEpisodeTask.observeField("nextEpisodeData", "onNextEpisodeDataLoaded")
m.top.observeField("allowCaptions", "onAllowCaptionsChange")
end sub
sub onAllowCaptionsChange()
if not m.top.allowCaptions then return
m.captionGroup = m.top.findNode("captionGroup")
m.captionGroup.createchildren(9, "LayoutGroup")
m.captionTask = createObject("roSGNode", "captionTask")
m.captionTask.observeField("currentCaption", "updateCaption")
m.captionTask.observeField("useThis", "checkCaptionMode")
m.top.observeField("currentSubtitleTrack", "loadCaption")
m.top.observeField("globalCaptionMode", "toggleCaption")
if m.global.session.user.settings["playback.subs.custom"] = false
m.top.suppressCaptions = false
else
m.top.suppressCaptions = true
toggleCaption()
end if
end sub
sub loadCaption()
if m.top.suppressCaptions
m.captionTask.url = m.top.currentSubtitleTrack
end if
end sub
sub toggleCaption()
m.captionTask.playerState = m.top.state + m.top.globalCaptionMode
if LCase(m.top.globalCaptionMode) = "on"
m.captionTask.playerState = m.top.state + m.top.globalCaptionMode + "w"
m.captionGroup.visible = true
else
m.captionGroup.visible = false
end if
end sub
sub updateCaption ()
m.captionGroup.removeChildrenIndex(m.captionGroup.getChildCount(), 0)
m.captionGroup.appendChildren(m.captionTask.currentCaption)
end sub
' Event handler for when video content field changes
sub onContentChange()
if not isValid(m.top.content) then return
m.top.observeField("position", "onPositionChanged")
end sub
sub onNextEpisodeDataLoaded()
m.checkedForNextEpisode = true
m.top.observeField("position", "onPositionChanged")
end sub
'
' Runs Next Episode button animation and sets focus to button
sub showNextEpisodeButton()
if m.top.content.contenttype <> 4 then return ' only display when content is type "Episode"
if m.nextupbuttonseconds = 0 then return ' is the button disabled?
if m.nextEpisodeButton.opacity <> 0 then return
userSession = m.global.session.user
if userSession.settings["playback.playnextepisode"] = "disabled" then return
if userSession.settings["playback.playnextepisode"] = "webclient" and not userSession.Configuration.EnableNextEpisodeAutoPlay then return
m.nextEpisodeButton.visible = true
m.showNextEpisodeButtonAnimation.control = "start"
m.nextEpisodeButton.setFocus(true)
end sub
'
'Update count down text
sub updateCount()
nextEpisodeCountdown = Int(m.top.duration - m.top.position)
if nextEpisodeCountdown < 0
nextEpisodeCountdown = 0
end if
m.nextEpisodeButton.text = tr("Next Episode") + " " + nextEpisodeCountdown.toStr()
end sub
'
' Runs hide Next Episode button animation and sets focus back to video
sub hideNextEpisodeButton()
m.hideNextEpisodeButtonAnimation.control = "start"
m.nextEpisodeButton.setFocus(false)
m.top.setFocus(true)
end sub
' Checks if we need to display the Next Episode button
sub checkTimeToDisplayNextEpisode()
if m.top.content.contenttype <> 4 then return ' only display when content is type "Episode"
if m.nextupbuttonseconds = 0 then return ' is the button disabled?
if isValid(m.top.duration) and isValid(m.top.position)
nextEpisodeCountdown = Int(m.top.duration - m.top.position)
if nextEpisodeCountdown < 0 and m.nextEpisodeButton.opacity = 0.9
hideNextEpisodeButton()
return
else if nextEpisodeCountdown > 1 and int(m.top.position) >= (m.top.duration - m.nextupbuttonseconds - 1)
updateCount()
if m.nextEpisodeButton.opacity = 0
showNextEpisodeButton()
end if
return
end if
end if
if m.nextEpisodeButton.visible or m.nextEpisodeButton.hasFocus()
m.nextEpisodeButton.visible = false
m.nextEpisodeButton.setFocus(false)
end if
end sub
' When Video Player state changes
sub onPositionChanged()
if isValid(m.captionTask)
m.captionTask.currentPos = Int(m.top.position * 1000)
end if
' Check if dialog is open
m.dialog = m.top.getScene().findNode("dialogBackground")
if not isValid(m.dialog)
checkTimeToDisplayNextEpisode()
end if
end sub
'
' When Video Player state changes
sub onState(msg)
if isValid(m.captionTask)
m.captionTask.playerState = m.top.state + m.top.globalCaptionMode
end if
' When buffering, start timer to monitor buffering process
if m.top.state = "buffering" and m.bufferCheckTimer <> invalid
' start timer
m.bufferCheckTimer.control = "start"
m.bufferCheckTimer.ObserveField("fire", "bufferCheck")
else if m.top.state = "error"
if not m.playReported and m.top.transcodeAvailable
m.top.retryWithTranscoding = true ' If playback was not reported, retry with transcoding
else
' If an error was encountered, Display dialog
dialog = createObject("roSGNode", "PlaybackDialog")
dialog.title = tr("Error During Playback")
dialog.buttons = [tr("OK")]
dialog.message = tr("An error was encountered while playing this item.")
m.top.getScene().dialog = dialog
end if
' Stop playback and exit player
m.top.control = "stop"
m.top.backPressed = true
else if m.top.state = "playing"
' Check if next episde is available
if isValid(m.top.showID)
if m.top.showID <> "" and not m.checkedForNextEpisode and m.top.content.contenttype = 4
m.getNextEpisodeTask.showID = m.top.showID
m.getNextEpisodeTask.videoID = m.top.id
m.getNextEpisodeTask.control = "RUN"
end if
end if
if m.playReported = false
ReportPlayback("start")
m.playReported = true
else
ReportPlayback()
end if
m.playbackTimer.control = "start"
else if m.top.state = "paused"
m.playbackTimer.control = "stop"
ReportPlayback()
else if m.top.state = "stopped"
m.playbackTimer.control = "stop"
ReportPlayback("stop")
m.playReported = false
end if
end sub
'
' Report playback to server
sub ReportPlayback(state = "update" as string)
if m.top.position = invalid then return
params = {
"ItemId": m.top.id,
"PlaySessionId": m.top.PlaySessionId,
"PositionTicks": int(m.top.position) * 10000000&, 'Ensure a LongInteger is used
"IsPaused": (m.top.state = "paused")
}
if m.top.content.live
params.append({
"MediaSourceId": m.top.transcodeParams.MediaSourceId,
"LiveStreamId": m.top.transcodeParams.LiveStreamId
})
m.bufferCheckTimer.duration = 30
end if
' Report playstate via worker task
playstateTask = m.global.playstateTask
playstateTask.setFields({ status: state, params: params })
playstateTask.control = "RUN"
end sub
'
' Check the the buffering has not hung
sub bufferCheck(msg)
if m.top.state <> "buffering"
' If video is not buffering, stop timer
m.bufferCheckTimer.control = "stop"
m.bufferCheckTimer.unobserveField("fire")
return
end if
if m.top.bufferingStatus <> invalid
' Check that the buffering percentage is increasing
if m.top.bufferingStatus["percentage"] > m.bufferPercentage
m.bufferPercentage = m.top.bufferingStatus["percentage"]
else if m.top.content.live = true
m.top.callFunc("refresh")
else
' If buffering has stopped Display dialog
dialog = createObject("roSGNode", "PlaybackDialog")
dialog.title = tr("Error Retrieving Content")
dialog.buttons = [tr("OK")]
dialog.message = tr("There was an error retrieving the data for this item from the server.")
m.top.getScene().dialog = dialog
' Stop playback and exit player
m.top.control = "stop"
m.top.backPressed = true
end if
end if
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if key = "OK" and m.nextEpisodeButton.hasfocus() and not m.top.trickPlayBar.visible
m.top.state = "finished"
hideNextEpisodeButton()
return true
else
'Hide Next Episode Button
if m.nextEpisodeButton.opacity > 0 or m.nextEpisodeButton.hasFocus()
m.nextEpisodeButton.opacity = 0
m.nextEpisodeButton.setFocus(false)
m.top.setFocus(true)
end if
end if
if not press then return false
if key = "down"
m.top.selectSubtitlePressed = true
return true
else if key = "up"
m.top.selectPlaybackInfoPressed = true
return true
else if key = "OK"
if m.nextEpisodeButton.hasfocus() and not m.top.trickPlayBar.visible
m.top.state = "finished"
hideNextEpisodeButton()
return true
else if m.top.state = "paused"
' OK will play/pause depending on current state
' return false to allow selection during seeking
m.top.control = "resume"
return false
else if m.top.state = "playing"
m.top.control = "pause"
return false
end if
end if
return false
end function