Repository navigation
feat: optional SponsorBlock support for YouTube videos - #200
Conversation
A background service seeks over the SponsorBlock segments of YouTube videos resolved by SendToKodi. The plugin publishes the video id of the resolved item; the service fetches the segments by hash prefix when playback starts, so resolving is not delayed and an unreachable API only disables the skipping. Off by default, with per-category settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JQPrLGgU3iCkqFiS1ZoLYq
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #200 +/- ##
==========================================
+ Coverage 84.65% 85.45% +0.79%
==========================================
Files 14 16 +2
Lines 2601 2778 +177
==========================================
+ Hits 2202 2374 +172
- Misses 399 404 +5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Drop the fetched segments when another item started during the fetch, instead of seeking the new item with them, and keep the service loop alive when a Kodi player call fails because playback just stopped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JQPrLGgU3iCkqFiS1ZoLYq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JQPrLGgU3iCkqFiS1ZoLYq
|
Thanks a lot!
Why though? Is not that more complicated? |
|
Fair question! The main reason: yt-dlp's SponsorBlock support is built for downloads: it cuts the segments out of the finished file with ffmpeg. SendToKodi streams, so there's no file to cut. Kodi has to seek over the segments, which means we need the segment times ourselves anyway. Fetching them ourselves also has some upsides:
The extra code is small: about 170 lines in Generated by Claude Code |
Closes #198
What
Optional SponsorBlock support: while a YouTube video resolved by SendToKodi plays, sponsor segments (and other selectable categories) are skipped. Off by default.
How
yt-dlp's
--sponsorblock-removecuts segments out of a downloaded file with ffmpeg. SendToKodi streams, so that doesn't apply. Kodi seeks over the segments instead:core/runtime/playback.py): for YouTube results,create_list_item_from_videostores the video id as a ListItem property (sendtokodi.youtube_id). It also writes the id together with the final stream path to a home-window property, because not every Kodi version passes ListItem properties on to the playing item. This covers single videos and playlist entries, because both go through this function.sponsorblock_service.py(xbmc.serviceextension inaddon.xml): ononAVStartedit identifies the video (item property first, otherwise the window property if its path matches the playing file, ignoring|headers). It then fetches the segments and pollsgetTime()every 0.25 s, callingseekTime(end)when playback enters a segment. Each segment is skipped once, so seeking back into one plays it.core/sponsorblock.py: settings, API URL by hash prefix (only 4 hex characters ofsha256(video_id)go to the server), response parsing (onlyactionType: skipand the selected categories), merging of adjacent segments, and the skip decision.Why we call the API ourselves instead of using yt-dlp's
SponsorBlockPP:extract_info, a failing API (after 3 retries) would break playback.SponsorBlockPPinherits fromFFmpegPostProcessorand probes for ffmpeg/ffprobe on every init.Settings (new "SponsorBlock" category)
Docs: README feature list,
docs/README.mdindex,settings.md,faq.md(YouTube without ads, privacy section),how-it-works.md.Testing
pytest: 380 passed. The newtests/test_sponsorblock.pycovers settings, video-id matching, URL/hash prefix, parsing/merging, 404/error handling and the skipper. It also covers the service against fake Kodi modules: skip, notification, disabled, foreign playback, API failure, an item change during the fetch, a failing player call, the loop and the reset.core/sponsorblock.pyhas 100 % coverage,sponsorblock_service.py96 %.settings.xml/strings.poand the path matching against Kodi'sPlayer.cpp. It found two races in the service loop, both fixed in 21d27d1 with regression tests.getPlayingItem()is not available and the window-property fallback is used).🤖 Generated with Claude Code
https://claude.ai/code/session_01JQPrLGgU3iCkqFiS1ZoLYq