components_video_OSD.bs

import "pkg:/source/utils/misc.bs"

sub init()
  m.inactivityTimer = m.top.findNode("inactivityTimer")
  m.endsAtTimer = m.top.findNode("endsAtTimer")
  m.endsAt = m.top.findNode("endsAt")
  m.videoLogo = m.top.findNode("videoLogo")
  m.videoOfficialRating = m.top.findNode("videoOfficialRating")
  m.videoTitle = m.top.findNode("videoTitle")
  m.videoSubtitleGroup = m.top.findNode("videoSubtitleGroup")
  m.videoPlayPause = m.top.findNode("videoPlayPause")
  m.videoPositionTime = m.top.findNode("videoPositionTime")
  m.videoRemainingTime = m.top.findNode("videoRemainingTime")
  m.progressBar = m.top.findNode("progressBar")
  m.progressBarBackground = m.top.findNode("progressBarBackground")

  m.top.observeField("json", "setFields")
  m.top.observeField("visible", "onVisibleChanged")
  m.top.observeField("hasFocus", "onFocusChanged")
  m.top.observeField("progressPercentage", "onProgressPercentageChanged")
  m.top.observeField("playbackState", "onPlaybackStateChanged")

  m.defaultButtonIndex = 1
  m.focusedButtonIndex = 1
  m.subtitleDividerCount = 0

  m.buttonMenuRight = m.top.findNode("buttonMenuRight")
  m.buttonMenuLeft = m.top.findNode("buttonMenuLeft")
  m.buttonMenuLeft.buttonFocused = m.defaultButtonIndex
  m.buttonMenuLeft.getChild(m.defaultButtonIndex).focus = true
end sub

' setFields: Grab data from json metadata and save it to local fields
sub setFields()
  if not isValid(m.top.json) or m.top.json = "" then return

  videoData = parseJson(m.top.json)
  mediaStreams = videoData.MediaStreams
  m.top.json = ""

  if videoData.Type <> invalid
    m.top.type = videoData.Type
  end if

  if videoData.logoImage <> invalid and videoData.logoImage <> ""
    m.top.videoLogo = videoData.logoImage
  end if

  if videoData.Name <> invalid
    m.top.videoTitle = videoData.Name
  end if

  if videoData.Overview <> invalid
    m.top.overview = videoData.Overview
  end if

  if videoData.SeriesStudio <> invalid
    m.top.seriesStudio = videoData.SeriesStudio
  end if

  if videoData.HasSubtitles <> invalid
    m.top.hasSubtitles = videoData.HasSubtitles
  end if

  if videoData.Chapters <> invalid and videoData.Chapters.Count() > 0
    m.top.hasChapters = true
  end if

  if videoData.SeriesName <> invalid
    m.top.seriesName = videoData.SeriesName
  end if

  if videoData.ParentIndexNumber <> invalid
    m.top.seasonNumber = videoData.ParentIndexNumber
  end if

  if videoData.IndexNumber <> invalid
    m.top.episodeNumber = videoData.IndexNumber
  end if

  if videoData.IndexNumberEnd <> invalid
    m.top.episodeNumberEnd = videoData.IndexNumberEnd
  end if

  if videoData.CommunityRating <> invalid
    m.top.communityRating = videoData.CommunityRating
  end if

  if videoData.OfficialRating <> invalid
    m.top.officialRating = videoData.OfficialRating
  end if

  if videoData.PremiereDate <> invalid
    m.top.premiereDate = videoData.PremiereDate
  end if

  if videoData.RunTimeTicks <> invalid
    m.top.runTimeTicks = videoData.RunTimeTicks
    m.top.runTimeMinutes = ticksToMinutes(m.top.runTimeTicks)
  end if

  if videoData.ProductionYear <> invalid
    m.top.productionYear = videoData.ProductionYear
  end if

  numVideoStreams = 0
  numAudioStreams = 0

  for each stream in mediaStreams
    if stream.Type = "Video"
      numVideoStreams++
    else if stream.Type = "Audio"
      numAudioStreams++
    end if
  end for

  m.top.numVideoStreams = numVideoStreams
  m.top.numAudioStreams = numAudioStreams

  setButtonStates()
  populateData()
end sub

sub populateData()
  setVideoLogoGroup()
  setVideoTitle()
  setVideoSubTitle()
end sub

' setButtonStates: Disable previous/next buttons if needed and remove any other unneeded buttons
sub setButtonStates()
  queueCount = m.global.QueueManager.callFunc("getCount")
  queueIndex = m.global.QueueManager.callFunc("getPosition")

  ' Disable these buttons as needed

  ' Item Previous
  if queueCount = 1 or queueIndex = 0
    itemPrevious = m.buttonMenuLeft.findNode("itemBack")
    itemPrevious.enabled = false
  end if
  ' Item Next
  if queueIndex + 1 >= queueCount
    itemNext = m.buttonMenuLeft.findNode("itemNext")
    itemNext.enabled = false
  end if

  ' Remove these buttons as needed

  ' Audio Track
  if m.top.numAudioStreams < 2
    m.buttonMenuLeft.removeChild(m.buttonMenuLeft.findNode("showAudioMenu"))
  end if
  ' Subtitles
  if not m.top.hasSubtitles
    m.buttonMenuLeft.removeChild(m.buttonMenuLeft.findNode("showSubtitleMenu"))
  end if
  ' Chapters
  if not m.top.hasChapters
    m.buttonMenuLeft.removeChild(m.buttonMenuLeft.findNode("chapterList"))
  end if
end sub

sub setEndsAtText()
  date = CreateObject("roDateTime")
  endTime = int(m.top.remainingPositionTime)
  date.fromSeconds(date.asSeconds() + endTime)
  date.toLocalTime()

  m.endsAt.text = tr("Ends at") + " " + formatTime(date)
end sub

sub setVideoLogoGroup()
  m.videoLogo.uri = m.top.videoLogo

  ' if m.top.officialRating <> invalid and m.top.officialRating <> ""
  '     m.videoOfficialRating.text = m.top.officialRating
  ' end if
end sub

sub setVideoTitle()
  m.videoTitle.text = m.top.videoTitle
end sub

sub setVideoSubTitle()
  ' start fresh by removing all subtitle nodes
  m.videoSubtitleGroup.removeChildrenIndex(m.videoSubtitleGroup.getChildCount(), 0)

  airDateNodeCreated = false

  ' EPISODE
  if m.top.type <> invalid
    if m.top.Type = "Episode" or m.top.Type = "Recording"
      ' Title
      if m.top.seriesName <> ""
        m.videoTitle.text = m.top.seriesName
      end if

      ' episodeInfo
      episodeInfoText = ""
      '
      ' Season number
      if m.top.seasonNumber <> invalid
        episodeInfoText = episodeInfoText + `${tr("S")}${m.top.seasonNumber}`
      else
        episodeInfoText = episodeInfoText + `${tr("S")}?`
      end if
      ' Episode number
      if m.top.episodeNumber <> invalid
        episodeInfoText = episodeInfoText + `:${tr("E")}${m.top.episodeNumber}`
      else
        episodeInfoText = episodeInfoText + `:${tr("E")}??`
      end if
      ' Episode number end
      if m.top.episodeNumberEnd <> invalid and m.top.episodeNumberEnd <> 0 and m.top.episodeNumberEnd > m.top.episodeNumber
        ' add entry for every episode eg. S6:E1E2
        for i = m.top.episodeNumber + 1 to m.top.episodeNumberEnd
          episodeInfoText = episodeInfoText + `${tr("E")}${m.top.episodeNumberEnd}`
        end for
      end if
      ' Episode name
      if m.top.videoTitle <> invalid and m.top.videoTitle <> ""
        episodeInfoText = episodeInfoText + ` - ${m.top.videoTitle}`
      end if

      if episodeInfoText <> ""
        episodeInfoNode = createSubtitleLabelNode("episodeInfo")
        episodeInfoNode.text = episodeInfoText
        displaySubtitleNode(episodeInfoNode)
      end if
    else if m.top.type = "Movie"
      ' videoAirDate
      if m.top.premiereDate <> invalid and m.top.premiereDate <> ""
        airDateNodeCreated = true

        premiereDateNode = createSubtitleLabelNode("videoAirDate")
        premiereDateNode.text = formatIsoDateVideo(m.top.premiereDate)

        displaySubtitleNode(premiereDateNode)
      end if
    end if
  end if

  ' append these to all video types
  '
  ' videoAirDate if needed
  if not airDateNodeCreated and m.top.premiereDate <> invalid and m.top.premiereDate <> ""
    premiereDateNode = createSubtitleLabelNode("videoAirDate")
    premiereDateNode.text = formatIsoDateVideo(m.top.premiereDate)
    displaySubtitleNode(premiereDateNode)
  end if

  ' videoRunTime
  if m.top.runTimeMinutes <> invalid and m.top.runTimeMinutes <> 0
    runTimeNode = createSubtitleLabelNode("videoRunTime")

    if m.top.runTimeMinutes < 2
      runTimeText = `${m.top.runTimeMinutes} ` + tr("min")
    else
      runTimeText = `${m.top.runTimeMinutes} ` + tr("mins")
    end if

    runTimeNode.text = runTimeText
    displaySubtitleNode(runTimeNode)
  end if

end sub

sub onProgressPercentageChanged()
  m.videoPositionTime.text = secondsToTimestamp(m.top.positionTime, true)
  m.videoRemainingTime.text = "-" + secondsToTimestamp(m.top.remainingPositionTime, true)
  m.progressBar.width = m.progressBarBackground.width * m.top.progressPercentage
  setEndsAtText()
end sub

sub onPlaybackStateChanged()
  if LCase(m.top.playbackState) = "playing"
    m.videoPlayPause.icon = "pkg:/images/icons/pause.png"
    return
  end if

  m.videoPlayPause.icon = "pkg:/images/icons/play.png"
end sub

sub resetFocusToDefaultButton()
  ' Remove focus from previously selected button
  for each child in m.buttonMenuLeft.getChildren(-1, 0)
    if isValid(child.focus)
      child.focus = false
    end if
  end for

  for each child in m.buttonMenuRight.getChildren(-1, 0)
    if isValid(child.focus)
      child.focus = false
    end if
  end for

  ' Set focus back to the default button
  m.buttonMenuLeft.setFocus(true)
  m.focusedButtonIndex = m.defaultButtonIndex
  m.buttonMenuLeft.getChild(m.defaultButtonIndex).focus = true
  m.buttonMenuLeft.buttonFocused = m.defaultButtonIndex
end sub

sub onVisibleChanged()
  if m.top.visible
    resetFocusToDefaultButton()
    m.endsAtTimer.observeField("fire", "setEndsAtText")
    m.endsAtTimer.control = "start"

    if m.top.playbackState <> "paused"
      m.inactivityTimer.observeField("fire", "inactiveCheck")
      m.inactivityTimer.control = "start"
    end if
  else
    m.inactivityTimer.control = "stop"
    m.endsAtTimer.control = "stop"
    m.inactivityTimer.unobserveField("fire")
    m.endsAtTimer.unobserveField("fire")
  end if
end sub

sub onFocusChanged()
  if m.top.hasfocus
    m.buttonMenuLeft.setFocus(true)
  end if
end sub

' inactiveCheck: Checks if the time since last keypress is greater than or equal to the allowed inactive time of the menu.
sub inactiveCheck()
  ' If user is currently seeing a dialog box, ignore inactive check
  if m.global.sceneManager.callFunc("isDialogOpen")
    return
  end if

  deviceInfo = CreateObject("roDeviceInfo")
  if deviceInfo.timeSinceLastKeypress() >= m.top.inactiveTimeout
    m.top.action = "hide"
  end if
end sub

sub onButtonSelected()
  if m.buttonMenuLeft.isInFocusChain()
    selectedButton = m.buttonMenuLeft.getChild(m.buttonMenuLeft.buttonFocused)
  else if m.buttonMenuRight.isInFocusChain()
    selectedButton = m.buttonMenuRight.getChild(m.buttonMenuRight.buttonFocused)
  else
    return
  end if

  if LCase(selectedButton.id) = "chapterlist"
    m.top.showChapterList = not m.top.showChapterList
  end if

  m.top.action = selectedButton.id
end sub

function createSubtitleLabelNode(labelId as string) as object
  labelNode = CreateObject("roSGNode", "LabelPrimaryMedium")
  labelNode.id = labelId
  labelNode.horizAlign = "left"
  labelNode.vertAlign = "center"
  labelNode.width = 0
  labelNode.height = 0
  labelNode.bold = true

  return labelNode
end function

function createSubtitleDividerNode() as object
  m.subtitleDividerCount++

  labelNode = CreateObject("roSGNode", "LabelPrimarySmall")
  labelNode.id = "divider" + m.subtitleDividerCount.toStr()
  labelNode.horizAlign = "left"
  labelNode.vertAlign = "center"
  labelNode.width = 0
  labelNode.height = 40
  labelNode.text = "•"
  labelNode.bold = true

  return labelNode
end function

sub displaySubtitleNode(node as object)
  if not isValid(node) then return

  subtitleChildrenCount = m.videoSubtitleGroup.getChildCount()
  if subtitleChildrenCount > 0
    ' add a divider
    dividerNode = createSubtitleDividerNode()
    m.videoSubtitleGroup.appendChild(dividerNode)
  end if

  m.videoSubtitleGroup.appendChild(node)
end sub

function onKeyEvent(key as string, press as boolean) as boolean
  if not press then return false

  if key = "play"
    m.top.action = "videoplaypause"
    return true
  end if

  if key = "OK"
    onButtonSelected()
    return true
  end if

  if key = "back" and m.top.visible
    m.top.action = "hide"

    return true
  end if

  return false
end function