Skip to content

Commit

Permalink
Sponsorblock progressbar (#18)
Browse files Browse the repository at this point in the history
* Generate sponsorblock bar

* Sponsorblock chapter label

* Add comment

* Fix typo

* Changelog
  • Loading branch information
iBicha authored Dec 1, 2022
1 parent 2159300 commit 64cf7b5
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Version check in settings page
- Support for web request body parsing
- Error dialog for video load fail
- SponsorBlock sections and category info

## [0.4.0] - 2022-11-24
### Added
Expand Down
9 changes: 8 additions & 1 deletion src/components/VideoPlayer/SponsorBlockTask.bs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ function TaskMain()
videoId = input.videoId

skipSegments = SponsorBlock.GetSkipSegmentsForVideo(videoId)
barPath = invalid

if skipSegments <> invalid
barPath = `tmp:/sponsorblock_bar_${videoId}.png`
SponsorBlock.GenerateProgressBarBackground(skipSegments, barPath)
end if

m.top.setField("output", {
videoId: videoId,
skipSegments: skipSegments
skipSegments: skipSegments,
barPath: barPath
})
end function

6 changes: 6 additions & 0 deletions src/components/VideoPlayer/Video.bs
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,15 @@ function StartSponsorBlockTask(videoId as string)
}
}).then(function(task as object)
skipSegments = task.output.skipSegments
barPath = task.output.barPath
if skipSegments <> invalid
m.videoPlayer.addFields({ skipSegments: skipSegments })
m.videoPlayer.seekMode = "accurate"
if barPath <> invalid
m.videoPlayer.trickPlayBar.trackImageUri = barPath
m.videoPlayer.trickPlayBar.filledBarBlendColor = "0xFF000080"
end if

m.videoPlayer.ObserveField("position", "OnPositionChangeSkipSponsorBlockSections")
end if
end function)
Expand Down
78 changes: 78 additions & 0 deletions src/components/VideoPlayer/VideoPlayer.bs
Original file line number Diff line number Diff line change
@@ -1,4 +1,82 @@
import "pkg:/source/utils/TimeUtils.bs"
import "pkg:/source/services/SponsorBlock.bs"

function Init()
SetChapterLabel()
m.timeLabel = FindTimeLabel()
m.top.trickPlayBar.observeField("visible", "OnTrickPlayBarVisible")
m.chapterLabelTimer = m.top.findNode("chapterLabelTimer")
m.chapterLabelTimer.observeField("fire", "OnChapterLabelTimer")
end function

' This is a hack to access the position of the trickplay
function FindTimeLabel() as object
children = m.top.trickPlayBar.getChildren(m.top.trickPlayBar.getChildCount(), 0)
label = invalid
for each child in children
' It's very unfortunate to have to find a label with 0:00 text,
' and that's on the left side of the screen. This might need completely custom trickPlayBar
if child.isSubtype("Label") and child.text = "0:00"
if label = invalid
label = child
else
if child.translation[0] < label.translation[0]
label = child
end if
end if
end if
end for
return label
end function

function OnTrickPlayBarVisible()
if m.top.trickPlayBar.visible
m.top.chapter = ""
m.chapterLabelTimer.control = "start"
else
m.chapterLabelTimer.control = "stop"
end if
end function

function OnChapterLabelTimer() as void
if m.timeLabel = invalid or m.top.skipSegments = invalid
return
end if

if m.sponsorBlockLastTime = m.timeLabel.text
return
end if
m.sponsorBlockLastTime = m.timeLabel.text

time = TimeUtils.ParseTime(m.timeLabel.text)
UpdateSponsorBlockChapter(time)
end function

function UpdateSponsorBlockChapter(time as integer) as void
segments = m.top.skipSegments
for each segment in segments
segmentRange = segment["segment"]
segmentStart = segmentRange[0]
segmentEnd = segmentRange[1]

if (segmentStart <= time) and (segmentEnd >= time)
m.top.chapter = SponsorBlock.SegmentTitle(segment["category"])
return
end if
end for
m.top.chapter = ""
end function

function SetChapterLabel()
m.chapterLabel = m.top.findNode("chapterLabel")
m.chapterLabel.reparent(m.top.trickPlayBar, false)
trickPlayBarWidth = m.top.trickPlayBar.boundingRect().width
#if DASH_THUMBNAILS
yPos = 25
#else
yPos = 55
#end if
m.chapterLabel.translation = [trickPlayBarWidth / 2 - m.chapterLabel.width / 2, yPos]
end function

function OnkeyEvent(key as string, press as boolean) as boolean
Expand Down
16 changes: 16 additions & 0 deletions src/components/VideoPlayer/VideoPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,21 @@
<component name="VideoPlayer" extends="Video">
<interface>
<field id="sender" type="node" />
<field id="chapter" type="string" alias="chapterLabel.text" />
</interface>
<children>
<Label
id="chapterLabel"
width="350"
height="25"
horizAlign="center"
vertAlign="center"
font="font:SmallestSystemFont"
/>
<Timer
id="chapterLabelTimer"
repeat="true"
duration="0.25"
/>
</children>
</component>
67 changes: 67 additions & 0 deletions src/source/services/SponsorBlock.bs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,52 @@ namespace SponsorBlock
const API_URL = "https://sponsor.ajay.app"
const SKIP_SEGMENT_ENDPOINT = "/api/skipSegments"

function SegmentColor(category as string) as integer
map = m.SponsorBlockColors
if map = invalid
map = {
sponsor: &h00D400FF,
selfpromo: &hFFFF00FF,
exclusive_access: &h008A5CFF,
interaction: &hCC00FFFF,
poi_highlight: &hFF1684FF,
intro: &h00FFFFFF,
outro: &h0202EDFF,
preview: &h008FD6FF,
filler: &h7300FFFF,
music_offtopic: &hFF9900FF
}
m.SponsorBlockColors = map
end if
if map.doesexist(category)
return map[category]
end if
return 0
end function

function SegmentTitle(category as string) as string
map = m.SponsorBlockTitles
if map = invalid
map = {
sponsor: "Sponsor",
selfpromo: "Unpaid/Self Promotion",
exclusive_access: "Exclusive Access",
interaction: "Interaction Reminder (Subscribe)",
poi_highlight: "Highlight",
intro: "Intermission/Intro Animation",
outro: "Endcards/Credits",
preview: "Preview/Recap",
filler: "Filler Tangent/Jokes",
music_offtopic: "Non-Music Section"
}
m.SponsorBlockTitles = map
end if
if map.doesexist(category)
return map[category]
end if
return ""
end function

function GetSkipSegmentsForVideo(videoId as string) as object
categories = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "poi_highlight", "chapter", "filler", "exclusive_access"]
actionTypes = ["skip", "mute", "chapter", "full", "poi"]
Expand All @@ -29,4 +75,25 @@ namespace SponsorBlock
return invalid
end function

function GenerateProgressBarBackground(segments as object, path as string)
bar = CreateObject("roBitmap", { width: 1000, height: 20, AlphaEnable: true })
bar.Clear(&hFFFFFF80)
width = bar.GetWidth()
height = bar.GetHeight()
for each segment in segments
pixelStart = (segment.segment[0] / segment.videoDuration) * width
pixelEnd = (segment.segment[1] / segment.videoDuration) * width
color = SponsorBlock.SegmentColor(segment.category)
' highlight's duration is zero, so it is not visble on the bar.
' Add a couple of pixels to see it
if pixelStart = pixelEnd
pixelEnd += 2
end if
bar.DrawRect(pixelStart, 0, pixelEnd - pixelStart, height, color)
end for
bar.Finish()
buffer = bar.GetPng(0, 0, width, height)
buffer.WriteFile(path)
end function

end namespace
16 changes: 16 additions & 0 deletions src/source/utils/TimeUtils.bs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,20 @@ namespace TimeUtils
return validstr(ma[mNum - 1])
end function

function ParseTime(time as string) as integer
hours = 0
minutes = 0
seconds = 0
tokens = time.Tokenize(":")
if tokens.Count() = 3
hours = tokens[0].ToInt()
minutes = tokens[1].ToInt()
seconds = tokens[2].ToInt()
else if tokens.Count() = 2
minutes = tokens[0].ToInt()
seconds = tokens[1].ToInt()
end if
return hours * 3600 + minutes * 60 + seconds
end function

end namespace

0 comments on commit 64cf7b5

Please sign in to comment.