Skip to content
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
111 changes: 39 additions & 72 deletions resources/lib/intro_skipper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
from resources.lib.utils import seconds_to_ticks, ticks_to_seconds, translate_path
from resources.lib.intro_skipper_utils import get_setting_skip_action, set_correct_skip_info


from .lazylogger import LazyLogger
from .dialogs import SkipDialog

from typing import Literal
from .play_utils import get_playing_data

log = LazyLogger(__name__)

Expand All @@ -33,125 +31,94 @@ def __init__(self, play_monitor):

def run(self):

from .play_utils import get_jellyfin_playing_item
settings = xbmcaddon.Addon()
plugin_path = settings.getAddonInfo('path')
plugin_path_real = translate_path(os.path.join(plugin_path))

skip_intro_dialog = None
skip_credit_dialog = None
skip_commercial_dialog = None
skip_preview_dialog = None
skip_recap_dialog = None


skip_dialog = {}

segments = None
playing_item_id = None

log.debug("SkipService: starting service")

while not xbmc.Monitor().abortRequested() and not self.stop_thread:
player = xbmc.Player()
if player.isPlaying():
item_id = get_jellyfin_playing_item()
play_data = get_playing_data()
item_id = play_data.get("item_id", None)
if item_id is not None:
log.debug("SkipService: playing item is from jellyfin : {0}".format(item_id))


log.debug("SkipService: segments : {0}".format(segments))
# If item id has changed or is new, retrieve segments
if playing_item_id is None or playing_item_id != item_id :
if playing_item_id is None or playing_item_id != item_id:
log.debug("SkipService: item is new, retrieving media segments : {0}".format(item_id))
segments = get_media_segments(item_id)

# Setting global playing item to current playing item
playing_item_id = item_id

# Handle skip only on jellyfin items
current_ticks = seconds_to_ticks(player.getTime())

# Handle Intros
skip_intro_dialog = self.handle_dialog(plugin_path_real, skip_intro_dialog, item_id, current_ticks, player, segments, "Intro")
# Handle Credits
skip_credit_dialog = self.handle_dialog(plugin_path_real, skip_credit_dialog, item_id, current_ticks, player, segments, "Outro")
# Handle commercial
skip_commercial_dialog = self.handle_dialog(plugin_path_real, skip_commercial_dialog, item_id, current_ticks, player, segments, "Commercial")
# Handle preview
skip_preview_dialog = self.handle_dialog(plugin_path_real, skip_preview_dialog, item_id, current_ticks, player, segments, "Preview")
# Handle recap
skip_recap_dialog = self.handle_dialog(plugin_path_real, skip_recap_dialog, item_id, current_ticks, player, segments, "Recap")

current_ticks = seconds_to_ticks(play_data.get("current_position"))

for segment in segments:
segment_type = segment.get("Type")
dialog = skip_dialog.get(segment_type, None)
log.debug("SkipService: dialog is: {}".format(dialog))
skip_dialog[segment_type] = self.handle_dialog(plugin_path_real, dialog, item_id, current_ticks, player, segment)

else:
playing_item_id = None
if skip_intro_dialog is not None:
log.debug("SkipService: Playback stopped, killing Intro dialog")
skip_intro_dialog.close()
skip_intro_dialog = None

if skip_credit_dialog is not None:
log.debug("SkipService: Playback stopped, killing Outro dialog")
skip_credit_dialog.close()
skip_credit_dialog = None

if skip_commercial_dialog is not None:
log.debug("SkipService: Playback stopped, killing Commercial dialog")
skip_commercial_dialog.close()
skip_commercial_dialog = None

if skip_preview_dialog is not None:
log.debug("SkipService: Playback stopped, killing Preview dialog")
skip_preview_dialog.close()
skip_preview_dialog = None

if skip_recap_dialog is not None:
log.debug("SkipService: Playback stopped, killing Recap dialog")
skip_recap_dialog.close()
skip_recap_dialog = None
segments = None
skip_dialog = {}
log.debug("SkipService: Playback stopped, setting variables to None")

if xbmc.Monitor().waitForAbort(1):
break

xbmc.sleep(200)


def handle_dialog(self, plugin_path_real: str, dialog: SkipDialog, item_id: str, current_ticks: float, player: xbmc.Player, segments, type: Literal["Commercial", "Preview", "Recap", "Outro", "Intro"]):
skip_action = get_setting_skip_action(type)
def handle_dialog(self, plugin_path_real: str, dialog: SkipDialog, item_id: str, current_ticks: float, player: xbmc.Player, segment: dict):
segment_type = segment.get("Type")
skip_action = get_setting_skip_action(segment_type)

# In case do nothing is selected return
if skip_action == "2":
log.debug("SkipService: ignore {0} is selected".format(type))
return None

if dialog is None:
log.debug("SkipService: init dialog")
dialog = SkipDialog("SkipDialog.xml", plugin_path_real, "default", "720p")
set_correct_skip_info(item_id, dialog, segments, type)

set_correct_skip_info(item_id, dialog, segment)

is_segment = False
if dialog.start is not None and dialog.end is not None:
# Resets the dismiss var so that button can reappear in case of navigation in the timecodes
if (current_ticks < dialog.start or current_ticks > dialog.end) and dialog.has_been_dissmissed is True:
log.debug("SkipService: {0} skip was dismissed. It is reset beacause timecode is outside of segment")
dialog.has_been_dissmissed = False

# Checks if segment is playing
is_segment = current_ticks >= dialog.start and current_ticks <= dialog.end

if skip_action == "1" and is_segment:
log.debug("SkipService: {0} is set to automatic skip, skipping segment".format(type))
log.debug("SkipService: {0} is set to automatic skip, skipping segment".format(segment_type))
# If auto skip is enabled, skips to semgent ends automatically
player.seekTime(ticks_to_seconds(dialog.end))
xbmcgui.Dialog().notification("JellyCon", "{0} Skipped".format(type))
xbmcgui.Dialog().notification("JellyCon", "{0} Skipped".format(segment_type))
elif skip_action == "0":
# Otherwise show skip dialog
if is_segment and not dialog.has_been_dissmissed:
log.debug("SkipService: {0} is playing, showing dialog".format(type))
log.debug("SkipService: {0} is playing, showing dialog".format(segment_type))
dialog.show()
else:
# Could not find doc on what happens when closing a closed dialog, but it seems fine
log.debug("SkipService: {0} is not playing, closing dialog".format(type))
log.debug("SkipService: {0} is not playing, closing dialog".format(segment_type))
dialog.close()

return dialog


def stop_service(self):
log.debug("IntroSkipperService Stop Called")
Expand Down
17 changes: 8 additions & 9 deletions resources/lib/intro_skipper_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def get_setting_skip_action(type: Literal["Commercial", "Preview", "Recap", "Out
elif (type == "Intro"):
return settings.getSetting("intro_skipper_action")
return ""

def get_setting_skip_start_offset(type: Literal["Commercial", "Preview", "Recap", "Outro", "Intro"]):
settings = xbmcaddon.Addon()
if (type == "Commercial"):
Expand All @@ -36,7 +36,7 @@ def get_setting_skip_start_offset(type: Literal["Commercial", "Preview", "Recap"
elif (type == "Intro"):
return settings.getSettingInt("intro_skipper_start_offset")
return 0

def get_setting_skip_end_offset(type: Literal["Commercial", "Preview", "Recap", "Outro", "Intro"]):
settings = xbmcaddon.Addon()
if (type == "Commercial"):
Expand All @@ -50,23 +50,22 @@ def get_setting_skip_end_offset(type: Literal["Commercial", "Preview", "Recap",
elif (type == "Intro"):
return settings.getSettingInt("intro_skipper_end_offset")
return 0
def set_correct_skip_info(item_id: str, skip_dialog: SkipDialog, segments, type: Literal["Commercial", "Preview", "Recap", "Outro", "Intro"]):

def set_correct_skip_info(item_id: str, skip_dialog: SkipDialog, segment: dict):
if (skip_dialog.media_id is None or skip_dialog.media_id != item_id) and item_id is not None:
# If playback item has changed (or is new), sets its id and set media segments info
log.debug("SkipDialogInfo : Media Id has changed to {0}, setting segments".format(item_id))
skip_dialog.media_id = item_id
skip_dialog.has_been_dissmissed = False
if segments is not None:
if segment is not None:
# Find the intro and outro timings
start = next((segment["StartTicks"] for segment in segments if segment["Type"] == type), None)
end = next((segment["EndTicks"] for segment in segments if segment["Type"] == type), None)
start = segment.get("StartTicks")
end = segment.get("EndTicks")

# Sets timings with offsets if defined in settings
if start is not None:
skip_dialog.start = start + seconds_to_ticks(get_setting_skip_start_offset(type))
log.debug("SkipDialogInfo : Setting {0} start to {1}".format(type, skip_dialog.start))
if end is not None:
skip_dialog.end = end - seconds_to_ticks(get_setting_skip_end_offset(type))
log.debug("SkipDialogInfo : Setting {0} end to {1}".format(type, skip_dialog.end))

20 changes: 6 additions & 14 deletions resources/lib/play_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ def set_list_item_props(item_id, list_item, result, server, extra_props, title):
album = result.get("Album")
if album:
details['album'] = album

list_item.setInfo("Music", infoLabels=details)

else:
Expand Down Expand Up @@ -1096,6 +1096,7 @@ def stop_all_playback():
clear_entries = []

home_window.clear_property("currently_playing_id")
home_window.clear_property("now_playing")

for item in played_information:
data = played_information.get(item)
Expand Down Expand Up @@ -1152,7 +1153,7 @@ def get_playing_data():
play_data = json.loads(play_data_string)
except ValueError:
# This isn't a JellyCon item
return None
return {}

played_information_string = home_window.get_property('played_information')
if played_information_string:
Expand All @@ -1173,7 +1174,7 @@ def get_playing_data():
playing_file = player.getPlayingFile()
except Exception as e:
log.error("get_playing_data : getPlayingFile() : {0}".format(e))
return None
return {}
log.debug("get_playing_data : getPlayingFile() : {0}".format(playing_file))
if server in playing_file and item_id is not None:
play_time = player.getTime()
Expand All @@ -1194,16 +1195,6 @@ def get_playing_data():

return {}

def get_jellyfin_playing_item():
home_window = HomeWindow()
play_data_string = home_window.get_property('now_playing')
try:
play_data = json.loads(play_data_string)
except ValueError:
# This isn't a JellyCon item
return None

return play_data.get("item_id")

def get_play_url(media_source, play_session_id, channel_id=None):
log.debug("get_play_url - media_source: {0}", media_source)
Expand Down Expand Up @@ -1367,7 +1358,7 @@ def onPlayBackStarted(self):

play_data = get_playing_data()

if play_data is None:
if not play_data:
return

play_data["paused"] = False
Expand Down Expand Up @@ -1716,6 +1707,7 @@ def get_item_playback_info(item_id, force_transcode):

return play_info_result


def get_media_segments(item_id):
url = "/MediaSegments/{}".format(item_id)
result = api.get(url)
Expand Down
Loading