Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add invidious support #45

Merged
merged 1 commit into from
May 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
This is an **unofficial** port of the [SponsorBlock](https://sponsor.ajay.app/) browser extension.
It works as an extension to the [YouTube Plugin](https://github.com/anxdpanic/plugin.video.youtube).

Once installed, the add-on will automatically skip sponsor segments in all YouTube videos you watch.
Once installed, the add-on will automatically skip sponsor segments in all YouTube and Invidious videos you watch.

For a detailed explanation of how SponsorSkip works please visit the [offical website](https://sponsor.ajay.app/).
For a detailed explanation of how SponsorBlock works please visit the [offical website](https://sponsor.ajay.app/).

## Installation

Expand Down
10 changes: 6 additions & 4 deletions addon.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.service.sponsorblock" version="0.4.0" name="SponsorBlock" provider-name="siku2">
<addon id="script.service.sponsorblock" version="0.5.0" name="SponsorBlock" provider-name="siku2">
<requires>
<import addon="script.module.requests" version="2.15.1"/>
<import addon="script.module.six" version="1.13.0"/>
<import addon="plugin.video.youtube" version="6.7.0"/>
<import addon="plugin.video.youtube" version="6.7.0" optional="true"/>
<import addon="plugin.video.invidious" version="2.1.1" optional="true"/>
</requires>

<extension point="xbmc.service" library="service.py"/>
Expand All @@ -12,15 +13,15 @@
<menu id="kodi.core.main">
<item library="context.py">
<label>32027</label>
<visible>String.IsEqual(ListItem.Property(Addon.ID),plugin.video.youtube)</visible>
<visible>String.IsEqual(ListItem.Property(Addon.ID),plugin.video.youtube) | String.IsEqual(ListItem.Property(Addon.ID),plugin.video.invidious)</visible>
</item>
</menu>
</extension>

<extension point="xbmc.addon.metadata">
<summary lang="en_GB">Skip YouTube in-video sponsors</summary>
<description lang="en_GB">
SponsorBlock browser extension ported to Kodi's YouTube plugin.
SponsorBlock browser extension ported to Kodi's YouTube and Invidious plugins.

SponsorBlock is a crowdsourced project to skip sponsor segments in YouTube videos.
Users submit when a sponsor happens and the add-on automatically skips sponsors it knows about.
Expand All @@ -31,6 +32,7 @@ Users submit when a sponsor happens and the add-on automatically skips sponsors
- Add option to reduce all skips by some time (#30)
- Ignore segments that do not fit into reduce skips setting (#31)
- Reset existing playback when new video starts playing (#32)
- Support for Kodi's Invidious plugin
</news>

<summary lang="de">Überspringe Sponsoren, betteln um Abonnenten und mehr in YouTube Videos</summary>
Expand Down
2 changes: 1 addition & 1 deletion resources/lib/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def __handle_playback_init(self, data):
self._player_listener.preload_segments(video_id)

def onNotification(self, sender, method, data): # type: (str, str, str) -> None
if sender != youtube_api.ADDON_ID:
if sender not in youtube_api.ADDON_IDS:
return

try:
Expand Down
4 changes: 2 additions & 2 deletions resources/lib/player_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def _sanity_check_segments(segments): # type: (Iterable[SponsorSegment]) -> boo

def get_sponsor_segments(
api, video_id
): # type: (SponsorBlockAPI, str) -> Optional[List[SponsorSegment]]
): # type: (SponsorBlockAPI, str) -> Optional[list[SponsorSegment]]
SethFalco marked this conversation as resolved.
Show resolved Hide resolved
try:
segments = api.get_skip_segments(video_id)
except NotFound:
Expand Down Expand Up @@ -80,7 +80,7 @@ def __init__(self, *args, **kwargs):
self._load_segment_lock = threading.Lock()
self._ignore_next_video_id = None
self._segments_video_id = None
self._segments = [] # List[SponsorSegment]
self._segments = [] # list[SponsorSegment]
self._next_segment = None # type: Optional[SponsorSegment]

# set by `onPlaybackStarted` and then read (/ reset) by `onAVStarted`
Expand Down
45 changes: 33 additions & 12 deletions resources/lib/youtube_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Module for accessing data from the youtube plugin."""
"""Module for accessing data from the YouTube plugin."""

import itertools
import json
Expand All @@ -12,7 +12,26 @@

_logger = logging.getLogger(__name__)

ADDON_ID = "plugin.video.youtube"
URI_FILTERS = {
"plugin.video.youtube": {
"scheme": "plugin",
"pathPrefix": "/play",
"query": "video_id"
},
"plugin.video.invidious": {
"scheme": "plugin",
"pathPrefix": "/",
"query": "videoId"
}
}
"""
Mapping of addon ID to URI filter to match respective supported videos.

Based on: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/events/UrlFilter
"""

ADDON_IDS = URI_FILTERS.keys()


NOTIFICATION_PLAYBACK_INIT = "Other.PlaybackInit"

Expand Down Expand Up @@ -63,11 +82,14 @@ def _video_id_from_art(art, has_context): # type: (dict, bool) -> Optional[str]
return parts[2]


# unique ids that explicitly identify a youtube video.
_EXPLICIT_UIDS = ("youtubeid", "youtube_id")
# unique ids that require context
"""
unique ids that explicitly identify a youtube video.
"""
_CONTEXT_UIDS = ("videoid", "video_id")

"""
unique ids that require context
"""

def _video_id_from_ids(unique_ids, has_context): # type: (dict, bool) -> Optional[str]
if has_context:
Expand Down Expand Up @@ -127,9 +149,6 @@ def get_playing_file_path(): # type: () -> str
return xbmc.getInfoLabel(VAR_PLAYER_FILE_AND_PATH)


SCHEME_PLUGIN = "plugin"
PATH_PLAY = "/play"

DOMAIN_GOOGLEVIDEO = "googlevideo.com"


Expand All @@ -147,14 +166,16 @@ def get_video_id(): # type: () -> Option[str]
except Exception:
return None

addon_id = path_url.netloc
uri_filter = URI_FILTERS[addon_id]

valid_url = (
path_url.scheme == SCHEME_PLUGIN
and path_url.netloc == ADDON_ID
and path_url.path.startswith(PATH_PLAY)
path_url.scheme == uri_filter["scheme"]
and path_url.path.startswith(uri_filter["pathPrefix"])
)
if valid_url:
query = urlparse.parse_qs(path_url.query)
return query.get("video_id")
return query.get(uri_filter["query"])

# has_context denotes whether the current video seems to be a youtube video
# being played outside of the YouTube add-on.
Expand Down