# -*- coding: utf-8 -*- ''' TvSkipIntro Add-on Copyright (C) 2018 aenema Amélioration et modification de ce script. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ''' import xbmcvfs, xbmc, xbmcaddon, json, os, xbmcgui, time, re # --- Initialisation de l'add-on et des chemins --- KODI_VERSION = int(xbmc.getInfoLabel("System.BuildVersion").split(".")[0]) addonInfo = xbmcaddon.Addon().getAddonInfo settings = xbmcaddon.Addon().getSetting profilePath = xbmcvfs.translatePath(addonInfo('profile')) addonPath = xbmcvfs.translatePath(addonInfo('path')) skipFile = os.path.join(profilePath, 'skipintro.json') defaultSkip = settings('default.skip') if not os.path.exists(profilePath): xbmcvfs.mkdir(profilePath) # --- Fonctions utilitaires de gestion des données (réglagles améliorées) --- def load_skip_data(): """Charge les données de saut d'intro depuis le fichier JSON.""" if not os.path.exists(skipFile): return [] try: with open(skipFile, 'r') as f: return json.load(f) except (IOError, json.JSONDecodeError): # En cas d'erreur de lecture ou de fichier corrompu, on renvoie une liste vide return [] def save_skip_data(data): """Sauvegarde des données de saut d'intro dans le fichier JSON.""" try: with open(skipFile, 'w') as f: json.dump(data, f, indent=2) except IOError: pass # Gérer silencieusement les erreurs d'écriture # --- Fonctions principales de l'add-on --- def cleantitle(title): if title is None: return # Utilisation d'une expression régulière plus concise pour nettoyer le titre title = title.lower() title = re.sub('&#\d+;', '', title) title = re.sub(r'(\[.*?\]|\(.*?\))', '', title) title = re.sub(r'[^a-z0-9]', '', title) return title def updateSkip(title, seconds=defaultSkip, start=0, service=True): data = load_skip_data() found = False for item in data: if cleantitle(item['title']) == cleantitle(title): item['service'] = service item['skip'] = seconds item['start'] = start found = True break if not found: # Si le titre n'est pas trouvé, l'ajoute comme si c'était un nouveau newskip(title, seconds, start, service) else: save_skip_data(data) def newskip(title, seconds=None, start=0, service=True): if seconds is None or seconds == '': seconds = defaultSkip data = load_skip_data() # Vérification si le titre existe déjà pour éviter les doublons if not any(cleantitle(item['title']) == cleantitle(title) for item in data): newIntro = {'title': title, 'service': service, 'skip': seconds, 'start': start} data.append(newIntro) save_skip_data(data) def getSkip(title): data = load_skip_data() skip = [i['skip'] for i in data if cleantitle(i['title']) == cleantitle(title) and i['service'] is True] if skip: return skip[0] else: newskip(title, defaultSkip) return defaultSkip def checkService(title): data = load_skip_data() service_status = [i['service'] for i in data if cleantitle(i['title']) == cleantitle(title)] return service_status[0] if service_status else True def checkStartTime(title): data = load_skip_data() start_time = [i['start'] for i in data if cleantitle(i['title']) == cleantitle(title)] return start_time[0] if start_time else 0 # --- Vérification initiale du fichier au démarrage --- if not os.path.exists(skipFile): newskip('default', defaultSkip) # --- Classe de service Kodi --- class Service(): WINDOW = xbmcgui.Window(10000) def __init__(self, *args): self.skipped = False self.currentShow = None def ServiceEntryPoint(self): monitor = xbmc.Monitor() while not monitor.abortRequested(): if monitor.waitForAbort(1): # Réduction du délai d'attente à 1s pour une meilleure réactivité break if xbmc.Player().isPlaying(): try: current_show = xbmc.getInfoLabel("VideoPlayer.TVShowTitle") if current_show and current_show != self.currentShow: self.currentShow = current_show self.skipped = False # Réinitialise pour une nouvelle série if self.currentShow and not self.skipped: self.SkipIntro(self.currentShow) except Exception as e: xbmc.log(f"Erreur dans le service de saut d'intro : {e}", xbmc.LOGINFO) else: self.skipped = False self.currentShow = None def SkipIntro(self, tvshow): try: if not xbmc.Player().isPlayingVideo(): return timeNow = xbmc.Player().getTime() status = checkService(tvshow) if not status: self.skipped = True return startTime = checkStartTime(tvshow) if int(startTime) >= int(timeNow): return # --- Utilisation d'un dialogue pour l'utilisateur --- dialog = CustomDialog('script-dialog.xml', addonPath, tvshow) dialog.doModal() self.skipped = True del dialog except Exception as e: xbmc.log(f"Erreur dans SkipIntro: {e}", xbmc.LOGINFO) # --- Classe pour le dialogue de l'interface graphique (GUI) --- OK_BUTTON = 201 NEW_BUTTON = 202 DISABLE_BUTTON = 210 ACTION_PREVIOUS_MENU = 10 ACTION_BACK = 92 INSTRUCTION_LABEL = 203 AUTHCODE_LABEL = 204 WARNING_LABEL = 205 CENTER_Y = 6 CENTER_X = 2 class CustomDialog(xbmcgui.WindowXMLDialog): def __init__(self, xmlFile, resourcePath, show): self.tvshow = show def onInit(self): self.skipValue = int(getSkip(self.tvshow)) skipLabel = 'SKIP INTRO: %s' % self.skipValue skipButton = self.getControl(OK_BUTTON) skipButton.setLabel(skipLabel) def onAction(self, action): if action in [ACTION_PREVIOUS_MENU, ACTION_BACK]: self.close() def onControl(self, control): pass def onFocus(self, control): pass def onClick(self, control): if control == OK_BUTTON: timeNow = xbmc.Player().getTime() skipTotal = int(timeNow) + int(self.skipValue) xbmc.Player().seekTime(int(skipTotal)) elif control == NEW_BUTTON: dialog = xbmcgui.Dialog() d = dialog.input('Skip Value (seconds)', type=xbmcgui.INPUT_NUMERIC) if d is None or d == '': self.close() return d2 = dialog.input('Prompt At (seconds)', type=xbmcgui.INPUT_NUMERIC) if d2 is None or d2 == '': d2 = 0 if str(d) != '0': newskip(self.tvshow, d, start=d2) elif control == DISABLE_BUTTON: updateSkip(self.tvshow, seconds=self.skipValue, service=False) if control in [OK_BUTTON, NEW_BUTTON, DISABLE_BUTTON]: self.close() # Démarrage du service Service().ServiceEntryPoint()