Display multiple captions at once

This commit is contained in:
1hitsong 2025-08-19 21:34:16 -04:00
parent 9a0ffa4710
commit f6ea8c441e
4 changed files with 125 additions and 85 deletions

View file

@ -1,5 +1,5 @@
{
"rules": {
"unused-variable": "error"
}
}
"rules": {
"unused-variable": "warn"
}
}

View file

@ -1,5 +1,6 @@
import "pkg:/source/api/baserequest.bs"
import "pkg:/source/enums/MediaPlaybackState.bs"
import "pkg:/source/enums/String.bs"
import "pkg:/source/utils/config.bs"
sub init()
@ -95,32 +96,102 @@ end function
sub updateCaption()
m.top.currentCaption = []
if LCase(m.top.playerState) = "playingon"
m.top.currentPos = m.top.currentPos + 100
texts = []
captionStyles = {}
for each entry in m.captionList
if entry["start"] <= m.top.currentPos and m.top.currentPos < entry["end"]
captionStyles = entry["styles"]
t = m.tags.replaceAll(entry["text"], "")
texts.push(t)
end if
end for
labels = []
for each text in texts
labels.push(newlabel (text))
end for
lines = newLayoutGroup(labels)
rect = newRect(lines)
m.top.currentCaption = [rect, lines]
m.top.captionStyles = captionStyles
else if LCase(m.top.playerState.right(1)) = "w"
if LCase(m.top.playerState.right(1)) = "w"
m.top.playerState = m.top.playerState.left(len (m.top.playerState) - 1)
return
end if
m.top.currentCaption = []
if not isStringEqual(m.top.playerState, "playingon") then return
m.top.currentPos = m.top.currentPos + 100
captions = []
for each entry in m.captionList
if entry["start"] <= m.top.currentPos and m.top.currentPos < entry["end"]
labels = []
for each text in entry["text"]
labels.push(newlabel(text))
end for
lines = newLayoutGroup(labels)
rect = newRect(lines)
finalStyles = prepareStyles(entry["styles"], rect)
lines.translation = finalStyles.translation
rectxy = lines.BoundingRect()
rect.translation = [finalStyles.translation[0], finalStyles.translation[1] - (rectxy.height / 2)]
captions.push([rect, lines])
end if
end for
m.top.currentCaption = captions
end sub
function prepareStyles(styles as object, rect) as object
captionStyles = styles
finalStyles = { translation: [960, 1020] }
verticalPosition = 1020
if not isValidAndNotEmpty(captionStyles)
return finalStyles
end if
captionLine = chainLookupReturn(captionStyles, "line", string.EMPTY)
' Line
if not isStringEqual(captionLine, string.EMPTY)
captionLineHeight = rect.BoundingRect().height
verticalPosition = processLineCue(captionLine, captionLineHeight)
end if
finalStyles.translation = [960, verticalPosition]
return finalStyles
end function
function processLineCue(captionLine as string, captionLineHeight as float) as integer
' A percentage
if captionLine.Instr("%") <> -1
calculatedPosition = 1080 * (val(captionLine) / 100)
calculatedPosition += captionLineHeight
if calculatedPosition > 1020
calculatedPosition = 1020
end if
return calculatedPosition
end if
' Note: Non-numeric values are treated as 0
lineValue = val(captionLine)
' A positive line value. Count from the top
if lineValue >= 0
calculatedPosition = (lineValue + 1) * captionLineHeight
if calculatedPosition > 1020
calculatedPosition = 1020
end if
return calculatedPosition
end if
' A negative line value. Count from the bottom
calculatedPosition = 1080 - Abs((lineValue + 1) * captionLineHeight)
if calculatedPosition < 0
calculatedPosition = 0
end if
return calculatedPosition
end function
function isTime(text)
return text.right(1) = chr(31)
end function
@ -158,20 +229,40 @@ function parseVTT(lines)
curEnd = -1
entries = []
currentLine = []
entry = {}
isNewLine = false
for i = 0 to lines.count() - 1
if isTime(lines[i])
currentLine = []
curStart = toMs(lines[i])
lineData = extractStyles(lines[i + 1])
curEnd = toMs(lineData.LookupCI("endTimestamp"))
curstyles = chainLookup(lineData, "styles")
entry = { "start": curStart, "end": curEnd, "styles": curstyles }
isNewLine = true
i += 1
else if curStart <> -1
continue for
end if
if isNewLine
trimmed = lines[i].trim()
if trimmed <> chr(0)
entry = { "start": curStart, "end": curEnd, "text": trimmed, "styles": curstyles }
finalText = m.tags.replaceAll(trimmed, "")
if isStringEqual(finalText, string.EMPTY)
entry.AddReplace("text", currentLine)
entries.push(entry)
isNewLine = false
else
currentLine.push(finalText)
end if
end if
end for
return entries
end function

View file

@ -384,7 +384,6 @@ sub onAllowCaptionsChange()
m.captionGroup = m.top.findNode("captionGroup")
m.captionGroup.createchildren(9, "LayoutGroup")
m.captionTask = createObject("roSGNode", "captionTask")
m.captionTask.observeField("captionStyles", "updateCaptionStyles")
m.captionTask.observeField("currentCaption", "updateCaption")
m.captionTask.observeField("useThis", "checkCaptionMode")
m.top.observeField("subtitleTrack", "loadCaption")
@ -416,63 +415,13 @@ sub toggleCaption()
end if
end sub
sub updateCaptionStyles()
captionStyles = m.captionTask.captionStyles
if not isValidAndNotEmpty(captionStyles)
m.captionGroup.translation = [960, 1020]
end if
captionLine = chainLookupReturn(captionStyles, "line", string.EMPTY)
' Line
if not isStringEqual(captionLine, string.EMPTY)
' A percentage
if captionLine.Instr("%") <> -1
calculatedPosition = 1080 * (val(captionLine) / 100)
calculatedPosition += m.captionGroup.BoundingRect().height
if calculatedPosition > 1020
calculatedPosition = 1020
end if
m.captionGroup.translation = [960, calculatedPosition]
return
end if
' Note: Non-numeric values are treated as 0
lineValue = val(captionLine)
' A positive line value. Count from the top
if lineValue >= 0
calculatedPosition = (lineValue + 1) * m.captionGroup.BoundingRect().height
if calculatedPosition > 1020
calculatedPosition = 1020
end if
m.captionGroup.translation = [960, calculatedPosition]
return
end if
' A negative line value. Count from the bottom
calculatedPosition = 1080 - Abs((lineValue + 1) * m.captionGroup.BoundingRect().height)
if calculatedPosition < 0
calculatedPosition = 0
end if
m.captionGroup.translation = [960, calculatedPosition]
return
end if
' No styles passed, reset to default position
m.captionGroup.translation = [960, 1020]
end sub
' Removes old subtitle lines and adds new subtitle lines
sub updateCaption()
m.captionGroup.removeChildrenIndex(m.captionGroup.getChildCount(), 0)
m.captionGroup.appendChildren(m.captionTask.currentCaption)
for each caption in m.captionTask.currentCaption
m.captionGroup.appendChildren(caption)
end for
end sub
' Event handler for when selectedSubtitle changes

View file

@ -30,7 +30,7 @@
</interface>
<children>
<Group id="captionGroup" translation="[960,1020]" />
<Group id="captionGroup" translation="[0,0]" />
<timer id="playbackTimer" repeat="true" duration="30" />
<timer id="bufferCheckTimer" repeat="true" />
<OSD id="osd" visible="false" inactiveTimeout="5" />