Skip to content

Commit

Permalink
feat: UpNext integration with skip credits button
Browse files Browse the repository at this point in the history
_compute_when_episode_ends was moved to videostream.py since it not need the player context to work and only need to expose data about video stream.
'best' upnext_mode was renamed to 'credits' (because 'best' does not mean anything).
translations was updated with easier to understand wording.
  • Loading branch information
lumiru authored and lumiru committed May 15, 2024
1 parent c1aef09 commit cf251d8
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 73 deletions.
12 changes: 6 additions & 6 deletions resources/language/resource.language.de_de/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -277,21 +277,21 @@ msgid "UpNext fixed or unavailable end detection duration"
msgstr "Vorlaufzeit für UpNext wenn \"Statisch\" oder keine Credits/Vorschau"

msgctxt "#30090"
msgid "UpNext integration"
msgid "Show UpNext dialog at"
msgstr "UpNext Integration"

msgctxt "#30091"
msgid "At credits start, if nothing after"
msgid "credits start"
msgstr "Zu Beginn der Credits, wenn danach keine Vorschau kommt"

msgctxt "#30092"
msgid "At preview start"
msgid "preview start"
msgstr "Zu Beginn der Vorschau"

msgctxt "#30093"
msgid "Fixed"
msgid "fixed time before the end"
msgstr "Statisch"

msgctxt "#30094"
msgid "Disabled"
msgstr "Deaktiviert"
msgid "never (disabled)"
msgstr "Deaktiviert"
12 changes: 6 additions & 6 deletions resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -279,21 +279,21 @@ msgid "UpNext fixed or unavailable end detection duration"
msgstr ""

msgctxt "#30090"
msgid "UpNext integration"
msgid "Show UpNext dialog at"
msgstr ""

msgctxt "#30091"
msgid "At credits start, if nothing after"
msgid "credits start"
msgstr ""

msgctxt "#30092"
msgid "At preview start"
msgid "preview start"
msgstr ""

msgctxt "#30093"
msgid "Fixed"
msgid "fixed time before the end"
msgstr ""

msgctxt "#30094"
msgid "Disabled"
msgstr ""
msgid "never (disabled)"
msgstr ""
10 changes: 5 additions & 5 deletions resources/language/resource.language.es_es/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -279,21 +279,21 @@ msgid "UpNext fixed or unavailable end detection duration"
msgstr ""

msgctxt "#30090"
msgid "UpNext integration"
msgid "Show UpNext dialog at"
msgstr ""

msgctxt "#30091"
msgid "At credits start, if nothing after"
msgid "credits start"
msgstr ""

msgctxt "#30092"
msgid "At preview start"
msgid "preview start"
msgstr ""

msgctxt "#30093"
msgid "Fixed"
msgid "fixed time before the end"
msgstr ""

msgctxt "#30094"
msgid "Disabled"
msgid "never (disabled)"
msgstr ""
20 changes: 10 additions & 10 deletions resources/language/resource.language.fr_fr/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -275,21 +275,21 @@ msgid "UpNext fixed or unavailable end detection duration"
msgstr "Durée fixe ou de détection indisponible pour UpNext"

msgctxt "#30090"
msgid "UpNext integration"
msgstr "Intégration à UpNext"
msgid "Show UpNext dialog at"
msgstr "Afficher la fenêtre UpNext quand"

msgctxt "#30091"
msgid "At credits start, if nothing after"
msgstr "Dès le générique, s'il n'y a rien après"
msgid "credits start"
msgstr "le générique commence"

msgctxt "#30092"
msgid "At preview start"
msgstr "Au début de la bande-annonce du suivant"
msgid "preview start"
msgstr "la preview commence"

msgctxt "#30093"
msgid "Fixed"
msgstr "Fixé"
msgid "fixed time before the end"
msgstr "une durée fixe avant la fin"

msgctxt "#30094"
msgid "Disabled"
msgstr "Désactivé"
msgid "never (disabled)"
msgstr "jamais (désactivé)"
12 changes: 6 additions & 6 deletions resources/language/resource.language.pt_br/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -276,21 +276,21 @@ msgid "UpNext fixed or unavailable end detection duration"
msgstr ""

msgctxt "#30090"
msgid "UpNext integration"
msgid "Show UpNext dialog at"
msgstr ""

msgctxt "#30091"
msgid "At credits start, if nothing after"
msgid "credits start"
msgstr ""

msgctxt "#30092"
msgid "At preview start"
msgid "preview start"
msgstr ""

msgctxt "#30093"
msgid "Fixed"
msgid "fixed time before the end"
msgstr ""

msgctxt "#30094"
msgid "Disabled"
msgstr ""
msgid "never (disabled)"
msgstr ""
47 changes: 10 additions & 37 deletions resources/lib/videoplayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,10 @@ def _handle_upnext(self):
},
"video_episode_play"
)
show_next_at_seconds = self._compute_when_episode_ends()
show_next_at_seconds = self._stream_data.end_timecode
if show_next_at_seconds is not None:
# Needs to wait 1s, otherwise, upnext will show next dialog at episode start...
xbmc.sleep(1000)
# Needs to wait 10s, otherwise, upnext will show next dialog at episode start...
xbmc.sleep(10000)
utils.crunchy_log("_handle_upnext: Next URL (shown at %ds / %ds): %s" % (
show_next_at_seconds,
self._stream_data.playable_item.duration,
Expand All @@ -247,37 +247,6 @@ def _handle_upnext(self):
except Exception:
utils.crunchy_log("_handle_upnext: Cannot send upnext notification", xbmc.LOGERROR)

def _compute_when_episode_ends(self) -> Optional[int]:
upnext_mode = G.args.addon.getSetting("upnext_mode")
if upnext_mode == "disabled":
return None

video_end = self._stream_data.playable_item.duration
fixed_duration = int(G.args.addon.getSetting("upnext_fixed_duration"), 10)
result = video_end - fixed_duration

skip_events_data = self._stream_data.unmodified_skip_events_data
if upnext_mode == "fixed" or not skip_events_data or (not skip_events_data.get("credits") and not skip_events_data.get("preview")):
return result

credits_start = skip_events_data.get("credits", {}).get("start")
credits_end = skip_events_data.get("credits", {}).get("end")
preview_start = skip_events_data.get("preview", {}).get("start")
preview_end = skip_events_data.get("preview", {}).get("end")
# If there are outro and preview
# and if the outro ends when the preview start
if upnext_mode == "best" and credits_start and credits_end and preview_start and credits_end + 3 > preview_start:
result = credits_start
# If there is a preview
elif preview_start:
result = preview_start
# If there is outro without preview
# and if the outro ends in the last 20 seconds video
elif upnext_mode == "best" and credits_start and credits_end and video_end <= credits_end + 20:
result = credits_start

return result

def thread_update_playhead(self):
""" background thread to update playback with crunchyroll in intervals """

Expand Down Expand Up @@ -339,13 +308,17 @@ def _check_and_filter_skip_data(self) -> bool:
if not self._stream_data.skip_events_data:
return False

# never skip preview (fetched for upnext)
if self._stream_data.skip_events_data.get('preview'):
self._stream_data.skip_events_data.pop('preview', None)

# if not enabled in config, remove from our list
if G.args.addon.getSetting("enable_skip_intro") != "true" and self._stream_data.skip_events_data.get(
if G.args.addon.getSetting('enable_skip_intro') != 'true' and self._stream_data.skip_events_data.get(
'intro'):
self._stream_data.skip_events_data.pop('intro', None)

if G.args.addon.getSetting("enable_skip_credits") != "true" and self._stream_data.skip_events_data.get(
'credits'):
if (G.args.addon.getSetting('enable_skip_credits') != 'true' or self._stream_data.end_marker == 'credits') and (
self._stream_data.skip_events_data.get('credits') ):
self._stream_data.skip_events_data.pop('credits', None)

return len(self._stream_data.skip_events_data) > 0
Expand Down
74 changes: 72 additions & 2 deletions resources/lib/videostream.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ def __init__(self):
self.stream_url: str | None = None
self.subtitle_urls: list[str] | None = None
self.skip_events_data: Dict = {}
self.unmodified_skip_events_data: Dict = {}
self.playheads_data: Dict = {}
# PlayableItem which is about to be played, that contains cms object data
self.playable_item: PlayableItem | None = None
# PlayableItem which contains cms obj data of playable_item's parent, if exists (Episodes, not Movies). currently not used.
self.playable_item_parent: PlayableItem | None = None
self.token: str | None = None
self.next_playable_item: PlayableItem | None = None
self.end_marker: str = "off"
self.end_timecode: int | None = None


class VideoStream(Object):
Expand Down Expand Up @@ -94,6 +95,11 @@ def get_player_stream_data(self) -> Optional[VideoPlayerStreamData]:
video_player_stream_data.playable_item_parent = async_data.get('playable_item_parent')
video_player_stream_data.next_playable_item = async_data.get('next_playable_item')

video_end = self._compute_when_episode_ends(video_player_stream_data)

video_player_stream_data.end_marker = video_end.get('marker')
video_player_stream_data.end_timecode = video_end.get('timecode')

return video_player_stream_data

async def _gather_async_data(self) -> Dict[str, Any]:
Expand Down Expand Up @@ -355,7 +361,7 @@ async def _get_skip_events(episode_id) -> Optional[Dict]:
return None

# prepare the data a bit
supported_skips = ['intro', 'credits']
supported_skips = ['intro', 'credits', 'preview']
prepared = dict()
for skip_type in supported_skips:
if req.get(skip_type) and req.get(skip_type).get('start') is not None and req.get(skip_type).get(
Expand Down Expand Up @@ -392,3 +398,67 @@ async def _get_upnext_episode(id: str) -> Optional[Dict]:
return None

return req.get("data")[0]

@staticmethod
def _compute_when_episode_ends(partial_stream_data: VideoPlayerStreamData) -> Dict[str, Any]:
""" Extract timecode for video end from skip_events_data.
Extracted timecode depends on *upnext_mode* user setting and available skip events data.
upnext_mode can hold 4 different behaviour.
- "disabled", so no need to compute anything.
- "fixed", so we should send the timecode for the last 15s (user can change this duration by *upnext_fixed_duration* settings).
- "preview", which means we have to retrieve preview timecode from skip event API.
If preview timecode is not available, go back to the same behaviour as "fixed" mode.
- "credits", which means we have to retrieve credits and preview timecode from skip event API.
If credits timecode is not available, go back to the same behaviour as "preview" mode.
Additionaly, we have to check there is no additional scenes after credits,
so we check if preview starts at credits end. Otherwise, video end timecode will be the preview start timecode.
"""

result = {
'marker': 'off',
'timecode': None
}
upnext_mode = G.args.addon.getSetting('upnext_mode')
if upnext_mode == 'disabled' or not partial_stream_data.next_playable_item:
return result

video_end = partial_stream_data.playable_item.duration
fixed_duration = int(G.args.addon.getSetting('upnext_fixed_duration'), 10)
# Standard behaviour is to show upnext 15s before the end of the video
result = {
'marker': 'fixed',
'timecode': video_end - fixed_duration
}

skip_events_data = partial_stream_data.skip_events_data
# If upnext selected mode is fixed or there is no available skip data
if upnext_mode == 'fixed' or not skip_events_data or (not skip_events_data.get('credits') and not skip_events_data.get('preview')):
return result

# Extract skip data
credits_start = skip_events_data.get('credits', {}).get('start')
credits_end = skip_events_data.get('credits', {}).get('end')
preview_start = skip_events_data.get('preview', {}).get('start')
preview_end = skip_events_data.get('preview', {}).get('end')

# If there is no data about preview but credits ends less than 20s before the end, consider time after credits_end is the preview
if not preview_start and credits_end and credits_end >= video_end - 20:
preview_start = credits_end
preview_end = video_end

# If there are outro and preview
# and if the outro ends when the preview start
if upnext_mode == 'credits' and credits_start and credits_end and preview_start and credits_end + 3 > preview_start:
result = {
'marker': 'credits',
'timecode': credits_start
}
# If there is a preview
elif preview_start:
result = {
'marker': 'preview',
'timecode': preview_start
}

return result
2 changes: 1 addition & 1 deletion resources/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
<default>disabled</default>
<constraints>
<options>
<option label="30091">best</option>
<option label="30091">credits</option>
<option label="30092">preview</option>
<option label="30093">fixed</option>
<option label="30094">disabled</option>
Expand Down

0 comments on commit cf251d8

Please sign in to comment.