diff --git a/resources/language/resource.language.de_de/strings.po b/resources/language/resource.language.de_de/strings.po
index 2e05c82..bd01e67 100644
--- a/resources/language/resource.language.de_de/strings.po
+++ b/resources/language/resource.language.de_de/strings.po
@@ -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"
\ No newline at end of file
+msgid "never (disabled)"
+msgstr "Deaktiviert"
diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po
index 40cde65..9d7d005 100644
--- a/resources/language/resource.language.en_gb/strings.po
+++ b/resources/language/resource.language.en_gb/strings.po
@@ -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 ""
\ No newline at end of file
+msgid "never (disabled)"
+msgstr ""
diff --git a/resources/language/resource.language.es_es/strings.po b/resources/language/resource.language.es_es/strings.po
index bbeae48..086ca6f 100644
--- a/resources/language/resource.language.es_es/strings.po
+++ b/resources/language/resource.language.es_es/strings.po
@@ -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 ""
diff --git a/resources/language/resource.language.fr_fr/strings.po b/resources/language/resource.language.fr_fr/strings.po
index be8b094..dd82e30 100644
--- a/resources/language/resource.language.fr_fr/strings.po
+++ b/resources/language/resource.language.fr_fr/strings.po
@@ -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é)"
diff --git a/resources/language/resource.language.pt_br/strings.po b/resources/language/resource.language.pt_br/strings.po
index 47101db..7dde13b 100644
--- a/resources/language/resource.language.pt_br/strings.po
+++ b/resources/language/resource.language.pt_br/strings.po
@@ -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 ""
\ No newline at end of file
+msgid "never (disabled)"
+msgstr ""
diff --git a/resources/lib/videoplayer.py b/resources/lib/videoplayer.py
index 8d2905e..ccab104 100644
--- a/resources/lib/videoplayer.py
+++ b/resources/lib/videoplayer.py
@@ -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,
@@ -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 """
@@ -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
diff --git a/resources/lib/videostream.py b/resources/lib/videostream.py
index 38ec4b2..b45dec2 100644
--- a/resources/lib/videostream.py
+++ b/resources/lib/videostream.py
@@ -39,7 +39,6 @@ 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
@@ -47,6 +46,8 @@ def __init__(self):
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):
@@ -88,12 +89,16 @@ def get_player_stream_data(self) -> Optional[VideoPlayerStreamData]:
video_player_stream_data.token = async_data.get('stream_data').get('token')
video_player_stream_data.skip_events_data = async_data.get('skip_events_data')
- video_player_stream_data.unmodified_skip_events_data = dict(async_data.get('skip_events_data'))
video_player_stream_data.playheads_data = async_data.get('playheads_data')
video_player_stream_data.playable_item = async_data.get('playable_item')
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]:
@@ -355,7 +360,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(
@@ -392,3 +397,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
diff --git a/resources/settings.xml b/resources/settings.xml
index 49b70ec..430c29c 100644
--- a/resources/settings.xml
+++ b/resources/settings.xml
@@ -132,7 +132,7 @@
disabled
-
+