# -*- coding: UTF-8 -*- ################################################################################# # # SubsSupport 1.2.0 for Enigma2 # Coded by mx3L (c) 2014 # # 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 2 # 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. # ################################################################################# from Queue import Queue from datetime import datetime import json from os import path as os_path, listdir import os from re import compile as re_compile import re import sys from threading import Thread import traceback from twisted.internet.defer import Deferred from twisted.web import client from Components.ActionMap import ActionMap, NumberActionMap, HelpableActionMap from Components.ConfigList import ConfigListScreen from Components.GUIComponent import GUIComponent from Components.Harddisk import harddiskmanager from Components.Label import Label from Components.Language import language from Components.MenuList import MenuList from Components.MultiContent import MultiContentEntryText, \ MultiContentEntryPixmapAlphaTest from Components.Renderer.Renderer import Renderer from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase from Components.Sources.Boolean import Boolean from Components.Sources.List import List from Components.Sources.StaticText import StaticText from Components.config import ConfigSubsection, ConfigSelection, ConfigYesNo, \ configfile, getConfigListEntry, config, ConfigText, ConfigDirectory, ConfigOnOff, \ ConfigNothing, ConfigInteger, NoSave, KEY_DELETE, KEY_BACKSPACE, \ KEY_TIMEOUT, KEY_ASCII from Screens.ChoiceBox import ChoiceBox from Screens.HelpMenu import HelpableScreen from Screens.InfoBarGenerics import InfoBarSeek, InfoBarNotifications from Screens.LocationBox import LocationBox from Screens.MessageBox import MessageBox from Screens.Screen import Screen from Tools import Notifications from Tools.Directories import SCOPE_SKIN_IMAGE, SCOPE_SKIN, resolveFilename, \ fileExists from Tools.ISO639 import LanguageCodes from Tools.LoadPixmap import LoadPixmap from compat import eTimer, FileList from e2_utils import messageCB, E2SettingsProvider, MyLanguageSelection, unrar, \ ConfigFinalText, Captcha, DelayMessageBox, MyConfigList, getFps, fps_float, \ getFonts, BaseMenuScreen from enigma import RT_HALIGN_RIGHT, RT_VALIGN_TOP, eSize, ePoint, RT_HALIGN_LEFT, \ RT_HALIGN_RIGHT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, eListboxPythonMultiContent, \ gFont, getDesktop, eServiceCenter, iServiceInformation, eServiceReference, \ iSeekableService, iPlayableService, iPlayableServicePtr, addFont, gFont, \ gRGB, loadPNG, ePythonMessagePump, eConsoleAppContainer, eLabel from parsers import SubRipParser, MicroDVDParser from process import SubsLoader, DecodeError, ParseError, ParserNotFoundError, \ LoadError from searchsubs import Messages from seek import SubsSeeker, SubtitlesDownloadError, SubtitlesErrors from seekers.utilities import detectSearchParams, languageTranslate from skin import parseColor, parsePosition, parseFont from utils import toString, SimpleLogger, toUnicode from . import _, __author__, __version__, __email__ try: from xml.etree.cElementTree import parse as parse_xml except ImportError: from xml.etree.ElementTree import parse as parse_xml try: from Screens.AudioSelection import QuickSubtitlesConfigMenu except ImportError: QuickSubtitlesConfigMenu = None # localization function def warningMessage(session, text): session.open(MessageBox, text, type=MessageBox.TYPE_WARNING, timeout=5) def debug(text, *args): if DEBUG: if len(args) == 1 and isinstance(args[0], tuple): text = text % args[0] else: text = text % (args) print "[SubsSupport]", toString('utf-8') # set the name of plugin in which this library belongs # PLUGIN_NAME = 'mediaplayer2' # set debug mode DEBUG = False # set supported encodings, you have to make sure, that you have corresponding python # libraries in %PYTHON_PATH%/encodings/ (ie. iso-8859-2 requires iso_8859_2.searchsubs library) # to choose encodings for region you want, visit: # http://docs.python.org/release/2.4.4/lib/standard-encodings.html # Common encodings for all languages ALL_LANGUAGES_ENCODINGS = ['utf-8', 'utf-16'] # other encodings, sorted according usage CENTRAL_EASTERN_EUROPE_ENCODINGS = ['windows-1250', 'iso-8859-2', 'maclatin2', 'IBM852'] WESTERN_EUROPE_ENCODINGS = ['windows-1252', 'iso-8859-15', 'macroman', 'ibm1140', 'IBM850'] RUSSIAN_ENCODINGS = ['windows-1251', 'cyrillic', 'maccyrillic', 'koi8_r', 'IBM866'] ARABIC_ENCODINGS = ['windows-1256', 'iso-8859-6', 'IBM864'] TURKISH_ENCODINGS = ['windows-1254', 'iso-8859-9', 'latin5', 'macturkish', 'ibm1026', 'IBM857'] GREEK_ENCODINGS = ['windows-1253', 'iso-8859-7', 'macgreek'] ENCODINGS = {("Central and Eastern Europe") : CENTRAL_EASTERN_EUROPE_ENCODINGS, ("Western Europe"):WESTERN_EUROPE_ENCODINGS, ("Russia"):RUSSIAN_ENCODINGS, ("Arabic"): ARABIC_ENCODINGS, ("Turkish"):TURKISH_ENCODINGS, ("Greek"):GREEK_ENCODINGS} # initializing parsers PARSERS = [SubRipParser, MicroDVDParser] def getDefaultFont(fontType): ubuntu = None openpli = None for f in getFonts(): if fontType == "regular": if f == "Subs": openpli = f elif f== "Ubuntu-M": ubuntu = f elif fontType == "italic": if f == "Subsi": openpli = f elif f=="Ubuntu-MI": ubuntu = f elif fontType == "bold": if f == "Subsb": openpli = f elif f =="Ubuntu-B": ubuntu = f if ubuntu: return ubuntu if openpli: return openpli return "Regular" def getEmbeddedFontSizeCfg(defaultFontSizeCfg): CONFIG_SUBTITLES_OPENPLI = "subtitles" CONFIG_FONTSIZE_OPENPLI = "subtitle_fontsize" CONFIG_SUBTITLES_VTI = "subtitle" CONFIG_FONTSIZE_VTI = "subtitlefontsize" try: subtitles_pli_cfg = getattr(config, CONFIG_SUBTITLES_OPENPLI) except KeyError: subtitles_pli_cfg = None if subtitles_pli_cfg is not None: try: return getattr(subtitles_pli_cfg, CONFIG_FONTSIZE_OPENPLI) except KeyError: pass try: subtitles_vti_cfg = getattr(config, CONFIG_SUBTITLES_VTI) except KeyError: subtitles_vti_cfg = None if subtitles_vti_cfg is not None: try: return getattr(subtitles_vti_cfg, CONFIG_FONTSIZE_VTI) except KeyError: pass return defaultFontSizeCfg GLOBAL_CONFIG_INIT = False fontChoiceList = [f for f in getFonts()] fontSizeChoiceList = [("%d" % i, "%d px" % i) for i in range(10, 60, 1)] positionChoiceList = [("0", _("top"))] positionChoiceList.extend([("%d" % i, "%d %%" % i) for i in range(1, 100, 1)]) positionChoiceList.append(("100", _("bottom"))) shadowSizeChoiceList = [("%d" % i, "%d px" % i) for i in range(1, 8, 1)] shadowOffsetChoiceList = [("%d" % i, "%d px" % i) for i in range(-8, -1, 1)] backgroundOffsetChoiceList = [("%d" % i, "%d px" % i) for i in range(5, 100, 1)] colorChoiceList = [] colorChoiceList.append(("ff0000", _("red"))) colorChoiceList.append(("DCDCDC", _("grey"))) colorChoiceList.append(("00ff00", _("green"))) colorChoiceList.append(("ff00ff", _("purple"))) colorChoiceList.append(("ffff00", _("yellow"))) colorChoiceList.append(("ffffff", _("white"))) colorChoiceList.append(("00ffff", _("blue"))) colorChoiceList.append(("000000", _("black"))) COLORFILE = os.path.join(os.path.dirname(__file__), 'colors.txt') print '[SubsSupport] looking for custom colors in', COLORFILE try: with open(COLORFILE, 'r') as f: for line in f: color = re.search('^(\w+)\s+([0-9A-Fa-f]{6})$', line) if color is not None: alias = color.group(1) hex_color = color.group(2) print '[SubsSupport] adding custom color', alias colorChoiceList.append((hex_color, alias)) except IOError as e: print '[SubsSupport] error while loading custom colors', str(e) alphaChoiceList = [("00", _("opaque"))] alphaChoiceList.extend([("%02x" % val, "%d %%"%(int(percent * 100 / float(32)))) for percent, val in enumerate(xrange(0, 256, 8)) if val != 0]) alphaChoiceList.append(("ff", _("transparent"))) def initGeneralSettings(configsubsection): configsubsection.pauseVideoOnSubtitlesMenu = ConfigYesNo(default=True) configsubsection.encodingsGroup = ConfigSelection(default="Central and Eastern Europe", choices=[(e, _(e)) for e in ENCODINGS.keys()]) def initExternalSettings(configsubsection): configsubsection.position = ConfigSelection(default="94", choices=positionChoiceList) configsubsection.font = ConfigSubsection() configsubsection.font.regular = ConfigSubsection() configsubsection.font.regular.type = ConfigSelection(default=getDefaultFont("regular"), choices=fontChoiceList) configsubsection.font.regular.alpha = ConfigSelection(default="00", choices=alphaChoiceList) configsubsection.font.regular.color = ConfigSelection(default="ffffff", choices=colorChoiceList) configsubsection.font.italic = ConfigSubsection() configsubsection.font.italic.type = ConfigSelection(default=getDefaultFont("italic"), choices=fontChoiceList) configsubsection.font.italic.alpha = ConfigSelection(default="00", choices=alphaChoiceList) configsubsection.font.italic.color = ConfigSelection(default="ffffff", choices=colorChoiceList) configsubsection.font.bold = ConfigSubsection() configsubsection.font.bold.type = ConfigSelection(default=getDefaultFont("bold"), choices=fontChoiceList) configsubsection.font.bold.alpha = ConfigSelection(default="00", choices=alphaChoiceList) configsubsection.font.bold.color = ConfigSelection(default="ffffff", choices=colorChoiceList) configsubsection.font.size = ConfigSelection(default="43", choices=fontSizeChoiceList) configsubsection.shadow = ConfigSubsection() configsubsection.shadow.enabled = ConfigOnOff(default=True) configsubsection.shadow.type = ConfigSelection(default="border", choices=[("offset", _("offset")), ("border", _('border'))]) configsubsection.shadow.color = ConfigSelection(default="000000", choices=colorChoiceList) configsubsection.shadow.size = ConfigSelection(default="2", choices=shadowSizeChoiceList) configsubsection.shadow.xOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList) configsubsection.shadow.yOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList) configsubsection.background = ConfigSubsection() configsubsection.background.enabled = ConfigOnOff(default=True) configsubsection.background.type = ConfigSelection(default="dynamic", choices=[("dynamic", _("dynamic")), ("static", _("static"))]) configsubsection.background.xOffset = ConfigSelection(default="10", choices=backgroundOffsetChoiceList) configsubsection.background.yOffset = ConfigSelection(default="10", choices=backgroundOffsetChoiceList) configsubsection.background.color = ConfigSelection(default="000000", choices=colorChoiceList) configsubsection.background.alpha = ConfigSelection(default="80", choices=alphaChoiceList) def initEmbeddedSettings(configsubsection): configsubsection.position = ConfigSelection(default="94", choices=positionChoiceList) configsubsection.font = ConfigSubsection() configsubsection.font.regular = ConfigSubsection() configsubsection.font.regular.type = ConfigSelection(default=getDefaultFont("regular"), choices=fontChoiceList) configsubsection.font.italic = ConfigSubsection() configsubsection.font.italic.type = ConfigSelection(default=getDefaultFont("italic"), choices=fontChoiceList) configsubsection.font.bold = ConfigSubsection() configsubsection.font.bold.type = ConfigSelection(default=getDefaultFont("bold"), choices=fontChoiceList) configsubsection.font.size = ConfigSelection(default="34", choices=fontSizeChoiceList) configsubsection.color = ConfigSelection(default="ffffff", choices=colorChoiceList) configsubsection.shadow = ConfigSubsection() configsubsection.shadow.size = ConfigSelection(default="3", choices=shadowSizeChoiceList) configsubsection.shadow.color = ConfigSelection(default="000000", choices=colorChoiceList) configsubsection.shadow.xOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList) configsubsection.shadow.yOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList) def initEngineSettings(configsubsection): configsubsection.expert = ConfigSubsection() configsubsection.expert.show = NoSave(ConfigYesNo(default=False)) configsubsection.expert.playerDelay = ConfigSelection(default="0", choices=[("%d" % i, "%d ms" % i) for i in range(0, 20000, 200)]) configsubsection.expert.startDelay = ConfigSelection(default="1200", choices=[("%d" % i, "%d ms" % i) for i in range(0, 100, 1500)]) configsubsection.expert.hideDelay = ConfigSelection(default="200", choices=[("%d" % i, "%d ms" % i) for i in range(0, 1000, 50)]) configsubsection.expert.ptsDelayCheck = ConfigSelection(default="200", choices=[("%d" % i, "%d ms" % i) for i in range(100, 1000, 100)]) configsubsection.expert.syncDelay = ConfigSelection(default="300", choices=[("%d" % i, "%d ms" % i) for i in range(100, 1000, 100)]) configsubsection.expert.refreshDelay = ConfigSelection(default="1000", choices=[("%d" % i, "%d ms" % i) for i in range(200, 3000, 200)]) def initSearchSettings(configsubsection): configsubsection.downloadHistory = ConfigSubsection() configsubsection.downloadHistory.enabled = ConfigYesNo(default=True) configsubsection.downloadHistory.limit = ConfigInteger(default=50, limits=(2, 200)) configsubsection.downloadHistory.path = ConfigDirectory(default = os.path.dirname(__file__), visible_width=30) configsubsection.downloadHistory.removeAction = ConfigSelection(default='list', choices=[('list', _("List")), ('file', _("List + File"))]) configsubsection.downloadHistory.removeActionAsk = ConfigYesNo(default = True) configsubsection.downloadPath = ConfigDirectory(default="/tmp/") configsubsection.tmpPath = ConfigDirectory(default="/tmp/") configsubsection.lang1 = ConfigFinalText(default=language.getLanguage()[:2]) configsubsection.lang2 = ConfigFinalText(default=language.getLanguage()[:2]) configsubsection.lang3 = ConfigFinalText(default=language.getLanguage()[:2]) configsubsection.timeout = ConfigSelection(default="10", choices=[(("%d" % i, "%d s" % i)) for i in range(5, 20)]) configsubsection.history = ConfigText(default="") configsubsection.movieProvider = ConfigSelection(default="all", choices=[("all", _("All")), ]) configsubsection.tvshowProvider = ConfigSelection(default="all", choices=[("all", _("All")), ]) configsubsection.manualSearch = ConfigYesNo(default=False) configsubsection.defaultSort = ConfigSelection(default='lang', choices=[('lang', _("Language")), ('provider', _("Provider"))]) configsubsection.saveAs = ConfigSelection(default='version', choices=[('default', _("Default")), ('version', _("Release")), ('video', _("Video filename"))]) configsubsection.saveAsFallback = ConfigSelection(default='version', choices=[('default', _("Default")), ('version', _("Release"))]) configsubsection.saveTo = ConfigSelection(default='custom', choices=[('custom', _('User defined')), ('video', _('Next to video'))]) configsubsection.addLangToSubsFilename = ConfigYesNo(default=False) configsubsection.askOverwriteExistingSubs = ConfigYesNo(default=True) configsubsection.loadSubtitlesAfterDownload = ConfigYesNo(default=True) configsubsection.openParamsDialogOnSearch = ConfigYesNo(default=True) configsubsection.showProvidersErrorMessage = ConfigYesNo(default=True) # session settings configsubsection.title = ConfigTextWithSuggestionsAndHistory(configsubsection.history, default="", fixed_size=False) configsubsection.type = ConfigSelection(default="movie", choices=[("tv_show", _("TV show")), ("movie", _("Movie"))]) configsubsection.year = ConfigInteger(default=0, limits=(0, 2100)) configsubsection.season = ConfigInteger(default=0, limits=(0, 100)) configsubsection.episode = ConfigInteger(default=0, limits=(0, 100)) configsubsection.provider= ConfigSelection(default="all", choices=[("all", _("All")), ]) configsubsection.useFilePath = ConfigYesNo(default=True) def initSubsSettings(configSubsection=None): global GLOBAL_CONFIG_INIT if configSubsection: print '[SubsSupport] using provided ConfigSubsection to store config' subtitles_settings = configSubsection elif 'PLUGIN_NAME' in globals(): print '[SubsSupport] using config.plugins.%s.%s to store config' % (PLUGIN_NAME, 'subtitles') plugin_settings = getattr(config.plugins, PLUGIN_NAME) setattr(plugin_settings, 'subtitles', ConfigSubsection()) subtitles_settings = getattr(plugin_settings, 'subtitles') elif GLOBAL_CONFIG_INIT: print "[SubsSupport] using global config (already initialized)" return config.plugins.subtitlesSupport else: print "[SubsSupport] using global config" config.plugins.subtitlesSupport = ConfigSubsection() subtitles_settings = config.plugins.subtitlesSupport GLOBAL_CONFIG_INIT = True initGeneralSettings(subtitles_settings) subtitles_settings.external = ConfigSubsection() initExternalSettings(subtitles_settings.external) subtitles_settings.embedded = ConfigSubsection() initEmbeddedSettings(subtitles_settings.embedded) subtitles_settings.engine = ConfigSubsection() initEngineSettings(subtitles_settings.engine) subtitles_settings.search = ConfigSubsection() initSearchSettings(subtitles_settings.search) return subtitles_settings class SubsStatusScreen(Screen, HelpableScreen): def __init__(self, session, setSubsDelay, getSubsDelay, subscribeDelay, unsubscribeDelay, toNextSub, toPrevSub, setSubsFps, getSubsFps, subsDelayStepInMs=200, showDelayInMs=False): desktopWidth = getDesktop(0).size().width() offset = 50 self.skin = """ """ % (offset, desktopWidth-(2*offset), (desktopWidth - (2*offset))/2 - 5, (desktopWidth-(2*offset))/2 + 5, (desktopWidth-(2*offset))/2 - 5) Screen.__init__(self, session) HelpableScreen.__init__(self) self.setSubsDelay = setSubsDelay self.getSubsDelay = getSubsDelay self.subscribeDelay = subscribeDelay self.unsubscribeDelay = unsubscribeDelay self.toNextSub = toNextSub self.toPrevSub = toPrevSub self.setSubsFps = setSubsFps self.getSubsFps = getSubsFps self.subsDelayStep = subsDelayStepInMs self.showDelayInMs = showDelayInMs self.fpsChoices = ["23.976", "23.980", "24.000", "25.000", "29.970", "30.000"] self['fps'] = StaticText() self['delay'] = StaticText() self['SubsArrowActions'] = HelpableActionMap(self, "DirectionActions", { 'right': (self.nextSubDelay, _("jump to next subtitle")), 'left':(self.prevSubDelay, _("jump to previous subtitle")), 'up':(self.incSubDelay, _("increase subtitles delay")), 'down':(self.decSubDelay, _("decrease subtitles delay")), }) self['SubsColorActions'] = HelpableActionMap(self, "ColorActions", { 'red': (self.reset, _("reset subtitles delay/fps")), 'blue': (self.changeFps, _("change subtitles fps")), }) self['OkCancelActions'] = HelpableActionMap(self, "OkCancelActions", { 'ok': (self.showHelp, _("displays this menu")), 'cancel':(self.close, _("exit")) }) self._subsDelay = None self.onLayoutFinish.append(self._subscribeDelay) self.onLayoutFinish.append(self.updateSubsFps) self.onLayoutFinish.append(self.updateSubsDelay) self.onClose.append(self._unsubscribeDelay) def _subscribeDelay(self): self.subscribeDelay(self._setSubsDelayAndUpdate) def _unsubscribeDelay(self): self.unsubscribeDelay(self._setSubsDelayAndUpdate) def _setSubsDelayAndUpdate(self, delay): self._subsDelay = delay self.updateSubsDelay() def _getSubsDelay(self): if self._subsDelay is None: self._subsDelay = self.getSubsDelay() return self._subsDelay def updateSubsFps(self): subsFps = self.getSubsFps() videoFps = getFps(self.session, True) if subsFps is None or videoFps is None: self['fps'].text = "%s: %s"%(_("Subtitles FPS"), _("unknown")) return if subsFps == videoFps: self['fps'].text = "%s: %s"%(_("Subtitles FPS"), _("original")) else: self['fps'].text = "%s: %s"%(_("Subtitles FPS"), str(subsFps)) def updateSubsDelay(self): subsDelay = self._getSubsDelay() if self.showDelayInMs: if subsDelay > 0: self["delay"].text = "%s: +%dms"%(_("Subtitles Delay"), subsDelay) else: self["delay"].text = "%s: %dms"%(_("Subtitles Delay"), subsDelay) else: if subsDelay > 0: self["delay"].text = "%s: +%.2fs"%(_("Subtitles Delay"), subsDelay/float(1000)) else: self["delay"].text = "%s: %.2fs"%(_("Subtitles Delay"), subsDelay/float(1000)) def nextSubDelay(self): self.toNextSub() def prevSubDelay(self): self.toPrevSub() def incSubDelay(self): self.setSubsDelay(self._getSubsDelay() + self.subsDelayStep) def decSubDelay(self): self.setSubsDelay(self._getSubsDelay() - self.subsDelayStep) def changeFps(self): subsFps = self.getSubsFps() if subsFps is None: return currIdx = self.fpsChoices.index(str(subsFps)) if currIdx == len(self.fpsChoices) -1: nextIdx = 0 else: nextIdx = currIdx+1 self.setSubsFps(fps_float(self.fpsChoices[nextIdx])) self.updateSubsFps() def reset(self): self.setSubsFps(getFps(self.session, True)) self.setSubsDelay(0) self.updateSubsFps() class SubsSupportStatus(object): def __init__(self, delayStepInMs=200, showDelayInMs=False, statusScreen=None): assert isinstance(self, SubsSupport), "not derived from SubsSupport!" self.__delayStepInMs = delayStepInMs self.__showDelayInMs = showDelayInMs self.__statusScreen = statusScreen self.__event_tracker = ServiceEventTracker(screen=self, eventmap= { iPlayableService.evStart: self.__serviceChanged, iPlayableService.evEnd: self.__serviceChanged, }) self["SubsStatusActions"] = HelpableActionMap(self, "SubtitlesActions", { "subtitlesStatus": (self.subsStatus, _("change external subtitles status")), } , -5) self.onClose.append(self.__closeSubsStatusScreen) def __serviceChanged(self): self.__closeSubsStatusScreen() def __closeSubsStatusScreen(self): try: self.__subsStatusScreen.close() except Exception: pass def subsStatus(self): setDelay = self.setSubsDelay getDelay = self.getSubsDelay subscribe = self.subscribeOnSubsDelayChanged unsubscribe = self.unsubscribeOnSubsDelayChanged toNextSub = self.setSubsDelayToNextSubtitle toPrevSub = self.setSubsDelayToPrevSubtitle getFps = self.getSubsFps setFps = self.setSubsFps if self.isSubsLoaded(): self.__subsStatusScreen = self.session.open(SubsStatusScreen, setDelay, getDelay, subscribe, unsubscribe, toNextSub, toPrevSub, setFps, getFps, self.__delayStepInMs, self.__showDelayInMs) else: if self.__statusScreen is not None: self.__statusScreen.setStatus(_("No external subtitles are loaded")) elif isinstance(self, InfoBarNotifications): Notifications.AddNotification(MessageBox, _("No external subtitles are loaded"), type=MessageBox.TYPE_INFO, timeout=2) class SubsSupportEmbedded(object): def __init__(self, embeddedSupport, preferEmbedded): self.embeddedSupport = embeddedSupport self.preferEmbedded = preferEmbedded self.__subStyles = {} self.selected_subtitle = None self.subtitle_window = self.session.instantiateDialog(SubsEmbeddedScreen, self.subsSettings.embedded) self.subtitle_window.hide() if isinstance(self, InfoBarBase): self.__event_tracker = ServiceEventTracker(screen=self, eventmap= { iPlayableService.evStart: self.__serviceChanged, iPlayableService.evEnd: self.__serviceChanged, # iPlayableService.evUpdatedInfo: self.__updatedInfo }) self.onClose.append(self.exitEmbeddedSubs) def __isEmbeddedEnabled(self): return self.subtitle_window.shown embeddedEnabled = property(__isEmbeddedEnabled) def getCurrentServiceSubtitle(self): service = self.session.nav.getCurrentService() return service and service.subtitle() def __serviceChanged(self): if self.selected_subtitle: self.selected_subtitle = None self.subtitle_window.hide() def __updatedInfo(self): if not self.selected_subtitle: subtitle = self.getCurrentServiceSubtitle() cachedsubtitle = subtitle.getCachedSubtitle() if cachedsubtitle: self.enableSubtitle(cachedsubtitle) def enableSubtitle(self, selectedSubtitle): print '[SubsSupportEmbedded] enableSubtitle', selectedSubtitle subtitle = self.getCurrentServiceSubtitle() self.selected_subtitle = selectedSubtitle if subtitle and self.selected_subtitle: self.resetEmbeddedSubs() subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle) self.subtitle_window.show() print '[SubsSupportEmbedded] enable embedded subtitles' else: print '[SubsSupportEmbedded] disable embedded subtitles' if subtitle: subtitle.disableSubtitles(self.subtitle_window.instance) self.subtitle_window.hide() def restartSubtitle(self): if self.selected_subtitle: print '[SubsSupportEmbedded] restart embedded subtitles' self.enableSubtitle(self.selected_subtitle) def resetEmbeddedSubs(self, reloadScreen=False): if QuickSubtitlesConfigMenu: return print '[SubsSupportEmbedded] updating embedded screen' from enigma import eWidget, eSubtitleWidget scale = ((1, 1), (1, 1)) embeddedSettings = self.subsSettings.embedded fontSize = embeddedSettings.font.size.value fontTypeR = embeddedSettings.font.regular.type.value fontTypeI = embeddedSettings.font.italic.type.value fontTypeB = embeddedSettings.font.bold.type.value foregroundColor = "#" + embeddedSettings.color.value foregroundColor = parseColor(foregroundColor) borderColor = "#" + embeddedSettings.shadow.color.value borderColor = parseColor(borderColor) borderWidth = int(embeddedSettings.shadow.size.value) offset = "%s,%s" % (embeddedSettings.shadow.xOffset.value, embeddedSettings.shadow.yOffset.value) shadowOffset = parsePosition(offset, scale) fontRegular = parseFont("%s;%s" % (fontTypeR, fontSize), scale) fontItalic = parseFont("%s;%s" % (fontTypeI, fontSize), scale) fontBold = parseFont("%s;%s" % (fontTypeB, fontSize), scale) self._loadEmbeddedStyle({"Subtitle_Regular":(fontRegular, 1, foregroundColor, borderColor, borderWidth, borderColor, shadowOffset), "Subtitle_Italic":(fontItalic, 1, foregroundColor, borderColor, borderWidth, borderColor, shadowOffset), "Subtitle_Bold":(fontBold, 1, foregroundColor, borderColor, borderWidth, borderColor, shadowOffset)}) if reloadScreen: print '[SubsSupportEmbedded] reloading embedded screen' subtitle = self.getCurrentServiceSubtitle() if subtitle: subtitle.disableSubtitles(self.subtitle_window.instance) self.session.deleteDialog(self.subtitle_window) self.subtitle_window = None self.subtitle_window = self.session.instantiateDialog(SubsEmbeddedScreen, self.subsSettings.embedded) self.subtitle_window.hide() self.restartSubtitle() def _parseEmbeddedStyles(self, filename): if filename in self.__subStyles: return self.defaultStyles[filename] skin = parse_xml(filename).getroot() for c in skin.findall("subtitles"): scale = ((1, 1), (1, 1)) substyles = {} for substyle in c.findall("sub"): get_attr = substyle.attrib.get font = parseFont(get_attr("font"), scale) col = get_attr("foregroundColor") if col: foregroundColor = parseColor(col) haveColor = 1 else: foregroundColor = gRGB(0xFFFFFF) haveColor = 0 col = get_attr("borderColor") if col: borderColor = parseColor(col) else: borderColor = gRGB(0) borderwidth = get_attr("borderWidth") if borderwidth is None: # default: use a subtitle border borderWidth = 3 else: borderWidth = int(borderwidth) col = get_attr("shadowColor") if col: shadowColor = parseColor(col) else: shadowColor = gRGB(0) col = get_attr("shadowOffset") if col: shadowOffset = get_attr("shadowOffset") else: shadowOffset = "-3,-3" shadowOffset = parsePosition(shadowOffset, scale) substyles[get_attr("name")] = (font, haveColor, foregroundColor, borderColor, borderWidth, shadowColor, shadowOffset) self.__subStyles[filename] = substyle return substyles def _loadEmbeddedStyle(self, substyles): from enigma import eWidget, eSubtitleWidget for faceName in substyles.keys(): s = substyles[faceName] face = eSubtitleWidget.__dict__[faceName] font, haveColor, foregroundColor, borderColor, borderWidth, shadowColor, shadowOffset = s[0], s[1], s[2], s[3], s[4], s[5], s[6] try: eSubtitleWidget.setFontStyle(face, font , haveColor, foregroundColor, borderColor, borderWidth) except TypeError: eSubtitleWidget.setFontStyle(face, font, haveColor, foregroundColor, shadowColor, shadowOffset) def resetEmbeddedDefaults(self): userSkin = resolveFilename(SCOPE_SKIN, 'skin_user.xml') defaultSkin = resolveFilename(SCOPE_SKIN, 'skin_default.xml') skinSubtitles = resolveFilename(SCOPE_SKIN, 'skin_subtitles.xml') skinPaths = [userSkin, skinSubtitles, defaultSkin] for skinPath in skinPaths: if fileExists(skinPath): styles = self._parseEmbeddedStyles(skinPath) if styles: print "[SubsEmbeddedSupport] reseting defaults from", skinPath self._loadEmbeddedStyle(styles) break def exitEmbeddedSubs(self): if self.subtitle_window is not None: self.session.deleteDialog(self.subtitle_window) self.subtitle_window = None if not QuickSubtitlesConfigMenu: self.resetEmbeddedDefaults() class SubsSupport(SubsSupportEmbedded): """Client class for subtitles If this class is not subclass of InfoBarBase you should use public function of this class to to connect your media player (resume,pause,exit,after seeking, subtitles setup) functions with subtitles @param session: set active session @param subsPath: set path for subtitles to load @param defaultPath: set default path when choosing external subtitles @param forceDefaultPath: always use default path when choosing external subtitles @param autoLoad: tries to auto load subtitles according to name of played file @param embeddedSupport: added support for embedded subtitles """ def __init__(self, session=None, subsPath=None, defaultPath=None, forceDefaultPath=False, autoLoad=True, showGUIInfoMessages=True, embeddedSupport=False, preferEmbedded=False, searchSupport=False, configEntry=None): if session is not None: self.session = session self.searchSupport = searchSupport self.subsSettings = initSubsSettings(configEntry) SubsSupportEmbedded.__init__(self, embeddedSupport, preferEmbedded) self.__subsScreen = self.session.instantiateDialog(SubsScreen, self.subsSettings.external) self.__subsScreen.hide() self.__subsEngine = SubsEngine(self.session, self.subsSettings.engine, self.__subsScreen) self.__subsLoader = SubsLoader(PARSERS, ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()]) self.__subsLoader.set_row_parsing(False) self.__loaded = False self.__working = False self.__firstStart = True self.__autoLoad = autoLoad self.__subsPath = None self.__subsDir = None self.__subsEnc = None self.__playerDelay = 0 self.__startDelay = int(self.subsSettings.engine.expert.startDelay.value) self.__defaultPath = None self.__isServiceSet = False self.__subclassOfInfobarBase = isinstance(self, InfoBarBase) self.__forceDefaultPath = forceDefaultPath self.__showGUIInfoMessages = showGUIInfoMessages self.__checkTimer = eTimer() self.__starTimer = eTimer() self.__starTimer.callback.append(self.__updateSubs) try: from Screens.InfoBar import InfoBar InfoBar.instance.subtitle_window.hide() except Exception: pass if self.__subclassOfInfobarBase: self.__event_tracker = ServiceEventTracker(screen=self, eventmap= { iPlayableService.evStart: self.__serviceStarted, iPlayableService.evEnd: self.__serviceStopped, iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged, }) self["SubsActions"] = HelpableActionMap(self, "SubtitlesActions", { "subtitles": (self.subsMenu, _("show subtitles menu")), } , -5) self.onClose.append(self.exitSubs) if defaultPath is not None and os.path.isdir(toString(defaultPath)): self.__defaultPath = toString(defaultPath) self.__subsDir = toString(defaultPath) if subsPath is not None and self.__autoLoad: self.loadSubs(subsPath) def loadSubs(self, subsPath, newService=True): """loads subtitles from subsPath @param subsPath: path to subtitles (http url supported) @param newService: set False if service remains the same @return: True if subtitles was successfully loaded @return: False if subtitles wasnt successfully loaded """ self.__working = True self.__subsPath = None if self.__defaultPath is not None: self.__subsDir = self.__defaultPath else: self.__subsDir = None if subsPath is not None: subsPath = toString(subsPath) if not subsPath.startswith('http'): if self.__defaultPath is not None and self.__forceDefaultPath: self.__subsDir = self.__defaultPath else: if os.path.isdir(os.path.dirname(subsPath)): self.__subsDir = os.path.dirname(subsPath) else: self.__subsDir = self.__defaultPath if not os.path.isfile(subsPath): print '[Subtitles] trying to load not existing path:', subsPath subsPath = None if subsPath is not None: subsList, self.__subsEnc = self.__processSubs(subsPath, self.__subsEnc) if subsList is not None: self.__subsPath = subsPath if newService: self.__subsEngine.reset() self.__subsEngine.pause() self.__subsEngine.setPlayerDelay(self.__playerDelay) self.__subsEngine.setSubsList(subsList) self.__loaded = True self.__working = False return True else: self.__subsEnc = None self.__subsPath = None self.__working = False return False def startSubs(self, time): """If subtitles are loaded then start to play them after time set in ms""" def wrapped(): self.__startTimer.start(time, True) if self.__working or self.__loaded: self.__afterWork(wrapped) def isSubsLoaded(self): return self.__loaded def getSubsFileFromSref(self): ref = self.session.nav.getCurrentlyPlayingServiceReference() if os.path.isdir(os.path.dirname(ref.getPath())): self.__subsDir = os.path.dirname(ref.getPath()) for parser in PARSERS: for ext in parser.parsing: subsPath = os.path.splitext(ref.getPath())[0] + ext if os.path.isfile(subsPath): return subsPath return None def resumeSubs(self): if self.__loaded: print '[Subtitles] resuming subtitles' self.showSubsDialog() self.__subsEngine.resume() def pauseSubs(self): if self.__loaded: print '[Subtitles] pausing subtitles' self.__subsEngine.pause() def playAfterSeek(self): if self.__loaded: self.showSubsDialog() self.__subsEngine.sync() def showSubsDialog(self): if self.__loaded: print '[Subtitles] show dialog' self.__subsScreen.show() def hideSubsDialog(self): if self.__loaded: print '[Subtitles] hide dialog' if self.__subsScreen: self.__subsScreen.hide() def setPlayerDelay(self, delayInMs): self.__playerDelay = delayInMs def subscribeOnSubsDelayChanged(self, fnc): if fnc not in self.__subsEngine.onSubsDelayChanged: self.__subsEngine.onSubsDelayChanged.append(fnc) def unsubscribeOnSubsDelayChanged(self, fnc): if fnc in self.__subsEngine.onSubsDelayChanged: self.__subsEngine.onSubsDelayChanged.remove(fnc) def setSubsDelay(self, delayInMs): if self.__loaded: self.__subsEngine.setSubsDelay(delayInMs) def setSubsDelayToNextSubtitle(self): if self.__loaded: self.__subsEngine.setSubsDelayToNextSubtitle() def setSubsDelayToPrevSubtitle(self): if self.__loaded: self.__subsEngine.setSubsDelayToPrevSubtitle() def getSubsDelay(self): if self.__loaded: return self.__subsEngine.getSubsDelay() def subscribeOnSubsFpsChanged(self, fnc): if fnc not in self.__subsEngine.onFpsChanged: self.__subsEngine.onFpsChanged.append(fnc) def unsubscribeOnSubsFpsChanged(self, fnc): if fnc in self.__subsEngine.onFpsChanged: self.__subsEngine.onFpsChanged.remove(fnc) def setSubsFps(self, fps): if self.__loaded: return self.__subsEngine.setSubsFps(fps) def getSubsFps(self): if self.__loaded: return self.__subsEngine.getSubsFps() def getSubsPath(self): return self.__subsPath def subsMenu(self): if not self.__working and not (self.__subclassOfInfobarBase and not self.__isServiceSet): self.__alreadyPausedVideo = False if self.subsSettings.pauseVideoOnSubtitlesMenu.value: print '[SubsSupport] stopVideoOnSubtitlesMenu: True' if isinstance(self, InfoBarSeek): if self.seekstate == InfoBarSeek.SEEK_STATE_PLAY: print '[SubsSupport] pausing video' self.setSeekState(InfoBarSeek.SEEK_STATE_PAUSE) else: print '[SubsSupport] video is already paused' self.__alreadyPausedVideo = True else: print '[SubsSupport] not subclass of InfobarSeek' self.session.openWithCallback(self.__subsMenuCB, SubsMenu, self, self.__subsPath, self.__subsDir, self.__subsEnc, self.embeddedSupport, self.embeddedEnabled, self.searchSupport) def resetSubs(self, resetEnc=True, resetEngine=True, newSubsScreen=False, newService=True): """ Resets subtitle state -> stops engine, reload encodings, reset paths.. @param resetEnc : start trying encodings from beginning of current encodings-group list @param resetEngine: clean active subtitle, subtitle list, reset engine vars @param newSubsSCreen: recreates subtitles screen @param newService: set to True if new servicereference is in use """ # start trying encodings from beginning of encodings_group list if resetEnc: self.__subsEnc = None self.__subsLoader.change_encodings(ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()]) self.__subsEngine.pause() # stop subtitles, clean active subtitle, subtitles list, reset delay # if new service -> remove service if resetEngine: self.__subsEngine.setSubsDelay(0) self.__subsEngine.reset() if newService: self.__firstStart = False # hide subtitles, reload screen with new settings # if newSubsScreen, remove current subscreen and create new one self.__resetSubsScreen(newSubsScreen) self.__subsPath = None self.__loaded = False def __resetSubsScreen(self, newSubsScreen=False): self.__subsScreen.hide() if newSubsScreen: self.__subsEngine.setRenderer(None) self.session.deleteDialog(self.__subsScreen) self.__subsScreen = self.session.instantiateDialog(self._getSubsScreenCls(), self.subsSettings.external) self.__subsEngine.setRenderer(self.__subsScreen) else: self.__subsScreen.reloadSettings() self.__subsScreen.show() def exitSubs(self): """This method should be called at the end of usage of this class""" self.hideSubsDialog() if self.__subsEngine: self.__subsEngine.exit() self.__subsEngine = None if self.__subsScreen: self.session.deleteDialog(self.__subsScreen) self.__subsScreen = None self.__starTimer.stop() self.__starTimer = None self.__checkTimer.stop() self.__checkTimer = None print '[SubsSupport] closing subtitleDisplay' def __subsMenuCB(self, subsPath, subsEmbedded, settingsChanged, changeEncoding, changedEncodingGroup, changedShadowType, reloadEmbeddedScreen, turnOff, forceReload=False): if self.embeddedEnabled and self.embeddedSupport and not subsEmbedded and not turnOff and not subsPath: print "embedded settings changed" self.resetEmbeddedSubs(reloadEmbeddedScreen) elif turnOff: print '[SubsSupport] turn off' if self.embeddedSupport and self.embeddedEnabled: self.enableSubtitle(None) if self.__loaded: self.resetSubs(newService=False) elif self.embeddedSupport and subsEmbedded: print '[SubsSupport] loading embedded subtitles' if self.__loaded: self.resetSubs() self.__subsScreen.hide() self.enableSubtitle(subsEmbedded) elif subsPath is not None: if self.embeddedEnabled: self.enableSubtitle(None) newScreen = changedShadowType self.__subsScreen.show() if self.__subsPath == subsPath: if not settingsChanged and not ((changeEncoding or changedEncodingGroup) or newScreen or forceReload): print '[SubsSupport] no changes made' elif settingsChanged and not (newScreen or changedEncodingGroup or forceReload): print '[SubSupport] reloading SubScreen' self.__subsEngine.pause() self.__resetSubsScreen() self.__subsEngine.resume() else: self.__subsEngine.pause() if changedEncodingGroup or (changedShadowType and not changeEncoding) or forceReload: self.__subsEnc = None if changedEncodingGroup: self.__subsLoader.change_encodings(ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()]) if newScreen: self.__resetSubsScreen(newSubsScreen=True) self.__subsEngine.reset(position=False) if self.loadSubs(subsPath, newService=False): self.__subsEngine.refresh() else: self.pauseSubs() self.__subsEnc = None if changedEncodingGroup: self.__subsLoader.change_encodings(ALL_LANGUAGES_ENCODINGS + ENCODINGS[self.subsSettings.encodingsGroup.getValue()]) if newScreen: self.__resetSubsScreen(newSubsScreen=True) self.__subsEngine.reset() if self.__loaded: if self.loadSubs(subsPath, newService=False): self.__subsEngine.refresh() else: if self.loadSubs(subsPath, newService=False): self.__subsEngine.resume() if not self.__alreadyPausedVideo and isinstance(self, InfoBarSeek): print '[SubsSupport] unpausing video' del self.__alreadyPausedVideo self.setSeekState(InfoBarSeek.SEEK_STATE_PLAY) def __processSubs(self, subsPath, subsEnc): showMessages = self.__showGUIInfoMessages and not (self.__firstStart and self.__subclassOfInfobarBase) try: return self.__subsLoader.load(subsPath, subsEnc, getFps(self.session)) except LoadError: if showMessages: warningMessage(self.session, _("Cannot load subtitles. Invalid path")) return None, None except DecodeError: if showMessages: warningMessage(self.session, _("Cannot decode subtitles. Try another encoding group")) return None, None except ParserNotFoundError: if showMessages: warningMessage(self.session, _("Cannot parse subtitles. Not supported subtitles format")) return None, None except ParseError: if showMessages: warningMessage(self.session, _("Cannot parse subtitles. Invalid subtitles format")) return None, None finally: self.__firstStart = False def __updateSubs(self): if self.__loaded: self.resumeSubs() return subsPath = self.getSubsFileFromSref() if subsPath is not None: if self.loadSubs(subsPath): self.resumeSubs() self.__working = False def __afterWork(self, fnc): def checkWorking(): if self.__working: print 'check working..' self.__checkTimer.start(200, True) else: self.__checkTimer.stop() fnc() self.__checkTimer.stop() self.__starTimer.stop() if self.__working: del self.__checkTimer.callback[:] self.__checkTimer.callback.append(checkWorking) self.__checkTimer.start(200, True) else: fnc() ############ Methods triggered by videoEvents when SubsSupport is subclass of Screen ################ def __serviceStarted(self): print '[SubsSupport] Service Started' def startSubs(): self.__starTimer.start(self.__startDelay, True) self.__isServiceSet = True # subtitles are loading or already loaded if self.__working or self.__loaded: self.__afterWork(startSubs) else: self.resetSubs(True) if self.__subsPath is None and self.__autoLoad: startSubs() def __serviceStopped(self): self.resetSubs(True) self.__isServiceSet = False def __seekableStatusChanged(self): if not hasattr(self, 'seekstate'): return if self.seekstate == self.SEEK_STATE_PLAY: self.pauseSubs() elif self.seekstate == self.SEEK_STATE_PAUSE: self.resumeSubs() elif self.seekstate == self.SEEK_STATE_EOF: self.resetSubs(True) ########### Methods which extends InfobarSeek seek methods def doSeekRelative(self, pts): if self.__loaded: # self.__subsEngine.preSeek(pts) super(SubsSupport, self).doSeekRelative(pts) self.playAfterSeek() else: super(SubsSupport, self).doSeekRelative(pts) def doSeek(self, pts): if self.__loaded: super(SubsSupport, self).doSeek(pts) self.playAfterSeek() else: super(SubsSupport, self).doSeek(pts) ############################################################ class SubsEmbeddedScreen(Screen): """ Pli defaults """ def __init__(self, session, embeddedSettings): desktop = getDesktop(0) size = desktop.size() vSizeOrig = size.height() hSizeOrig = size.width() if QuickSubtitlesConfigMenu: vPosition = 0 vSize = vSizeOrig else: vPositionPercent = int(embeddedSettings.position.value) fontSize = int(getEmbeddedFontSizeCfg(embeddedSettings.font.size).value) vSize = fontSize * 4 + 10 vPosition = int(vPositionPercent * float((vSizeOrig - vSize) / 100)) vPosition = vPosition if vPosition > 0 else 0 vSize = vSizeOrig - vPosition self.skin = """""" % (vPosition, hSizeOrig, vSize) Screen.__init__(self, session) class SubtitlesWidget(GUIComponent): STATE_NO_BACKGROUND, STATE_BACKGROUND = range(2) def __init__(self, boundDynamic=True, boundXOffset=10, boundYOffset=10, boundSize=None, fontSize=25, positionPercent=94): GUIComponent.__init__(self) self.state = self.STATE_BACKGROUND self.boundDynamic = boundDynamic self.boundXOffset = boundXOffset self.boundYOffset = boundYOffset self.font = (gFont("Regular", fontSize), fontSize) self.positionPercent = positionPercent desktopSize = getDesktop(0).size() self.desktopSize = (desktopSize.width(), desktopSize.height()) self.boundSize = boundSize or (self.desktopSize[0], self.calcWidgetHeight()) def GUIcreate(self, parent): self.instance = eLabel(parent) self.instance2 = eLabel(parent) self.postWidgetCreate() def GUIdelete(self): self.preWidgetRemove() self.instance = None self.instance2 = None def postWidgetCreate(self): self.instance2.hide() self.update() def preWidgetRemove(self): pass def calcWidgetYPosition(self): return int((self.desktopSize[1] - self.calcWidgetHeight() - self.boundYOffset) / float(100) * self.positionPercent) def calcWidgetHeight(self): return int(4 * self.font[1] + 15) def update(self): ds = self.desktopSize bs = self.boundSize = (self.desktopSize[0], self.calcWidgetHeight()) self.instance2.resize(eSize(int(ds[0]), int(bs[1]))) self.instance2.move(ePoint(int(0), int(self.calcWidgetYPosition()))) self.instance2.setHAlign(self.instance2.alignCenter) self.instance2.setVAlign(self.instance2.alignCenter) self.instance2.setFont(self.font[0]) self.instance2.setTransparent(True) if not self.boundDynamic: self.instance.resize(eSize(int(bs[0]), int(bs[1]))) self.instance.move(ePoint(int(ds[0] / 2 - bs[0] / 2), int(self.calcWidgetYPosition()))) self.instance.setFont(self.font[0]) self.instance.setHAlign(self.instance.alignCenter) self.instance.setVAlign(self.instance.alignCenter) def setText(self, text): if self.instance and self.instance2: if self.state == self.STATE_NO_BACKGROUND: self.instance.hide() self.instance2.setText(text) self.instance2.show() elif self.state == self.STATE_BACKGROUND: self.instance2.hide() if not text: self.instance.hide() return if self.boundDynamic: # hack so empty spaces are part of calculateSize calculation self.instance2.setText(text.replace(' ','.')) ds = self.desktopSize bs = self.boundSize ws = self.instance2.calculateSize() ws = (ws.width() + self.boundXOffset * 2, ws.height() + self.boundYOffset * 2) wp = self.instance2.position() wp = (wp.x(), wp.y()) wpy = wp[1] + (bs[1] - ws[1]) / 2 wpx = ds[0] / 2 - ws[0] / 2 self.instance.resize(eSize(int(ws[0]), int(ws[1]))) self.instance.move(ePoint(int(wpx), int(wpy))) else: bs = self.boundSize ds = self.desktopSize self.instance.resize(eSize(int(bs[0]), int(bs[1]))) self.instance.move(ePoint(int(ds[0] / 2 - bs[0] / 2), int(self.calcWidgetYPosition()))) self.instance.setHAlign(self.instance.alignCenter) self.instance.setVAlign(self.instance.alignCenter) self.instance.setText(text) self.instance.show() def setPosition(self, percent): self.positionPercent = percent self.update() def setBoundDynamic(self, value): self.boundDynamic = value self.update() def setBoundOffset(self, offsetX, offsetY): self.boundXOffset = int(offsetX) self.boundYOffset = int(offsetY) self.update() def setForegroundColor(self, color): self.instance.setForegroundColor(parseColor(color)) self.instance2.setForegroundColor(parseColor(color)) def setBackgroundColor(self, color): if color[1:3] == "ff": self.state = self.STATE_NO_BACKGROUND else: self.state = self.STATE_BACKGROUND self.instance.setBackgroundColor(parseColor(color)) def setBorderColor(self, color): self.instance.setBorderColor(parseColor(color)) self.instance2.setBorderColor(parseColor(color)) def setBorderWidth(self, width): self.instance.setBorderWidth(int(width)) self.instance2.setBorderWidth(int(width)) def setShadowColor(self, color): self.instance.setShadowColor(parseColor(color)) self.instance2.setShadowColor(parseColor(color)) def setShadowOffset(self, offset, scale): self.instance.setShadowOffset(parsePosition(offset, scale)) self.instance2.setShadowOffset(parsePosition(offset, scale)) def setFont(self, font): if self.font[1] == font[1]: self.font = font self.instance.setFont(font[0]) self.instance2.setFont(font[0]) else: self.font = font self.update() class SubsScreen(Screen): def __init__(self, session, externalSettings): self.subShown = False self.__shadowType = 'border' self.__eLabelHasBorderParams = False self.externalSettings = externalSettings fontSize = int(externalSettings.font.size.getValue()) self.font = { "regular":{ 'gfont':(gFont(externalSettings.font.regular.type.value, fontSize), fontSize), 'color':externalSettings.font.regular.alpha.value + externalSettings.font.regular.color.value }, "italic":{ 'gfont':(gFont(externalSettings.font.italic.type.value, fontSize), fontSize), 'color':externalSettings.font.italic.alpha.value + externalSettings.font.italic.color.value }, "bold":{ 'gfont':(gFont(externalSettings.font.bold.type.value, fontSize), fontSize), 'color':externalSettings.font.bold.alpha.value + externalSettings.font.bold.type.value } } self.selectedFont = "regular" self.currentColor = externalSettings.font.regular.color.value self.skin = """ """ % (getDesktop(0).size().width(), getDesktop(0).size().height()) Screen.__init__(self, session) self.stand_alone = True self["subtitles"] = SubtitlesWidget() self.onLayoutFinish.append(self.__checkElabelCaps) self.onLayoutFinish.append(self.reloadSettings) def __checkElabelCaps(self): if hasattr(self["subtitles"].instance, 'setBorderWidth') and hasattr(self["subtitles"].instance, 'setBorderColor'): self.__eLabelHasBorderParams = True elif self.__shadowType == 'border': self.__shadowType = 'offset' def setShadowType(self, type): if type == 'border' and self.__eLabelHasBorderParams: self.__shadowType = 'border' else: self.__shadowType = 'offset' def setShadow(self, type, color, size=None, xOffset=None, yOffset=None): self.setShadowType(type) if self.__shadowType == 'border': self["subtitles"].setBorderColor("#" + color) elif self.__shadowType == 'offset': self["subtitles"].setShadowColor("#" + color) if self.__shadowType == 'border' and size is not None: self["subtitles"].setBorderWidth(size) elif self.__shadowType == 'offset' and (xOffset is not None and yOffset is not None): self["subtitles"].setShadowOffset(str(-xOffset) + ',' + str(-yOffset), self.scale) def setBackground(self, type, alpha, color, xOffset=None, yOffset=None): if type == 'dynamic': self["subtitles"].setBoundDynamic(True) self["subtitles"].setBoundOffset(xOffset, yOffset) else: self["subtitles"].setBoundDynamic(False) color = "#" + alpha + color self["subtitles"].setBackgroundColor(color) def setColor(self, color): self.currentColor = color color = "#" + color self["subtitles"].setForegroundColor(color) def setPosition(self, position): self["subtitles"].setPosition(position) def setFonts(self, font): self.font = font self['subtitles'].setFont(self.font['regular']['gfont']) def reloadSettings(self): shadowType = self.externalSettings.shadow.type.getValue() shadowColor = self.externalSettings.shadow.color.getValue() shadowSize = int(self.externalSettings.shadow.size.getValue()) shadowXOffset = int(self.externalSettings.shadow.xOffset.getValue()) shadowYOffset = int(self.externalSettings.shadow.yOffset.getValue()) shadowEnabled = int(self.externalSettings.shadow.enabled.getValue()) if not shadowEnabled: shadowXOffset = shadowYOffset = shadowSize = 0 backgroundType = self.externalSettings.background.type.getValue() backgroundAlpha = self.externalSettings.background.alpha.getValue() backgroundColor = self.externalSettings.background.color.getValue() backgroundXOffset = self.externalSettings.background.xOffset.getValue() backgroundYOffset = self.externalSettings.background.yOffset.getValue() backgroundEnabled = self.externalSettings.background.enabled.getValue() if not backgroundEnabled: backgroundAlpha = "ff" backgroundColor = "ffffff" fontSize = int(self.externalSettings.font.size.getValue()) position = int(self.externalSettings.position.getValue()) self.setPosition(position) self.setShadow(shadowType, shadowColor, shadowSize, shadowXOffset, shadowYOffset) self.setBackground(backgroundType, backgroundAlpha, backgroundColor, backgroundXOffset, backgroundYOffset) externalSettings = self.externalSettings self.setFonts({ "regular":{ 'gfont':(gFont(externalSettings.font.regular.type.value, fontSize), fontSize), 'color':externalSettings.font.regular.alpha.value + externalSettings.font.regular.color.value }, "italic":{ 'gfont':(gFont(externalSettings.font.italic.type.value, fontSize), fontSize), 'color':externalSettings.font.italic.alpha.value + externalSettings.font.italic.color.value }, "bold":{ 'gfont':(gFont(externalSettings.font.bold.type.value, fontSize), fontSize), 'color':externalSettings.font.bold.alpha.value + externalSettings.font.bold.color.value } }) def setSubtitle(self, sub): if sub['style'] != self.selectedFont: self.selectedFont = sub['style'] self['subtitles'].setFont(self.font[sub['style']]['gfont']) color = sub['color'] if color == "default": color = self.font[sub['style']]['color'] self.setColor(color) self["subtitles"].setText(toString(sub['text'])) self.subShown = True def hideSubtitle(self): if self.subShown: self["subtitles"].setText("") self.subShown = False class SubsEngine(object): def __init__(self, session, engineSettings, renderer): self.session = session self.engineSettings = engineSettings self.renderer = renderer self.subsList = None self.position = 0 self.sub = None self.subsFpsRatio = 1 self.onSubsFpsChanged = [] self.subsDelay = 0 self.onSubsDelayChanged = [] self.playerDelay = 0 self.syncDelay = 300 self.hideInterval = 200 * 90 self.__seek = None self.__pts = None self.__ptsDelay = None self.__callbackPts = None self.preDoPlay = [self.updateSubPosition] self.refreshTimer = eTimer() self.refreshTimer.callback.append(self.play) self.refreshTimerDelay = 1000 self.hideTimer = eTimer() self.hideTimer.callback.append(self.checkHideSub) self.hideTimer.callback.append(self.incSubPosition) self.hideTimer.callback.append(self.doPlay) self.getPlayPtsTimer = eTimer() self.getPlayPtsTimer.callback.append(self.getPts) self.getPlayPtsTimer.callback.append(self.validPts) self.getPlayPtsTimer.callback.append(self.callbackPts) self.getPlayPtsTimerDelay = 200 self.resume = self.play self.addNotifiers() def addNotifiers(self): def hideInterval(configElement): self.hideInterval = int(configElement.value) * 90 def playerDelay(configElement): self.playerDelay = int(configElement.value) * 90 def syncDelay(configElement): self.syncDelay = int(configElement.value) def getPlayPtsTimerDelay(configElement): self.getPlayPtsTimerDelay = int(configElement.value) def refreshTimerDelay(configElement): self.refreshTimerDelay = int(configElement.value) self.engineSettings.expert.hideDelay.addNotifier(hideInterval) self.engineSettings.expert.playerDelay.addNotifier(playerDelay) self.engineSettings.expert.syncDelay.addNotifier(syncDelay) self.engineSettings.expert.ptsDelayCheck.addNotifier(getPlayPtsTimerDelay) self.engineSettings.expert.refreshDelay.addNotifier(refreshTimerDelay) def removeNotifiers(self): del self.engineSettings.expert.hideDelay.notifiers[:] del self.engineSettings.expert.playerDelay.notifiers[:] del self.engineSettings.expert.syncDelay.notifiers[:] del self.engineSettings.expert.ptsDelayCheck.notifiers[:] del self.engineSettings.expert.refreshDelay.notifiers[:] def getPlayPts(self, callback, delay=None): self.getPlayPtsTimer.stop() self.__callbackPts = callback self.__ptsDelay = delay self.__pts = None if delay is None: delay = 1 self.getPlayPtsTimer.start(delay, True) def getPts(self): try: if not self.__seek: service = self.session.nav.getCurrentService() self.__seek = service.seek() except Exception: return r = self.__seek.getPlayPosition() if r[0]: self.__pts = None else: self.__pts = long(r[1]) + self.playerDelay def validPts(self): pass def callbackPts(self): if self.__pts is not None: self.getPlayPtsTimer.stop() self.__callbackPts() else: delay = self.getPlayPtsTimerDelay if self.__ptsDelay is not None: delay = self.__ptsDelay self.getPlayPtsTimer.start(delay) def setSubsList(self, subslist): self.subsList = subslist def setRenderer(self, renderer): self.renderer = renderer def setPlayerDelay(self, playerDelay): self.pause() self.playerDelay = playerDelay * 90 self.resume() def setSubsFps(self, subsFps): print "[SubsEngine] setSubsFps - setting fps to %s"% str(subsFps) videoFps = getFps(self.session, True) if videoFps is None: print "[SubsEngine] setSubsFps - cannot get video fps!" else: self.pause() self.subsFpsRatio = subsFps/ float(videoFps) for f in self.onSubsFpsChanged: f(self.getSubsFps()) self.resume() def getSubsFps(self): videoFps = getFps(self.session, True) if videoFps is None: return None return fps_float(self.subsFpsRatio * videoFps) def setSubsDelay(self, delayInMs): print "[SubsEngine] setSubsDelay - setting delay to %sms"% str(delayInMs) self.pause() self.subsDelay = int(delayInMs) * 90 for f in self.onSubsDelayChanged: f(self.getSubsDelay()) self.resume() def getSubsDelay(self): return self.subsDelay / 90 def getSubsPosition(self): return self.position def setSubsDelayToNextSubtitle(self): def setDelay(): if not self.renderer.subShown: print '[SubsEngine] setDelayToNextSubtitle - next pos: %d of %d'%(self.position, len(self.subsList)) toSub = self.subsList[self.position] # position is incremented right after subtitle is hidden so we don't do anything elif self.renderer.subShown and self.position != len(self.subsList) -1: print '[SubsEngine] setDelayToNextSubtitle - next pos: %d of %d'%(self.position+1, len(self.subsList)) toSub = self.subsList[self.position+1] else: print '[SubsEngine] setDelayToNextSubtitle - we are on last subtitle' return toSubDelay = (self.__pts - (toSub['start'] * self.subsFpsRatio))/90 self.setSubsDelay(toSubDelay) self.stopTimers() self.getPlayPts(setDelay) def setSubsDelayToPrevSubtitle(self): def setDelay(): if not self.renderer.subShown: print '[SubsEngine] setDelayToPrevSubtitle - skipping to start of current sub' # position is incremented right after subtitle is hidden so we have to go one back toSub = self.subsList[self.position-1] elif self.renderer.subShown and self.position != 0: print '[SubsEngine] setDelayToPrevSubtitle - prev pos: %d of %d'%(self.position-1, len(self.subsList)) toSub = self.subsList[self.position -1] else: print '[SubsEngine] setDelayToPrevSubtitle - we are on first subtitle' return toSubDelay = (self.__pts - (toSub['start'] * self.subsFpsRatio))/90 self.setSubsDelay(toSubDelay) self.stopTimers() self.getPlayPts(setDelay) def reset(self, position=True): self.stopTimers() self.hideSub() if position: self.position = 0 self.__seek = None self.__pts = None self.__callbackPts = None self.sub = None self.subsDelay = 0 self.subsFpsRatio = 1 def refresh(self): self.stopTimers() self.hideSub() self.refreshTimer.start(self.refreshTimerDelay, True) def pause(self): self.stopTimers() self.hideSub() def play(self): self.stopTimers() self.hideSub() self.getPlayPts(self.prePlay) def sync(self): self._oldPts = None def checkPts(): if self._oldPts is None: self._oldPts = self.__pts self.getPlayPts(checkPts, self.syncDelay) # video is frozen no progress made elif self._oldPts == self.__pts: self._oldPts = None self.getPlayPts(checkPts, self.syncDelay) # abnormal pts elif (self.__pts > self._oldPts + self.syncDelay * 90 + (200 * 90)) or ( self.__pts < self._oldPts + self.syncDelay * 90 - (200 * 90)): self._oldPts = None self.getPlayPts(checkPts, self.syncDelay) # normal playback else: del self._oldPts self.updateSubPosition() self.doPlay() self.stopTimers() self.hideSub() self.getPlayPts(checkPts, self.syncDelay) def prePlay(self): for f in self.preDoPlay: f() self.doPlay() def doPlay(self): if self.position == len(self.subsList): print '[SubsEngine] reached end of subtitle list' self.position = len(self.subsList) - 1 self.stopTimers() else: self.sub = self.subsList[self.position] self.getPlayPts(self.doWait) def doWait(self): subStartPts = int(self.sub['start'] * self.subsFpsRatio) + self.subsDelay if self.__pts < subStartPts: diffPts = subStartPts - self.__pts diffMs = diffPts / 90 if diffMs > 50: self.getPlayPts(self.doWait, diffMs) else: print '[SubsEngine] sub shown sooner by %dms' % diffMs self.renderSub() else: subsEndPts = (self.sub['end'] * self.subsFpsRatio) + self.subsDelay if subsEndPts - self.__pts < 0: #print '[SubsEngine] sub should be already shown - %dms, skipping...'%((subsEndPts - self.__pts)/90) self.getPlayPts(self.skipSubs, 100) else: print '[SubsEngine] sub shown later by %dms' % ((self.__pts - subStartPts)/90) self.renderSub() def skipSubs(self): if self.position == len(self.subsList) - 1: self.incSubPosition() else: self.updateSubPosition() self.doPlay() def renderSub(self): duration = int(self.sub['duration'] * self.subsFpsRatio) self.renderer.setSubtitle(self.sub) self.hideTimer.start(duration, True) def checkHideSub(self): if self.subsList[-1] == self.sub: self.hideSub() elif (self.subsList[self.position]['end'] * self.subsFpsRatio) + self.hideInterval < (self.subsList[self.position + 1]['start'] * self.subsFpsRatio): self.hideSub() def hideSub(self): self.renderer.hideSubtitle() def incSubPosition(self): self.position += 1 def updateSubPosition(self): playPts = self.__pts print '[SubsEngine] pre-update sub position:', self.position subStartPts = (self.subsList[self.position]['start'] * self.subsFpsRatio) + self.subsDelay subStartEndPts = (self.subsList[self.position]['end'] * self.subsFpsRatio) + self.subsDelay # seek backwards if subStartPts > playPts: subPrevEndPts = (self.subsList[self.position -1]['end'] * self.subsFpsRatio) + self.subsDelay while self.position > 0 and subPrevEndPts > playPts: self.position -= 1 subPrevEndPts = (self.subsList[self.position - 1]['end'] * self.subsFpsRatio) + self.subsDelay # seek forward elif subStartPts < playPts and subStartEndPts < playPts: while self.position < len(self.subsList) - 1 and subStartPts < playPts and subStartEndPts < playPts: self.position += 1 subStartPts = (self.subsList[self.position]['start'] * self.subsFpsRatio) + self.subsDelay subStartEndPts = (self.subsList[self.position]['end'] * self.subsFpsRatio) + self.subsDelay print '[SubsEngine] post-update sub position:', self.position def showDialog(self): self.renderer.show() def hideSubtitlesDialog(self): self.renderer.hide() def stopTimers(self): if self.refreshTimer is not None: self.refreshTimer.stop() if self.getPlayPtsTimer is not None: self.getPlayPtsTimer.stop() if self.hideTimer is not None: self.hideTimer.stop() def exit(self): self.hideTimer = None self.refreshTimer = None self.getPlayPtsTimer = None del self.onSubsDelayChanged[:] del self.onSubsFpsChanged[:] self.removeNotifiers() class PanelList(MenuList): def __init__(self, list, height=30): MenuList.__init__(self, list, False, eListboxPythonMultiContent) self.l.setItemHeight(height) self.l.setFont(0, gFont("Regular", 20)) self.l.setFont(1, gFont("Regular", 17)) def PanelListEntry(name, mode): res = [(name, mode)] res.append(MultiContentEntryText(pos=(5, 5), size=(330, 25), font=0, flags=RT_VALIGN_CENTER, text=name)) return res def PanelColorListEntry(name, value, colorName, colorValue, sizePanelX): res = [(name)] res.append(MultiContentEntryText(pos=(0, 5), size=(sizePanelX, 30), font=1, flags=RT_HALIGN_LEFT, text=name, color=colorName)) res.append(MultiContentEntryText(pos=(0, 5), size=(sizePanelX, 30), font=1, flags=RT_HALIGN_RIGHT, text=value, color=colorValue)) return res class SubsMenu(Screen): skin = """ """ def __init__(self, session, infobar, subfile=None, subdir=None, encoding=None, embeddedSupport=False, embeddedEnabled=False, searchSupport=False): Screen.__init__(self, session) self.infobar = infobar self.subfile = subfile self.subdir = subdir self.encoding = encoding self.embeddedSupport = embeddedSupport self.embeddedEnabled = embeddedEnabled self.searchSupport = searchSupport self.embeddedSubtitle = None self.newSelection = False self.changeEncoding = False self.changedEncodingGroup = False self.changedShadowType = False self.changedSettings = False self.reloadEmbeddedScreen = False self.turnOff = False self.forceReload = False self["title_label"] = Label(_("Currently choosed subtitles")) self["subfile_label"] = Label("") self["subfile_list"] = PanelList([], 25) self["menu_list"] = PanelList([], 28) self["copyright"] = Label("") # self["copyright"] = Label("created by %s <%s>"%(__author__,__email__)) self["actions"] = ActionMap(["SetupActions", "DirectionActions"], { "ok": self.ok, "cancel": self.cancel, }, -2) self.onLayoutFinish.append(self.initTitle) self.onLayoutFinish.append(self.initGUI) self.onLayoutFinish.append(self.disableSelection) def disableSelection(self): self["subfile_list"].selectionEnabled(False) def initTitle(self): self.setTitle("SubsSupport %s" % __version__) def initGUI(self): self.initSubInfo() self.initMenu() def initSubInfo(self): subInfo = [] if self.embeddedEnabled or self.embeddedSubtitle: self["subfile_label"].setText(_("Embedded Subtitles")) self["subfile_label"].instance.setForegroundColor(parseColor("#ffff00")) elif self.subfile is not None: self["subfile_label"].setText(toString(os.path.split(self.subfile)[1])) self["subfile_label"].instance.setForegroundColor(parseColor("#DAA520")) if self.newSelection: pass # subInfo.append(PanelColorListEntry(_("State:"),_("not loaded"), 0xDAA520, 0xffff00, 300)) elif self.encoding and not self.newSelection: # subInfo.append(PanelColorListEntry(_("State:"),_("loaded"), 0xDAA520, 0x00ff00, 300)) subInfo.append(PanelColorListEntry(_("Encoding:"), self.encoding, 0xDAA520, 0xffffff, 300)) elif not self.encoding and not self.newSelection: # subInfo.append(PanelColorListEntry(_("State:"),_("not loaded"), 0xDAA520, 0xffff00, 300)) subInfo.append(PanelColorListEntry(_("Encoding:"), _("cannot decode"), 0xDAA520, 0xffffff, 300)) else: self["subfile_label"].setText(_("None")) self["subfile_label"].instance.setForegroundColor(parseColor("#DAA520")) self["subfile_list"].setList(subInfo) def initMenu(self): self.menu = [(_('Choose subtitles'), 'choose')] if self.searchSupport: self.menu.append((_("Search subtitles"), 'search')) if not self.embeddedEnabled: if self.subfile is not None and not self.newSelection: self.menu.append((_('Change encoding'), 'encoding')) self.menu.append((_('Subtitles settings'), 'settings')) if self.embeddedEnabled and QuickSubtitlesConfigMenu: self.menu.append((_('Subtitles settings (embedded)'), 'settings_embedded_pli')) if self.embeddedEnabled and not QuickSubtitlesConfigMenu: self.menu.append((_('Subtitles settings (embedded)'), 'settings_embedded')) if self.subfile is not None or self.embeddedEnabled: self.menu.append((_('Turn off subtitles'), 'subsoff')) list = [PanelListEntry(x, y) for x, y in self.menu] self["menu_list"].setList(list) def ok(self): mode = self["menu_list"].getCurrent()[0][1] if mode == 'choose': self.session.openWithCallback(self.subsChooserCB, SubsChooser, self.infobar.subsSettings, self.subdir, self.embeddedSupport, False, True) elif mode =='search': self.searchSubs() elif mode == 'settings': self.session.openWithCallback(self.subsSetupCB, SubsSetupMainMisc, self.infobar.subsSettings) elif mode == 'settings_embedded': self.session.openWithCallback(self.subsSetupEmbeddedCB, SubsSetupEmbedded, self.infobar.subsSettings.embedded) elif mode == 'settings_embedded_pli': self.session.open(QuickSubtitlesConfigMenu, self.infobar) elif mode == 'encoding': self.changeEncoding = True self.cancel() elif mode == 'subsoff': self.turnOff = True self.cancel() def getSearchTitleList(self, sName, sPath): searchTitles = [] if sName: searchTitles.append(sName) if sPath: dirname = os.path.basename(os.path.dirname(sPath)) dirnameFix = dirname.replace('.', ' ').replace('_', ' ').replace('-', ' ') filename = os.path.splitext(os.path.basename(sPath))[0] filenameFix = filename.replace('.', ' ').replace('_', ' ').replace('-', ' ') if filename not in searchTitles: searchTitles.append(filename) if filenameFix not in searchTitles: searchTitles.append(filenameFix) if dirname not in searchTitles: searchTitles.append(dirname) if dirnameFix not in searchTitles: searchTitles.append(dirnameFix) return searchTitles def searchSubs(self): def checkDownloadedSubsSelection(downloadedSubtitle=None): if downloadedSubtitle: self.subsChooserCB(downloadedSubtitle, False, True) def paramsDialogCB(callback=None): if callback: self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, sPath, titleList, resetSearchParams=False) def showProvidersErrorCB(callback): if not callback: subsSettings.search.showProvidersErrorMessage.value = False if subsSettings.search.openParamsDialogOnSearch.value: self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, titleList, enabledList=False) else: self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, sPath, titleList) if self.searchSupport: ref = self.session.nav.getCurrentlyPlayingServiceReference() try: sPath = ref.getPath() except Exception: sPath = None try: sName = ref.getName() except Exception: sName = None titleList = self.getSearchTitleList(sName, sPath) subsSettings = self.infobar.subsSettings seeker = E2SubsSeeker(self.session, subsSettings.search, debug=True) if seeker.providers_error and subsSettings.search.showProvidersErrorMessage.value: msg = _("Some subtitles providers are not working") + ".\n" msg += _("For more details please check search settings") + "." msg += "\n\n" msg += _("Do you want to show this message again?") self.session.openWithCallback(showProvidersErrorCB, MessageBox, msg, type=MessageBox.TYPE_YESNO) elif subsSettings.search.openParamsDialogOnSearch.value: self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, titleList, enabledList=False) else: self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, sPath, titleList) def subsChooserCB(self, subfile=None, embeddedSubtitle=None, forceReload=False): if subfile is not None and self.subfile != subfile: self.subfile = subfile self.subdir = os.path.dirname(self.subfile) self.newSelection = True self.embeddedEnabled = False self.cancel() elif subfile is not None and self.subfile == subfile and forceReload: self.forceReload = True self.cancel() elif embeddedSubtitle and embeddedSubtitle != self.infobar.selected_subtitle: self.embeddedSubtitle = embeddedSubtitle self.cancel() # self.initGUI() def subsSetupCB(self, changedSettings=False, changedEncodingGroup=False, changedShadowType=False): self.changedSettings = changedSettings self.changedEncodingGroup = changedEncodingGroup self.changedShadowType = changedShadowType def subsSetupEmbeddedCB(self, reloadEmbeddedScreen=False): self.reloadEmbeddedScreen = reloadEmbeddedScreen def cancel(self): self.close(self.subfile, self.embeddedSubtitle, self.changedSettings, self.changeEncoding, self.changedEncodingGroup, self.changedShadowType, self.reloadEmbeddedScreen, self.turnOff, self.forceReload) # rework class SubsSetupExternal(BaseMenuScreen): @staticmethod def getConfigList(externalSettings): configList = [] shadowType = externalSettings.shadow.type.getValue() shadowEnabled = externalSettings.shadow.enabled.getValue() backgroundType = externalSettings.background.type.getValue() backgroundEnabled = externalSettings.background.enabled.getValue() configList.append(getConfigListEntry(_("Font type (Regular)"), externalSettings.font.regular.type)) configList.append(getConfigListEntry(_("Font color (Regular)"), externalSettings.font.regular.color)) configList.append(getConfigListEntry(_("Font transparency (Regular)"), externalSettings.font.regular.alpha)) configList.append(getConfigListEntry(_("Font type (Italic)"), externalSettings.font.italic.type)) configList.append(getConfigListEntry(_("Font color (Italic"), externalSettings.font.italic.color)) configList.append(getConfigListEntry(_("Font transparency (Italic)"), externalSettings.font.italic.alpha)) configList.append(getConfigListEntry(_("Font type (Bold)"), externalSettings.font.bold.type)) configList.append(getConfigListEntry(_("Font color (Bold)"), externalSettings.font.bold.color)) configList.append(getConfigListEntry(_("Font transparency (Bold)"), externalSettings.font.bold.alpha)) configList.append(getConfigListEntry(_("Font size"), externalSettings.font.size)) configList.append(getConfigListEntry(_("Position"), externalSettings.position)) configList.append(getConfigListEntry(_("Shadow"), externalSettings.shadow.enabled)) if shadowEnabled: configList.append(getConfigListEntry(_("Shadow type"), externalSettings.shadow.type)) if shadowType == 'offset': configList.append(getConfigListEntry(_("Shadow X-offset"), externalSettings.shadow.xOffset)) configList.append(getConfigListEntry(_("Shadow Y-offset"), externalSettings.shadow.yOffset)) else: configList.append(getConfigListEntry(_("Shadow size"), externalSettings.shadow.size)) configList.append(getConfigListEntry(_("Shadow color"), externalSettings.shadow.color)) configList.append(getConfigListEntry(_("Background"), externalSettings.background.enabled)) if backgroundEnabled: configList.append(getConfigListEntry(_("Background type"), externalSettings.background.type)) if backgroundType == 'dynamic': configList.append(getConfigListEntry(_("Background X-offset"), externalSettings.background.xOffset)) configList.append(getConfigListEntry(_("Background Y-offset"), externalSettings.background.yOffset)) configList.append(getConfigListEntry(_("Background color"), externalSettings.background.color)) configList.append(getConfigListEntry(_("Background transparency"), externalSettings.background.alpha)) return configList def __init__(self, session, externalSettings): BaseMenuScreen.__init__(self, session, _("External Subtitles settings")) self.externalSettings = externalSettings def buildMenu(self): self["config"].setList(self.getConfigList(self.externalSettings)) def keySave(self): changedShadowType = self.externalSettings.shadow.type.isChanged() for x in self["config"].list: x[1].save() configfile.save() self.close(True, changedShadowType) def keyLeft(self): ConfigListScreen.keyLeft(self) current = self["config"].getCurrent()[1] if current in [self.externalSettings.shadow.type, self.externalSettings.shadow.enabled, self.externalSettings.background.enabled, self.externalSettings.background.type]: self.buildMenu() def keyRight(self): ConfigListScreen.keyRight(self) current = self["config"].getCurrent()[1] if current in [self.externalSettings.shadow.type, self.externalSettings.shadow.enabled, self.externalSettings.background.enabled, self.externalSettings.background.type]: self.buildMenu() class SubsSetupMainMisc(BaseMenuScreen): def __init__(self, session, subsSettings): BaseMenuScreen.__init__(self, session, _("Subtitles setting")) self.subsSettings = subsSettings self.showExpertSettings = ConfigYesNo(default=False) def buildMenu(self): configList = [] configList.append(getConfigListEntry(_("Pause video on opening subtitles menu"), self.subsSettings.pauseVideoOnSubtitlesMenu)) configList.append(getConfigListEntry("-"*200, ConfigNothing())) configList.extend(SubsSetupExternal.getConfigList(self.subsSettings.external)) configList.append(getConfigListEntry(_("Encoding"), self.subsSettings.encodingsGroup)) configList.append(getConfigListEntry(_("Show expert settings"), self.showExpertSettings)) if self.showExpertSettings.value: engineSettings = self.subsSettings.engine configList.append(getConfigListEntry(_("Hide delay"), engineSettings.expert.hideDelay)) configList.append(getConfigListEntry(_("Sync delay"), engineSettings.expert.syncDelay)) configList.append(getConfigListEntry(_("Player delay"), engineSettings.expert.playerDelay)) configList.append(getConfigListEntry(_("Refresh delay"), engineSettings.expert.refreshDelay)) configList.append(getConfigListEntry(_("PTS check delay"), engineSettings.expert.ptsDelayCheck)) self["config"].setList(configList) def keySave(self): changedEncodingGroup = self.subsSettings.encodingsGroup.isChanged() changedShadowType = self.subsSettings.external.shadow.type.isChanged() for x in self["config"].list: x[1].save() configfile.save() self.close(True, changedEncodingGroup, changedShadowType) def keyLeft(self): ConfigListScreen.keyLeft(self) current = self["config"].getCurrent()[1] if current in [self.subsSettings.external.shadow.type, self.subsSettings.external.shadow.enabled, self.showExpertSettings, self.subsSettings.external.background.enabled, self.subsSettings.external.background.type]: self.buildMenu() def keyRight(self): ConfigListScreen.keyRight(self) current = self["config"].getCurrent()[1] if current in [self.subsSettings.external.shadow.type, self.subsSettings.external.shadow.enabled, self.showExpertSettings, self.subsSettings.external.background.enabled, self.subsSettings.external.background.type]: self.buildMenu() class SubsSetupEmbedded(BaseMenuScreen): @staticmethod def initConfig(configsubsection): configsubsection.position = ConfigSelection(default="94", choices=positionChoiceList) configsubsection.font = ConfigSubsection() configsubsection.font.regular = ConfigSubsection() configsubsection.font.regular.type = ConfigSelection(default=getDefaultFont("regular"), choices=fontChoiceList) configsubsection.font.italic = ConfigSubsection() configsubsection.font.italic.type = ConfigSelection(default=getDefaultFont("italic"), choices=fontChoiceList) configsubsection.font.bold = ConfigSubsection() configsubsection.font.bold.type = ConfigSelection(default=getDefaultFont("bold"), choices=fontChoiceList) configsubsection.font.size = ConfigSelection(default="34", choices=fontSizeChoiceList) configsubsection.color = ConfigSelection(default="ffffff", choices=colorChoiceList) configsubsection.shadow = ConfigSubsection() configsubsection.shadow.size = ConfigSelection(default="3", choices=shadowSizeChoiceList) configsubsection.shadow.color = ConfigSelection(default="000000", choices=colorChoiceList) configsubsection.shadow.xOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList) configsubsection.shadow.yOffset = ConfigSelection(default="-3", choices=shadowOffsetChoiceList) @staticmethod def getConfigList(embeddedSettings): fontSizeCfg = getEmbeddedFontSizeCfg(embeddedSettings.font.size) configList = [] configList.append(getConfigListEntry(_("Font type (Regular)"), embeddedSettings.font.regular.type)) configList.append(getConfigListEntry(_("Font type (Italic)"), embeddedSettings.font.italic.type)) configList.append(getConfigListEntry(_("Font type (Bold)"), embeddedSettings.font.bold.type)) configList.append(getConfigListEntry(_("Font size"), fontSizeCfg)) configList.append(getConfigListEntry(_("Position"), embeddedSettings.position)) configList.append(getConfigListEntry(_("Color"), embeddedSettings.color)) configList.append(getConfigListEntry(_("Shadow X-offset"), embeddedSettings.shadow.xOffset)) configList.append(getConfigListEntry(_("Shadow Y-offset"), embeddedSettings.shadow.yOffset)) return configList def __init__(self, session, embeddedSettings): BaseMenuScreen.__init__(self, session, _("Embedded subtitles settings")) self.embeddedSettings = embeddedSettings def buildMenu(self): self["config"].setList(self.getConfigList((self.embeddedSettings))) def keySave(self): reloadEmbeddedScreen = (self.embeddedSettings.position.isChanged() or getEmbeddedFontSizeCfg(self.embeddedSettings.font.size).isChanged()) for x in self["config"].list: x[1].save() configfile.save() self.close(reloadEmbeddedScreen) class SubsSetupGeneral(BaseMenuScreen): def __init__(self, session, generalSettings): BaseMenuScreen.__init__(self, session, _("General settings")) self.generalSettings = generalSettings def buildMenu(self): self["config"].setList([ getConfigListEntry(_("Pause video on opening subtitles menu"), self.generalSettings.pauseVideoOnSubtitlesMenu), getConfigListEntry(_("Encoding"), self.generalSettings.encodingsGroup), ]) def FileEntryComponent(name, absolute=None, isDir=False): res = [ (absolute, isDir) ] res.append((eListboxPythonMultiContent.TYPE_TEXT, 35, 1, 470, 20, 0, RT_HALIGN_LEFT, toString(name))) if isDir: png = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "extensions/directory.png")) else: png = LoadPixmap(os.path.join(os.path.dirname(__file__), 'img', 'subtitles.png')) if png is not None: res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, 10, 2, 20, 20, png)) return res class SubFileList(FileList): def __init__(self, defaultDir): extensions = [] for parser in PARSERS: extensions += list(parser.parsing) FileList.__init__(self, defaultDir, matchingPattern="(?i)^.*\." + '(' + '|'.join(ext[1:] for ext in extensions) + ')', useServiceRef=False) def changeDir(self, directory, select = None): self.list = [] # if we are just entering from the list of mount points: if self.current_directory is None: if directory and self.showMountpoints: self.current_mountpoint = self.getMountpointLink(directory) else: self.current_mountpoint = None self.current_directory = directory directories = [] files = [] if directory is None and self.showMountpoints: # present available mountpoints for p in harddiskmanager.getMountedPartitions(): path = os.path.join(p.mountpoint, "") if path not in self.inhibitMounts and not self.inParentDirs(path, self.inhibitDirs): self.list.append(FileEntryComponent(name = p.description, absolute = path, isDir = True)) files = [ ] directories = [ ] elif directory is None: files = [ ] directories = [ ] elif self.useServiceRef: # we should not use the 'eServiceReference(string)' constructor, because it doesn't allow ':' in the directoryname root = eServiceReference(2, 0, directory) if self.additional_extensions: root.setName(self.additional_extensions) serviceHandler = eServiceCenter.getInstance() list = serviceHandler.list(root) while 1: s = list.getNext() if not s.valid(): del list break if s.flags & s.mustDescent: directories.append(s.getPath()) else: files.append(s) directories.sort() files.sort() else: if fileExists(directory): try: files = os.listdir(directory) except: files = [] files.sort() tmpfiles = files[:] for x in tmpfiles: if os.path.isdir(directory + x): directories.append(directory + x + "/") files.remove(x) if self.showDirectories: if directory: if self.showMountpoints and directory == self.current_mountpoint: self.list.append(FileEntryComponent(name = "<" +_("List of storage devices") + ">", absolute = None, isDir = True)) elif (directory != self.topDirectory) and not (self.inhibitMounts and self.getMountpoint(directory) in self.inhibitMounts): self.list.append(FileEntryComponent(name = "<" +_("Parent directory") + ">", absolute = '/'.join(directory.split('/')[:-2]) + '/', isDir = True)) for x in directories: if not (self.inhibitMounts and self.getMountpoint(x) in self.inhibitMounts) and not self.inParentDirs(x, self.inhibitDirs): name = x.split('/')[-2] self.list.append(FileEntryComponent(name = name, absolute = x, isDir = True)) if self.showFiles: for x in files: if self.useServiceRef: path = x.getPath() name = path.split('/')[-1] else: path = directory + x name = x if (self.matchingPattern is None) or self.matchingPattern.search(path): self.list.append(FileEntryComponent(name = name, absolute = x , isDir = False)) if self.showMountpoints and len(self.list) == 0: self.list.append(FileEntryComponent(name = _("nothing connected"), absolute = None, isDir = False)) self.l.setList(self.list) if select is not None: i = 0 self.moveToIndex(0) for x in self.list: p = x[0][0] if isinstance(p, eServiceReference): p = p.getPath() if p == select: self.moveToIndex(i) i += 1 class SubsChooserMenuList(MenuList): def __init__(self, embeddedAvailable=False, searchSupport=False, historySupport=False): MenuList.__init__(self, [], False, eListboxPythonMultiContent) self.l.setItemHeight(30) self.l.setFont(0, gFont("Regular", 20)) menulist = [] if embeddedAvailable: res = [('embedded')] res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 5), size=(35, 25), png=loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'key_red.png')))) res.append(MultiContentEntryText(pos=(60, 5), size=(350, 25), font=0, flags=RT_VALIGN_CENTER, text=_("Choose from embedded subtitles"))) menulist.append(res) if historySupport: res = [('downloaded')] res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 5), size=(35, 25), png=loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'key_yellow.png')))) res.append(MultiContentEntryText(pos=(60, 5), size=(350, 25), font=0, flags=RT_VALIGN_CENTER, text=_("Choose from downloaded subtitles"))) menulist.append(res) if searchSupport: res = [('search')] res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 5), size=(35, 25), png=loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'key_blue.png')))) res.append(MultiContentEntryText(pos=(60, 5), size=(350, 25), font=0, flags=RT_VALIGN_CENTER, text=_("Choose from web subtitles"))) menulist.append(res) if embeddedAvailable or historySupport or searchSupport: self.l.setList(menulist) class E2SubsSeeker(SubsSeeker): def __init__(self, session, searchSettings, debug=False): self.session = session self.download_thread = None self.search_settings = searchSettings download_path = searchSettings.downloadPath.value tmp_path = searchSettings.tmpPath.value class SubsSearchSettingsProvider(E2SettingsProvider): def __init__(self, providerName, defaults, configSubSection): E2SettingsProvider.__init__(self, providerName, configSubSection, defaults) SubsSeeker.__init__(self, download_path, tmp_path, captcha_cb=self.captcha_cb, delay_cb=self.delay_cb, message_cb=messageCB, settings_provider_cls=SubsSearchSettingsProvider, settings_provider_args=searchSettings, debug=debug) self.providers_error = False for p in self.seekers: if p.error is not None: self.providers_error = True def downloadSubtitle(self, success_cb, error_cb, cancel_cb, selected_subtitle, subtitles_dict, settings, path=None, fname=None): download_fnc = super(E2SubsSeeker, self).downloadSubtitle if settings.get('ask_overwrite', True): overwrite_cb = self.overwrite_cb else: overwrite_cb = None params = (selected_subtitle, subtitles_dict, self.choice_cb, path, fname, overwrite_cb, settings) download_thread = SubsDownloadThread(self.session, download_fnc, params, success_cb, error_cb, cancel_cb) self.download_thread = download_thread self.download_thread.start() def _unpack_rarsub(self, rar_path, dest_dir): assert self.download_thread is not None assert self.download_thread.is_alive() files = self.download_thread.getUnrar(rar_path, dest_dir) return filter(lambda x:os.path.splitext(x)[1] in ('.srt', '.sub', 'txt'), files) def overwrite_cb(self, subfile): assert self.download_thread is not None assert self.download_thread.is_alive() return self.download_thread.getOverwrite(subfile) def choice_cb(self, subfiles): assert self.download_thread is not None assert self.download_thread.is_alive() return self.download_thread.getChoice(subfiles) def captcha_cb(self, image_path): assert self.download_thread is not None assert self.download_thread.is_alive() return self.download_thread.getCaptcha(image_path) def delay_cb(self, seconds): assert self.download_thread is not None assert self.download_thread.is_alive() message = _("Subtitles will be downloaded in") + " " + str(seconds) + " " + _("seconds") self.download_thread.getDelay(seconds, message) class SubsChooser(Screen): skin = """ """ def __init__(self, session, subsSettings, subdir=None, embeddedSupport=False, searchSupport=False, historySupport=False, titleList=None): Screen.__init__(self, session) self.session = session self.subsSettings = subsSettings defaultDir = subdir if subdir is not None and not subdir.endswith('/'): defaultDir = subdir + '/' self.embeddedList = None self.embeddedSubtitle = None if embeddedSupport: service = self.session.nav.getCurrentService() subtitle = service and service.subtitle() self.embeddedList = subtitle and subtitle.getSubtitleList() self.searchSupport = searchSupport self.historySupport = historySupport self.titleList = titleList ref = self.session.nav.getCurrentlyPlayingServiceReference() videoPath = ref and ref.getPath() if videoPath and os.path.isfile(videoPath): self.videoPath = videoPath else: self.videoPath = None videoName = ref and os.path.split(ref.getPath())[1] self["filename"] = StaticText(videoName) self["file_list"] = SubFileList(defaultDir) self["menu_list"] = SubsChooserMenuList(self.embeddedList, searchSupport, historySupport) self["actions"] = NumberActionMap(["OkCancelActions", "ColorActions"], { "ok": self.ok, "cancel": self.close, "red": self.embeddedSubsSelection, "yellow":self.downloadedSubsSelection, "blue": self.webSubsSelection, }, -2) self.onLayoutFinish.append(self.updateTitle) self.onLayoutFinish.append(self.disableMenuList) def updateTitle(self): self.setTitle(_("Choose Subtitles")) def disableMenuList(self): self["menu_list"].selectionEnabled(False) def ok(self): if self['file_list'].canDescent(): self['file_list'].descent() else: filePath = os.path.join(self['file_list'].current_directory, self['file_list'].getFilename()) print '[SubsFileChooser]' , filePath self.close(filePath, False) def checkEmbeddedSubsSelection(self, embeddedSubtitle=None): if embeddedSubtitle: self.close(None, embeddedSubtitle) def embeddedSubsSelection(self): if self.embeddedList: self.session.openWithCallback(self.checkEmbeddedSubsSelection, SubsEmbeddedSelection) def webSubsSelection(self): def checkDownloadedSubsSelection(downloadedSubtitle=None): if downloadedSubtitle: self.close(downloadedSubtitle, False, True) def paramsDialogCB(callback=None): if callback: self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, self.videoPath, self.titleList, resetSearchParams=False) def showProvidersErrorCB(callback): if not callback: subsSettings.search.showProvidersErrorMessage.value = False if subsSettings.search.openParamsDialogOnSearch.value: self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, self.titleList, enabledList=False) else: self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, self.videoPath, self.titleList) subsSettings = self.subsSettings if not self.searchSupport: return seeker = E2SubsSeeker(self.session, subsSettings.search, debug=True) if seeker.providers_error and subsSettings.search.showProvidersErrorMessage.value: msg = _("Some subtitles providers are not working") + ".\n" msg += _("For more details please check search settings") + "." msg += "\n\n" msg += _("Do you want to show this message again?") self.session.openWithCallback(showProvidersErrorCB, MessageBox, msg, type=MessageBox.TYPE_YESNO) elif subsSettings.search.openParamsDialogOnSearch.value: self.session.openWithCallback(paramsDialogCB, SubsSearchParamsMenu, seeker, subsSettings.search, self.titleList, enabledList=False) else: self.session.openWithCallback(checkDownloadedSubsSelection, SubsSearch, seeker, subsSettings.search, self.videoPath, self.titleList) def downloadedSubsSelectionCB(self, subtitles, downloadedSubtitle=None): fpath = os.path.join(self.subsSettings.search.downloadHistory.path.value,'hsubtitles.json') try: json.dump(subtitles, open(fpath,"w")) except Exception as e: print '[SubsFileChooser] downloadedSubsSelectionCB - %s'% str(e) if downloadedSubtitle: self.close(downloadedSubtitle, False, True) def downloadedSubsSelection(self): if not self.historySupport: return fpath = os.path.join(self.subsSettings.search.downloadHistory.path.value,'hsubtitles.json') try: subtitles = json.load(open(fpath, "r")) except Exception as e: print '[SubsFileChooser] downloadedSubsSelection - %s'% str(e) subtitles = [] self.session.openWithCallback(self.downloadedSubsSelectionCB, SubsDownloadedSelection, subtitles, self.subsSettings.search.downloadHistory) class SubsDownloadedSelection(Screen): class InfoScreen(Screen): skin = """ """ def __init__(self, session, subtitle): Screen.__init__(self, session) self["path"] = StaticText(_(toString(subtitle['fpath']))) skin = """ {"templates": {"default": (50, [ MultiContentEntryPixmapAlphaBlend(pos = (0, 15), size = (24, 24), png=0), # key, MultiContentEntryText(pos = (30, 0), size = (325, 50), font = 0, flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text=1, color=0xFF000004), # name, MultiContentEntryText(pos = (375, 0), size = (165, 50), font = 0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 2, color=0xFF000004), # provider, MultiContentEntryText(pos = (520, 0), size = (170, 50), font = 0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 3, color=0xFF000004), # date, ], True, "showOnDemand"), }, "fonts": [gFont("Regular", 19), gFont("Regular", 16)], "itemHeight": 50 } """ def __init__(self, session, subtitles, historySettings, marked=None): Screen.__init__(self, session) self["header_name"] = StaticText(_("Name")) self["header_provider"] = StaticText(_("Provider")) self["header_date"] = StaticText(_("Download date")) self["subtitles"] = List() self["entries_sum"] = StaticText() self["key_red"] = StaticText(_("Remove (file)")) self["key_blue"] = StaticText(_("Settings")) self["actions"] = ActionMap(["ColorActions", "OkCancelActions", "InfoActions"], { "ok": self.ok, "cancel": self.cancel, "info":self.showInfo, "red":self.removeEntry, "blue":self.openSettings, }, -2) self["infoActions"] = ActionMap(["ColorActions", "OkCancelActions", "DirectionActions", "InfoActions"], { "ok": self.closeInfoDialog, "cancel": self.closeInfoDialog, "info": self.closeInfoDialog, "red": self.closeInfoDialog, "green":self.closeInfoDialog, "blue": self.closeInfoDialog, "up": self.closeInfoDialog, "upUp":self.closeInfoDialog, "down":self.closeInfoDialog, "downUp": self.closeInfoDialog, "right":self.closeInfoDialog, "rightUp":self.closeInfoDialog, "left":self.closeInfoDialog, "leftUp":self.closeInfoDialog, } ) self["infoActions"].setEnabled(False) self.subtitles = subtitles self.historySettings = historySettings self.marked = marked or [] self.onLayoutFinish.append(self.updateWindowTitle) self.onLayoutFinish.append(self.updateSubsList) self.onLayoutFinish.append(self.updateEntriesSum) self.onLayoutFinish.append(self.updateRemoveAction) def updateWindowTitle(self): self.setTitle(_("Downloaded Subtitles")) def updateSubsList(self): imgDict = {'unk':loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'countries', 'UNK.png'))} subtitleListGUI = [] for sub in self.subtitles[:]: fpath = toString(sub['fpath']) if not os.path.isfile(fpath): self.subtitles.remove(sub) continue if sub.get('country','unk') not in imgDict: countryImgPath = os.path.join(os.path.dirname(__file__), 'img', 'countries', sub['country'] + '.png') if os.path.isfile(countryImgPath): imgDict[sub['country']] = loadPNG(toString(countryImgPath)) countryPng = imgDict[sub['country']] else: countryPng = imgDict['unk'] if sub in self.marked: color = 0x00ff00 else: color = 0xffffff date = datetime.fromtimestamp(os.path.getctime(fpath)).strftime("%d-%m-%Y %H:%M") name = os.path.splitext(os.path.basename(fpath))[0] subtitleListGUI.append((countryPng,toString(name), toString(sub['provider']), date, color),) imgDict = None self['subtitles'].list = subtitleListGUI def updateEntriesSum(self): limit = int(self.historySettings.limit.value) self["entries_sum"].text = _("Entries count:") + " " + str(len(self.subtitles)) + " / " + str(limit) def updateRemoveAction(self): if self.historySettings.removeAction.value == 'file': self["key_red"].text = _("Remove Entry (List+File)") else: self["key_red"].text = _("Remove Entry (List)") def removeEntry(self): def removeEntryCB(doRemove=False): if doRemove: if self.historySettings.removeAction.value == 'file': try: os.unlink(subtitle['fpath']) except OSError as e: print "[SubsDownloadedSelection] cannot remove - %s"%(str(e)) self.session.open(MessageBox, _("There was an error while removing subtitle, please check log"), type=MessageBox.TYPE_ERROR) else: self.subtitles.remove(subtitle) curridx = self['subtitles'].index self.updateSubsList() self['subtitles'].index = curridx -1 self.updateEntriesSum() else: self.subtitles.remove(subtitle) curridx = self['subtitles'].index self.updateSubsList() self['subtitles'].index = curridx -1 self.updateEntriesSum() if self["subtitles"].count() > 0: subtitle = self.subtitles[self["subtitles"].index] if self.historySettings.removeAction.value == 'file': if self.historySettings.removeActionAsk.value: message = _("Subtitle") + " '" + toString(subtitle['name'])+ "' " + _("will be removed from file system") message += "\n\n" + _("Do you want to proceed?") self.session.openWithCallback(removeEntryCB, MessageBox, message, type=MessageBox.TYPE_YESNO) else: removeEntryCB(True) else: if self.historySettings.removeActionAsk.value: message = _("Subtitle") + " '" + toString(subtitle['name'])+ "' " + _("will be removed from list") message += "\n\n" + _("Do you want to proceed?") self.session.openWithCallback(removeEntryCB, MessageBox, message, type=MessageBox.TYPE_YESNO) else: removeEntryCB(True) def openSettings(self): def menuCB(callback=None): self.updateEntriesSum() self.updateRemoveAction() self.session.openWithCallback(menuCB, SubsDownloadedSubtitlesMenu, self.historySettings) def showInfo(self): if self["subtitles"].count() > 0: subtitle = self.subtitles[self["subtitles"].index] self["actions"].setEnabled(False) self["infoActions"].setEnabled(True) self.__infoScreen = self.session.instantiateDialog(self.InfoScreen, subtitle) self.__infoScreen.show() def closeInfoDialog(self): self.session.deleteDialog(self.__infoScreen) self["infoActions"].setEnabled(False) self["actions"].setEnabled(True) def ok(self): if self["subtitles"].count() > 0: subtitle = self.subtitles[self["subtitles"].index] self.close(self.subtitles, subtitle['fpath']) self.close(self.subtitles, None) def cancel(self): self.close(self.subtitles, None) class SubsDownloadedSubtitlesMenu(Screen, ConfigListScreen): skin = """ """ def __init__(self, session, historySettings): Screen.__init__(self, session) ConfigListScreen.__init__(self, [], session=session) self["actions"] = ActionMap(["SetupActions", "ColorActions"], { "cancel": self.keyCancel, "green": self.keySave, "red": self.keyCancel, "blue": self.resetDefaults, }, -2) self["key_green"] = Label(_("Save")) self["key_red"] = Label(_("Cancel")) self["key_blue"] = Label(_("Reset Defaults")) self["key_yellow"] = Label("") self.historySettings = historySettings self.buildMenu() self.onLayoutFinish.append(self.updateTitle) def updateTitle(self): self.setTitle(_("Downloaded Subtitles - Settings")) def buildMenu(self): menuList = [] menuList.append(getConfigListEntry(_("Max history entries"), self.historySettings.limit)) menuList.append(getConfigListEntry(_("Remove action") , self.historySettings.removeAction)) menuList.append(getConfigListEntry(_("Ask on remove action"), self.historySettings.removeActionAsk)) self["config"].setList(menuList) def resetDefaults(self): for x in self["config"].list: x[1].value = x[1].default self.buildMenu() def keyOK(self): if self["config"].getCurrent()[1] == self.historySettings.path: self.session.openWithCallback(self.changeDir, LocationBox, _("Select Directory"), currDir=self.historySettings.path.value) def keySave(self): for x in self["config"].list: x[1].save() configfile.save() self.close(True) def keyCancel(self): for x in self["config"].list: x[1].cancel() self.close() def keyLeft(self): ConfigListScreen.keyLeft(self) def keyRight(self): ConfigListScreen.keyRight(self) # source from openpli class SubsEmbeddedSelection(Screen): skin = """ {"templates": {"default": (25, [ MultiContentEntryText(pos = (0, 0), size = (35, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # key, MultiContentEntryText(pos = (40, 0), size = (60, 25), font = 0, flags = RT_HALIGN_LEFT, text = 2), # number, MultiContentEntryText(pos = (110, 0), size = (120, 25), font = 0, flags = RT_HALIGN_LEFT, text = 3), # description, MultiContentEntryText(pos = (240, 0), size = (200, 25), font = 0, flags = RT_HALIGN_LEFT, text = 4), # language, ], True, "showNever"), }, "fonts": [gFont("Regular", 20), gFont("Regular", 16)], "itemHeight": 25 } """ def __init__(self, session): Screen.__init__(self, session) self["streams"] = List([], enableWrapAround=True) self["actions"] = ActionMap(["SetupActions", "DirectionActions", "MenuActions"], { "ok": self.keyOk, "cancel": self.cancel, }, -2) self.onLayoutFinish.append(self.updateTitle) self.onLayoutFinish.append(self.fillList) def updateTitle(self): self.setTitle(_("Choose subtitles")) def fillList(self): idx = 0 streams = [] subtitlelist = self.getSubtitleList() for x in subtitlelist: number = str(x[1]) description = "?" language = "" try: if x[4] != "und": if LanguageCodes.has_key(x[4]): language = LanguageCodes[x[4]][0] else: language = x[4] except: language = "" if x[0] == 0: description = "DVB" number = "%x" % (x[1]) elif x[0] == 1: description = "teletext" number = "%x%02x" % (x[3] and x[3] or 8, x[2]) elif x[0] == 2: types = ("unknown", "embedded", "SSA file", "ASS file", "SRT file", "VOB file", "PGS file") try: description = types[x[2]] except: description = _("unknown") + ": %s" % x[2] number = str(int(number) + 1) print x, number, description, language streams.append((x, "", number, description, language)) idx += 1 self["streams"].list = streams def getSubtitleList(self): service = self.session.nav.getCurrentService() subtitle = service and service.subtitle() subtitlelist = subtitle and subtitle.getSubtitleList() embeddedlist = [] for x in subtitlelist: if x[0] == 2: types = ("unknown", "embedded", "SSA file", "ASS file", "SRT file", "VOB file", "PGS file") # filter embedded subtitles if x[2] not in [1, 2, 3, 4, 5, 6]: continue embeddedlist.append(x) return embeddedlist self.selectedSubtitle = None return subtitlelist def cancel(self): self.close() def keyOk(self): cur = self["streams"].getCurrent() self.close(cur[0][:4]) class SimpleObserverList(list): def __init__(self, *args): list.__init__(self, *args) self.observer_fncs = list() def add_observer(self, observer_fnc): self.observer_fncs.append(observer_fnc) def remove_observer(self, observer_fnc): self.observer_fncs.remove(observer_fnc) def append (self, value): list.append(self, value) for f in self.observer_fncs: f() def remove(self, value): list.remove(self, value) for f in self.observer_fncs: f() class SubsDownloadThread(Thread): THREADS = SimpleObserverList() CAPTCHA_REQUEST = 0 DELAY_REQUEST = 1 FINISH_REQUEST_SUCCESS = 2 FINISH_REQUEST_ERROR = 3 OVERWRITE_REQUEST = 4 CHOICE_REQUEST = 5 UNRAR_REQUEST = 6 def __init__(self, session, fnc, params, callback, errorback, cancelback): Thread.__init__(self) self.session = session self.fnc = fnc self.params = params self.callback = callback self.errorback = errorback self.cancelback = cancelback self.cancelled = False self.messageIn = Queue() self.messageOut = Queue() self.messagePump = ePythonMessagePump() self.messagePump.recv_msg.get().append(self._runInMainThread) def start(self): SubsDownloadThread.THREADS.append(self) Thread.start(self) def run(self): try: ret = self.fnc(*self.params) self.messageOut.put((self.FINISH_REQUEST_SUCCESS, ret)) self.messagePump.send(0) except Exception: exc_value, exc_traceback = sys.exc_info()[1:] exc_value.tb = exc_traceback self.messageOut.put((self.FINISH_REQUEST_ERROR, exc_value)) self.messagePump.send(0) def _runInMainThread(self, val): ret = self.messageOut.get() request = ret[0] if request == self.CAPTCHA_REQUEST: imagePath = ret[1] Captcha(self.session, self.getCaptchaCB, imagePath) elif request == self.DELAY_REQUEST: seconds, message = ret[1], ret[2] self.session.openWithCallback(self.getDelayCB, DelayMessageBox, seconds, message) elif request == self.FINISH_REQUEST_SUCCESS: subFile = ret[1] if self.cancelled: self.cancelback() else: self.callback(subFile) SubsDownloadThread.THREADS.remove(self) elif request == self.FINISH_REQUEST_ERROR: error = ret[1] self.errorback(error) SubsDownloadThread.THREADS.remove(self) elif request == self.CHOICE_REQUEST: subFiles = ret[1] choiceTitle = _("There are more subtitles in unpacked archive\n please select which one do you want to use") choiceList = [(os.path.basename(subfile), subfile) for subfile in subFiles] self.session.openWithCallback(self.getChoiceCB, ChoiceBox, choiceTitle, choiceList) elif request == self.OVERWRITE_REQUEST: overwriteText = _("Subtitles with this name already exist\nDo you want to overwrite them") + "?" self.session.openWithCallback(self.getOverwriteCB, MessageBox, overwriteText, MessageBox.TYPE_YESNO) elif request == self.UNRAR_REQUEST: rarPath = ret[1] destDir = ret[2] unrar(rarPath, destDir, self.getUnrarCB, self.getUnrarCB) def getUnrar(self, subFile, destPath): self.messageOut.put((self.UNRAR_REQUEST, subFile, destPath)) self.messagePump.send(0) ret = self.messageIn.get() if isinstance(ret, str): raise Exception(ret) return ret def getUnrarCB(self, callback): self.messageIn.put(callback) def getOverwrite(self, subFile): self.messageOut.put((self.OVERWRITE_REQUEST, subFile)) self.messagePump.send(0) return self.messageIn.get() def getOverwriteCB(self, callback): self.messageIn.put(callback) def getChoice(self, files): self.messageOut.put((self.CHOICE_REQUEST, files)) self.messagePump.send(0) return self.messageIn.get() def getChoiceCB(self, selfile): if selfile: selfile = selfile[1] if selfile is None: self.cancelled = True return self.messageIn.put(selfile) def getCaptcha(self, imagePath): self.messageOut.put((self.CAPTCHA_REQUEST, imagePath)) self.messagePump.send(0) return self.messageIn.get() def getCaptchaCB(self, word): word = word or "" self.messageIn.put(word) def getDelay(self, seconds, message): self.messageOut.put((self.DELAY_REQUEST, seconds, message)) self.messagePump.send(0) return self.messageIn.get() def getDelayCB(self, callback=None): return self.messageIn.put(None) class SubsSearchProcess(object): processes = [] process_path = os.path.join(os.path.dirname(__file__), 'searchsubs.py') def __init__(self): self.log = SimpleLogger('SubsSearchProcess', SimpleLogger.LOG_INFO) self.toRead = None self.pPayload = None self.data = "" self.__stopping = False self.appContainer = eConsoleAppContainer() self.appContainer.stdoutAvail.append(self.dataOutCB) self.appContainer.stderrAvail.append(self.dataErrCB) self.appContainer.appClosed.append(self.finishedCB) def recieveMessages(self, data): def getMessage(data): mSize = int(data[:7]) mPayload = data[7:mSize] mPart = mSize > len(data) return mSize, mPayload, mPart def readMessage(payload): try: message = json.loads(payload) except EOFError: pass except Exception: self.log.debug('data is not in JSON format! - %s' % str(payload)) else: self.log.debug('message successfully recieved') self.toRead = None self.pPayload = None self.handleMessage(message) def readStart(data): mSize, mPayload, mPart = getMessage(data) if not mPart: data = data[mSize:] readMessage(mPayload) if len(data) > 0: readStart(data) else: self.toRead = mSize - len(data) self.pPayload = mPayload def readContinue(data): nextdata = data[:self.toRead] self.pPayload += nextdata data = data[len(nextdata):] self.toRead -= len(nextdata) if self.toRead == 0: readMessage(self.pPayload) if len(data) > 0: readStart(data) if self.pPayload is not None: readContinue(data) else: readStart(data) def handleMessage(self, data): self.log.debug('handleMessage "%s"', data) if data['message'] == Messages.MESSAGE_UPDATE_CALLBACK: self.updateCB(data['value']) if data['message'] == Messages.MESSAGE_FINISHED_SCRIPT: self.successCB(data['value']) if data['message'] == Messages.MESSAGE_CANCELLED_SCRIPT: print 'script successfully cancelled' if data['message'] == Messages.MESSAGE_ERROR_SCRIPT: self.errorCB(data['value']) def start(self, params, updateCB, successCB, errorCB): self.processes.append(self) self.updateCB = updateCB self.successCB = successCB self.errorCB = errorCB cmd = "python %s" % self.process_path self.log.debug("start - '%s'", cmd) self.appContainer.execute(cmd) self.write(params) def running(self): return self.appContainer.running() def stop(self): def check_stopped(): if not self.appContainer.running(): timer.stop() del self.__i return if self.__i == 0: self.__i += 1 self.log.debug('2. sending SIGKILL') self.appContainer.kill() elif self.__i == 1: timer.stop() raise Exception("cannot kill process") if self.__stopping: self.log.debug('already stopping..') return self.__stopping = True self.log.debug('stopping process..') self.__i = 0 if self.appContainer.running(): self.log.debug('1. sending SIGINT') self.appContainer.sendCtrlC() timer = eTimer() timer.callback.append(check_stopped) timer.start(2000, False) else: self.log.debug('process is already stopped') def write(self, data): dump = json.dumps(data) dump = "%07d%s" % (len(dump), dump) self.appContainer.write(dump) def dataErrCB(self, data): self.log.debug("dataErrCB: '%s'", data) self.error = data def dataOutCB(self, data): self.log.debug("dataOutCB: '%s", data) self.recieveMessages(data) def finishedCB(self, retval): self.processes.remove(self) self.log.debug('process finished, retval:%d', retval) class Suggestions(object): def __init__(self): self._cancelled = False def __str__(self): return self.__class__.__name__ def cancel(self): self._cancelled = True def getSuggestions(self, queryString, successCB, errorCB): if queryString is not None: d = self._getSuggestions(queryString) self.successCB = successCB self.errorCB = errorCB d.addCallbacks(self.getSuggestionsSuccess, self.getSuggestionsError) def getSuggestionsSuccess(self, data): if not self._cancelled: self.successCB(self._processResult(data)) def getSuggestionsError(self, failure): if not self._cancelled: failure.printTraceback() self.errorCB(failure) def _getSuggestions(self): return Deferred() def _processResult(self, data): return data class OpenSubtitlesSuggestions(Suggestions): def _getSuggestions(self, queryString): query = "http://www.opensubtitles.org/libs/suggest.php?format=json2&SubLanguageID=null&MovieName=" + queryString return client.getPage(query, timeout=6) def _processResult(self, data): return json.loads(data)['result'] class HistorySuggestions(Suggestions): def __init__(self, historyCfg): Suggestions.__init__(self) self.historyCfg = historyCfg def _getSuggestions(self, queryString): def getHistory(queryString): historyList = self.historyCfg.value.split(',') historyList = [{'name':name, 'total':len(historyList) - idx} for idx, name in enumerate(historyList)] d.callback(historyList) d = Deferred() getHistory(queryString) return d class SuggestionsListScreen(Screen): s = getDesktop(0).size() desktopSize = (s.width(), s.height()) windowSize = (int(0.35 * desktopSize[0]), 160) skin = """ {"templates": {"default": (23, [ MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title, ], True, "showOnDemand"), "notselected": (23, [ MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title, ], False, "showOnDemand") }, "fonts": [gFont("Regular", 18), gFont("Regular", 16)], "itemHeight": 23 } """ % (int(0.6 * desktopSize[0]), windowSize[0], windowSize[0], int(0.05 * windowSize[0]), int(0.9 * windowSize[0])) def __init__(self, session, title, configTextWithSuggestions): Screen.__init__(self, session) self.activeState = False self.list = [] self.suggestlist = [] self["suggestionslist"] = List(self.list) self["suggestionstitle"] = StaticText(toString(title)) self.configTextWithSuggestion = configTextWithSuggestions def update(self, suggestions): if suggestions and len(suggestions) > 0: if not self.shown: self.show() suggestions.sort(key=lambda x: int(x['total'])) suggestions.reverse() if len(suggestions): self.list = [] for s in suggestions: self.list.append((toString(s['name']),)) self["suggestionslist"].setList(self.list) self["suggestionslist"].setIndex(0) else: self.hide() def getlistlenght(self): return len(self.list) def up(self): if self.list and len(self.list) > 0: self["suggestionslist"].selectPrevious() return self.getSelection() def down(self): if self.list and len(self.list) > 0: self["suggestionslist"].selectNext() return self.getSelection() def pageUp(self): if self.list and len(self.list) > 0: self["suggestionslist"].selectPrevious() return self.getSelection() def pageDown(self): if self.list and len(self.list) > 0: self["suggestionslist"].selectNext() return self.getSelection() def activate(self): self.enableSelection(True) return self.getSelection() def deactivate(self): self.enableSelection(False) return self.getSelection() def getSelection(self): if not self["suggestionslist"].getCurrent(): return None return self["suggestionslist"].getCurrent()[0] def enableSelection(self, value): if value: self['suggestionslist'].style = 'default' else: self['suggestionslist'].style = 'notselected' class HistoryListScreen(SuggestionsListScreen): s = getDesktop(0).size() desktopSize = (s.width(), s.height()) windowSize = (int(0.35 * desktopSize[0]), 160) skin = """ {"templates": {"default": (23, [ MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title, ], True, "showOnDemand"), "notselected": (23, [ MultiContentEntryText(pos = (0, 0), size = (360, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # title, ], False, "showOnDemand") }, "fonts": [gFont("Regular", 18), gFont("Regular", 16)], "itemHeight": 23 } """ % (int(0.05 * desktopSize[0]), windowSize[0], windowSize[0], int(0.05 * windowSize[0]), int(0.9 * windowSize[0])) def __init__(self, session, title, configTextWithSuggestions): SuggestionsListScreen.__init__(self, session, title, configTextWithSuggestions) class ConfigTextWithSuggestionsAndHistory(ConfigText): def __init__(self, historyCfg, default="", fixed_size=True, visible_width=False): ConfigText.__init__(self, default, fixed_size, visible_width) self.historyCfg = historyCfg self.historyClass = HistorySuggestions self.historyWindow = None self.__history = None self.suggestionsClass = OpenSubtitlesSuggestions self.suggestionsWindow = None self.__suggestions = None self.currentWindow = None def handleKey(self, key): ConfigText.handleKey(self, key) if key in [KEY_DELETE, KEY_BACKSPACE, KEY_ASCII, KEY_TIMEOUT]: self.getSuggestions() def onSelect(self, session): ConfigText.onSelect(self, session) if session is not None: if self.suggestionsWindow is None: suggestionsWindowTitle = _("Suggestions") + " (" + _("press green") + ")" self.suggestionsWindow = session.instantiateDialog(SuggestionsListScreen, suggestionsWindowTitle , self) self.suggestionsWindow.deactivate() self.suggestionsWindow.show() historyWindowTitle = _("History") + " (" + _("press red") + ")" if self.historyWindow is None: self.historyWindow = session.instantiateDialog(HistoryListScreen, historyWindowTitle, self) self.historyWindow.deactivate() self.historyWindow.show() self.getSuggestions() self.getHistory() def onDeselect(self, session): self.cancelGetSuggestions() self.cancelGetHistory() ConfigText.onDeselect(self, session) if self.suggestionsWindow is not None: self.suggestionsWindow.hide() if self.historyWindow is not None: self.historyWindow.hide() def getCurrentSelection(self): if self.currentWindow.getlistlenght() > 0: return self.currentWindow.getSelection() def currentListUp(self): if self.currentWindow.getlistlenght() > 0: self.value = self.currentWindow.up() def currentListDown(self): if self.currentWindow.getlistlenght() > 0: self.value = self.currentWindow.down() def currentListPageDown(self): if self.currentWindow.getlistlenght() > 0: self.value = self.currentWindow.pageDown() def currentListPageUp(self): if self.currentWindow.getlistlenght() > 0: self.value = self.currentWindow.pageUp() def propagateSuggestions(self, suggestionsList): self.cancelGetSuggestions() if self.suggestionsWindow: self.suggestionsWindow.update(suggestionsList) def propagateHistory(self, historyList): self.cancelGetHistory() if self.historyWindow: self.historyWindow.update(historyList) def enableSuggestions(self, value): if value: if self.suggestionsWindow: self.tmpValue = self.value selection = self.suggestionsWindow.activate() if selection is None: print 'empty suggesstions list' return False self.value = selection self.currentWindow = self.suggestionsWindow return True else: print 'Error - suggestionsWindow no longer exists' return False else: self.cancelGetSuggestions() if self.suggestionsWindow: self.suggestionsWindow.deactivate() self.currentWindow = None self.getSuggestions() return True else: print 'Error - suggestionsWindow no longer exists' return False def enableHistory(self, value): if value: if self.historyWindow: self.tmpValue = self.value selection = self.historyWindow.activate() if selection is None: print "Error - empty history list" return False self.value = selection self.currentWindow = self.historyWindow return True else: print 'Error - historyWindow no longer exists' return False else: self.cancelGetHistory() if self.historyWindow: self.historyWindow.deactivate() self.currentWindow = None self.getHistory() return True else: print 'Error - historyWindow no longer exists' return False def cancelGetSuggestions(self): if self.__suggestions is not None: self.__suggestions.cancel() def cancelGetHistory(self): if self.__history is not None: self.__history.cancel() def gotSuggestionsError(self, val): print "[ConfigTextWithSuggestions] gotSuggestionsError:", val def gotHistoryError(self, val): print "[ConfigTextWithSuggestions] gotHistoryError:", val def getSuggestions(self): self.__suggestions = self.suggestionsClass().getSuggestions(self.value, self.propagateSuggestions, self.gotSuggestionsError) def getHistory(self): self.__history = self.historyClass(self.historyCfg).getSuggestions(self.value, self.propagateHistory, self.gotHistoryError) def cancelSuggestions(self): self.value = self.tmpValue self.enableSuggestions(False) self.enableHistory(False) class Message(object): def __init__(self, infowidget, errorwidget): self.infowidget = infowidget self.errorwidget = errorwidget self.timer = eTimer() self.timer.callback.append(self.hide) def info(self, text, timeout=None): self.timer.stop() self.errorwidget.hide() self.infowidget.setText(text) self.infowidget.show() if timeout: self.timer.start(timeout, True) def error(self, text, timeout=None): self.timer.stop() self.infowidget.hide() self.errorwidget.setText(text) self.errorwidget.show() if timeout: self.timer.start(timeout, True) def hide(self): self.timer.stop() self.errorwidget.hide() self.infowidget.hide() class SearchParamsHelper(object): def __init__(self, seeker, searchSettings): self.seeker = seeker self.searchSettings = searchSettings self.searchTitle = searchSettings.title self.searchType = searchSettings.type self.searchYear = searchSettings.year self.searchSeason = searchSettings.season self.searchEpisode = searchSettings.episode self.searchProvider = searchSettings.provider self.searchUseFilePath = searchSettings.useFilePath def resetSearchParams(self): self.searchType.value = self.searchType.default self.searchType.save() self.searchTitle.value = self.searchTitle.default self.searchTitle.save() self.searchSeason.value = self.searchSeason.default self.searchSeason.save() self.searchEpisode.value = self.searchEpisode.default self.searchEpisode.save() self.searchYear.value = self.searchYear.default self.searchYear.save() self.searchProvider.value = self.searchProvider.default self.searchProvider.save() self.searchUseFilePath.value = self.searchUseFilePath.default def detectSearchParams(self, searchExpression): self.resetSearchParams() params = detectSearchParams(searchExpression) if params[2]: self.searchType.value = "tv_show" self.searchTitle.value = params[2] self.searchSeason.value = params[3] and int(params[3]) or 0 self.searchEpisode.value = params[4] and int(params[4]) or 0 else: self.searchType.value = "movie" self.searchTitle.value = params[0] self.searchYear.value = params[1] and int(params[1]) or 0 self.updateProviders() def getSearchParams(self): langs = [self.searchSettings.lang1.value, self.searchSettings.lang2.value, self.searchSettings.lang3.value] provider = self.searchProvider.value title = self.searchTitle.value tvshow = self.searchType.value == "tv_show" and title or "" year = self.searchYear.value and not tvshow and str(self.searchYear.value) or "" season = self.searchSeason.value and tvshow and str(self.searchSeason.value) or "" episode = self.searchEpisode.value and tvshow and str(self.searchEpisode.value) or "" return provider, langs, title, year, tvshow, season, episode def updateProviders(self): tvshow = self.searchType.value == "tv_show" providers = self.seeker.getProviders([self.searchSettings.lang1.value, self.searchSettings.lang2.value, self.searchSettings.lang3.value], not tvshow, tvshow) choiceList = [] choiceList.append(("all", _("All"))) choiceList.extend((p.id, p.provider_name) for p in providers) self.searchProvider.setChoices(choiceList) if self.searchProvider.value not in [p.id for p in providers]: if tvshow: self.searchProvider.value = self.searchSettings.tvshowProvider.value else: self.searchProvider.value = self.searchSettings.movieProvider.value tvshowProviders = self.seeker.getProviders(movie=False, tvshow=True) choiceList = [] choiceList.append(("all", _("All"))) choiceList.extend((p.id, p.provider_name) for p in tvshowProviders) self.searchSettings.tvshowProvider.setChoices(choiceList) movieProviders = self.seeker.getProviders(movie=True, tvshow=False) choiceList = [] choiceList.append(("all", _("All"))) choiceList.extend((p.id, p.provider_name) for p in movieProviders) self.searchSettings.movieProvider.setChoices(choiceList) class SubsSearchDownloadOptions(Screen, ConfigListScreen): skin=""" """ def __init__(self, session, subtitle, saveAs, saveTo, addLang, dPath, vPath=None): Screen.__init__(self, session) saveAsOptions = [] saveAsOptions.append(('version', _("Release"))) if vPath is not None and os.path.isfile(vPath): saveAsOptions.append(('video', _("Video filename"))) saveAsOptions.append(('custom', _("User defined"))) saveToOptions = [] saveToOptions.append(('custom', _('User defined'))) if vPath is not None and os.path.isfile(vPath): saveToOptions.append(('video', _('Next to video'))) if saveAs == 'default': # we don't know what the default filename will be saveAs = 'version' elif saveAs == 'video' and vPath is None: saveAs = 'version' if saveTo == 'video' and vPath is None: saveTo = 'custom' self.configSaveAs = ConfigSelection(default=saveAs, choices=saveAsOptions) self.configSaveTo = ConfigSelection(default=saveTo, choices=saveToOptions) self.configAddLang = ConfigYesNo(default=addLang) configList = [ getConfigListEntry(_("Save to"), self.configSaveTo), getConfigListEntry(_("Save as"), self.configSaveAs), getConfigListEntry(_("Append language to filename"), self.configAddLang), ] ConfigListScreen.__init__(self, configList, session) self.subtitle = subtitle self.dPath = dPath self.vPath = vPath self["fname"] = StaticText() self["dpath"] = StaticText() self["key_red"] = StaticText(_("Filename")) self["key_green"] = StaticText(_("Path")) self["key_blue"] = StaticText(_("Reset")) self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "ColorActions"], { "right": self.keyRight, "left":self.keyLeft, "ok":self.confirm, "cancel":self.cancel, "red": self.editFName, "green": self.editDPath, "blue":self.resetDefaults }, -2) self.onLayoutFinish.append(self.updateWindowTitle) self.onLayoutFinish.append(self.updateFName) self.onLayoutFinish.append(self.updateDPath) def buildMenu(self): configList = [] configList.append(getConfigListEntry(_("Save to"), self.configSaveTo)) configList.append(getConfigListEntry(_("Save as"), self.configSaveAs)) if self.configSaveAs.value != "custom": configList.append(getConfigListEntry(_("Append language to filename"), self.configAddLang)) self["config"].setList(configList) def updateWindowTitle(self): self.setTitle(_("Download options")) def updateFName(self): fname = None if self.configSaveAs.value == "video": fname = os.path.splitext(os.path.basename(self.vPath))[0] elif self.configSaveAs.value == "version": fname = os.path.splitext(self.subtitle['filename'])[0] if self.configAddLang.value and not self.configSaveAs.value == "custom": fname = "%s.%s"%(fname, languageTranslate(self.subtitle['language_name'], 0, 2)) if fname: self["fname"].text = toString(fname) def updateDPath(self): dpath = None if self.configSaveTo.value == "video": dpath = os.path.dirname(self.vPath) elif self.configSaveTo.value == "custom": dpath = self.dPath if dpath: self["dpath"].text = toString(dpath) def resetDefaults(self): for x in self["config"].list: x[1].value = x[1].default self.buildMenu() self.updateFName() self.updateDPath() def editFName(self): def editFnameCB(callback=None): if callback is not None and len(callback): self["fname"].text = callback self.configSaveAs.value = "custom" self.buildMenu() self.updateFName() from Screens.VirtualKeyBoard import VirtualKeyBoard self.session.openWithCallback(editFnameCB, VirtualKeyBoard, _("Edit Filename"), text= toString(self["fname"].text.strip())) def editDPath(self): def editDPathCB(callback=None): if callback is not None and len(callback): self["dpath"].text = callback self.configSaveTo.value = "custom" self["config"].invalidate(self.configSaveTo) self.session.openWithCallback(editDPathCB, LocationBox, _("Edit download path"), currDir = toString(self["dpath"].text.strip())) def confirm(self): fname = self["fname"].text.strip() if len(fname) == "": self.session.open(MessageBox, _("Filename cannot be empty!"), type=MessageBox.TYPE_WARNING) return dpath = self["dpath"].text.strip() if not os.path.isdir(dpath): self.session.open(MessageBox, _("Path doesn't exist!"), type=MessageBox.TYPE_WARNING) return self.close(dpath, fname) def cancel(self): self.close(None, None) def keyRight(self): saveAsConfig = False if self['config'].getCurrent()[1] == self.configSaveAs: saveAsConfig = True currIdx = self.configSaveAs.choices.index(self.configSaveAs.value) if currIdx == len(self.configSaveAs.choices) -1: nextChoice = self.configSaveAs.choices[0] else: nextChoice = self.configSaveAs.choices[currIdx+1] if nextChoice == 'custom': self.configSaveAs.value = nextChoice ConfigListScreen.keyRight(self) if saveAsConfig: self.buildMenu() if self['config'].getCurrent()[1] in (self.configSaveAs, self.configAddLang): self.updateFName() elif self['config'].getCurrent()[1] in (self.configSaveTo,): self.updateDPath() def keyLeft(self): saveAsConfig = False if self['config'].getCurrent()[1] == self.configSaveAs: saveAsConfig = True currIdx = self.configSaveAs.choices.index(self.configSaveAs.value) if currIdx == 0: nextChoice = self.configSaveAs.choices[len(self.configSaveAs.choices)-1] else: nextChoice = self.configSaveAs.choices[currIdx-1] if nextChoice == 'custom': self.configSaveAs.value = nextChoice ConfigListScreen.keyLeft(self) if saveAsConfig: self.buildMenu() if self['config'].getCurrent()[1] in (self.configSaveAs, self.configAddLang): self.updateFName() elif self['config'].getCurrent()[1] in (self.configSaveTo,): self.updateDPath() class SubsSearchContextMenu(Screen): skin = """ {"templates": {"default": (22, [ MultiContentEntryText(pos = (5, 0), size = (380, 22), font = 0, color = 0xffffff, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 0), # langname, ], True, "showOnDemand"), }, "fonts": [gFont("Regular", 19)], "itemHeight":22, } """ def __init__(self, session): Screen.__init__(self, session) self.options = [] self["subtitle_release"] = StaticText() self["context_menu"] = List() def up(self): self["context_menu"].selectNext() def down(self): self["context_menu"].selectPrevious() def right(self): self["context_menu"].selectNext() def left(self): self["context_menu"].selectPrevious() def updateGUI(self, subtitle, options): self["subtitle_release"].text = toString(subtitle['filename']) self["context_menu"].list = [(o[0],) for o in options] self.options = options def getSelection(self): return self.options[self["context_menu"].index][1] class SubsSearch(Screen): skin = """ {"templates": {"default": (22, [ MultiContentEntryText(pos = (0, 0), size = (200, 22), font = 0, color = 0xDAA520, flags = RT_HALIGN_LEFT, text = 0), # langname, MultiContentEntryText(pos = (205, 0), size = (400, 22), font = 0, flags = RT_HALIGN_LEFT, text = 1) ], False, "showNever"), }, "fonts": [gFont("Regular", 18)], "itemHeight":22, } {"templates": {"default": (23, [ MultiContentEntryPixmapAlphaBlend(pos = (0, 0), size = (24, 24), png=0), # key, MultiContentEntryText(pos = (30, 0), size = (100, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # language, MultiContentEntryText(pos = (140, 0), size = (335, 25), font = 0, flags = RT_HALIGN_LEFT, text = 2), # filename, MultiContentEntryText(pos = (500, 0), size = (135, 25), font = 0, flags = RT_HALIGN_LEFT, text = 3), # size, MultiContentEntryPixmapAlphaBlend(pos = (645, 0), size = (24, 24), png=4), # syncPng, ], True, "showOnDemand"), "old": (23, [ MultiContentEntryPixmapAlphaBlend(pos = (0, 0), size = (24, 24), png=0), # key, MultiContentEntryText(pos = (30, 0), size = (60, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # language, MultiContentEntryText(pos = (100, 0), size = (335, 25), font = 0, flags = RT_HALIGN_LEFT, text = 2), # filename, MultiContentEntryText(pos = (445, 0), size = (130, 25), font = 0, flags = RT_HALIGN_LEFT, text = 3), # size, MultiContentEntryText(pos = (585, 0), size = (95, 25), font = 0, flags = RT_HALIGN_LEFT, text = 4), # sync, ], True, "showOnDemand"), }, "fonts": [gFont("Regular", 18), gFont("Regular", 16)], "itemHeight": 23 } """ def __init__(self, session, seeker, searchSettings, filepath=None, searchTitles=None, resetSearchParams=True, standAlone=False): Screen.__init__(self, session) self.searchSettings = searchSettings self.standAlone = standAlone searchTitles = searchTitles or [""] self.searchParamsHelper = SearchParamsHelper(seeker, searchSettings) self.seeker = seeker self.searchExpression = searchTitles[0] self.searchTitles = searchTitles self.filepath = filepath self.isLocalFilepath = filepath and os.path.isfile(filepath) or False self.searchTitle = searchSettings.title self.searchType = searchSettings.type self.searchYear = searchSettings.year self.searchSeason = searchSettings.season self.searchEpisode = searchSettings.episode self.searchProvider = searchSettings.provider self.searchUseFilePath = searchSettings.useFilePath self.__downloadedSubtitles = [] self.__downloading = False self.__searching = False self["loadmessage"] = Label("") self["errormessage"] = Label("") self["search_info"] = List([]) self["header_country"] = StaticText(_("Language")) self["header_release"] = StaticText(_("Release")) self["header_provider"] = StaticText(_("Provider")) self["header_sync"] = StaticText(_("S")) self["subtitles"] = List([]) self["key_menu_img"] = Boolean() self["key_red"] = StaticText(_("Update")) self["key_green"] = StaticText(_("Search")) self["key_yellow"] = StaticText(_("History")) self["key_blue"] = StaticText(_("Settings")) self["okCancelActions"] = ActionMap(["OkCancelActions"], { "ok": self.keyOk, "cancel": self.keyCancel, }) self["menuActions"] = ActionMap(["ColorActions", "MenuActions"], { "red":self.updateSearchParams, "green":self.searchSubs, "yellow":self.openDownloadHistory, "blue":self.openSettings, "menu":self.openContextMenu, }) self["listActions"] = ActionMap(["DirectionActions"], { "up": self.keyUp, "upRepeated": self.keyUp, "down": self.keyDown, "downRepeated": self.keyDown, "right":self.keyRight, "rightRepeated":self.keyRight, "left":self.keyLeft, "leftRepeated":self.keyLeft, }, -2) self["searchActions"] = ActionMap(["OkCancelActions"], { "ok":self.cancelSearchSubs, "cancel":self.close, }) self["searchActions"].setEnabled(False) self.__contextMenu = self.session.instantiateDialog(SubsSearchContextMenu) self.__contextMenu.hide() self["contextMenuActions"] = ActionMap(["DirectionActions", "OkCancelActions", "MenuActions"], { "up":self.__contextMenu.up, "down":self.__contextMenu.down, "right":self.__contextMenu.right, "left": self.__contextMenu.left, "ok": self.contextMenuOk, "cancel":self.contextMenuCancel, "menu":self.contextMenuCancel, }) self["contextMenuActions"].setEnabled(False) self.message = Message(self['loadmessage'], self['errormessage']) self.onLayoutFinish.append(self.updateTitle) self.onLayoutFinish.append(self.__getSubtitlesRenderer) if resetSearchParams: self.onLayoutFinish.append(self.detectSearchParams) self.onLayoutFinish.append(self.searchParamsHelper.updateProviders) self.onLayoutFinish.append(self.updateSearchInfoList) self.onLayoutFinish.append(self.updateBottomMenu) if not searchSettings.manualSearch.value and not self.standAlone: self.onLayoutFinish.append(self.searchSubs) else: self.onLayoutFinish.append(self.searchMessage) self.onClose.append(self.__contextMenu.hide) self.onClose.append(self.__contextMenu.doClose) self.onClose.append(self.message.hide) self.onClose.append(self.searchParamsHelper.resetSearchParams) self.onClose.append(self.stopSearchSubs) self.onClose.append(self.closeSeekers) def __getSubtitlesRenderer(self): from Components.Sources.Source import Source from Components.Renderer.Listbox import Listbox for r in self.renderer: if isinstance(r, Listbox): s = r while not isinstance(s,Source): s = s.source if s == self['subtitles']: self.__listboxRenderer = r break def updateTitle(self): self.title = _("Subtitles search") def updateSearchInfoList(self): searchInfoList = [] lang1 = self.searchSettings.lang1.value lang2 = self.searchSettings.lang2.value lang3 = self.searchSettings.lang3.value lang1 = lang1 in LanguageCodes and LanguageCodes[lang1][0] or lang1 lang2 = lang2 in LanguageCodes and LanguageCodes[lang2][0] or lang2 lang3 = lang3 in LanguageCodes and LanguageCodes[lang3][0] or lang3 langs = [lang1] if lang2 not in langs: langs.append(lang2) if lang3 not in langs: langs.append(lang3) languages = ", ".join(_(lang) for lang in langs) year = self.searchYear.value and str(self.searchYear.value) or "" season = self.searchSeason.value and str(self.searchSeason.value) or "" episode = self.searchEpisode.value and str(self.searchEpisode.value) or "" useFilePathStr = self.searchUseFilePath.value and _("yes") or _("no") searchInfoList.append((_("Title") + ":", self.searchTitle.value)) searchInfoList.append((_("Type") + ":", self.searchType.getText())) if self.searchType.value == "movie": searchInfoList.append((_("Year") + ":", year)) else: searchInfoList.append((_("Season") + ":", season)) searchInfoList.append((_("Episode") + ":", episode)) searchInfoList.append((_("Provider") + ":", self.searchProvider.getText())) searchInfoList.append((_("Preferred languages") + ":", languages)) searchInfoList.append((_("Use File path") + ":", useFilePathStr)) self['search_info'].list = searchInfoList def updateSubsList(self): imgDict = { 'sync':loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'check.png')), 'unk':loadPNG(os.path.join(os.path.dirname(__file__), 'img', 'countries', 'UNK.png')) } subtitleListGUI = [] for sub in self.subtitlesList: sync = 'sync' in sub and sub['sync'] or False if sub['country'] not in imgDict: countryImgPath = os.path.join(os.path.dirname(__file__), 'img', 'countries', sub['country'] + '.png') if os.path.isfile(countryImgPath): countryPng = loadPNG(countryImgPath) imgDict[sub['country']] = countryPng else: countryPng = imgDict['unk'] syncPng = sync and imgDict['sync'] or None subtitleListGUI.append((countryPng, _(toString(sub['language_name'])), toString(sub['filename']), toString(sub['provider']), syncPng),) imgDict = None self['subtitles'].list = subtitleListGUI def updateBottomMenu(self): if self.__searching: self["key_red"].text = "" self["key_green"].text = "" self["key_yellow"].text = "" self["key_blue"].text = "" self["key_menu_img"].boolean = False elif self.__downloading: self["key_red"].text = "" self["key_green"].text = "" self["key_yellow"].text = "" self["key_blue"].text = "" self["key_menu_img"].boolean = False else: self["key_red"].text = (_("Update")) self["key_green"].text = (_("Search")) self["key_yellow"].text = (_("History")) self["key_blue"].text = (_("Settings")) if self["subtitles"].count() > 0: self["key_menu_img"].boolean = True def updateActionMaps(self): if self.__searching: self["okCancelActions"].setEnabled(False) self["listActions"].setEnabled(False) self["menuActions"].setEnabled(False) self["searchActions"].setEnabled(True) self["contextMenuActions"].setEnabled(False) elif self.__downloading: self["okCancelActions"].setEnabled(False) self["listActions"].setEnabled(False) self["menuActions"].setEnabled(False) self["searchActions"].setEnabled(False) self["contextMenuActions"].setEnabled(False) elif self.__contextMenu.shown: self["okCancelActions"].setEnabled(False) self["listActions"].setEnabled(False) self["menuActions"].setEnabled(False) self["searchActions"].setEnabled(False) self["contextMenuActions"].setEnabled(True) else: self["okCancelActions"].setEnabled(True) self["listActions"].setEnabled(True) self["menuActions"].setEnabled(True) self["searchActions"].setEnabled(False) self["contextMenuActions"].setEnabled(False) def detectSearchParams(self): self.searchParamsHelper.detectSearchParams(self.searchExpression) def closeSeekers(self): for seeker in self.seeker.seekers: seeker.close() def keyOk(self): if self['subtitles'].count(): self.downloadSubs(self.subtitlesList[self["subtitles"].index]) def keyCancel(self): self.close() def keyUp(self): if self['subtitles'].count(): self.message.hide() self['subtitles'].selectPrevious() def keyDown(self): if self['subtitles'].count(): self.message.hide() self['subtitles'].selectNext() def keyRight(self): if self['subtitles'].count(): self.message.hide() self.__listboxRenderer.move(self.__listboxRenderer.instance.pageDown) def keyLeft(self): if self['subtitles'].count(): self.message.hide() self.__listboxRenderer.move(self.__listboxRenderer.instance.pageUp) def searchMessage(self): self.message.info(_("Update search parameters if not correct\n and press green button for search")) def searchSubs(self): def searchSubsUpdate(args): pfinished, status, value = args if status: self.__finished[pfinished] = value else: self.__finished[pfinished] = {'list':[], 'status':status,'message':str(value)} progressMessage = "%s - %d%%" % (_("loading subtitles list"), int(len(self.__finished.keys()) / float(len(provider)) * 100)) progressMessage +="\n" + _("subtitles found") + " (%d)"%(sum(len(self.__finished[p]['list']) for p in self.__finished.keys())) progressMessage +="\n\n" + _("Press OK to Stop") self.message.info(progressMessage) self.stopSearchSubs() self.subtitlesList = [] self.subtitlesDict = {} self.__finished = {} p = self.searchParamsHelper.getSearchParams() langs, title, year, tvshow, season, episode = p[1], p[2], p[3], p[4], p[5], p[6] providers = self.seeker.getProviders(langs, not tvshow, tvshow) if self.searchProvider.value == "all": provider = providers else: provider = [p for p in providers if p.id == self.searchProvider.value] filepath = self.searchUseFilePath.value and self.filepath or None timeout = float(self.searchSettings.timeout.value) params = { 'search':{ 'providers':[p.id for p in provider], 'title':title, 'filepath':filepath, 'langs':langs, 'year': year, 'tvshow': tvshow, 'season': season, 'episode': episode, 'timeout': timeout }, 'settings': dict((s.id, s.settings_provider.getSettingsDict()) for s in self.seeker.seekers) } progressMessage = "%s - %d%%" % (_("loading subtitles list"), 0) progressMessage +="\n" + _("subtitles found") + " (%d)"% 0 progressMessage +="\n\n" + _("Press OK to Stop") self.message.info(progressMessage) self.__searching = True self.updateActionMaps() self.updateSubsList() self.updateBottomMenu() SubsSearchProcess().start(params, searchSubsUpdate, self.searchSubsSuccess, self.searchSubsError) def cancelSearchSubs(self): self.stopSearchSubs() self.searchSubsSuccess(self.__finished) def stopSearchSubs(self): for p in SubsSearchProcess.processes: p.stop() print len(SubsSearchProcess.processes), 'processes still running' def searchSubsSuccess(self, subtitles): print '[SubsSearch] search success' self.message.hide() self.subtitlesDict = subtitles subtitlesList = self.seeker.getSubtitlesList(subtitles) subtitlesList = self.seeker.sortSubtitlesList(subtitlesList, sort_sync=True) langs = [self.searchSettings.lang1.value, self.searchSettings.lang2.value, self.searchSettings.lang3.value] if self.searchSettings.defaultSort.value == 'lang': subtitlesList = self.seeker.sortSubtitlesList(subtitlesList, langs, sort_langs=True) elif self.searchSettings.defaultSort.value == 'provider': subtitlesList = self.seeker.sortSubtitlesList(subtitlesList, langs, sort_provider=True) self.subtitlesList = subtitlesList if len(self.subtitlesList) == 0: noSubtitlesMessage = _("No subtitles found :(") noSubtitlesMessage += "\n" + _("Try update(simplify) search expression and try again..") self.message.info(noSubtitlesMessage) self.__searching = False self.updateSubsList() self.updateBottomMenu() self.updateActionMaps() def searchSubsError(self, error): print '[SubsSearch] search error', str(error) self.message.error(error.message, 4000) self.subtitlesList = [] self.subtitlesDict = {} self.updateSubsList() self.__searching = False self.updateBottomMenu() self.updateActionMaps() def downloadSubs(self, subtitle, downloadDir=None, fName=None, saveAs=None, saveTo=None, langToFilename=None, askOverwrite=None, closeOnSuccess = None): if saveAs is None: saveAs = self.searchSettings.saveAs.value if saveAs == 'video' and not self.isLocalFilepath: saveAs = self.searchSettings.saveAsFallback.value if langToFilename is None: langToFilename = self.searchSettings.addLangToSubsFilename.value if askOverwrite is None: askOverwrite = self.searchSettings.askOverwriteExistingSubs.value if closeOnSuccess is None: closeOnSuccess = self.searchSettings.loadSubtitlesAfterDownload.value if downloadDir is None: if saveTo is None: saveTo = self.searchSettings.saveTo.value if saveTo == 'video' and self.isLocalFilepath: downloadDir = os.path.dirname(self.filepath) else: downloadDir = self.searchSettings.downloadPath.value self.__downloading = True self.__downloadingSubtitle = subtitle self.updateActionMaps() self.updateBottomMenu() self.message.info(_('downloading subtitles...')) self.__closeOnSuccess = closeOnSuccess settings = { "save_as": saveAs, "lang_to_filename":langToFilename, "ask_overwrite":askOverwrite } self.seeker.downloadSubtitle(self.downloadSubsSuccess, self.downloadSubsError, self.downloadSubsCancel, subtitle, self.subtitlesDict, settings, downloadDir, fName) def downloadSubsSuccess(self, subFile): print '[SubsSearch] download success %s' % toString(subFile) dsubtitle = { "name":toUnicode(os.path.basename(subFile)), "country": toUnicode(self.__downloadingSubtitle['country']), "provider":toUnicode(self.__downloadingSubtitle['provider']), "fpath": toUnicode(subFile), } if self.searchSettings.downloadHistory.enabled.value: fpath = os.path.join(self.searchSettings.downloadHistory.path.value, 'hsubtitles.json') try: subtitles = json.load(open(fpath,"r")) except Exception as e: print '[SubsSearch] cannot load download history:', e subtitles = [] limit = int(self.searchSettings.downloadHistory.limit.value) if dsubtitle in subtitles: subtitles.remove(dsubtitle) if len(subtitles) >= limit: print '[SubsSearch] download history limit reached!, removing oldest entries' del subtitles[-(len(subtitles)-limit):] subtitles.insert(0, dsubtitle) try: json.dump(subtitles, open(fpath,'w')) except Exception as e: print '[SubsSearch] cannot save download history:', e self.__downloadedSubtitles.append(dsubtitle) self.afterDownloadSuccess(dsubtitle) self.message.hide() self.__downloading = False del self.__downloadingSubtitle self.updateBottomMenu() self.updateActionMaps() def downloadSubsError(self, e): print '[SubsSearch] download error', str(e) self.__downloading = False del self.__downloadingSubtitle self.updateBottomMenu() self.updateActionMaps() errorMessageFormat = "[{0}]: {1}" if isinstance(e, SubtitlesDownloadError): if e.code == SubtitlesErrors.CAPTCHA_RETYPE_ERROR: self.message.error(errorMessageFormat.format(e.provider, _("captcha doesn't match, try again...")), 4000) elif e.code == SubtitlesErrors.INVALID_CREDENTIALS_ERROR: self.message.error(errorMessageFormat.format(e.provider, _("invalid credentials provided, correct them and try again")), 4000) elif e.code == SubtitlesErrors.NO_CREDENTIALS_ERROR: self.message.error(errorMessageFormat.format(e.provider, _("no credentials provided, set them and try again")), 4000) else: self.message.error(str(e), 4000) else: print "".join(traceback.format_tb(e.tb)) self.message.error(str(e), 4000) def downloadSubsCancel(self): print '[SubsSearch] download cancelled' self.__downloading = False del self.__downloadingSubtitle self.updateBottomMenu() self.updateActionMaps() self.message.hide() def afterDownloadSuccess(self, subtitle): if not self.standAlone and self.__closeOnSuccess: self.close(subtitle['fpath']) self.__closeOnSuccess = None def openContextMenu(self): if not self["subtitles"].count() > 0: return downloadOptions = [ (_("Download (user defined)"), "d_custom"), (_("Download (next to video)"), "d_video"), (_("Download (more...)"), "d_more"), ] downloadAndLoadOptions = [ (_("Download and Load (user defined) "), "do_custom"), (_("Download and Load (next to video)"), "do_video"), (_("Download and Load (more...)"), "do_more"), ] if not self.isLocalFilepath or self.standAlone: downloadOptions.remove(downloadOptions[1]) downloadAndLoadOptions.remove(downloadAndLoadOptions[1]) if self.standAlone: del downloadAndLoadOptions[:] options = [] if not self.searchSettings.loadSubtitlesAfterDownload.value or self.standAlone: if len(downloadOptions) > 0: if self.searchSettings.saveTo.value == "custom": options.append(downloadOptions[0]) elif self.searchSettings.saveTo.value == "video" and self.isLocalFilepath: options.append(downloadOptions[1]) elif self.searchSettings.saveTo.value == "more": options.append(downloadOptions[-1]) for o in downloadOptions: if o not in options: options.append(o) options.extend(downloadAndLoadOptions) else: if len(downloadAndLoadOptions) > 0: if self.searchSettings.saveTo.value == "custom": options.append(downloadAndLoadOptions[0]) elif self.searchSettings.saveTo.value == "video" and self.isLocalFilepath: options.append(downloadAndLoadOptions[1]) elif self.searchSettings.saveTo.value == "more": options.append(downloadAndLoadOptions[-1]) for o in downloadAndLoadOptions: if o not in options: options.append(o) options.extend(downloadOptions) subtitle = self.subtitlesList[self["subtitles"].index] self.__contextMenu.updateGUI(subtitle, options) self.__contextMenu.show() self.updateActionMaps() def contextMenuOk(self): def downloadMoreCB(dPath, fName): if dPath and fName: self.downloadSubs(subtitle, downloadDir=dPath, fName=fName, closeOnSuccess=closeOnSuccess) answer = self.__contextMenu.getSelection() self.__contextMenu.hide() self.updateActionMaps() subtitle = self.subtitlesList[self["subtitles"].index] if answer == "d_custom": self.downloadSubs(subtitle, saveTo='custom', closeOnSuccess=False) elif answer == 'd_video': self.downloadSubs(subtitle, saveTo='video', closeOnSuccess=False) elif answer == 'do_custom': self.downloadSubs(subtitle, saveTo='custom', closeOnSuccess=True) elif answer == 'do_video': self.downloadSubs(subtitle, saveTo='video', closeOnSuccess=True) elif answer == 'do_more': closeOnSuccess = True elif answer == 'd_more': closeOnSuccess = False if answer == 'd_more' or answer == 'do_more': saveAs = self.searchSettings.saveAs.value saveTo = self.searchSettings.saveTo.value addLang = self.searchSettings.addLangToSubsFilename.value dPath = self.searchSettings.downloadPath.value vPath = self.filepath self.session.openWithCallback(downloadMoreCB, SubsSearchDownloadOptions, subtitle, saveAs, saveTo, addLang, dPath, vPath) def contextMenuCancel(self): self.__contextMenu.hide() self.updateActionMaps() def updateSearchParams(self): def updateSearchParamsCB(callback=None): if callback: self.updateSearchInfoList() self.updateBottomMenu() if not self.searchSettings.manualSearch.value: self.searchSubs() self.session.openWithCallback(updateSearchParamsCB, SubsSearchParamsMenu, self.seeker, self.searchSettings, self.searchTitles, False) def openDownloadHistory(self): def openDownloadHistoryCB(subtitles, subtitle=None): if len(subtitles) > 0: if not self.searchSettings.downloadHistory.enabled.value: for i in self.__downloadedSubtitles: if i in subtitles: subtitles.remove(i) try: json.dump(subtitles, open(fpath,"w")) except Exception as e: print '[SubsSearch] save download history:', e if subtitle is not None: if not self.standAlone: self.close(subtitle) fpath = os.path.join(self.searchSettings.downloadHistory.path.value, 'hsubtitles.json') try: subtitles = json.load(open(fpath,"r")) except Exception as e: print '[SubsSearch] cannot load download history:', e subtitles = [] self.session.openWithCallback(openDownloadHistoryCB, SubsDownloadedSelection, subtitles, self.searchSettings.downloadHistory, self.__downloadedSubtitles) def openSettings(self): def openSettingsCB(langChanged = False): self.seeker.tmp_path = self.searchSettings.tmpPath.value self.seeker.download_path = self.searchSettings.downloadPath.value self.searchParamsHelper.updateProviders() self.updateSearchInfoList() self.updateBottomMenu() if langChanged and not self.searchSettings.manualSearch.value: self.searchSubs() self.session.openWithCallback(openSettingsCB, SubsSearchSettings, self.searchSettings, self.seeker, self.isLocalFilepath) class SubsSearchSettings(Screen, ConfigListScreen): @staticmethod def getConfigList(searchSettings): configList = [] configList.append(getConfigListEntry(_("Preferred subtitles language") + ' 1', searchSettings.lang1)) configList.append(getConfigListEntry(_("Preferred subtitles language") + ' 2', searchSettings.lang2)) configList.append(getConfigListEntry(_("Preferred subtitles language") + ' 3', searchSettings.lang3)) configList.append(getConfigListEntry(_("Preferred Movie provider"), searchSettings.movieProvider)) configList.append(getConfigListEntry(_("Preferred TV show provider"), searchSettings.tvshowProvider)) configList.append(getConfigListEntry(_("Manual search"), searchSettings.manualSearch)) configList.append(getConfigListEntry(_("Subtitles provider timeout"), searchSettings.timeout)) configList.append(getConfigListEntry(_("Check search parameters before subtitles search"), searchSettings.openParamsDialogOnSearch)) configList.append(getConfigListEntry(_("Sort subtitles list by"), searchSettings.defaultSort)) configList.append(getConfigListEntry(_("Save subtitles as"), searchSettings.saveAs)) configList.append(getConfigListEntry(_("Save subtitles as (fallback)"), searchSettings.saveAsFallback)) configList.append(getConfigListEntry(_("Add subtitle's language to filename"), searchSettings.addLangToSubsFilename)) configList.append(getConfigListEntry(_("Save subtitles to"), searchSettings.saveTo)) configList.append(getConfigListEntry(_("Subtitles download path"), searchSettings.downloadPath)) configList.append(getConfigListEntry(_("Subtitles temp path"), searchSettings.tmpPath)) configList.append(getConfigListEntry(_("Always ask before overwriting existing subtitles"), searchSettings.askOverwriteExistingSubs)) configList.append(getConfigListEntry(_("Load subtitles after download"), searchSettings.loadSubtitlesAfterDownload)) historySettings = searchSettings.downloadHistory configList.append(getConfigListEntry(_("Save downloaded subtitles to history"), historySettings.enabled)) if historySettings.enabled.value: configList.append(getConfigListEntry(_("Load/Save download history directory"), historySettings.path)) return configList skin = """ {"templates": {"default": (23, [ MultiContentEntryText(pos = (0, 0), size = (200, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # name, MultiContentEntryText(pos = (210, 0), size = (180, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # lang, MultiContentEntryText(pos = (400, 0), size = (200, 25), font = 0, flags = RT_HALIGN_RIGHT, text = 2, color=0xFF000003) # enabled, ], True, "showOnDemand"), "notselected": (23, [ MultiContentEntryText(pos = (0, 0), size = (200, 25), font = 0, flags = RT_HALIGN_LEFT, text = 0), # name, MultiContentEntryText(pos = (210, 0), size = (180, 25), font = 0, flags = RT_HALIGN_LEFT, text = 1), # lang, MultiContentEntryText(pos = (400, 0), size = (200, 25), font = 0, flags = RT_HALIGN_RIGHT, text = 2, color=0xFF000003) # enabled, ], False, "showOnDemand") }, "fonts": [gFont("Regular", 18), gFont("Regular", 16)], "itemHeight": 23 } """ FOCUS_CONFIG, FOCUS_PROVIDERS = range(2) def __init__(self, session, searchSettings, seeker, isLocalFilepath=True): Screen.__init__(self, session) ConfigListScreen.__init__(self, [], session=session) self.searchSettings = searchSettings self.searchParamsHelper = SearchParamsHelper(seeker, searchSettings) self.providers = seeker.seekers self.isLocalFilepath = isLocalFilepath self.focus = self.FOCUS_CONFIG self['providers'] = List([]) self["header_name"] = StaticText(_("Provider name")) self["header_lang"] = StaticText(_("Supported languages")) self["header_state"] = StaticText(_("State")) self["key_green"] = Label(_("Save")) self["key_red"] = Label(_("Cancel")) self["key_blue"] = Label(_("Reset Defaults")) self["key_yellow"] = Label(_("Switch List")) self["actions"] = ActionMap(["DirectionActions", "SetupActions", "OkCancelActions", "ColorActions", "ListboxActions"], { "ok": self.keyOk, "cancel": self.keyCancel, "save":self.keySave, "up": self.keyUp, "down": self.keyDown, "right":self.keyRight, "left":self.keyLeft, "blue":self.resetDefaults, "yellow":self.switchList, "pageUp":self.switchList, "pageDown":self.switchList, }, -2) self.onLayoutFinish.append(self.setWindowTitle) self.onLayoutFinish.append(self.buildMenu) self.onLayoutFinish.append(self.updateProvidersList) self.onLayoutFinish.append(self.setConfigFocus) def setWindowTitle(self): self.setTitle(_("Subtitles search settings")) def buildMenu(self): self["config"].setList(self.getConfigList(self.searchSettings)) def updateProvidersList(self): providerListGUI = [] for provider in self.providers: providerName = provider.provider_name providerLangs = ','.join(provider.supported_langs) if provider.error is not None: providerState = _("error") providerStateColor = 0xff0000 elif provider.settings_provider.getSetting('enabled'): providerState = _("enabled") providerStateColor = 0x00ff00 else: providerState = _("disabled") providerStateColor = 0xffff00 providerListGUI.append((toString(providerName), providerLangs, providerState, providerStateColor)) self['providers'].list = providerListGUI def setConfigFocus(self): self.focus = self.FOCUS_CONFIG self['config'].instance.setSelectionEnable(True) self['providers'].style = 'notselected' def switchList(self): if self.focus == self.FOCUS_PROVIDERS: self.focus = self.FOCUS_CONFIG self['providers'].style = "notselected" self['config'].instance.setSelectionEnable(True) else: self.focus = self.FOCUS_PROVIDERS self['config'].instance.setSelectionEnable(False) self['providers'].style = 'default' def keyOk(self): if self.focus == self.FOCUS_PROVIDERS: provider = self.providers[self['providers'].index] if provider.error: self.showProviderError(provider) else: self.openProviderSettings(provider) else: current = self['config'].getCurrent()[1] if current == self.searchSettings.downloadPath: currentPath = self.searchSettings.downloadPath.value self.session.openWithCallback(self.setDownloadPath, LocationBox, "", "", currentPath) elif current == self.searchSettings.tmpPath: currentPath = self.searchSettings.tmpPath.value self.session.openWithCallback(self.setTmpPath, LocationBox, "", "", currentPath) elif current == self.searchSettings.downloadHistory.path: currentPath = self.searchSettings.downloadHistory.path.value self.session.openWithCallback(self.setHistoryPath, LocationBox, "", "", currentPath) elif current in [self.searchSettings.lang1, self.searchSettings.lang2, self.searchSettings.lang3]: self.session.openWithCallback(self.setLanguage, MyLanguageSelection, current.value) def setLanguage(self, language=None): if language: self['config'].getCurrent()[1].value = language self.buildMenu() def setDownloadPath(self, downloadPath=None): if downloadPath: self.searchSettings.downloadPath.value = downloadPath self.buildMenu() def setTmpPath(self, tmpPath=None): if tmpPath: self.searchSettings.tmpPath.value = tmpPath self.buildMenu() def setHistoryPath(self, historyPath=None): if historyPath: self.searchSettings.downloadHistory.path.value = historyPath self.buildMenu() def keySave(self): langChanged = (self.searchSettings.lang1.isChanged() or self.searchSettings.lang2.isChanged() or self.searchSettings.lang3.isChanged()) for x in self["config"].list: x[1].save() self.close(langChanged) def keyCancel(self): for x in self["config"].list: x[1].cancel() self.close() def keyUp(self): if self.focus == self.FOCUS_CONFIG: self['config'].instance.moveSelection(self["config"].instance.moveUp) else: if self['providers'].index == 0: self['providers'].index = len(self['providers'].list) - 1 else: self['providers'].selectPrevious() def keyDown(self): if self.focus == self.FOCUS_CONFIG: self['config'].instance.moveSelection(self["config"].instance.moveDown) else: if self['providers'].index == len(self['providers'].list) -1: self['providers'].index = 0 else: self['providers'].selectNext() def keyRight(self): if self.focus == self.FOCUS_CONFIG: ConfigListScreen.keyRight(self) if self['config'].getCurrent()[1] in [self.searchSettings.saveTo, self.searchSettings.downloadHistory.enabled]: self.buildMenu() def keyLeft(self): if self.focus == self.FOCUS_CONFIG: ConfigListScreen.keyLeft(self) if self['config'].getCurrent()[1] in [self.searchSettings.saveTo, self.searchSettings.downloadHistory.enabled]: self.buildMenu() def resetDefaults(self): for x in self["config"].list: x[1].value = x[1].default self.buildMenu() def showProviderError(self, provider): providerError = provider.error if isinstance(providerError, tuple): err_msg = providerError[1] else: err_msg = "unknown error" if isinstance(providerError,Exception): if isinstance(providerError, ImportError): # No module named ... err_msg= _("missing") + " python-%s "% (providerError.message.split()[-1]) + _("library") else: err_msg = providerError.message msg = "%s: %s"%(provider.provider_name, err_msg) self.session.open(MessageBox, msg, MessageBox.TYPE_WARNING, timeout = 5) def openProviderSettings(self, provider): self.session.openWithCallback(self.openProviderSettingsCB, SubsSearchProviderMenu, provider) def openProviderSettingsCB(self, changed=False): if changed: configIndex = self['config'].getCurrentIndex() providersIndex = self['providers'].index self.updateProvidersList() self.searchParamsHelper.updateProviders() self.buildMenu() self['config'].setCurrentIndex(configIndex) self['providers'].index = providersIndex class SubsSearchParamsMenu(Screen, ConfigListScreen): LIST_CONFIG = 0 LIST_SUGGESTIONS = 1 LIST_HISTORY = 2 def __init__(self, session, seeker, searchSettings, titleList=None, resetSearchParams=True, enabledList=True, windowTitle=None): s = getDesktop(0).size() desktopSize = (s.width(), s.height()) windowSize = (550, 330) self.skin = """ """ % ((desktopSize[1] / 2) - (windowSize[1] / 2) + 50) Screen.__init__(self, session) ConfigListScreen.__init__(self, [], session=session) self["config"] = MyConfigList([], session, enabledList) if not self.handleInputHelpers in self["config"].onSelectionChanged: self["config"].onSelectionChanged.append(self.handleInputHelpers) self.searchParamsHelper = SearchParamsHelper(seeker, searchSettings) self.searchSettings = searchSettings if titleList is None: titleList = [] self.sourceTitleList = titleList self.sourceTitle = titleList and titleList[0] or "" self.currentList = self.LIST_CONFIG self.windowTitle = windowTitle if len(self.sourceTitleList) == 0: self['sourceTitleInfo'] = StaticText(_("Source title not provided")) else: self['sourceTitleInfo'] = StaticText("%s [%d/%d]" % (_("Source title"), 1, len(self.sourceTitleList))) self['sourceTitle'] = StaticText(self.sourceTitle) self['searchTitleParams'] = StaticText(_("Search parameters")) self['pressOkTitleInfo'] = StaticText(_("Press OK to confirm")) self["suggestionActions"] = ActionMap(["DirectionActions", "ColorActions", "OkCancelActions"], { "red": self.cancelToHistoryList, "green": self.cancelToSuggestionsList, "ok": self.switchToConfigList, "cancel":self.cancelToConfigList, "right": self.keyRight, "rightRepeated": self.keyRight, "left": self.keyLeft, "leftRepeated": self.keyLeft, "up": self.keyUp, "upRepeated": self.keyUp, "down": self.keyDown, "downRepeated": self.keyDown }, -2) self["configActions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions"], { "ok": self.keyOK, "cancel": self.keyCancel, "green": self.switchToSuggestionsList, "red": self.switchToHistoryList, "blue": self.toggleSourceTitle, "right": self.keyRight, "rightRepeated": self.keyRight, "left": self.keyLeft, "leftRepeated": self.keyLeft, "up": self.keyUp, "upRepeated": self.keyUp, "down": self.keyDown, "downRepeated": self.keyDown }, -2) self['suggestionActions'].setEnabled(False) if resetSearchParams and titleList is not None: self.onLayoutFinish.append(self.detectSearchParams) self.onLayoutFinish.append(self.buildMenu) self.onLayoutFinish.append(self.setWindowTitle) self.onLayoutFinish.append(self.saveAll) self.onClose.append(self.removeSuggestionWindows) def setWindowTitle(self): if self.windowTitle is not None: self.setTitle(self.windowTitle) else: self.setTitle(_("Update Search params")) def buildMenu(self): menuList = [] menuList.append(getConfigListEntry(_("Title") , self.searchSettings.title)) menuList.append(getConfigListEntry(_("Type") , self.searchSettings.type)) if self.searchSettings.type.value == "movie": menuList.append(getConfigListEntry(_("Year") , self.searchSettings.year)) else: menuList.append(getConfigListEntry(_("Season") , self.searchSettings.season)) menuList.append(getConfigListEntry(_("Episode") , self.searchSettings.episode)) menuList.append(getConfigListEntry(_("Provider") , self.searchSettings.provider)) menuList.append(getConfigListEntry(_("Use File path"), self.searchSettings.useFilePath)) self["config"].list = menuList self["config"].setList(menuList) def detectSearchParams(self): self.searchParamsHelper.detectSearchParams(self.sourceTitle) self.searchParamsHelper.updateProviders() def toggleSourceTitle(self): if len(self.sourceTitleList) == 0: return currIdx = self.sourceTitleList.index(self.sourceTitle) if self.sourceTitle == self.sourceTitleList[-1]: currIdx = 0 else: currIdx += 1 self.sourceTitle = self.sourceTitleList[currIdx] self['sourceTitle'].text = self.sourceTitle self['sourceTitleInfo'].text = "%s [%d/%d]" % (_("Source title"), currIdx + 1, len(self.sourceTitleList)) self.detectSearchParams() self.buildMenu() def switchToSuggestionsList(self): if not self['config'].enabled: return if self["config"].getCurrent()[1] == self.searchSettings.title: self["configActions"].setEnabled(False) self["suggestionActions"].setEnabled(True) self["config"].invalidateCurrent() self["config"].getCurrent()[1].enableHistory(False) if self["config"].getCurrent()[1].enableSuggestions(True): self.currentList = self.LIST_SUGGESTIONS else: self.cancelToConfigList() def switchToHistoryList(self): if not self['config'].enabled: return if self["config"].getCurrent()[1] == self.searchSettings.title: self["configActions"].setEnabled(False) self["suggestionActions"].setEnabled(True) self["config"].invalidateCurrent() self["config"].getCurrent()[1].enableSuggestions(False) if self["config"].getCurrent()[1].enableHistory(True): self.currentList = self.LIST_HISTORY else: self.cancelToConfigList() def switchToConfigList(self): self["config"].getCurrent()[1].enableSuggestions(False) self["config"].getCurrent()[1].enableHistory(False) self["suggestionActions"].setEnabled(False) self["configActions"].setEnabled(True) self.currentList = self.LIST_CONFIG def cancelToHistoryList(self): self["config"].invalidateCurrent() self["config"].getCurrent()[1].cancelSuggestions() self.switchToHistoryList() def cancelToSuggestionsList(self): self["config"].invalidateCurrent() self["config"].getCurrent()[1].cancelSuggestions() self.switchToSuggestionsList() def cancelToConfigList(self): self["config"].invalidateCurrent() self["config"].getCurrent()[1].cancelSuggestions() self.switchToConfigList() def addToHistory(self): history = self.searchSettings.history.value.split(',') if history[0] == '': del history[0] if self.searchSettings.title.value in history: history.remove((self.searchSettings.title.value)) history.insert(0, (self.searchSettings.title.value)) if len(history) == 30: history.pop() self.searchSettings.history.value = ",".join(history) self.searchSettings.history.save() def keySave(self): for x in self["config"].list: x[1].save() self.close(True) def keyCancel(self): for x in self["config"].list: x[1].cancel() self.close() def keyOK(self): if self.currentList == self.LIST_CONFIG: self.addToHistory() self.saveAll() self.close(True) elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY): self.switchToConfigList() def keyDown(self): if not self['config'].enabled: self['config'].enableList() elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY): self["config"].getCurrent()[1].currentListDown() self["config"].invalidateCurrent() elif self.currentList == self.LIST_CONFIG: self['config'].instance.moveSelection(self["config"].instance.moveDown) def keyUp(self): if not self['config'].enabled: self['config'].enableList() elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY): self["config"].getCurrent()[1].currentListUp() self["config"].invalidateCurrent() elif self.currentList == self.LIST_CONFIG: self['config'].instance.moveSelection(self["config"].instance.moveUp) def keyLeft(self): if not self['config'].enabled: self['config'].enableList() elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY): self["config"].getCurrent()[1].currentListPageUp() self["config"].invalidateCurrent() elif self.currentList == self.LIST_CONFIG: ConfigListScreen.keyLeft(self) if self['config'].getCurrent()[1] == self.searchSettings.type: self.searchParamsHelper.updateProviders() self.buildMenu() def keyRight(self): if not self['config'].enabled: self['config'].enableList() elif self.currentList in (self.LIST_SUGGESTIONS, self.LIST_HISTORY): self["config"].getCurrent()[1].currentListPageDown() self["config"].invalidateCurrent() elif self.currentList == self.LIST_CONFIG: ConfigListScreen.keyRight(self) if self['config'].getCurrent()[1] == self.searchSettings.type: self.searchParamsHelper.updateProviders() self.buildMenu() def removeSuggestionWindows(self): if hasattr(self.searchSettings.title, 'suggestionsWindow'): suggestionsWindow = self.searchSettings.title.suggestionsWindow if suggestionsWindow is not None: self.session.deleteDialog(suggestionsWindow) self.searchSettings.title.suggestionsWindow = None if hasattr(self.searchSettings.title, 'historyWindow'): historyWindow = self.searchSettings.title.historyWindow if historyWindow is not None: self.session.deleteDialog(historyWindow) self.searchSettings.title.historyWindow = None class SubsSearchProviderMenu(BaseMenuScreen): def __init__(self, session, provider): title = toString(provider.provider_name) +" " + _("settings") BaseMenuScreen.__init__(self, session, title) self.provider = provider def buildMenu(self): settingsProvider = self.provider.settings_provider self["config"].setList(settingsProvider.getE2Settings())