diff --git a/specials/autoplay.py b/specials/autoplay.py index 552e5be8..3f2ea054 100644 --- a/specials/autoplay.py +++ b/specials/autoplay.py @@ -8,12 +8,9 @@ from builtins import range import os -from core import channeltools -from core import jsontools +from core import channeltools, jsontools from core.item import Item -from platformcode import config, logger -from platformcode import platformtools -from platformcode import launcher +from platformcode import config, logger, platformtools, launcher from time import sleep from platformcode.config import get_setting @@ -28,7 +25,7 @@ colorKOD = '0xFF65B3DA' def context(): ''' - Agrega la opcion Configurar AutoPlay al menu contextual + Add the Configure AutoPlay option to the context menu :return: ''' @@ -47,12 +44,12 @@ context = context() def show_option(channel, itemlist, text_color=colorKOD, thumbnail=None, fanart=None): ''' - Agrega la opcion Configurar AutoPlay en la lista recibida + Add the option Configure AutoPlay in the received list :param channel: str :param itemlist: list (lista donde se desea integrar la opcion de configurar AutoPlay) - :param text_color: str (color para el texto de la opcion Configurar Autoplay) - :param thumbnail: str (direccion donde se encuentra el thumbnail para la opcion Configurar Autoplay) + :param text_color: str (color for the text of the option Configure Autoplay) + :param thumbnail: str (address where the thumbnail is for the Configure Autoplay option) :return: ''' from channelselector import get_thumb @@ -85,13 +82,13 @@ def show_option(channel, itemlist, text_color=colorKOD, thumbnail=None, fanart=N def start(itemlist, item): ''' - Metodo principal desde donde se reproduce automaticamente los enlaces - - En caso la opcion de personalizar activa utilizara las opciones definidas por el usuario. - - En caso contrario intentara reproducir cualquier enlace que cuente con el idioma preferido. + Main method from which the links are automatically reproduced + - In case the option to activate it will use the options defined by the user. + - Otherwise it will try to reproduce any link that has the preferred language. - :param itemlist: list (lista de items listos para reproducir, o sea con action='play') - :param item: item (el item principal del canal) - :return: intenta autoreproducir, en caso de fallar devuelve el itemlist que recibio en un principio + :param itemlist: list (list of items ready to play, ie with action = 'play') + :param item: item (the main item of the channel) + :return: try to auto-reproduce, in case of failure it returns the itemlist that it received in the beginning ''' logger.info() @@ -103,11 +100,11 @@ def start(itemlist, item): if not config.is_xbmc(): - #platformtools.dialog_notification('AutoPlay ERROR', 'Sólo disponible para XBMC/Kodi') + # platformtools.dialog_notification('AutoPlay ERROR', 'Sólo disponible para XBMC/Kodi') return itemlist if not autoplay_node: - # Obtiene el nodo AUTOPLAY desde el json + # Get AUTOPLAY node from json autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY') channel_id = item.channel @@ -122,12 +119,12 @@ def start(itemlist, item): if not channel_id in autoplay_node: # or not active: return itemlist - # Agrega servidores y calidades que no estaban listados a autoplay_node + # Add servers and qualities not listed to autoplay_node new_options = check_value(channel_id, itemlist) - # Obtiene el nodo del canal desde autoplay_node + # Get the channel node from autoplay_node channel_node = autoplay_node.get(channel_id, {}) - # Obtiene los ajustes des autoplay para este canal + # Get the autoplay settings for this channel settings_node = channel_node.get('settings', {}) if get_setting('autoplay') or settings_node['active']: @@ -138,66 +135,65 @@ def start(itemlist, item): favorite_servers = [] favorite_quality = [] - #2nd lang, vemos si se quiere o no filtrar + # 2nd lang, see if you want to filter or not status_language = config.get_setting("filter_languages", channel_id) - # Guarda el valor actual de "Accion y Player Mode" en preferencias + # Save the current value of "Action and Player Mode" in preferences user_config_setting_action = config.get_setting("default_action") user_config_setting_player = config.get_setting("player_mode") - # Habilita la accion "Ver en calidad alta" (si el servidor devuelve más de una calidad p.e. gdrive) + # Enable the "View in high quality" action (if the server returns more than one quality, eg gdrive) if user_config_setting_action != 2: config.set_setting("default_action", 2) if user_config_setting_player != 0: config.set_setting("player_mode", 0) - # Informa que AutoPlay esta activo - #platformtools.dialog_notification('AutoPlay Activo', '', sound=False) + # Report that AutoPlay is active + # platformtools.dialog_notification('AutoPlay Activo', '', sound=False) - # Prioridades a la hora de ordenar itemlist: - # 0: Servidores y calidades - # 1: Calidades y servidores - # 2: Solo servidores - # 3: Solo calidades - # 4: No ordenar + # Priorities when ordering itemlist: + # 0: Servers and qualities + # 1: Qualities and servers + # 2: Servers only + # 3: Only qualities + # 4: Do not order if (settings_node['custom_servers'] and settings_node['custom_quality']) or get_setting('autoplay'): - priority = settings_node['priority'] # 0: Servidores y calidades o 1: Calidades y servidores + priority = settings_node['priority'] # 0: Servers and qualities or 1: Qualities and servers elif settings_node['custom_servers']: - priority = 2 # Solo servidores + priority = 2 # Servers only elif settings_node['custom_quality']: - priority = 3 # Solo calidades + priority = 3 # Only qualities else: - priority = 4 # No ordenar + priority = 4 # Do not order - # Obtiene las listas servidores, calidades disponibles desde el nodo del json de AutoPlay + # Get server lists, qualities available from AutoPlay json node server_list = channel_node.get('servers', []) for server in server_list: server = server.lower() quality_list = channel_node.get('quality', []) - # Si no se definen calidades la se asigna default como calidad unica + # If no qualities are defined, default is assigned as unique quality. if len(quality_list) == 0: quality_list =['default'] - # Se guardan los textos de cada servidor y calidad en listas p.e. favorite_servers = ['verystream', 'openload', - # 'streamcloud'] + # The texts of each server and quality are stored in lists, e.g. favorite_servers = ['verystream', 'openload', 'streamcloud'] for num in range(1, 4): favorite_servers.append(channel_node['servers'][settings_node['server_%s' % num]].lower()) favorite_quality.append(channel_node['quality'][settings_node['quality_%s' % num]]) - # Se filtran los enlaces de itemlist y que se correspondan con los valores de autoplay + # Itemlist links are filtered and correspond to autoplay values for n, item in enumerate(itemlist): autoplay_elem = dict() b_dict = dict() - # Comprobamos q se trata de un item de video + # We check that it is a video item if 'server' not in item: continue - #2nd lang lista idiomas + # 2nd lang language list if item.language not in favorite_langs: favorite_langs.append(item.language) - # Agrega la opcion configurar AutoPlay al menu contextual + # Add the option to configure AutoPlay to the context menu if 'context' not in item: item.context = list() if not [x for x in context if x['action'] == 'autoplay_config']: @@ -206,15 +202,14 @@ def start(itemlist, item): "channel": "autoplay", "from_channel": channel_id}) - # Si no tiene calidad definida le asigna calidad 'default' + # If it does not have a defined quality, it assigns a 'default' quality. if item.quality == '': item.quality = 'default' - # Se crea la lista para configuracion personalizada - if priority < 2: # 0: Servidores y calidades o 1: Calidades y servidores + # The list for custom settings is created + if priority < 2: # 0: Servers and qualities or 1: Qualities and servers - # si el servidor y la calidad no se encuentran en las listas de favoritos o la url esta repetida, - # descartamos el item + # if the server and the quality are not in the favorites lists or the url is repeated, we discard the item if item.server.lower() not in favorite_servers or item.quality not in favorite_quality \ or item.url in url_list_valid: item.type_b = True @@ -225,10 +220,9 @@ def start(itemlist, item): autoplay_elem["indice_server"] = favorite_servers.index(item.server.lower()) autoplay_elem["indice_quality"] = favorite_quality.index(item.quality) - elif priority == 2: # Solo servidores + elif priority == 2: # Servers only - # si el servidor no se encuentra en la lista de favoritos o la url esta repetida, - # descartamos el item + # if the server is not in the favorites list or the url is repeated, we discard the item if item.server.lower() not in favorite_servers or item.url in url_list_valid: item.type_b = True b_dict['videoitem'] = item @@ -239,8 +233,7 @@ def start(itemlist, item): elif priority == 3: # Solo calidades - # si la calidad no se encuentra en la lista de favoritos o la url esta repetida, - # descartamos el item + # if the quality is not in the favorites list or the url is repeated, we discard the item if item.quality not in favorite_quality or item.url in url_list_valid: item.type_b = True b_dict['videoitem'] = item @@ -249,13 +242,13 @@ def start(itemlist, item): autoplay_elem["indice_lang"] = favorite_langs.index(item.language) autoplay_elem["indice_quality"] = favorite_quality.index(item.quality) - else: # No ordenar + else: # Do not order - # si la url esta repetida, descartamos el item + # if the url is repeated, we discard the item if item.url in url_list_valid: continue - # Si el item llega hasta aqui lo añadimos al listado de urls validas y a autoplay_list + # If the item reaches here we add it to the list of valid urls and to autoplay_list url_list_valid.append(item.url) item.plan_b=True autoplay_elem['videoitem'] = item @@ -263,20 +256,20 @@ def start(itemlist, item): # autoplay_elem['quality'] = item.quality autoplay_list.append(autoplay_elem) - # Ordenamos segun la prioridad - if priority == 0: # Servidores y calidades + # We order according to priority + if priority == 0: # Servers and qualities autoplay_list.sort(key=lambda orden: (orden['indice_lang'], orden['indice_server'], orden['indice_quality'])) - elif priority == 1: # Calidades y servidores + elif priority == 1: # Qualities and servers autoplay_list.sort(key=lambda orden: (orden['indice_lang'], orden['indice_quality'], orden['indice_server'])) - elif priority == 2: # Solo servidores + elif priority == 2: # Servers only autoplay_list.sort(key=lambda orden: (orden['indice_lang'], orden['indice_server'])) - elif priority == 3: # Solo calidades + elif priority == 3: # Only qualities autoplay_list.sort(key=lambda orden: (orden['indice_lang'], orden['indice_quality'])) - # Se prepara el plan b, en caso de estar activo se agregan los elementos no favoritos al final + # Plan b is prepared, in case it is active the non-favorite elements are added at the end try: plan_b = settings_node['plan_b'] except: @@ -284,23 +277,22 @@ def start(itemlist, item): text_b = '' if plan_b: autoplay_list.extend(autoplay_b) - # Si hay elementos en la lista de autoplay se intenta reproducir cada elemento, hasta encontrar uno - # funcional o fallen todos + # If there are elements in the autoplay list, an attempt is made to reproduce each element, until one is found or all fail. if autoplay_list or (plan_b and autoplay_b): - #played = False + # played = False max_intentos = 5 max_intentos_servers = {} - # Si se esta reproduciendo algo detiene la reproduccion + # If something is playing it stops playing if platformtools.is_playing(): platformtools.stop_video() for autoplay_elem in autoplay_list: play_item = Item - # Si no es un elemento favorito si agrega el texto plan b + # If it is not a favorite element if you add the text plan b if autoplay_elem['videoitem'].type_b: text_b = '(Plan B)' if not platformtools.is_playing() and not PLAYED: @@ -308,7 +300,7 @@ def start(itemlist, item): if videoitem.server.lower() not in max_intentos_servers: max_intentos_servers[videoitem.server.lower()] = max_intentos - # Si se han alcanzado el numero maximo de intentos de este servidor saltamos al siguiente + # If the maximum number of attempts of this server have been reached, we jump to the next if max_intentos_servers[videoitem.server.lower()] == 0: continue @@ -318,10 +310,9 @@ def start(itemlist, item): platformtools.dialog_notification("AutoPlay %s" %text_b, "%s%s%s" % ( videoitem.server.upper(), lang, videoitem.quality.upper()), sound=False) - # TODO videoitem.server es el id del server, pero podria no ser el nombre!!! + # TODO videoitem.server is the id of the server, but it might not be the name !!! - # Intenta reproducir los enlaces - # Si el canal tiene metodo play propio lo utiliza + # Try to play the links If the channel has its own play method, use it try: channel = __import__('channels.%s' % channel_id, None, None, ["channels.%s" % channel_id]) except: @@ -334,19 +325,19 @@ def start(itemlist, item): else: videoitem = resolved_item[0] - # Si no directamente reproduce y marca como visto + # If not directly reproduce and mark as seen - # Verifica si el item viene de la videoteca + #Check if the item comes from the video library try: if base_item.contentChannel =='videolibrary': - # Marca como visto + # Mark as seen from platformcode import xbmc_videolibrary xbmc_videolibrary.mark_auto_as_watched(base_item) - # Rellena el video con los datos del item principal y reproduce + # Fill the video with the data of the main item and play play_item = base_item.clone(url=videoitem) platformtools.play_video(play_item.url, autoplay=True) else: - # Si no viene de la videoteca solo reproduce + # If it doesn't come from the video library, just play platformtools.play_video(videoitem, autoplay=True) except: pass @@ -358,18 +349,16 @@ def start(itemlist, item): except: logger.debug(str(len(autoplay_list))) - # Si hemos llegado hasta aqui es por q no se ha podido reproducir + # If we have come this far, it is because it could not be reproduced max_intentos_servers[videoitem.server.lower()] -= 1 - # Si se han alcanzado el numero maximo de intentos de este servidor - # preguntar si queremos seguir probando o lo ignoramos + # If the maximum number of attempts of this server has been reached, ask if we want to continue testing or ignore it. if max_intentos_servers[videoitem.server.lower()] == 0: text = config.get_localized_string(60072) % videoitem.server.upper() - if not platformtools.dialog_yesno("AutoPlay", text, - config.get_localized_string(60073)): + if not platformtools.dialog_yesno("AutoPlay", text, config.get_localized_string(60073)): max_intentos_servers[videoitem.server.lower()] = max_intentos - # Si no quedan elementos en la lista se informa + # If there are no items in the list, it is reported if autoplay_elem == autoplay_list[-1]: platformtools.dialog_notification('AutoPlay', config.get_localized_string(60072) % videoitem.server.upper()) @@ -378,7 +367,7 @@ def start(itemlist, item): if new_options: platformtools.dialog_notification("AutoPlay", config.get_localized_string(60076), sound=False) - # Restaura si es necesario el valor previo de "Accion y Player Mode" en preferencias + # Restore if necessary the previous value of "Action and Player Mode" in preferences if user_config_setting_action != 2: config.set_setting("default_action", user_config_setting_action) if user_config_setting_player != 0: @@ -389,15 +378,15 @@ def start(itemlist, item): def init(channel, list_servers, list_quality, reset=False): ''' - Comprueba la existencia de canal en el archivo de configuracion de Autoplay y si no existe lo añade. - Es necesario llamar a esta funcion al entrar a cualquier canal que incluya la funcion Autoplay. + Check the existence of a channel in the Autoplay configuration file and if it does not exist, add it. + It is necessary to call this function when entering any channel that includes the Autoplay function. - :param channel: (str) id del canal - :param list_servers: (list) lista inicial de servidores validos para el canal. No es necesario incluirlos todos, - ya que la lista de servidores validos se ira actualizando dinamicamente. - :param list_quality: (list) lista inicial de calidades validas para el canal. No es necesario incluirlas todas, - ya que la lista de calidades validas se ira actualizando dinamicamente. - :return: (bool) True si la inicializacion ha sido correcta. + :param channel: (str) channel id + :param list_servers: (list) initial list of valid servers for the channel. It is not necessary to include them all, + since the list of valid servers will be updated dynamically. + :param list_quality: (list) initial list of valid qualities for the channel. It is not necessary to include them all, + since the list of valid qualities will be updated dynamically. + :return: (bool) True if the initialization was successful. ''' logger.info() change = False @@ -418,13 +407,13 @@ def init(channel, list_servers, list_quality, reset=False): if channel not in autoplay_node or reset: change = True - # Se comprueba que no haya calidades ni servidores duplicados + # It is verified that there are no duplicate qualities or servers if 'default' not in list_quality: list_quality.append('default') # list_servers = list(set(list_servers)) # list_quality = list(set(list_quality)) - # Creamos el nodo del canal y lo añadimos + # We create the channel node and add it channel_node = {"servers": list_servers, "quality": list_quality, "settings": { @@ -458,11 +447,11 @@ def init(channel, list_servers, list_quality, reset=False): def check_value(channel, itemlist): - ''' comprueba la existencia de un valor en la lista de servidores o calidades - si no existiera los agrega a la lista en el json + ''' + checks the existence of a value in the list of servers or qualities if it does not exist adds them to the list in the json :param channel: str - :param values: list (una de servidores o calidades) + :param values: list (one of servers or qualities) :param value_type: str (server o quality) :return: list ''' @@ -471,7 +460,7 @@ def check_value(channel, itemlist): change = False if not autoplay_node: - # Obtiene el nodo AUTOPLAY desde el json + # Get AUTOPLAY node from json autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY') channel_node = autoplay_node.get(channel) @@ -507,7 +496,7 @@ def autoplay_config(item): channel_name = channel_parameters['title'] if not autoplay_node: - # Obtiene el nodo AUTOPLAY desde el json + # Get AUTOPLAY node from json autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY') channel_node = autoplay_node.get(item.from_channel, {}) @@ -515,21 +504,19 @@ def autoplay_config(item): allow_option = True - active_settings = {"id": "active", "label": config.get_localized_string(60079), - "type": "bool", "default": False, "enabled": allow_option, - "visible": allow_option} + active_settings = {"id": "active", "label": config.get_localized_string(60079), "type": "bool", + "default": False, "enabled": allow_option, "visible": allow_option} list_controls.append(active_settings) dict_values['active'] = settings_node.get('active', False) - hide_servers = {"id": "hide_servers", "label": config.get_localized_string(70747), - "type": "bool", "default": False, "enabled": "eq(-" + str(len(list_controls)) + ",true)", - "visible": allow_option} + hide_servers = {"id": "hide_servers", "label": config.get_localized_string(70747), "type": "bool", + "default": False, "enabled": "eq(-" + str(len(list_controls)) + ",true)", "visible": allow_option} list_controls.append(hide_servers) dict_values['hide_servers'] = settings_node.get('hide_servers', False) - # Idioma + # Language status_language = config.get_setting("filter_languages", item.from_channel) if not status_language: status_language = 0 @@ -546,7 +533,7 @@ def autoplay_config(item): "type": "label", "enabled": True, "visible": True} list_controls.append(separador) - # Seccion servidores favoritos + # Favorite servers section server_list = channel_node.get("servers", []) if not server_list: enabled = False @@ -578,7 +565,7 @@ def autoplay_config(item): if settings_node.get("server_%s" % num, 0) > len(server_list) - 1: dict_values["server_%s" % num] = 0 - # Seccion Calidades favoritas + # Favorite Qualities Section quality_list = channel_node.get("quality", []) if not quality_list: enabled = False @@ -586,8 +573,7 @@ def autoplay_config(item): else: enabled = "eq(-" + str(len(list_controls)) + ",true)" - custom_quality_settings = {"id": "custom_quality", "label": config.get_localized_string(60083), - "type": "bool", "default": False, "enabled": enabled, "visible": True} + custom_quality_settings = {"id": "custom_quality", "label": config.get_localized_string(60083), "type": "bool", "default": False, "enabled": enabled, "visible": True} custom_quality_pos = len(list_controls) list_controls.append(custom_quality_settings) if dict_values['active'] and enabled: @@ -617,7 +603,7 @@ def autoplay_config(item): list_controls.append(plan_b) - # Seccion Prioridades + # Priorities Section priority_list = [config.get_localized_string(70174), config.get_localized_string(70175)] set_priority = {"id": "priority", "label": config.get_localized_string(60085), "type": "list", "default": 0, "enabled": True, "visible": "eq(-5,true)+eq(-9,true)+eq(-12,true)", "lvalues": priority_list} @@ -626,7 +612,7 @@ def autoplay_config(item): - # Abrir cuadro de dialogo + # Open dialog box platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, callback='save', item=item, caption='%s - AutoPlay' % channel_name, custom_button={'visible': True, @@ -637,7 +623,7 @@ def autoplay_config(item): def save(item, dict_data_saved): ''' - Guarda los datos de la ventana de configuracion + Save the data from the configuration window :param item: item :param dict_data_saved: dict @@ -647,7 +633,7 @@ def save(item, dict_data_saved): global autoplay_node if not autoplay_node: - # Obtiene el nodo AUTOPLAY desde el json + # Get AUTOPLAY node from json autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY') new_config = dict_data_saved @@ -665,7 +651,7 @@ def save(item, dict_data_saved): def get_languages(channel): ''' - Obtiene los idiomas desde el json del canal + Get the languages ​​from the channel's json :param channel: str :return: list @@ -685,9 +671,9 @@ def get_languages(channel): def is_active(channel): ''' - Devuelve un booleano q indica si esta activo o no autoplay en el canal desde el que se llama + Returns a boolean that indicates whether or not autoplay is active on the channel from which it is called - :return: True si esta activo autoplay para el canal desde el que se llama, False en caso contrario. + :return:True if autoplay is active for the channel from which it is called, False otherwise. ''' logger.info() global autoplay_node @@ -696,18 +682,18 @@ def is_active(channel): return False if not autoplay_node: - # Obtiene el nodo AUTOPLAY desde el json + # Get AUTOPLAY node from json autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY') - # Obtine el canal desde el q se hace la llamada + # Get the channel from which the call is made #import inspect #module = inspect.getmodule(inspect.currentframe().f_back) #canal = module.__name__.split('.')[1] canal = channel - # Obtiene el nodo del canal desde autoplay_node + # Get the channel node from autoplay_node channel_node = autoplay_node.get(canal, {}) - # Obtiene los ajustes des autoplay para este canal + # Get the autoplay settings for this channel settings_node = channel_node.get('settings', {}) return settings_node.get('active', False) or get_setting('autoplay') @@ -727,7 +713,7 @@ def reset(item, dict): # def set_status(status): # logger.info() -# # Obtiene el nodo AUTOPLAY desde el json +# # Get AUTOPLAY node from json # autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY') # autoplay_node['status'] = status # @@ -737,7 +723,7 @@ def reset(item, dict): def get_channel_AP_HS(item): autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY') channel_node = autoplay_node.get(item.channel, {}) - if not channel_node: # non ha mai aperto il menu del canale quindi in autoplay_data.json non c'e la key + if not channel_node: # never opened the channel menu so in autoplay_data.json there is no key try: channelFile = __import__('channels.' + item.channel, fromlist=["channels.%s" % item.channel]) except: diff --git a/specials/checkhost.py b/specials/checkhost.py index 5593aae3..31ea9187 100644 --- a/specials/checkhost.py +++ b/specials/checkhost.py @@ -18,7 +18,7 @@ addonid = addon.getAddonInfo('id') LIST_SITE = ['http://www.ansa.it/', 'https://www.google.it']#, 'https://www.google.com'] -# lista di siti che non verranno raggiunti con i DNS del gestore +# list of sites that will not be reached with the manager's DNS LST_SITE_CHCK_DNS = ['https://www.casacinema.me/', 'https://cb01-nuovo-indirizzo.info/'] #'https://www.italia-film.pw', 'https://www.cb01.uno/',] # tolti @@ -41,13 +41,8 @@ class Kdicc(): def check_Ip(self): """ - controllo l'ip - se ip_addr = 127.0.0.1 o ip_addr = '' allora il device non - e' connesso al modem/router - check the ip - if ip_addr = 127.0.0.1 or ip_addr = '' then the device does not - is connected to the modem/router + if ip_addr = 127.0.0.1 or ip_addr = '' then the device does not is connected to the modem/router return: bool """ @@ -59,7 +54,7 @@ class Kdicc(): def check_Adsl(self): """ - controllo se il device raggiunge i siti + check if the device reaches the sites """ urls = LIST_SITE @@ -67,8 +62,8 @@ class Kdicc(): http_errr = 0 for rslt in r: xbmc.log("check_Adsl rslt: %s" % rslt['code'], level=xbmc.LOGNOTICE) - # Errno -2 potrebbe essere mancanza di connessione adsl o sito non raggiungibile.... - # anche nei casi in cui ci sia il cambio gestore. + # Errno -2 could be lack of adsl connection or unreachable site .... + # even in cases where there is a change of manager. if rslt['code'] == '111' or '[Errno -3]' in str(rslt['code']) or 'Errno -2' in str(rslt['code']): http_errr +=1 @@ -80,7 +75,7 @@ class Kdicc(): def check_Dns(self): """ - Controllo se i DNS raggiungono certi siti + Control if DNS reaches certain sites """ if self.lst_site_check_dns == []: urls = LST_SITE_CHCK_DNS @@ -88,7 +83,7 @@ class Kdicc(): urls = self.lst_site_check_dns r = self.rqst(urls) - xbmc.log("check_Dns risultato: %s" % r, level=xbmc.LOGNOTICE) + xbmc.log("check_Dns result: %s" % r, level=xbmc.LOGNOTICE) http_errr = 0 for rslt in r: xbmc.log("check_Dns rslt: %s" % rslt['code'], level=xbmc.LOGNOTICE) @@ -103,7 +98,7 @@ class Kdicc(): def rqst(self, lst_urls): """ - url deve iniziare con http(s):' + url must start with http(s):' return : (esito, sito, url, code, reurl) """ rslt_final = [] @@ -114,7 +109,7 @@ class Kdicc(): for sito in lst_urls: rslt = {} try: - r = requests.head(sito, allow_redirects = True)#, timeout=7) # da errore dopo l'inserimento in lib di httplib2 + r = requests.head(sito, allow_redirects = True) #, timeout=7) # from error after lib insertion of httplib2 if r.url.endswith('/'): r.url = r.url[:-1] if str(sito) != str(r.url): @@ -130,17 +125,17 @@ class Kdicc(): xbmc.log("Risultato nel try: %s" % (r,), level=xbmc.LOGNOTICE) except requests.exceptions.ConnectionError as conn_errr: - # Errno 10061 per s.o. win - # gli Errno 10xxx e 11xxx saranno da compattare in qualche modo? - # gli errori vengono inglobati in code = '111' in quanto in quel momento - # non vengono raggiunti per una qualsiasi causa + # Errno 10061 for s.o. win + # will the Errno 10xxx and 11xxx be to be compacted in any way? + # the errors are incorporated in code = '111' since at that moment + # they are not reached for any reason if '[Errno 111]' in str(conn_errr) or 'Errno 10060' in str(conn_errr) \ or 'Errno 10061' in str(conn_errr) \ or '[Errno 110]' in str(conn_errr) \ or 'ConnectTimeoutError' in str(conn_errr) \ or 'Errno 11002' in str(conn_errr) or 'ReadTimeout' in str(conn_errr) \ or 'Errno 11001' in str(conn_errr) \ - or 'Errno -2' in str(conn_errr): # questo errore è anche nel code: -2 + or 'Errno -2' in str(conn_errr): # this error is also in the code: -2 rslt['code'] = '111' rslt['url'] = str(sito) rslt['http_err'] = 'Connection error' @@ -169,11 +164,11 @@ class Kdicc(): else: rslt['code'] = code.status except httplib2.ServerNotFoundError as msg: - # sia per mancanza di ADSL che per i siti non esistenti + # both for lack of ADSL and for non-existent sites rslt['code'] = -2 except socket.error as msg: - # per siti irraggiungibili senza DNS corretti - #[Errno 111] Connection refused + # for unreachable sites without correct DNS + # [Errno 111] Connection refused rslt['code'] = 111 except: rslt['code'] = 'Connection error' @@ -181,8 +176,7 @@ class Kdicc(): def view_Advise(self, txt = '' ): """ - Avviso per utente - testConnected + Notice per user testConnected """ ip = self.check_Ip() if ip: @@ -201,37 +195,36 @@ class Kdicc(): txt = config.get_localized_string(707402) dialog.notification(addonname, txt, xbmcgui.NOTIFICATION_INFO, 10000) """ - def richiamato in launcher.py + def called in launcher.py """ def test_conn(is_exit, check_dns, view_msg, lst_urls, lst_site_check_dns, in_addon): ktest = Kdicc(is_exit, check_dns, view_msg, lst_urls, lst_site_check_dns, in_addon) - # se non ha l'ip lo comunico all'utente + # if it does not have the IP, I will communicate it to the user if not ktest.check_Ip(): - # non permetto di entrare nell'addon # I don't let you get into the addon - # inserire codice lingua + # enter language code if view_msg == True: ktest.view_Advise(config.get_localized_string(70720)) if ktest.is_exit == True: exit() - # se non ha connessione ADSL lo comunico all'utente + # if it has no ADSL connection, I will communicate it to the user if not ktest.check_Adsl(): if view_msg == True: ktest.view_Advise(config.get_localized_string(70721)) if ktest.is_exit == True: exit() - # se ha i DNS filtrati lo comunico all'utente + # if it has DNS filtered, I will communicate it to the user if check_dns == True: if not ktest.check_Dns(): if view_msg == True: ktest.view_Advise(config.get_localized_string(70722)) - xbmc.log("############ Inizio Check DNS ############", level=xbmc.LOGNOTICE) + xbmc.log("############ Start Check DNS ############", level=xbmc.LOGNOTICE) xbmc.log("## IP: %s" % (ktest.ip_addr), level=xbmc.LOGNOTICE) xbmc.log("## DNS: %s" % (ktest.dns), level=xbmc.LOGNOTICE) - xbmc.log("############ Fine Check DNS ############", level=xbmc.LOGNOTICE) + xbmc.log("############# End Check DNS #############", level=xbmc.LOGNOTICE) # if check_dns == True: # if ktest.check_Ip() == True and ktest.check_Adsl() == True and ktest.check_Dns() == True: # return True @@ -243,21 +236,17 @@ def test_conn(is_exit, check_dns, view_msg, # else: # return False -# def per la creazione del file channels.json +# def for creating the channels.json file def check_channels(inutile=''): """ - leggo gli host dei canali dal file channels.json - li controllo - scrivo il file channels-test.json - con il codice di errore e il nuovio url in caso di redirect + I read the channel hosts from the channels.json file, I check them, + I write the channels-test.json file with the error code and the new url in case of redirect - gli url DEVONO avere http(s) + urls MUST have http (s) - Durante il controllo degli urls vengono rieffettuati - i controlli di ip, asdl e dns. - Questo perchè può succedere che in un qualsiasi momento - la connessione possa avere problemi. Nel caso accada, il controllo e - relativa scrittura del file viene interrotto con messaggio di avvertimento + During the urls check the ip, asdl and dns checks are carried out. + This is because it can happen that at any time the connection may have problems. If it does, check it + relative writing of the file is interrupted with a warning message """ logger.info() @@ -272,37 +261,37 @@ def check_channels(inutile=''): for chann, host in sorted(data.items()): ris = [] - # per avere un'idea della tempistica - # utile solo se si controllano tutti i canali - # per i canali con error 522 si perdono circa 40 sec... + # to get an idea of ​​the timing + # useful only if you control all channels + # for channels with error 522 about 40 seconds are lost ... logger.info("check #### INIZIO #### channel - host :%s - %s " % (chann, host)) rslt = Kdicc(lst_urls = [host]).http_Resp() - # tutto ok + # all right if rslt['code'] == 200: risultato[chann] = host # redirect elif str(rslt['code']).startswith('3'): - #risultato[chann] = str(rslt['code']) +' - '+ rslt['redirect'][:-1] + # risultato[chann] = str(rslt['code']) +' - '+ rslt['redirect'][:-1] if rslt['redirect'].endswith('/'): rslt['redirect'] = rslt['redirect'][:-1] risultato[chann] = rslt['redirect'] - # sito inesistente + # non-existent site elif rslt['code'] == -2: risultato[chann] = 'Host Sconosciuto - '+ str(rslt['code']) +' - '+ host - # sito non raggiungibile - probabili dns non settati + # site not reachable - probable dns not set elif rslt['code'] == 111: risultato[chann] = ['Host non raggiungibile - '+ str(rslt['code']) +' - '+ host] else: - # altri tipi di errore - #risultato[chann] = 'Errore Sconosciuto - '+str(rslt['code']) +' - '+ host + # other types of errors + # risultato[chann] = 'Errore Sconosciuto - '+str(rslt['code']) +' - '+ host risultato[chann] = host logger.info("check #### FINE #### rslt :%s " % (rslt)) fileJson_test = 'channels-test.json' - # scrivo il file aggiornato + # I write the updated file with open(folderJson+'/'+fileJson_test, 'w') as f: data = json.dump(risultato, f, sort_keys=True, indent=4) logger.info(data) diff --git a/specials/downloads.py b/specials/downloads.py index ae6014ea..2467b63d 100644 --- a/specials/downloads.py +++ b/specials/downloads.py @@ -11,17 +11,13 @@ if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int from future.builtins import filter from past.utils import old_div -import re -import time -import unicodedata -import xbmc +import re, time, unicodedata, xbmc from channelselector import get_thumb from core import filetools, jsontools, scraper, scrapertools, servertools, videolibrarytools, support from core.downloader import Downloader from core.item import Item -from platformcode import config, logger -from platformcode import platformtools +from platformcode import config, logger, platformtools from core.support import log, dbg, typo from servers import torrent diff --git a/specials/favorites.py b/specials/favorites.py index 51089d8f..bc4ba832 100644 --- a/specials/favorites.py +++ b/specials/favorites.py @@ -1,19 +1,16 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------ -# Lista de vídeos favoritos +# List of favorite videos # ------------------------------------------------------------ -import os -import time +import os, time -from core import filetools -from core import scrapertools +from core import filetools, scrapertools from core.item import Item -from platformcode import config, logger -from platformcode import platformtools +from platformcode import config, logger, platformtools try: - # Fijamos la ruta a favourites.xml + # We set the path to favorites.xml if config.is_xbmc(): import xbmc @@ -32,8 +29,7 @@ def mainlist(item): for name, thumb, data in read_favourites(): if "plugin://plugin.video.%s/?" % config.PLUGIN_NAME in data: - url = scrapertools.find_single_match(data, 'plugin://plugin.video.%s/\?([^;]*)' % config.PLUGIN_NAME) \ - .replace(""", "") + url = scrapertools.find_single_match(data, 'plugin://plugin.video.%s/\?([^;]*)' % config.PLUGIN_NAME).replace(""", "") item = Item().fromurl(url) item.title = name @@ -45,7 +41,7 @@ def mainlist(item): elif type(item.context) != list: item.context = [] - item.context.extend([{"title": config.get_localized_string(30154), # "Quitar de favoritos" + item.context.extend([{"title": config.get_localized_string(30154), # "Remove from favorites " "action": "delFavourite", "channel": "favorites", "from_title": item.title}, @@ -88,7 +84,7 @@ def addFavourite(item): logger.info() # logger.debug(item.tostring('\n')) - # Si se llega aqui mediante el menu contextual, hay que recuperar los parametros action y channel + # If you get here through the context menu, you must retrieve the action and channel parameters if item.from_action: item.__dict__["action"] = item.__dict__.pop("from_action") if item.from_channel: @@ -100,8 +96,7 @@ def addFavourite(item): favourites_list.append((titulo, item.thumbnail, data)) if save_favourites(favourites_list): - platformtools.dialog_ok(config.get_localized_string(30102), titulo, - config.get_localized_string(30108)) # 'se ha añadido a favoritos' + platformtools.dialog_ok(config.get_localized_string(30102), titulo, config.get_localized_string(30108)) # 'added to favorites' def delFavourite(item): @@ -117,8 +112,7 @@ def delFavourite(item): favourites_list.remove(fav) if save_favourites(favourites_list): - platformtools.dialog_ok(config.get_localized_string(30102), item.title, - config.get_localized_string(30105).lower()) # 'Se ha quitado de favoritos' + platformtools.dialog_ok(config.get_localized_string(30102), item.title, config.get_localized_string(30105).lower()) # 'Removed from favorites' platformtools.itemlist_refresh() break @@ -127,22 +121,21 @@ def renameFavourite(item): logger.info() # logger.debug(item.tostring('\n')) - # Buscar el item q queremos renombrar en favourites.xml + # Find the item we want to rename in favorites.xml favourites_list = read_favourites() for i, fav in enumerate(favourites_list): if fav[0] == item.from_title: - # abrir el teclado + # open keyboard new_title = platformtools.dialog_input(item.from_title, item.title) if new_title: favourites_list[i] = (new_title, fav[1], fav[2]) if save_favourites(favourites_list): - platformtools.dialog_ok(config.get_localized_string(30102), item.from_title, - "se ha renombrado como:", new_title) # 'Se ha quitado de favoritos' + platformtools.dialog_ok(config.get_localized_string(30102), item.from_title, config.get_localized_string(60086) + ' ', new_title) # 'Removed from favorites' platformtools.itemlist_refresh() ################################################## -# Funciones para migrar favoritos antiguos (.txt) +# Features to migrate old favorites (.txt) def readbookmark(filepath): logger.info() import urllib @@ -176,7 +169,7 @@ def readbookmark(filepath): except: plot = lines[4].strip() - # Campos contentTitle y canal añadidos + # ContentTitle and channel fields added if len(lines) >= 6: try: contentTitle = urllib.unquote_plus(lines[5].strip()) @@ -199,7 +192,7 @@ def readbookmark(filepath): def check_bookmark(readpath): - # Crea un listado con las entradas de favoritos + # Create a list with favorite entries itemlist = [] if readpath.startswith("special://") and config.is_xbmc(): @@ -207,12 +200,12 @@ def check_bookmark(readpath): readpath = xbmc.translatePath(readpath) for fichero in sorted(filetools.listdir(readpath)): - # Ficheros antiguos (".txt") + # Old files (".txt") if fichero.endswith(".txt"): - # Esperamos 0.1 segundos entre ficheros, para que no se solapen los nombres de archivo + # We wait 0.1 seconds between files, so that the file names do not overlap time.sleep(0.1) - # Obtenemos el item desde el .txt + # We get the item from the .txt canal, titulo, thumbnail, plot, server, url, contentTitle = readbookmark(filetools.join(readpath, fichero)) if canal == "": canal = "favorites" @@ -222,21 +215,21 @@ def check_bookmark(readpath): filetools.rename(filetools.join(readpath, fichero), fichero[:-4] + ".old") itemlist.append(item) - # Si hay Favoritos q guardar + # If there are Favorites to save if itemlist: favourites_list = read_favourites() for item in itemlist: data = "ActivateWindow(10025,"plugin://plugin.video.kod/?" + item.tourl() + "",return)" favourites_list.append((item.title, item.thumbnail, data)) if save_favourites(favourites_list): - logger.debug("Conversion de txt a xml correcta") + logger.debug("Correct txt to xml conversion") -# Esto solo funcionara al migrar de versiones anteriores, ya no existe "bookmarkpath" +# This will only work when migrating from previous versions, there is no longer a "bookmarkpath" try: if config.get_setting("bookmarkpath") != "": check_bookmark(config.get_setting("bookmarkpath")) else: - logger.info("No existe la ruta a los favoritos de versiones antiguas") + logger.info("No path to old version favorites") except: pass diff --git a/specials/filtertools.py b/specials/filtertools.py index f8d05001..8dae6e0d 100644 --- a/specials/filtertools.py +++ b/specials/filtertools.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------ -# filtertools - se encarga de filtrar resultados +# filtertools - is responsible for filtering results # ------------------------------------------------------------ from builtins import object @@ -27,7 +27,7 @@ filter_global = None __channel__ = "filtertools" -# TODO echar un ojo a https://pyformat.info/, se puede formatear el estilo y hacer referencias directamente a elementos +# TODO take a look at https://pyformat.info/, you can format the style and make references directly to elements class ResultFilter(object): @@ -37,8 +37,7 @@ class ResultFilter(object): self.quality_allowed = dict_filter[TAG_QUALITY_ALLOWED] def __str__(self): - return "{active: '%s', language: '%s', quality_allowed: '%s'}" % \ - (self.active, self.language, self.quality_allowed) + return "{active: '%s', language: '%s', quality_allowed: '%s'}" % (self.active, self.language, self.quality_allowed) class Filter(object): @@ -59,7 +58,7 @@ class Filter(object): TAG_LANGUAGE: dict_filtered_shows[tvshow][TAG_LANGUAGE], TAG_QUALITY_ALLOWED: dict_filtered_shows[tvshow][TAG_QUALITY_ALLOWED]}) - # opcion general "no filtrar" + # general option "do not filter" elif global_filter_language != 0: from core import channeltools list_controls, dict_settings = channeltools.get_channel_controls_settings(item.channel) @@ -71,8 +70,7 @@ class Filter(object): language = control["lvalues"][global_filter_language] # logger.debug("language %s" % language) except: - logger.error("No se ha encontrado el valor asociado al codigo '%s': %s" % - (global_filter_lang_id, global_filter_language)) + logger.error("The value associated with the code was not found '%s': %s" % (global_filter_lang_id, global_filter_language)) break self.result = ResultFilter({TAG_ACTIVE: True, TAG_LANGUAGE: language, TAG_QUALITY_ALLOWED: []}) @@ -84,7 +82,7 @@ class Filter(object): def access(): """ - Devuelve si se puede usar o no filtertools + Returns whether or not filtertools can be used """ allow = False @@ -96,24 +94,23 @@ def access(): def context(item, list_language=None, list_quality=None, exist=False): """ - Para xbmc/kodi y mediaserver ya que pueden mostrar el menú contextual, se añade un menu para configuración - la opción de filtro, sólo si es para series. - Dependiendo del lugar y si existe filtro se añadirán más opciones a mostrar. - El contexto -solo se muestra para series-. + For xbmc / kodi and mediaserver since they can show the contextual menu, a filter option is added to the configuration menu, only if it is for series. + Depending on the place and if there is a filter, more options will be added to show. + The context -is shown only for series-. - @param item: elemento para obtener la información y ver que contexto añadir + @param item: eelement to get the information and see what context to add @type item: item - param list_language: listado de idiomas posibles + param list_language: list of possible languages @type list_language: list[str] - @param list_quality: listado de calidades posibles + @param list_quality: list of possible qualities @type list_quality: list[str] - @param exist: si existe el filtro + @param exist: if the filter exists @type exist: bool - @return: lista de opciones a mostrar en el menú contextual + @return: list of options to display in the context menu @rtype: list """ - # Dependiendo de como sea el contexto lo guardamos y añadimos las opciones de filtertools. + # Depending on how the context is, we save it and add the filtertools options. if isinstance(item.context, str): _context = item.context.split("|") elif isinstance(item.context, list): @@ -152,10 +149,8 @@ def context(item, list_language=None, list_quality=None, exist=False): def show_option(itemlist, channel, list_language, list_quality): if access(): - itemlist.append(Item(channel=__channel__, title=config.get_localized_string(60429) % - COLOR.get("parent_item", "auto"), action="load", - list_language=list_language, - list_quality=list_quality, from_channel=channel)) + itemlist.append(Item(channel=__channel__, title=config.get_localized_string(60429) % COLOR.get("parent_item", "auto"), action="load", + list_language=list_language, list_quality=list_quality, from_channel=channel)) return itemlist @@ -169,14 +164,14 @@ def check_conditions(_filter, list_item, item, list_language, list_quality, qual if _filter.language: # logger.debug("title es %s" % item.title) - #2nd lang + # 2nd lang from platformcode import unify _filter.language = unify.set_lang(_filter.language).upper() - # viene de episodios + # comes from episodes if isinstance(item.language, list): - #2nd lang + # 2nd lang for n, lang in enumerate(item.language): item.language[n] = unify.set_lang(lang).upper() @@ -184,9 +179,9 @@ def check_conditions(_filter, list_item, item, list_language, list_quality, qual language_count += 1 else: is_language_valid = False - # viene de findvideos + # comes from findvideos else: - #2nd lang + # 2nd lang item.language = unify.set_lang(item.language).upper() if item.language.lower() == _filter.language.lower(): @@ -198,7 +193,7 @@ def check_conditions(_filter, list_item, item, list_language, list_quality, qual quality = "" if _filter.quality_allowed and item.quality != "": - # if hasattr(item, 'quality'): # esta validación no hace falta por que SIEMPRE se devuelve el atributo vacío + # if hasattr (item, 'quality'): # this validation is not necessary because the empty attribute is ALWAYS returned if item.quality.lower() in _filter.quality_allowed: quality = item.quality.lower() quality_count += 1 @@ -206,7 +201,7 @@ def check_conditions(_filter, list_item, item, list_language, list_quality, qual is_quality_valid = False if is_language_valid and is_quality_valid: - #TODO 2nd lang: habría que ver si conviene unificar el idioma aqui o no + #TODO 2nd lang: we should see if it is convenient to unify the language here or not item.list_language = list_language if list_quality: item.list_quality = list_quality @@ -216,34 +211,32 @@ def check_conditions(_filter, list_item, item, list_language, list_quality, qual # logger.debug(" -Enlace añadido") elif not item.language: list_item.append(item) - logger.debug(" idioma valido?: %s, item.language: %s, filter.language: %s" % - (is_language_valid, item.language, _filter.language)) - logger.debug(" calidad valida?: %s, item.quality: %s, filter.quality_allowed: %s" - % (is_quality_valid, quality, _filter.quality_allowed)) + logger.debug(" idioma valido?: %s, item.language: %s, filter.language: %s" % (is_language_valid, item.language, _filter.language)) + logger.debug(" calidad valida?: %s, item.quality: %s, filter.quality_allowed: %s" % (is_quality_valid, quality, _filter.quality_allowed)) return list_item, quality_count, language_count, _filter.language def get_link(list_item, item, list_language, list_quality=None, global_filter_lang_id="filter_languages"): """ - Devuelve una lista de enlaces, si el item está filtrado correctamente se agrega a la lista recibida. + Returns a list of links, if the item is correctly filtered it is added to the received list. - @param list_item: lista de enlaces + @param list_item: list of links @type list_item: list[Item] - @param item: elemento a filtrar + @param item: element to filter @type item: Item - @param list_language: listado de idiomas posibles + @param list_language: list of possible languages @type list_language: list[str] - @param list_quality: listado de calidades posibles + @param list_quality: list of possible qualities @type list_quality: list[str] - @param global_filter_lang_id: id de la variable de filtrado por idioma que está en settings + @param global_filter_lang_id: id of the filtering variable by language that is in settings @type global_filter_lang_id: str - @return: lista de Item + @return: Item list @rtype: list[Item] """ logger.info() - # si los campos obligatorios son None salimos + # if the required fields are None we leave if list_item is None or item is None: return [] @@ -256,8 +249,7 @@ def get_link(list_item, item, list_language, list_quality=None, global_filter_la logger.debug("filter: '%s' datos: '%s'" % (item.show, filter_global)) if filter_global and filter_global.active: - list_item, quality_count, language_count = \ - check_conditions(filter_global, list_item, item, list_language, list_quality)[:3] + list_item, quality_count, language_count = check_conditions(filter_global, list_item, item, list_language, list_quality)[:3] else: item.context = context(item) list_item.append(item) @@ -267,17 +259,17 @@ def get_link(list_item, item, list_language, list_quality=None, global_filter_la def get_links(list_item, item, list_language, list_quality=None, global_filter_lang_id="filter_languages"): """ - Devuelve una lista de enlaces filtrados. + Returns a list of filtered links. - @param list_item: lista de enlaces + @param list_item: list of links @type list_item: list[Item] - @param item: elemento a filtrar + @param item: element to filter @type item: item - @param list_language: listado de idiomas posibles + @param list_language: list of possible languages @type list_language: list[str] - @param list_quality: listado de calidades posibles + @param list_quality: list of possible qualities @type list_quality: list[str] - @param global_filter_lang_id: id de la variable de filtrado por idioma que está en settings + @param global_filter_lang_id: id of the filtering variable by language that is in settings @type global_filter_lang_id: str @return: lista de Item @rtype: list[Item] @@ -285,18 +277,18 @@ def get_links(list_item, item, list_language, list_quality=None, global_filter_l logger.info() - # si los campos obligatorios son None salimos + # if the required fields are None we leave if list_item is None or item is None: return [] - # si list_item está vacío volvemos, no se añade validación de plataforma para que Plex pueda hacer filtro global + # if list_item is empty we go back, no platform validation is added so Plex can do global filter if len(list_item) == 0: return list_item second_lang = config.get_setting('second_language') - #Ordena segun servidores favoritos, elima servers de blacklist y desactivados + # Sort by favorite servers, delete blacklist servers and disabled from core import servertools list_item= servertools.filter_servers(list_item) @@ -313,8 +305,7 @@ def get_links(list_item, item, list_language, list_quality=None, global_filter_l if _filter and _filter.active: for item in list_item: - new_itemlist, quality_count, language_count, first_lang = check_conditions(_filter, new_itemlist, item, list_language, - list_quality, quality_count, language_count) + new_itemlist, quality_count, language_count, first_lang = check_conditions(_filter, new_itemlist, item, list_language, list_quality, quality_count, language_count) #2nd lang if second_lang and second_lang != 'No' and first_lang.lower() != second_lang.lower() : @@ -322,7 +313,6 @@ def get_links(list_item, item, list_language, list_quality=None, global_filter_l _filter2 = _filter _filter2.language = second_lang for it in new_itemlist: - if isinstance(it.language, list): if not second_lang in it.language: second_list.append(it) @@ -330,32 +320,27 @@ def get_links(list_item, item, list_language, list_quality=None, global_filter_l second_list = new_itemlist break for item in list_item: - new_itemlist, quality_count, language_count, second_lang = check_conditions(_filter2, second_list, item, list_language, - list_quality, quality_count, language_count) + new_itemlist, quality_count, language_count, second_lang = check_conditions(_filter2, second_list, item, list_language, list_quality, quality_count, language_count) - logger.debug("ITEMS FILTRADOS: %s/%s, idioma [%s]: %s, calidad_permitida %s: %s" - % (len(new_itemlist), len(list_item), _filter.language, language_count, _filter.quality_allowed, - quality_count)) + logger.debug("FILTERED ITEMS: %s/%s, language [%s]: %s, allowed quality %s: %s" % (len(new_itemlist), len(list_item), _filter.language, language_count, _filter.quality_allowed, quality_count)) if len(new_itemlist) == 0: list_item_all = [] for i in list_item: list_item_all.append(i.tourl()) - _context = [ - {"title": config.get_localized_string(60430) % _filter.language, "action": "delete_from_context", - "channel": "filtertools", "to_channel": item.channel}] + _context = [{"title": config.get_localized_string(60430) % _filter.language, "action": "delete_from_context", "channel": "filtertools", "to_channel": item.channel}] if _filter.quality_allowed: msg_quality_allowed = " y calidad %s" % _filter.quality_allowed else: msg_quality_allowed = "" - + msg_lang = ' %s' % first_lang.upper() if second_lang and second_lang != 'No': msg_lang = 's %s ni %s' % (first_lang.upper(), second_lang.upper()) - + new_itemlist.append(Item(channel=__channel__, action="no_filter", list_item_all=list_item_all, show=item.show, title=config.get_localized_string(60432) % (_filter.language, msg_quality_allowed), @@ -392,15 +377,15 @@ def no_filter(item): def mainlist(channel, list_language, list_quality): """ - Muestra una lista de las series filtradas + Shows a list of the leaked series - @param channel: nombre del canal para obtener las series filtradas + @param channel: channel name to get filtered series @type channel: str - @param list_language: lista de idiomas del canal + @param list_language: channel language list @type list_language: list[str] - @param list_quality: lista de calidades del canal + @param list_quality: channel quality list @type list_quality: list[str] - @return: lista de Item + @return: Item list @rtype: list[Item] """ logger.info() @@ -439,7 +424,7 @@ def mainlist(channel, list_language, list_quality): def config_item(item): """ - muestra una serie filtrada para su configuración + displays a filtered series for your setup @param item: item @type item: Item @@ -447,7 +432,7 @@ def config_item(item): logger.info() logger.info("item %s" % item.tostring()) - # OBTENEMOS LOS DATOS DEL JSON + # WE GET THE JSON DATA dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_FILTER) tvshow = item.show.lower().strip() @@ -462,7 +447,7 @@ def config_item(item): pass if default_lang == '': - platformtools.dialog_notification("FilterTools", "No hay idiomas definidos") + platformtools.dialog_notification("FilterTools", "There are no defined languages") return else: lang_selected = dict_series.get(tvshow, {}).get(TAG_LANGUAGE, default_lang) @@ -525,7 +510,7 @@ def config_item(item): "visible": True, }) - # concatenamos list_controls con list_controls_calidad + # we concatenate list_controls with list_controls_quality list_controls.extend(list_controls_calidad) title = config.get_localized_string(60441) % (COLOR.get("selected", "auto"), item.show) @@ -566,11 +551,11 @@ def delete(item, dict_values): def save(item, dict_data_saved): """ - Guarda los valores configurados en la ventana + Save the configured values ​​in the window @param item: item @type item: Item - @param dict_data_saved: diccionario con los datos salvados + @param dict_data_saved: dictionary with saved data @type dict_data_saved: dict """ logger.info() @@ -583,7 +568,7 @@ def save(item, dict_data_saved): dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_FILTER) tvshow = item.show.strip().lower() - logger.info("Se actualiza los datos") + logger.info("Data is updated") list_quality = [] for _id, value in list(dict_data_saved.items()): @@ -613,7 +598,7 @@ def save(item, dict_data_saved): def save_from_context(item): """ - Salva el filtro a través del menú contextual + Save the filter through the context menu @param item: item @type item: item @@ -630,9 +615,9 @@ def save_from_context(item): sound = False if result: - message = "FILTRO GUARDADO" + message = "SAVED FILTER" else: - message = "Error al guardar en disco" + message = "Error saving to disk" sound = True heading = "%s [%s]" % (item.show.strip(), item.language) @@ -644,14 +629,14 @@ def save_from_context(item): def delete_from_context(item): """ - Elimina el filtro a través del menú contextual + Delete the filter through the context menu @param item: item @type item: item """ logger.info() - # venimos desde get_links y no se ha obtenido ningún resultado, en menu contextual y damos a borrar + # We come from get_links and no result has been obtained, in context menu and we delete if item.to_channel != "": item.from_channel = item.to_channel @@ -665,9 +650,9 @@ def delete_from_context(item): sound = False if result: - message = "FILTRO ELIMINADO" + message = "FILTER REMOVED" else: - message = "Error al guardar en disco" + message = "Error saving to disk" sound = True heading = "%s [%s]" % (item.show.strip(), lang_selected) diff --git a/specials/kodfavorites.py b/specials/kodfavorites.py index 0322ec3c..615b3117 100644 --- a/specials/kodfavorites.py +++ b/specials/kodfavorites.py @@ -1,21 +1,21 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------ -# Alfa favoritos +# KoD favorites # ============== -# - Lista de enlaces guardados como favoritos, solamente en Alfa, no Kodi. -# - Los enlaces se organizan en carpetas (virtuales) que puede definir el usuario. -# - Se utiliza un sólo fichero para guardar todas las carpetas y enlaces: kodfavourites-default.json -# - Se puede copiar kodfavourites-default.json a otros dispositivos ya que la única dependencia local es el thumbnail asociado a los enlaces, -# pero se detecta por código y se ajusta al dispositivo actual. -# - Se pueden tener distintos ficheros de alfavoritos y alternar entre ellos, pero solamente uno de ellos es la "lista activa". -# - Los ficheros deben estar en config.get_data_path() y empezar por kodfavourites- y terminar en .json +# - List of links saved as favorites, only in Alpha, not Kodi. +# - Links are organized in (virtual) folders that can be defined by the user. +# - A single file is used to save all folders and links: kodfavourites-default.json +# - kodfavourites-default.json can be copied to other devices since the only local dependency is the thumbnail associated with the links, +# but it is detected by code and adjusts to the current device. +# - You can have different alphabet files and alternate between them, but only one of them is the "active list". +# - Files must be in config.get_data_path () and start with kodfavourites- and end in .json -# Requerimientos en otros módulos para ejecutar este canal: -# - Añadir un enlace a este canal en channelselector.py -# - Modificar platformtools.py para controlar el menú contextual y añadir "Guardar enlace" en set_context_commands +# Requirements in other modules to run this channel: +# - Add a link to this channel in channelselector.py +# - Modify platformtools.py to control the context menu and add "Save link" in set_context_commands # ------------------------------------------------------------ -#from builtins import str +# from builtins import str import sys PY3 = False if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int @@ -32,35 +32,34 @@ from core import filetools, jsontools def fechahora_actual(): return datetime.now().strftime('%Y-%m-%d %H:%M') -# Helpers para listas -# ------------------- +# List Helpers PREFIJO_LISTA = 'kodfavorites-' -# Devuelve el nombre de la lista activa (Ej: kodfavourites-default.json) +# Returns the name of the active list (Ex: kodfavourites-default.json) def get_lista_activa(): return config.get_setting('lista_activa', default = PREFIJO_LISTA + 'default.json') -# Extrae nombre de la lista del fichero, quitando prefijo y sufijo (Ej: kodfavourites-Prueba.json => Prueba) +# Extract list name from file, removing prefix and suffix (Ex: kodfavourites-Test.json => Test) def get_name_from_filename(filename): return filename.replace(PREFIJO_LISTA, '').replace('.json', '') -# Componer el fichero de lista a partir de un nombre, añadiendo prefijo y sufijo (Ej: Prueba => kodfavourites-Prueba.json) +# Compose the list file from a name, adding prefix and suffix (Ex: Test => kodfavourites-Test.json) def get_filename_from_name(name): return PREFIJO_LISTA + name + '.json' -# Apuntar en un fichero de log los códigos de los ficheros que se hayan compartido +# Record the codes of the files that have been shared in a log file def save_log_lista_shared(msg): msg = fechahora_actual() + ': ' + msg + os.linesep fullfilename = os.path.join(config.get_data_path(), 'kodfavorites_shared.log') with open(fullfilename, 'a') as f: f.write(msg); f.close() -# Limpiar texto para usar como nombre de fichero +# Clean text to use as file name def text_clean(txt, disallowed_chars = '[^a-zA-Z0-9\-_()\[\]. ]+', blank_char = ' '): import unicodedata try: txt = unicode(txt, 'utf-8') - except NameError: # unicode is a default on python 3 + except NameError: # unicode is a default on python 3 pass txt = unicodedata.normalize('NFKD', txt).encode('ascii', 'ignore') txt = txt.decode('utf-8').strip() @@ -70,13 +69,12 @@ def text_clean(txt, disallowed_chars = '[^a-zA-Z0-9\-_()\[\]. ]+', blank_char = -# Clase para cargar y guardar en el fichero de Alfavoritos -# -------------------------------------------------------- +# Class to load and save in the KoDFavorites file class KodfavouritesData(object): def __init__(self, filename = None): - # Si no se especifica ningún fichero se usa la lista_activa (si no la hay se crea) + # If no file is specified, the active_list is used (if not, it is created) if filename == None: filename = get_lista_activa() @@ -84,7 +82,7 @@ class KodfavouritesData(object): if not os.path.exists(self.user_favorites_file): fichero_anterior = os.path.join(config.get_data_path(), 'user_favorites.json') - if os.path.exists(fichero_anterior): # formato anterior, convertir (a eliminar después de algunas versiones) + if os.path.exists(fichero_anterior): # old format, convert (to delete after some versions) jsondata = jsontools.load(filetools.read(fichero_anterior)) self.user_favorites = jsondata self.info_lista = {} @@ -94,7 +92,7 @@ class KodfavouritesData(object): self.user_favorites = [] else: jsondata = jsontools.load(filetools.read(self.user_favorites_file)) - if not 'user_favorites' in jsondata or not 'info_lista' in jsondata: # formato incorrecto + if not 'user_favorites' in jsondata or not 'info_lista' in jsondata: # incorrect format self.user_favorites = [] else: self.user_favorites = jsondata['user_favorites'] @@ -103,8 +101,8 @@ class KodfavouritesData(object): if len(self.user_favorites) == 0: self.info_lista = {} - - # Crear algunas carpetas por defecto + + # Create some default folders self.user_favorites.append({ 'title': config.get_localized_string(30122), 'items': [] }) self.user_favorites.append({ 'title': config.get_localized_string(30123), 'items': [] }) self.user_favorites.append({ 'title': config.get_localized_string(70149), 'items': [] }) @@ -112,7 +110,7 @@ class KodfavouritesData(object): self.save() def save(self): - if 'created' not in self.info_lista: + if 'created' not in self.info_lista: self.info_lista['created'] = fechahora_actual() self.info_lista['updated'] = fechahora_actual() @@ -120,34 +118,34 @@ class KodfavouritesData(object): jsondata['user_favorites'] = self.user_favorites jsondata['info_lista'] = self.info_lista if not filetools.write(self.user_favorites_file, jsontools.dump(jsondata)): - platformtools.dialog_ok('Alfa', config.get_localized_string(70614), os.path.basename(self.user_favorites_file)) + platformtools.dialog_ok('KoD', config.get_localized_string(70614), os.path.basename(self.user_favorites_file)) # ============================ -# Añadir desde menú contextual +# Add from context menu # ============================ def addFavourite(item): logger.info() alfav = KodfavouritesData() - # Si se llega aquí mediante el menú contextual, hay que recuperar los parámetros action y channel + # If you get here through the context menu, you must retrieve the action and channel parameters if item.from_action: item.__dict__['action'] = item.__dict__.pop('from_action') if item.from_channel: item.__dict__['channel'] = item.__dict__.pop('from_channel') - # Limpiar título + #Clear title item.title = re.sub(r'\[COLOR [^\]]*\]', '', item.title.replace('[/COLOR]', '')).strip() if item.text_color: item.__dict__.pop('text_color') - # Diálogo para escoger/crear carpeta + # Dialog to choose / create folder i_perfil = _selecciona_perfil(alfav, config.get_localized_string(70546)) if i_perfil == -1: return False - # Detectar que el mismo enlace no exista ya en la carpeta - campos = ['channel','action','url','extra','list_type'] # si todos estos campos coinciden se considera que el enlace ya existe + # Detect that the same link does not already exist in the folder + campos = ['channel','action','url','extra','list_type'] # if all these fields match the link is considered to already exist for enlace in alfav.user_favorites[i_perfil]['items']: it = Item().fromurl(enlace) repe = True @@ -159,25 +157,25 @@ def addFavourite(item): platformtools.dialog_notification(config.get_localized_string(70615), config.get_localized_string(70616)) return False - # Si es una película/serie, completar información de tmdb si no se tiene activado tmdb_plus_info (para season/episodio no hace falta pq ya se habrá hecho la "segunda pasada") + # If it is a movie / series, fill in tmdb information if tmdb_plus_info is not activated (for season / episode it is not necessary because the "second pass" will have already been done) if (item.contentType == 'movie' or item.contentType == 'tvshow') and not config.get_setting('tmdb_plus_info', default=False): from core import tmdb - tmdb.set_infoLabels(item, True) # obtener más datos en "segunda pasada" (actores, duración, ...) + tmdb.set_infoLabels(item, True) # get more data in "second pass" (actors, duration, ...) - # Añadir fecha en que se guarda + # Add date saved item.date_added = fechahora_actual() - # Guardar + # save alfav.user_favorites[i_perfil]['items'].append(item.tourl()) alfav.save() platformtools.dialog_notification(config.get_localized_string(70531), config.get_localized_string(70532) % alfav.user_favorites[i_perfil]['title']) - + return True # ==================== -# NAVEGACIÓN +# NAVIGATION # ==================== def mainlist(item): @@ -211,7 +209,7 @@ def mainlist(item): itemlist.append(Item(channel=item.channel, action='mostrar_perfil', title=perfil['title'], plot=plot, i_perfil=i_perfil, context=context)) itemlist.append(item.clone(action='crear_perfil', title=config.get_localized_string(70542), folder=False)) - + itemlist.append(item.clone(action='mainlist_listas', title=config.get_localized_string(70603))) return itemlist @@ -240,15 +238,15 @@ def mostrar_perfil(item): it.plot += '[CR][COLOR blue]Url:[/COLOR] ' + it.url if isinstance(it.url, str) else '...' if it.date_added != '': it.plot += '[CR][COLOR blue]' + config.get_localized_string(70469) + ':[/COLOR] ' + it.date_added - # Si no es una url, ni tiene la ruta del sistema, convertir el path ya que se habrá copiado de otro dispositivo. - # Sería más óptimo que la conversión se hiciera con un menú de importar, pero de momento se controla en run-time. + # If it is not a url, nor does it have the system path, convert the path since it will have been copied from another device. + # It would be more optimal if the conversion was done with an import menu, but at the moment it is controlled in run-time. if it.thumbnail and '://' not in it.thumbnail and not it.thumbnail.startswith(ruta_runtime): ruta, fichero = filetools.split(it.thumbnail) - if ruta == '' and fichero == it.thumbnail: # en linux el split con un path de windows no separa correctamente + if ruta == '' and fichero == it.thumbnail: # in linux the split with a windows path does not separate correctly ruta, fichero = filetools.split(it.thumbnail.replace('\\','/')) - if 'channels' in ruta and 'thumb' in ruta: + if 'channels' in ruta and 'thumb' in ruta: it.thumbnail = filetools.join(ruta_runtime, 'resources', 'media', 'channels', 'thumb', fichero) - elif 'themes' in ruta and 'default' in ruta: + elif 'themes' in ruta and 'default' in ruta: it.thumbnail = filetools.join(ruta_runtime, 'resources', 'media', 'themes', 'default', fichero) itemlist.append(it) @@ -256,28 +254,27 @@ def mostrar_perfil(item): return itemlist -# Rutinas internas compartidas -# ---------------------------- +# Shared internal routines -# Diálogo para seleccionar/crear una carpeta. Devuelve índice de la carpeta en user_favorites (-1 si cancel) +# Dialog to select / create a folder. Returns index of folder on user_favorites (-1 if cancel) def _selecciona_perfil(alfav, titulo='Seleccionar carpeta', i_actual=-1): acciones = [(perfil['title'] if i_p != i_actual else '[I][COLOR pink]%s[/COLOR][/I]' % perfil['title']) for i_p, perfil in enumerate(alfav.user_favorites)] acciones.append('Crear nueva carpeta') i_perfil = -1 - while i_perfil == -1: # repetir hasta seleccionar una carpeta o cancelar + while i_perfil == -1: # repeat until a folder is selected or cancel ret = platformtools.dialog_select(titulo, acciones) - if ret == -1: return -1 # pedido cancel + if ret == -1: return -1 # order cancel if ret < len(alfav.user_favorites): i_perfil = ret - else: # crear nueva carpeta + else: # create new folder if _crea_perfil(alfav): i_perfil = len(alfav.user_favorites) - 1 return i_perfil -# Diálogo para crear una carpeta +# Dialog to create a folder def _crea_perfil(alfav): titulo = platformtools.dialog_input(default='', heading=config.get_localized_string(70551)) if titulo is None or titulo == '': @@ -289,8 +286,7 @@ def _crea_perfil(alfav): return True -# Gestión de perfiles y enlaces -# ----------------------------- +# Profile and link management def crear_perfil(item): logger.info() @@ -325,7 +321,7 @@ def eliminar_perfil(item): if not alfav.user_favorites[item.i_perfil]: return False - # Pedir confirmación + # Ask for confirmation if not platformtools.dialog_yesno(config.get_localized_string(70618), config.get_localized_string(70619)): return False del alfav.user_favorites[item.i_perfil] @@ -342,9 +338,9 @@ def acciones_enlace(item): config.get_localized_string(70624), config.get_localized_string(70548), config.get_localized_string(70625), config.get_localized_string(70626), config.get_localized_string(70627), config.get_localized_string(70628)] - ret = platformtools.dialog_select('Acción a ejecutar', acciones) - if ret == -1: - return False # pedido cancel + ret = platformtools.dialog_select('Action to execute', acciones) + if ret == -1: + return False # order cancel elif ret == 0: return editar_enlace_titulo(item) elif ret == 1: @@ -375,11 +371,11 @@ def editar_enlace_titulo(item): if not alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]: return False it = Item().fromurl(alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]) - - titulo = platformtools.dialog_input(default=it.title, heading='Cambiar título del enlace') + + titulo = platformtools.dialog_input(default=it.title, heading='Change link title') if titulo is None or titulo == '' or titulo == it.title: return False - + it.title = titulo alfav.user_favorites[item.i_perfil]['items'][item.i_enlace] = it.tourl() @@ -397,13 +393,13 @@ def editar_enlace_color(item): if not alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]: return False it = Item().fromurl(alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]) - + colores = ['green','yellow','red','blue','white','orange','lime','aqua','pink','violet','purple','tomato','olive','antiquewhite','gold'] opciones = ['[COLOR %s]%s[/COLOR]' % (col, col) for col in colores] - ret = platformtools.dialog_select('Seleccionar color:', opciones) + ret = platformtools.dialog_select('Select color:', opciones) - if ret == -1: return False # pedido cancel + if ret == -1: return False # order cancel it.text_color = colores[ret] alfav.user_favorites[item.i_perfil]['items'][item.i_enlace] = it.tourl() @@ -421,13 +417,13 @@ def editar_enlace_thumbnail(item): if not alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]: return False it = Item().fromurl(alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]) - - # A partir de Kodi 17 se puede usar xbmcgui.Dialog().select con thumbnails (ListItem & useDetails=True) + + # Starting with Kodi 17, you can use xbmcgui.Dialog (). Select with thumbnails (ListItem & useDetails = True) is_kodi17 = (config.get_platform(True)['num_version'] >= 17.0) if is_kodi17: import xbmcgui - # Diálogo para escoger thumbnail (el del canal o iconos predefinidos) + # Dialog to choose thumbnail (the channel or predefined icons) opciones = [] ids = [] try: @@ -444,7 +440,7 @@ def editar_enlace_thumbnail(item): ids.append(channel_parameters['thumbnail']) except: pass - + resource_path = os.path.join(config.get_runtime_path(), 'resources', 'media', 'themes', 'default') for f in sorted(os.listdir(resource_path)): if f.startswith('thumb_') and not f.startswith('thumb_intervenido') and f != 'thumb_back.png': @@ -458,11 +454,11 @@ def editar_enlace_thumbnail(item): ids.append(os.path.join(resource_path, f)) if is_kodi17: - ret = xbmcgui.Dialog().select('Seleccionar thumbnail:', opciones, useDetails=True) + ret = xbmcgui.Dialog().select('Select thumbnail:', opciones, useDetails=True) else: - ret = platformtools.dialog_select('Seleccionar thumbnail:', opciones) + ret = platformtools.dialog_select('Select thumbnail:', opciones) - if ret == -1: return False # pedido cancel + if ret == -1: return False # order cancel it.thumbnail = ids[ret] @@ -480,8 +476,8 @@ def editar_enlace_carpeta(item): if not alfav.user_favorites[item.i_perfil]: return False if not alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]: return False - # Diálogo para escoger/crear carpeta - i_perfil = _selecciona_perfil(alfav, 'Mover enlace a:', item.i_perfil) + # Dialog to choose / create folder + i_perfil = _selecciona_perfil(alfav, 'Move link to:', item.i_perfil) if i_perfil == -1 or i_perfil == item.i_perfil: return False alfav.user_favorites[i_perfil]['items'].append(alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]) @@ -499,26 +495,26 @@ def editar_enlace_lista(item): if not alfav.user_favorites[item.i_perfil]: return False if not alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]: return False - # Diálogo para escoger lista + # Dialog to choose list opciones = [] itemlist_listas = mainlist_listas(item) for it in itemlist_listas: - if it.lista != '' and '[<---]' not in it.title: # descarta item crear y lista activa + if it.lista != '' and '[<---]' not in it.title: # discard item create and active list opciones.append(it.lista) if len(opciones) == 0: - platformtools.dialog_ok('Alfa', 'No hay otras listas dónde mover el enlace.', 'Puedes crearlas desde el menú Gestionar listas de enlaces') + platformtools.dialog_ok('KoD', 'There are no other lists where to move the link.', 'You can create them from the Manage link lists menu') return False - ret = platformtools.dialog_select('Seleccionar lista destino', opciones) + ret = platformtools.dialog_select('Select destination list', opciones) - if ret == -1: - return False # pedido cancel + if ret == -1: + return False # order cancel alfav_destino = KodfavouritesData(opciones[ret]) - # Diálogo para escoger/crear carpeta en la lista de destino - i_perfil = _selecciona_perfil(alfav_destino, 'Seleccionar carpeta destino', -1) + # Dialog to choose / create folder in the destination list + i_perfil = _selecciona_perfil(alfav_destino, 'Select destination folder', -1) if i_perfil == -1: return False alfav_destino.user_favorites[i_perfil]['items'].append(alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]) @@ -544,8 +540,7 @@ def eliminar_enlace(item): return True -# Mover perfiles y enlaces (arriba, abajo, top, bottom) -# ------------------------ +# Move profiles and links (up, down, top, bottom) def mover_perfil(item): logger.info() alfav = KodfavouritesData() @@ -568,28 +563,28 @@ def mover_enlace(item): return True -# Mueve un item determinado (numérico) de una lista (arriba, abajo, top, bottom) y devuelve la lista modificada +# Move a certain item (numeric) from a list (up, down, top, bottom) and return the modified list def _mover_item(lista, i_selected, direccion): last_i = len(lista) - 1 - if i_selected > last_i or i_selected < 0: return lista # índice inexistente en lista + if i_selected > last_i or i_selected < 0: return lista # non-existent index in list if direccion == 'arriba': - if i_selected == 0: # Ya está arriba de todo + if i_selected == 0: # It's already on top of everything return lista lista.insert(i_selected - 1, lista.pop(i_selected)) elif direccion == 'abajo': - if i_selected == last_i: # Ya está abajo de todo + if i_selected == last_i: # It's already down return lista lista.insert(i_selected + 1, lista.pop(i_selected)) elif direccion == 'top': - if i_selected == 0: # Ya está arriba de todo + if i_selected == 0: # It's already on top of everything return lista lista.insert(0, lista.pop(i_selected)) elif direccion == 'bottom': - if i_selected == last_i: # Ya está abajo de todo + if i_selected == last_i: # It's already down return lista lista.insert(last_i, lista.pop(i_selected)) @@ -598,7 +593,7 @@ def _mover_item(lista, i_selected, direccion): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Gestionar diferentes listas de alfavoritos +# Manage different alphabetical lists # ------------------------------------------ def mainlist_listas(item): @@ -607,19 +602,19 @@ def mainlist_listas(item): item.category = 'Listas' lista_activa = get_lista_activa() - + import glob path = os.path.join(config.get_data_path(), PREFIJO_LISTA+'*.json') for fichero in glob.glob(path): lista = os.path.basename(fichero) nombre = get_name_from_filename(lista) - titulo = nombre if lista != lista_activa else '[COLOR gold]%s[/COLOR] [<---]' % nombre - + titulo = nombre if lista != lista_activa else nombre + itemlist.append(item.clone(action='acciones_lista', lista=lista, title=titulo, folder=False)) itemlist.append(item.clone(action='acciones_nueva_lista', title=config.get_localized_string(70642), folder=False)) - + return itemlist @@ -631,7 +626,7 @@ def acciones_lista(item): ret = platformtools.dialog_select(item.lista, acciones) - if ret == -1: + if ret == -1: return False # pedido cancel elif ret == 0: return activar_lista(item) @@ -650,7 +645,7 @@ def activar_lista(item): fullfilename = os.path.join(config.get_data_path(), item.lista) if not os.path.exists(fullfilename): - platformtools.dialog_ok('Alfa', config.get_localized_string(70630), item.lista) + platformtools.dialog_ok('KoD', config.get_localized_string(70630), item.lista) return False config.set_setting('lista_activa', item.lista) @@ -668,9 +663,9 @@ def renombrar_lista(item): fullfilename_current = os.path.join(config.get_data_path(), item.lista) if not os.path.exists(fullfilename_current): - platformtools.dialog_ok('Alfa', config.get_localized_string(70630), fullfilename_current) + platformtools.dialog_ok('KoD', config.get_localized_string(70630), fullfilename_current) return False - + nombre = get_name_from_filename(item.lista) titulo = platformtools.dialog_input(default=nombre, heading=config.get_localized_string(70612)) if titulo is None or titulo == '' or titulo == nombre: @@ -680,17 +675,17 @@ def renombrar_lista(item): filename = get_filename_from_name(titulo) fullfilename = os.path.join(config.get_data_path(), filename) - # Comprobar que el nuevo nombre no exista + # Check that the new name does not exist if os.path.exists(fullfilename): - platformtools.dialog_ok('Alfa', config.get_localized_string(70613), fullfilename) + platformtools.dialog_ok('KoD', config.get_localized_string(70613), fullfilename) return False - # Rename del fichero + # Rename the file if not filetools.rename(fullfilename_current, filename): - platformtools.dialog_ok('Alfa', config.get_localized_string(70631), fullfilename) + platformtools.dialog_ok('KoD', config.get_localized_string(70631), fullfilename) return False - # Update settings si es la lista activa + # Update settings if it is the active list if item.lista == get_lista_activa(): config.set_setting('lista_activa', filename) @@ -704,11 +699,11 @@ def eliminar_lista(item): fullfilename = os.path.join(config.get_data_path(), item.lista) if not os.path.exists(fullfilename): - platformtools.dialog_ok('Alfa', config.get_localized_string(70630), item.lista) + platformtools.dialog_ok('KoD', config.get_localized_string(70630), item.lista) return False if item.lista == get_lista_activa(): - platformtools.dialog_ok('Alfa', config.get_localized_string(70632), item.lista) + platformtools.dialog_ok('KoD', config.get_localized_string(70632), item.lista) return False if not platformtools.dialog_yesno(config.get_localized_string(70606), config.get_localized_string(70633) + ' %s ?' % item.lista): return False @@ -720,15 +715,15 @@ def eliminar_lista(item): def informacion_lista(item): logger.info() - + fullfilename = os.path.join(config.get_data_path(), item.lista) if not os.path.exists(fullfilename): - platformtools.dialog_ok('Alfa', config.get_localized_string(70630), item.lista) + platformtools.dialog_ok('KoD', config.get_localized_string(70630), item.lista) return False alfav = KodfavouritesData(item.lista) - - txt = 'Lista: [COLOR gold]%s[/COLOR]' % item.lista + + txt = 'Lista: %s' % item.lista txt += '[CR]' + config.get_localized_string(70634) + ' ' + alfav.info_lista['created'] + ' ' + config.get_localized_string(70635) + ' ' + alfav.info_lista['updated'] if 'downloaded_date' in alfav.info_lista: @@ -736,7 +731,7 @@ def informacion_lista(item): if 'tinyupload_date' in alfav.info_lista: txt += '[CR]' + config.get_localized_string(70638) + ' ' + alfav.info_lista['tinyupload_date'] + ' ' + config.get_localized_string(70639) + ' [COLOR blue]' + alfav.info_lista['tinyupload_code'] + '[/COLOR]' - + txt += '[CR]' + config.get_localized_string(70640) + ' ' + str(len(alfav.user_favorites)) for perfil in alfav.user_favorites: txt += '[CR]- %s (%d %s)' % (perfil['title'], len(perfil['items']), config.get_localized_string(70641)) @@ -750,21 +745,21 @@ def compartir_lista(item): fullfilename = os.path.join(config.get_data_path(), item.lista) if not os.path.exists(fullfilename): - platformtools.dialog_ok('Alfa', config.get_localized_string(70630), fullfilename) + platformtools.dialog_ok('KoD', config.get_localized_string(70630), fullfilename) return False try: progreso = platformtools.dialog_progress_bg(config.get_localized_string(70643), config.get_localized_string(70644)) - - # Acceso a la página principal de tinyupload para obtener datos necesarios + + # Access to the tinyupload home page to obtain necessary data from core import httptools, scrapertools data = httptools.downloadpage('http://s000.tinyupload.com/index.php').data upload_url = scrapertools.find_single_match(data, 'form action="([^"]+)') sessionid = scrapertools.find_single_match(upload_url, 'sid=(.+)') - + progreso.update(10, config.get_localized_string(70645), config.get_localized_string(70646)) - # Envío del fichero a tinyupload mediante multipart/form-data + # Sending the file to tinyupload using multipart / form-data from future import standard_library standard_library.install_aliases() from lib import MultipartPostHandler @@ -773,31 +768,31 @@ def compartir_lista(item): params = { 'MAX_FILE_SIZE' : '52428800', 'file_description' : '', 'sessionid' : sessionid, 'uploaded_file' : open(fullfilename, 'rb') } handle = opener.open(upload_url, params) data = handle.read() - + progreso.close() if not 'File was uploaded successfuly' in data: logger.debug(data) - platformtools.dialog_ok('Alfa', config.get_localized_string(70647)) + platformtools.dialog_ok('KoD', config.get_localized_string(70647)) return False codigo = scrapertools.find_single_match(data, 'href="index\.php\?file_id=([^"]+)') except: - platformtools.dialog_ok('Alfa', config.get_localized_string(70647), item.lista) + platformtools.dialog_ok('KoD', config.get_localized_string(70647), item.lista) return False - # Apuntar código en fichero de log y dentro de la lista + # Point code in log file and inside the list save_log_lista_shared(config.get_localized_string(70648) + ' ' + item.lista + ' ' + codigo + ' ' + config.get_localized_string(70649)) - + alfav = KodfavouritesData(item.lista) alfav.info_lista['tinyupload_date'] = fechahora_actual() alfav.info_lista['tinyupload_code'] = codigo alfav.save() - platformtools.dialog_ok('Alfa', config.get_localized_string(70650), codigo) + platformtools.dialog_ok('KoD', config.get_localized_string(70650), codigo) return True - + def acciones_nueva_lista(item): @@ -810,8 +805,8 @@ def acciones_nueva_lista(item): ret = platformtools.dialog_select(config.get_localized_string(70608), acciones) - if ret == -1: - return False # pedido cancel + if ret == -1: + return False # order cancel elif ret == 0: return crear_lista(item) @@ -845,12 +840,12 @@ def crear_lista(item): filename = get_filename_from_name(titulo) fullfilename = os.path.join(config.get_data_path(), filename) - # Comprobar que el fichero no exista ya + # Check that the file does not already exist if os.path.exists(fullfilename): - platformtools.dialog_ok('Alfa', config.get_localized_string(70613), fullfilename) + platformtools.dialog_ok('KoD', config.get_localized_string(70613), fullfilename) return False - # Provocar que se guarde con las carpetas vacías por defecto + # Cause it to be saved with empty folders by default alfav = KodfavouritesData(filename) platformtools.itemlist_refresh() @@ -860,7 +855,7 @@ def crear_lista(item): def descargar_lista(item, url): logger.info() from core import httptools, scrapertools - + if 'tinyupload.com/' in url: try: from urllib.parse import urlparse @@ -869,15 +864,15 @@ def descargar_lista(item, url): down_url, url_name = scrapertools.find_single_match(data, ' href="(download\.php[^"]*)">([^<]*)') url_json = '{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(url)) + down_url except: - platformtools.dialog_ok('Alfa', config.get_localized_string(70655), url) + platformtools.dialog_ok('KoD', config.get_localized_string(70655), url) return False elif 'zippyshare.com/' in url: from core import servertools video_urls, puedes, motivo = servertools.resolve_video_urls_for_playing('zippyshare', url) - + if not puedes: - platformtools.dialog_ok('Alfa', config.get_localized_string(70655), motivo) + platformtools.dialog_ok('KoD', config.get_localized_string(70655), motivo) return False url_json = video_urls[0][1] # https://www58.zippyshare.com/d/qPzzQ0UM/25460/kodfavourites-testeanding.json url_name = url_json[url_json.rfind('/')+1:] @@ -893,19 +888,19 @@ def descargar_lista(item, url): # Download json data = httptools.downloadpage(url_json).data - - # Verificar formato json de kodfavourites y añadir info de la descarga + + # Verify ksonfavourites json format and add download info jsondata = jsontools.load(data) if 'user_favorites' not in jsondata or 'info_lista' not in jsondata: logger.debug(data) - platformtools.dialog_ok('Alfa', config.get_localized_string(70656)) + platformtools.dialog_ok('KoD', config.get_localized_string(70656)) return False jsondata['info_lista']['downloaded_date'] = fechahora_actual() jsondata['info_lista']['downloaded_from'] = url data = jsontools.dump(jsondata) - # Pedir nombre para la lista descargada + # Ask for name for downloaded list nombre = get_name_from_filename(url_name) titulo = platformtools.dialog_input(default=nombre, heading=config.get_localized_string(70657)) if titulo is None or titulo == '': @@ -915,14 +910,14 @@ def descargar_lista(item, url): filename = get_filename_from_name(titulo) fullfilename = os.path.join(config.get_data_path(), filename) - # Si el nuevo nombre ya existe pedir confirmación para sobrescribir + # If the new name already exists ask for confirmation to overwrite if os.path.exists(fullfilename): - if not platformtools.dialog_yesno('Alfa', config.get_localized_string(70613), config.get_localized_string(70658), filename): + if not platformtools.dialog_yesno('KoD', config.get_localized_string(70613), config.get_localized_string(70658), filename): return False - - if not filetools.write(fullfilename, data): - platformtools.dialog_ok('Alfa', config.get_localized_string(70659), filename) - platformtools.dialog_ok('Alfa', config.get_localized_string(70660), filename) + if not filetools.write(fullfilename, data): + platformtools.dialog_ok('KoD', config.get_localized_string(70659), filename) + + platformtools.dialog_ok('KoD', config.get_localized_string(70660), filename) platformtools.itemlist_refresh() return True diff --git a/specials/news.py b/specials/news.py index 3d9a4ad1..2287a15a 100644 --- a/specials/news.py +++ b/specials/news.py @@ -11,25 +11,19 @@ from core.support import typo PY3 = False if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int -import glob -import os -import re -import time +import glob, os, re, time from threading import Thread from channelselector import get_thumb, auto_filter -from core import channeltools -from core import jsontools -from core import scrapertools, support +from core import channeltools, jsontools, scrapertools, support from core.item import Item -from platformcode import config, logger -from platformcode import platformtools +from platformcode import config, logger, platformtools THUMBNAILS = {'0': 'posters', '1': 'banners', '2': 'squares'} __perfil__ = config.get_setting('perfil', "news") -# Fijar perfil de color +# Set color profile perfil = [['0xFF0B7B92', '0xFF89FDFB', '0xFFACD5D4'], ['0xFFB31313', '0xFFFF9000', '0xFFFFEE82'], ['0xFF891180', '0xFFCB22D7', '0xFFEEA1EB'], @@ -141,7 +135,7 @@ def get_channels_list(): list_canales = {'peliculas': [], 'series': [],'anime': [], 'italiano':[], 'documentales': []} any_active = False - # Rellenar listas de canales disponibles + # Fill available channel lists channels_path = os.path.join(config.get_runtime_path(), "channels", '*.json') channel_language = config.get_setting("channel_language", default="all") if channel_language =="auto": @@ -151,16 +145,16 @@ def get_channels_list(): channel_id = os.path.basename(infile)[:-5] channel_parameters = channeltools.get_channel_parameters(channel_id) - # No incluir si es un canal inactivo + # Do not include if it is an inactive channel if not channel_parameters["active"]: continue - # No incluir si el canal es en un idioma filtrado + # Do not include if the channel is in a filtered language if channel_language != "all" and channel_language not in str(channel_parameters["language"]) \ and "*" not in channel_parameters["language"]: continue - # Incluir en cada categoria, si en su configuracion el canal esta activado para mostrar novedades + # Include in each category, if in your configuration the channel is activated to show news for categoria in list_canales: include_in_newest = config.get_setting("include_in_newest_" + categoria, channel_id) @@ -267,7 +261,7 @@ def novedades(item): if any_active and len(list_canales[item.extra])>0: import math - # fix float porque la division se hace mal en python 2.x + # fix float because division is done poorly in python 2.x number_of_channels = float(100) / len(list_canales[item.extra]) for index, channel in enumerate(list_canales[item.extra]): @@ -294,7 +288,7 @@ def novedades(item): progreso.update(percentage, "", config.get_localized_string(60520) % channel_title) get_newest(channel_id, item.extra) - # Modo Multi Thread: esperar q todos los hilos terminen + # Multi Thread mode: wait for all threads to finish if multithread: pendent = [a for a in threads if a.isAlive()] t = float(100) / len(pendent) @@ -326,15 +320,15 @@ def novedades(item): if mode != 'normal': result_mode=0 - if result_mode == 0: # Agrupados por contenido + if result_mode == 0: # Grouped by content ret = group_by_content(list_newest) - elif result_mode == 1: # Agrupados por canales + elif result_mode == 1: # Grouped by channels ret = group_by_channel(list_newest) - else: # Sin agrupar + else: # Ungrouped ret = no_group(list_newest) while time.time() - start_time < 2: - # mostrar cuadro de progreso con el tiempo empleado durante almenos 2 segundos + # show progress chart with time spent for at least 2 seconds time.sleep(0.5) if mode == 'normal': progreso.close() @@ -356,8 +350,8 @@ def get_newest(channel_id, categoria): global list_newest global list_newest_tourl - # Solicitamos las novedades de la categoria (item.extra) buscada en el canal channel - # Si no existen novedades para esa categoria en el canal devuelve una lista vacia + # We request the news of the category (item.extra) searched in the channel channel + # If there are no news for that category in the channel, it returns an empty list try: puede = True @@ -381,7 +375,7 @@ def get_newest(channel_id, categoria): exist=True else: cache_node = {} - #logger.debug('cache node: %s' % cache_node) + # logger.debug('cache node: %s' % cache_node) for item in list_result: # logger.info("item="+item.tostring()) item.channel = channel_id @@ -399,11 +393,11 @@ def get_newest(channel_id, categoria): def get_title(item): - #support.log("ITEM NEWEST ->", item) + # support.log("ITEM NEWEST ->", item) # item.contentSerieName c'è anche se è un film if item.contentSerieName and item.contentType != 'movie': # Si es una serie title = item.contentSerieName - #title = re.compile("\[.*?\]", re.DOTALL).sub("", item.contentSerieName) + # title = re.compile("\[.*?\]", re.DOTALL).sub("", item.contentSerieName) if not scrapertools.get_season_and_episode(title) and item.contentEpisodeNumber: # contentSeason non c'è in support if not item.contentSeason: @@ -414,14 +408,14 @@ def get_title(item): if seas: title = "%s - %s" % (seas, title) - elif item.contentTitle: # Si es una pelicula con el canal adaptado + elif item.contentTitle: # If it is a movie with the adapted channel title = item.contentTitle - elif item.contentTitle: # Si el canal no esta adaptado + elif item.contentTitle: # If the channel is not adapted title = item.contentTitle - else: # Como ultimo recurso + else: # As a last resort title = item.title - # Limpiamos el titulo de etiquetas de formato anteriores + # We clean the title of previous format labels title = re.compile("\[/*COLO.*?\]", re.DOTALL).sub("", title) title = re.compile("\[/*B\]", re.DOTALL).sub("", title) title = re.compile("\[/*I\]", re.DOTALL).sub("", title) @@ -452,9 +446,9 @@ def no_group(list_result_canal): global channels_id_name for i in list_result_canal: - #support.log("NO GROUP i -> ", i) + # support.log("NO GROUP i -> ", i) canale = channels_id_name[i.channel] - canale = canale # per differenziarlo dal colore delle altre voci + canale = canale # to differentiate it from the color of the other items i.title = get_title(i) + " [" + canale + "]" # i.text_color = color3 @@ -471,12 +465,12 @@ def group_by_channel(list_result_canal): for i in list_result_canal: if i.channel not in dict_canales: dict_canales[i.channel] = [] - # Formatear titulo + # Format title i.title = get_title(i) - # Añadimos el contenido al listado de cada canal + # We add the content to the list of each channel dict_canales[i.channel].append(i) - # Añadimos el contenido encontrado en la lista list_result + # We add the content found in the list_result list for c in sorted(dict_canales): itemlist.append(Item(channel="news", title=channels_id_name[c] + ':', text_color=color1, text_bold=True)) @@ -498,10 +492,10 @@ def group_by_content(list_result_canal): list_result = [] for i in list_result_canal: - # Formatear titulo + # Format title i.title = get_title(i) - # Eliminar tildes y otros caracteres especiales para la key + # Remove tildes and other special characters for the key import unicodedata try: new_key = i.title.lower().strip().decode("UTF-8") @@ -511,16 +505,16 @@ def group_by_content(list_result_canal): new_key = i.title if new_key in dict_contenidos: - # Si el contenido ya estaba en el diccionario añadirlo a la lista de opciones... + #If the content was already in the dictionary add it to the list of options ... dict_contenidos[new_key].append(i) - else: # ...sino añadirlo al diccionario + else: # ...but add it to the dictionary dict_contenidos[new_key] = [i] - # Añadimos el contenido encontrado en la lista list_result + # We add the content found in the list_result list for v in list(dict_contenidos.values()): title = v[0].title if len(v) > 1: - # Eliminar de la lista de nombres de canales los q esten duplicados + # Remove duplicate q's from the channel names list canales_no_duplicados = [] for i in v: if i.channel not in canales_no_duplicados: @@ -622,16 +616,16 @@ def setting_channel(item): channel_id = os.path.basename(infile)[:-5] channel_parameters = channeltools.get_channel_parameters(channel_id) - # No incluir si es un canal inactivo + # Do not include if it is an inactive channel if not channel_parameters["active"]: continue - # No incluir si el canal es en un idioma filtrado + # Do not include if the channel is in a filtered language if channel_language != "all" and channel_language not in str(channel_parameters["language"]) \ and "*" not in channel_parameters["language"]: continue - # No incluir si en su configuracion el canal no existe 'include_in_newest' + # Do not include if the channel does not exist 'include_in_newest' in your configuration include_in_newest = config.get_setting("include_in_newest_" + item.extra, channel_id) if include_in_newest is None: continue diff --git a/specials/renumbertools.py b/specials/renumbertools.py deleted file mode 100644 index ddb22080..00000000 --- a/specials/renumbertools.py +++ /dev/null @@ -1,1008 +0,0 @@ -# -*- coding: utf-8 -*- -# -------------------------------------------------------------------------------- -# renumeratetools - se encarga de renumerar episodios -# -------------------------------------------------------------------------------- - -#from builtins import str -import sys -PY3 = False -if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int -from builtins import range -from builtins import object - -import os - -try: - import xbmcgui -except: - xbmcgui = None - -from platformcode import config, logger -from core import jsontools -from core.item import Item -from platformcode import platformtools - -TAG_TVSHOW_RENUMERATE = "TVSHOW_RENUMBER" -TAG_SEASON_EPISODE = "season_episode" -__channel__ = "renumbertools" - - -def access(): - """ - Devuelve si se puede usar o no renumbertools - """ - allow = False - - if config.is_xbmc(): - allow = True - - return allow - - -def context(item): - """ - Para xbmc/kodi que pueden mostrar el menú contextual, se añade un menu para configuración - la opción de renumerar, sólo si es para series. - - @param item: elemento para obtener la información y ver que contexto añadir - @type item: item - @return: lista de opciones a mostrar en el menú contextual - @rtype: list - """ - - # Dependiendo de como sea el contexto lo guardamos y añadimos las opciones de filtertools. - if isinstance(item.context, str): - _context = item.context.split("|") - elif isinstance(item.context, list): - _context = item.context - else: - _context = [] - - if access(): - dict_data = {"title": config.get_localized_string(70585), "action": "config_item", "channel": "renumbertools"} - _context.append(dict_data) - - return _context - - -def show_option(channel, itemlist): - if access(): - itemlist.append(Item(channel=__channel__, title="[COLOR yellow]" + config.get_localized_string(70586)+ "[/COLOR]", - action="load", from_channel=channel)) - - return itemlist - - -def load(item): - return mainlist(channel=item.from_channel) - - -def mainlist(channel): - """ - Muestra una lista de las series renumeradas - - :param channel: nombre del canal para obtener las series renumeradas - :type channel: str - :return: lista de Item - :rtype: list[Item] - """ - logger.info() - itemlist = [] - dict_series = jsontools.get_node_from_file(channel, TAG_TVSHOW_RENUMERATE) - - idx = 0 - for tvshow in sorted(dict_series): - tag_color = "0xff008000" - if idx % 2 == 0: - tag_color = "blue" - - idx += 1 - name = tvshow - title = config.get_localized_string(70587)+" [COLOR %s][%s][/COLOR]" % (tag_color, name) - - itemlist.append(Item(channel=__channel__, action="config_item", title=title, show=name, from_channel=channel)) - - if len(itemlist) == 0: - itemlist.append(Item(channel=channel, action="mainlist", - title=config.get_localized_string(70588) + ' ' + config.get_localized_string(70585))) - - return itemlist - - -def config_item(item): - """ - muestra una serie renumerada para su configuración - - :param item: item - :type item: Item - """ - logger.info("item %s" % item.tostring("\n")) - - dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_RENUMERATE) - data = dict_series.get(item.show, {}) - - if data: - data = data.get(TAG_SEASON_EPISODE, []) - - ventana = RenumberWindow(show=item.show, channel=item.from_channel, data=data) - del ventana - else: - # tenemos información y devolvemos los datos añadidos para que se muestre en la ventana - if data: - return add_season(data) - # es la primera vez que se añaden datos (usando menú contextual) por lo que no devolvemos nada - # para evitar error al listar los items - else: - data = add_season(data) - write_data(item.from_channel, item.show, data) - - -def numbered_for_tratk(channel, show, season, episode): - """ - Devuelve la temporada y episodio convertido para que se marque correctamente en tratk.tv - - @param channel: Nombre del canal - @type channel: str - @param show: Nombre de la serie a comprobar - @type show: str - @param season: Temporada que devuelve el scrapper - @type season: int - @param episode: Episodio que devuelve el scrapper - @type episode: int - @return: season, episode - @rtype: int, int - """ - logger.info() - - if access(): - show = show.lower() - - new_season = season - new_episode = episode - dict_series = jsontools.get_node_from_file(channel, TAG_TVSHOW_RENUMERATE) - - # ponemos en minusculas el key, ya que previamente hemos hecho lo mismo con show. - for key in list(dict_series.keys()): - new_key = key.lower() - if new_key != key: - dict_series[new_key] = dict_series[key] - del dict_series[key] - - if show in dict_series: - logger.debug(config.get_localized_string(70589) + " %s" % dict_series[show]) - - if len(dict_series[show]['season_episode']) > 1: - for row in dict_series[show]['season_episode']: - - if new_episode > row[1]: - new_episode -= row[1] - new_season = row[0] - break - - else: - new_season = dict_series[show]['season_episode'][0][0] - new_episode += dict_series[show]['season_episode'][0][1] - - logger.debug("%s:%s" % (new_season, new_episode)) - else: - # no se tiene acceso se devuelven los datos. - new_season = season - new_episode = episode - - return new_season, new_episode - - -def borrar(channel, show): - logger.info() - heading = config.get_localized_string(70590) - line1 = config.get_localized_string(70591) + ' [COLOR blue]' + show.strip() + '[/COLOR], ' + config.get_localized_string(70592) - - if platformtools.dialog_yesno(heading, line1) == 1: - dict_series = jsontools.get_node_from_file(channel, TAG_TVSHOW_RENUMERATE) - dict_series.pop(show, None) - - result, json_data = jsontools.update_node(dict_series, channel, TAG_TVSHOW_RENUMERATE) - - if result: - message = config.get_localized_string(60444) - else: - message = config.get_localized_string(70593) - - heading = show.strip() - platformtools.dialog_notification(heading, message) - - -def add_season(data=None): - logger.debug("data %s" % data) - heading = config.get_localized_string(70594) - # default = 2 - # se reordena la lista - list_season_episode = data - if list_season_episode: - list_season_episode.sort(key=lambda el: int(el[0]), reverse=False) - - # if list_season_episode: - # # mostrar temporada + 1 de la lista - # # TODO buscar la primera posicion libre - # default = list_season_episode[0][0]+1 - - season = platformtools.dialog_numeric(0, heading) # , str(default)) - for element in list_season_episode: - if int(season) == element[0]: - platformtools.dialog_notification(config.get_localized_string(70595), config.get_localized_string(70596)) - return - - # si hemos insertado un valor en la temporada - if season != "" and int(season) > 0: - heading = config.get_localized_string(70597) - # default = 0 - # if list_season_episode: - # for e in list_season_episode: - # # mostrar suma episodios de la lista - # # sumar hasta el indice del primer libre encontrado - # default += e[1] - episode = platformtools.dialog_numeric(0, heading) # , str(default)) - - # si hemos insertado un valor en el episodio - if episode != "": - if list_season_episode: - list_season_episode.insert(0, [int(season), int(episode)]) - new_list_season_episode = list_season_episode[:] - return new_list_season_episode - else: - return [[int(season), int(episode)]] - - -def write_data(channel, show, data): - # OBTENEMOS LOS DATOS DEL JSON - dict_series = jsontools.get_node_from_file(channel, TAG_TVSHOW_RENUMERATE) - tvshow = show.strip() - list_season_episode = dict_series.get(tvshow, {}).get(TAG_SEASON_EPISODE, []) - logger.debug("data %s" % list_season_episode) - - if data: - # cambiamos el orden para que se vea en orden descendente y usarse bien en el _data.json - data.sort(key=lambda el: int(el[0]), reverse=True) - dict_renumerate = {TAG_SEASON_EPISODE: data} - - dict_series[tvshow] = dict_renumerate - else: - # hemos borrado todos los elementos, por lo que se borra la serie del fichero - dict_series.pop(tvshow, None) - - result, json_data = jsontools.update_node(dict_series, channel, TAG_TVSHOW_RENUMERATE) - - if result: - if data: - message = config.get_localized_string(60446) - else: - message = config.get_localized_string(60444) - else: - message = config.get_localized_string(70593) - - heading = show.strip() - platformtools.dialog_notification(heading, message) - - -if xbmcgui: - - # Align - ALIGN_LEFT = 0 - ALIGN_RIGHT = 1 - ALIGN_CENTER_X = 2 - ALIGN_CENTER_Y = 4 - ALIGN_CENTER = 6 - ALIGN_TRUNCATED = 8 - ALIGN_JUSTIFY = 10 - - # button ids - ID_BUTTON_CLOSE = 3003 - ID_BUTTON_ADD_SEASON = 3008 - ID_BUTTON_INFO = 3009 - ID_CHECK_UPDATE_INTERNET = 3010 - ID_BUTTON_OK = 3012 - ID_BUTTON_CANCEL = 3013 - ID_BUTTON_DELETE = 3014 - - - class RenumberWindow(xbmcgui.WindowDialog): - def __init__(self, *args, **kwargs): - logger.debug() - - #### Compatibilidad con Kodi 18 #### - if config.get_platform(True)['num_version'] < 18: - if xbmcgui.__version__ == "1.2": - self.setCoordinateResolution(1) - else: - self.setCoordinateResolution(5) - - self.show = kwargs.get("show") - self.channel = kwargs.get("channel") - self.data = kwargs.get("data") - self.init = True - - self.mediapath = os.path.join(config.get_runtime_path(), 'resources', 'skins', 'Default', 'media') - self.font = "font12" - - window_bg = xbmcgui.ControlImage(320, 130, 600, 440, - os.path.join(self.mediapath, 'Windows', 'DialogBack.png')) - self.addControl(window_bg) - - header_bg = xbmcgui.ControlImage(window_bg.getX(), window_bg.getY() + 8, window_bg.getWidth(), 35, - os.path.join(self.mediapath, 'Windows', 'dialogheader.png')) - self.addControl(header_bg) - - btn_close_w = 64 - self.btn_close = xbmcgui.ControlButton(window_bg.getX() + window_bg.getWidth() - btn_close_w - 13, - header_bg.getY() + 6, btn_close_w, 30, '', - focusTexture=os.path.join(self.mediapath, 'Controls', - 'DialogCloseButton-focus.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'DialogCloseButton.png')) - self.addControl(self.btn_close) - - header_title_x = window_bg.getX() + 20 - header_title = xbmcgui.ControlFadeLabel(header_title_x, header_bg.getY() + 5, self.btn_close.getX() - - header_title_x, 30, font="font12_title", textColor="0xFFFFA500", - _alignment=ALIGN_CENTER) - self.addControl(header_title) - header_title.addLabel(self.show) - - self.controls_bg = xbmcgui.ControlImage(window_bg.getX() + 20, header_bg.getY() + header_bg.getHeight() + 6, - 562, 260, - os.path.join(self.mediapath, 'Windows', 'BackControls.png')) - self.addControl(self.controls_bg) - - self.scroll_bg = xbmcgui.ControlImage(window_bg.getX() + window_bg.getWidth() - 25, self.controls_bg.getY(), - 10, - self.controls_bg.getHeight(), os.path.join(self.mediapath, 'Controls', - 'ScrollBack.png')) - self.addControl(self.scroll_bg) - self.scroll_bg.setVisible(False) - - self.scroll2_bg = xbmcgui.ControlImage(window_bg.getX() + window_bg.getWidth() - 25, - self.controls_bg.getY(), - 10, self.controls_bg.getHeight(), os.path.join(self.mediapath, - 'Controls', - 'ScrollBar.png')) - self.addControl(self.scroll2_bg) - self.scroll2_bg.setVisible(False) - - btn_add_season = xbmcgui.ControlButton(window_bg.getX() + 20, self.controls_bg.getY() + - self.controls_bg.getHeight() + 14, 165, 30, config.get_localized_string(70600), - font=self.font, focusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKey.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKeyNF.png'), - alignment=ALIGN_CENTER) - self.addControl(btn_add_season) - - self.btn_info = xbmcgui.ControlButton(window_bg.getX() + 210, btn_add_season.getY(), 120, 30, config.get_localized_string(60348), - font=self.font, focusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKey.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKeyNF.png'), - alignment=ALIGN_CENTER) - self.addControl(self.btn_info) - - check_update_internet_w = 235 - # Versiones antiguas no admite algunas texturas - if xbmcgui.__version__ in ["1.2", "2.0"]: - self.check_update_internet = xbmcgui.ControlRadioButton( - window_bg.getX() + window_bg.getWidth() - check_update_internet_w - 20, btn_add_season.getY() - 3, - check_update_internet_w, 34, config.get_localized_string(70601), font=self.font, - focusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png')) - else: - self.check_update_internet = xbmcgui.ControlRadioButton( - window_bg.getX() + window_bg.getWidth() - check_update_internet_w - 20, btn_add_season.getY() - 3, - check_update_internet_w, 34, config.get_localized_string(70601), font=self.font, - focusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'), - focusOnTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-focus.png'), - noFocusOnTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-focus.png'), - focusOffTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-nofocus.png'), - noFocusOffTexture=os.path.join(self.mediapath, 'Controls', 'radiobutton-nofocus.png')) - - self.addControl(self.check_update_internet) - self.check_update_internet.setEnabled(False) - - hb_bg = xbmcgui.ControlImage(window_bg.getX() + 20, btn_add_season.getY() + btn_add_season.getHeight() + 13, - window_bg.getWidth() - 40, 2, - os.path.join(self.mediapath, 'Controls', 'ScrollBack.png')) - self.addControl(hb_bg) - - self.btn_ok = xbmcgui.ControlButton(window_bg.getX() + 68, hb_bg.getY() + hb_bg.getHeight() + 13, 120, 30, - 'OK', font=self.font, - focusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKey.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKeyNF.png'), - alignment=ALIGN_CENTER) - self.addControl(self.btn_ok) - - self.btn_cancel = xbmcgui.ControlButton(self.btn_info.getX() + 30, self.btn_ok.getY(), 120, 30, config.get_localized_string(70002), - font=self.font, - focusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKey.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKeyNF.png'), - alignment=ALIGN_CENTER) - self.addControl(self.btn_cancel) - - self.btn_delete = xbmcgui.ControlButton(self.btn_cancel.getX() + self.btn_cancel.getWidth() + 50, - self.btn_ok.getY(), 120, 30, config.get_localized_string(60437), font=self.font, - focusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKey.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKeyNF.png'), - alignment=ALIGN_CENTER) - self.addControl(self.btn_delete) - - self.controls = [] - self.onInit() - self.setFocus(self.controls[0].edit_season) - self.doModal() - - def onInit(self, *args, **kwargs): - try: - # listado temporada / episodios - pos_y = self.controls_bg.getY() + 10 - - # eliminamos los componentes al repintar la ventana - for linea in self.controls: - self.removeControls(linea.list_elements()) - - # mostramos el scroll si hay más de 5 elementos - if len(self.data) > 5: - self.controls_bg.setWidth(545) - self.scroll_bg.setVisible(True) - self.scroll2_bg.setVisible(True) - else: - self.controls_bg.setWidth(562) - self.scroll_bg.setVisible(False) - self.scroll2_bg.setVisible(False) - - self.controls = [] - # cambiamos el orden para que se vea en orden ascendente - self.data.sort(key=lambda el: int(el[0]), reverse=False) - - for index, e in enumerate(self.data): - pos_x = self.controls_bg.getX() + 15 - label_season_w = 100 - label_season = xbmcgui.ControlLabel(pos_x, pos_y + 3, label_season_w, 34, - config.get_localized_string(60385), font=self.font, textColor="0xFF2E64FE") - self.addControl(label_season) - label_season.setVisible(False) - - pos_x += label_season_w + 5 - - # TODO mirar retro-compatilibidad - # if xbmcgui.ControlEdit == ControlEdit: - # edit_season = xbmcgui.ControlEdit(0, 0, 0, 0, '', font=self.font, isPassword=False, - # textColor='', - # focusTexture=os.path.join(self.mediapath, 'Controls', - # 'MenuItemFO.png'), - # noFocusTexture=os.path.join(self.mediapath, 'Controls', - # 'MenuItemNF.png'), window=self) - # else: - - # control bugeado se tiene que usar metodos sets para que se cree correctamente. - edit_season = xbmcgui.ControlEdit(0, 0, 0, 0, "", self.font, "", '', 4, isPassword=False, - focusTexture=os.path.join(self.mediapath, 'Controls', - 'MenuItemFO.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'MenuItemNF.png')) - self.addControl(edit_season) - edit_season.setText(str(e[0])) - # edit_season.setLabel("Temporada:", font=self.font, textColor="0xFF2E64FE") - edit_season.setPosition(pos_x, pos_y - 2) - edit_season.setWidth(25) - edit_season.setHeight(35) - edit_season.setVisible(False) - - label_episode_w = 90 - pos_x += edit_season.getWidth() + 60 - label_episode = xbmcgui.ControlLabel(pos_x, pos_y + 3, label_episode_w, 34, config.get_localized_string(70598), - font=self.font, textColor="0xFF2E64FE") - self.addControl(label_episode) - label_episode.setVisible(False) - - pos_x += label_episode_w + 5 - # control bugeado se tiene que usar metodos sets para que se cree correctamente. - edit_episode = xbmcgui.ControlEdit(0, 0, 0, 0, "", self.font, "", '', 4, isPassword=False, - focusTexture=os.path.join(self.mediapath, 'Controls', - 'MenuItemFO.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'MenuItemNF.png')) - self.addControl(edit_episode) - edit_episode.setText(str(e[1])) - # edit_episode.setLabel("Episodios:", font=self.font, textColor="0xFF2E64FE") - edit_episode.setPosition(pos_x, pos_y - 2) - edit_episode.setWidth(40) - edit_episode.setHeight(35) - edit_episode.setVisible(False) - - btn_delete_season_w = 120 - btn_delete_season = xbmcgui.ControlButton(self.controls_bg.getX() + self.controls_bg.getWidth() - - btn_delete_season_w - 14, pos_y, btn_delete_season_w, 30, - config.get_localized_string(70599), font=self.font, - focusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKey.png'), - noFocusTexture=os.path.join(self.mediapath, 'Controls', - 'KeyboardKeyNF.png'), - alignment=ALIGN_CENTER) - self.addControl(btn_delete_season) - btn_delete_season.setVisible(False) - - hb_bg = xbmcgui.ControlImage(self.controls_bg.getX() + 10, pos_y + 40, - self.controls_bg.getWidth() - 20, - 2, os.path.join(self.mediapath, 'Controls', 'ScrollBack.png')) - self.addControl(hb_bg) - hb_bg.setVisible(False) - - group = ControlGroup(label_season=label_season, edit_season=edit_season, - label_episode=label_episode, - edit_episode=edit_episode, btn_delete_season=btn_delete_season, hb=hb_bg) - - pos_y += 50 - - if index < 5: - group.set_visible(True) - - self.controls.append(group) - - if len(self.data) > 5: - self.move_scroll() - - except Exception as Ex: - logger.error("HA HABIDO UNA HOSTIA %s" % Ex) - - # def onClick(self, control_id): - # pass - # - # def onFocus(self, control_id): - # pass - - def onControl(self, control): - # logger.debug("%s" % control.getId()) - control_id = control.getId() - - if control_id == ID_BUTTON_OK: - write_data(self.channel, self.show, self.data) - self.close() - if control_id in [ID_BUTTON_CLOSE, ID_BUTTON_CANCEL]: - self.close() - elif control_id == ID_BUTTON_DELETE: - self.close() - borrar(self.channel, self.show) - elif control_id == ID_BUTTON_ADD_SEASON: - # logger.debug("data que enviamos: {}".format(self.data)) - data = add_season(self.data) - if data: - self.data = data - # logger.debug("data que recibimos: {}".format(self.data)) - self.onInit() - - # si hay más de 5 elementos movemos el scroll - if len(self.data) > 5: - self.scroll(len(self.data) - 2, 1) - self.move_scroll() - - elif control_id == ID_BUTTON_INFO: - self.method_info() - else: - for x, grupo in enumerate(self.controls): - if control_id == self.controls[x].btn_delete_season.getId(): - # logger.debug("A data %s" % self.data) - self.removeControls(self.controls[x].list_elements()) - del self.controls[x] - del self.data[x] - # logger.debug("D data %s" % self.data) - self.onInit() - - return - - def onAction(self, action): - # logger.debug("%s" % action.getId()) - # logger.debug("focus %s" % self.getFocusId()) - # Obtenemos el foco - focus = self.getFocusId() - - action = action.getId() - # Flecha izquierda - if action == xbmcgui.ACTION_MOVE_LEFT: - # Si el foco no está en ninguno de los 6 botones inferiores, y esta en un "list" cambiamos el valor - if focus not in [ID_BUTTON_ADD_SEASON, ID_BUTTON_INFO, ID_CHECK_UPDATE_INTERNET, - ID_BUTTON_OK, ID_BUTTON_CANCEL, ID_BUTTON_DELETE]: - - # Localizamos en el listado de controles el control que tiene el focus - # todo mirar tema del cursor en el valor al desplazar lateralmente - for x, linea in enumerate(self.controls): - if focus == linea.edit_season.getId(): - return self.setFocus(self.controls[x].btn_delete_season) - elif focus == linea.edit_episode.getId(): - return self.setFocus(self.controls[x].edit_season) - elif focus == linea.btn_delete_season.getId(): - return self.setFocus(self.controls[x].edit_episode) - - # Si el foco está en alguno de los 6 botones inferiores, movemos al siguiente - else: - if focus in [ID_BUTTON_ADD_SEASON, ID_BUTTON_INFO, ID_CHECK_UPDATE_INTERNET]: - if focus == ID_BUTTON_ADD_SEASON: - self.setFocusId(ID_BUTTON_INFO) - # TODO cambiar cuando se habilite la opcion de actualizar por internet - # self.setFocusId(ID_CHECK_UPDATE_INTERNET) - elif focus == ID_BUTTON_INFO: - self.setFocusId(ID_BUTTON_ADD_SEASON) - elif focus == ID_CHECK_UPDATE_INTERNET: - self.setFocusId(ID_BUTTON_INFO) - - elif focus in [ID_BUTTON_OK, ID_BUTTON_CANCEL, ID_BUTTON_DELETE]: - if focus == ID_BUTTON_OK: - self.setFocusId(ID_BUTTON_DELETE) - elif focus == ID_BUTTON_CANCEL: - self.setFocusId(ID_BUTTON_OK) - elif focus == ID_BUTTON_DELETE: - self.setFocusId(ID_BUTTON_CANCEL) - - # Flecha derecha - elif action == xbmcgui.ACTION_MOVE_RIGHT: - # Si el foco no está en ninguno de los 6 botones inferiores, y esta en un "list" cambiamos el valor - if focus not in [ID_BUTTON_ADD_SEASON, ID_BUTTON_INFO, ID_CHECK_UPDATE_INTERNET, - ID_BUTTON_OK, ID_BUTTON_CANCEL, ID_BUTTON_DELETE]: - - # Localizamos en el listado de controles el control que tiene el focus - # todo mirar tema del cursor en el valor al desplazar lateralmente - for x, linea in enumerate(self.controls): - if focus == linea.edit_season.getId(): - return self.setFocus(self.controls[x].edit_episode) - elif focus == linea.edit_episode.getId(): - return self.setFocus(self.controls[x].btn_delete_season) - elif focus == linea.btn_delete_season.getId(): - return self.setFocus(self.controls[x].edit_season) - - # Si el foco está en alguno de los 6 botones inferiores, movemos al siguiente - else: - if focus in [ID_BUTTON_ADD_SEASON, ID_BUTTON_INFO, ID_CHECK_UPDATE_INTERNET]: - if focus == ID_BUTTON_ADD_SEASON: - self.setFocusId(ID_BUTTON_INFO) - if focus == ID_BUTTON_INFO: - self.setFocusId(ID_BUTTON_ADD_SEASON) - # TODO cambiar cuando se habilite la opcion de actualizar por internet - # self.setFocusId(ID_CHECK_UPDATE_INTERNET) - if focus == ID_CHECK_UPDATE_INTERNET: - self.setFocusId(ID_BUTTON_OK) - - elif focus in [ID_BUTTON_OK, ID_BUTTON_CANCEL, ID_BUTTON_DELETE]: - if focus == ID_BUTTON_OK: - self.setFocusId(ID_BUTTON_CANCEL) - if focus == ID_BUTTON_CANCEL: - self.setFocusId(ID_BUTTON_DELETE) - if focus == ID_BUTTON_DELETE: - self.setFocusId(ID_BUTTON_OK) - - # Flecha arriba - elif action == xbmcgui.ACTION_MOVE_UP: - self.move_up(focus) - # Flecha abajo - elif action == xbmcgui.ACTION_MOVE_DOWN: - self.move_down(focus) - # scroll up - elif action == xbmcgui.ACTION_MOUSE_WHEEL_UP: - self.move_up(focus) - # scroll down - elif action == xbmcgui.ACTION_MOUSE_WHEEL_DOWN: - self.move_down(focus) - - # ACTION_PAGE_DOWN = 6 - # ACTION_PAGE_UP = 5 - - # Menú previo o Atrás - elif action in [xbmcgui.ACTION_PREVIOUS_MENU, xbmcgui.ACTION_NAV_BACK]: - self.close() - - def move_down(self, focus): - # logger.debug("focus " + str(focus)) - # Si el foco está en uno de los tres botones medios, bajamos el foco a la otra linea de botones - if focus in [ID_BUTTON_ADD_SEASON, ID_BUTTON_INFO, ID_CHECK_UPDATE_INTERNET]: - if focus == ID_BUTTON_ADD_SEASON: - self.setFocusId(ID_BUTTON_OK) - elif focus == ID_BUTTON_INFO: - self.setFocusId(ID_BUTTON_CANCEL) - elif focus == ID_CHECK_UPDATE_INTERNET: - self.setFocusId(ID_BUTTON_DELETE) - # Si el foco está en uno de los tres botones inferiores, subimos el foco al primer control del listado - elif focus in [ID_BUTTON_OK, ID_BUTTON_CANCEL, ID_BUTTON_DELETE]: - first_visible = 0 - for x, linea in enumerate(self.controls): - if linea.get_visible(): - first_visible = x - break - - if focus == ID_BUTTON_OK: - self.setFocus(self.controls[first_visible].edit_season) - elif focus == ID_BUTTON_CANCEL: - self.setFocus(self.controls[first_visible].edit_episode) - elif focus == ID_BUTTON_DELETE: - self.setFocus(self.controls[first_visible].btn_delete_season) - # nos movemos entre los elementos del listado - else: - # Localizamos en el listado de controles el control que tiene el focus - for x, linea in enumerate(self.controls): - if focus == linea.edit_season.getId(): - if x + 1 < len(self.controls): - if not self.controls[x + 1].get_visible(): - self.scroll(x, 1) - - return self.setFocus(self.controls[x + 1].edit_season) - else: - return self.setFocusId(ID_BUTTON_ADD_SEASON) - elif focus == linea.edit_episode.getId(): - if x + 1 < len(self.controls): - if not self.controls[x + 1].get_visible(): - self.scroll(x, 1) - - return self.setFocus(self.controls[x + 1].edit_episode) - else: - self.setFocusId(ID_BUTTON_INFO) - elif focus == linea.btn_delete_season.getId(): - if x + 1 < len(self.controls): - if not self.controls[x + 1].get_visible(): - self.scroll(x, 1) - - return self.setFocus(self.controls[x + 1].btn_delete_season) - else: - return self.setFocusId(ID_BUTTON_INFO) - # TODO cambiar cuando se habilite la opcion de actualizar por internet - # return self.setFocusId(ID_CHECK_UPDATE_INTERNET) - - def move_up(self, focus): - # Si el foco está en uno de los tres botones medios, subimos el foco al último control del listado - if focus in [ID_BUTTON_ADD_SEASON, ID_BUTTON_INFO, ID_CHECK_UPDATE_INTERNET]: - last_visible = 0 - for x, linea in reversed(list(enumerate(self.controls))): - if linea.get_visible(): - last_visible = x - break - - if focus == ID_BUTTON_ADD_SEASON: - self.setFocus(self.controls[last_visible].edit_season) - elif focus == ID_BUTTON_INFO: - self.setFocus(self.controls[last_visible].edit_episode) - elif focus == ID_CHECK_UPDATE_INTERNET: - self.setFocus(self.controls[last_visible].btn_delete_season) - # Si el foco está en uno de los tres botones inferiores, subimos el foco a la otra linea de botones - elif focus in [ID_BUTTON_OK, ID_BUTTON_CANCEL, ID_BUTTON_DELETE]: - if focus == ID_BUTTON_OK: - self.setFocusId(ID_BUTTON_ADD_SEASON) - elif focus == ID_BUTTON_CANCEL: - self.setFocusId(ID_BUTTON_INFO) - elif focus == ID_BUTTON_DELETE: - self.setFocusId(ID_BUTTON_INFO) - # TODO cambiar cuando se habilite la opcion de actualizar por internet - # self.setFocusId(ID_CHECK_UPDATE_INTERNET) - # nos movemos entre los elementos del listado - else: - # Localizamos en el listado de controles el control que tiene el focus - for x, linea in enumerate(self.controls): - if focus == linea.edit_season.getId(): - if x > 0: - if not self.controls[x - 1].get_visible(): - self.scroll(x, -1) - - return self.setFocus(self.controls[x - 1].edit_season) - else: - return self.setFocusId(ID_BUTTON_OK) - elif focus == linea.edit_episode.getId(): - if x > 0: - if not self.controls[x - 1].get_visible(): - self.scroll(x, -1) - - return self.setFocus(self.controls[x - 1].edit_episode) - else: - self.setFocusId(ID_BUTTON_CANCEL) - elif focus == linea.btn_delete_season.getId(): - if x > 0: - if not self.controls[x - 1].get_visible(): - self.scroll(x, -1) - - return self.setFocus(self.controls[x - 1].btn_delete_season) - else: - return self.setFocusId(ID_BUTTON_DELETE) - # TODO cambiar cuando se habilite la opcion de actualizar por internet - # return self.setFocusId(ID_CHECK_UPDATE_INTERNET) - - def scroll(self, position, movement): - try: - for index, group in enumerate(self.controls): - # ponemos todos los elementos como no visibles - group.set_visible(False) - - if movement > 0: - pos_fin = position + movement + 1 - pos_inicio = pos_fin - 5 - else: - pos_inicio = position + movement - pos_fin = pos_inicio + 5 - - # logger.debug("position {}, movement {}, pos_inicio{}, pos_fin{}, self.data.length{}". - # format(position, movement, pos_inicio, pos_fin, len(self.data))) - pos_y = self.controls_bg.getY() + 10 - for i in range(pos_inicio, pos_fin): - pos_x = self.controls_bg.getX() + 15 - - self.controls[i].label_season.setPosition(pos_x, pos_y + 3) - - pos_x += self.controls[i].label_season.getWidth() + 5 - self.controls[i].edit_season.setPosition(pos_x, pos_y - 2) - - pos_x += self.controls[i].edit_season.getWidth() + 60 - self.controls[i].label_episode.setPosition(pos_x, pos_y + 3) - - pos_x += self.controls[i].label_episode.getWidth() + 5 - self.controls[i].edit_episode.setPosition(pos_x, pos_y - 2) - - self.controls[i].btn_delete_season.setPosition( - self.controls_bg.getX() + self.controls_bg.getWidth() - - self.controls[i].btn_delete_season.getWidth() - 14, - pos_y) - - self.controls[i].hb.setPosition(self.controls_bg.getX() + 10, pos_y + 40) - - pos_y += 50 - - # logger.debug("ponemos como True %s" % i) - self.controls[i].set_visible(True) - - self.move_scroll() - - except Exception as Ex: - logger.error("HA HABIDO UNA HOSTIA %s" % Ex) - - def move_scroll(self): - visible_controls = [group for group in self.controls if group.get_visible() == True] - hidden_controls = [group for group in self.controls if group.get_visible() == False] - scroll_position = self.controls.index(visible_controls[0]) - scrollbar_height = self.scroll_bg.getHeight() - (len(hidden_controls) * 10) - scrollbar_y = self.scroll_bg.getPosition()[1] + (scroll_position * 10) - self.scroll2_bg.setPosition(self.scroll_bg.getPosition()[0], scrollbar_y) - self.scroll2_bg.setHeight(scrollbar_height) - - @staticmethod - def method_info(): - title = config.get_localized_string(60348) - # text = "La primera temporada que se añade siempre empieza en \"0\" episodios, la segunda temporada que se " - # text += "añade empieza en el número total de episodios de la primera temporada, la tercera temporada será " - # text += "la suma de los episodios de las temporadas previas y así sucesivamente.\n" - # text += "[COLOR blue]\nEjemplo de serie divida en varias temporadas:\n" - # text += "\nFairy Tail:\n" - # text += " - SEASON 1: EPISODE 48 --> [season 1, episode: 0]\n" - # text += " - SEASON 2: EPISODE 48 --> [season 2, episode: 48]\n" - # text += " - SEASON 3: EPISODE 54 --> [season 3, episode: 96 ([48=season2] + [48=season1])]\n" - # text += " - SEASON 4: EPISODE 175 --> [season 4: episode: 150 ([54=season3] + [48=season2] + [48=season3" \ - # "])][/COLOR]\n" - # text += "[COLOR green]\nEjemplo de serie que continua en la temporada de la original:\n" - # text += "\nFate/Zero 2nd Season:\n" - # text += " - SEASON 1: EPISODE 12 --> [season 1, episode: 13][/COLOR]\n" - - # text += "[COLOR blue]\nEjemplo de serie que es la segunda temporada de la original:\n" - # text += "\nFate/kaleid liner Prisma☆Illya 2wei!:\n" - # text += " - SEASON 1: EPISODE 12 --> [season 2, episode: 0][/COLOR]\n" - text = config.get_localized_string(70602) - - return TextBox("DialogTextViewer.xml", os.getcwd(), "Default", title=title, text=text) - - - class ControlGroup(object): - """ - conjunto de controles, son los elementos que se muestra por línea de una lista. - """ - - def __init__(self, label_season, edit_season, label_episode, edit_episode, btn_delete_season, hb): - self.visible = False - self.label_season = label_season - self.edit_season = edit_season - self.label_episode = label_episode - self.edit_episode = edit_episode - self.btn_delete_season = btn_delete_season - self.hb = hb - - def list_elements(self): - return [self.label_season, self.edit_season, self.label_episode, self.edit_episode, self.btn_delete_season, - self.hb] - - def get_visible(self): - return self.visible - - def set_visible(self, visible): - self.visible = visible - self.label_season.setVisible(visible) - self.edit_season.setVisible(visible) - self.label_episode.setVisible(visible) - self.edit_episode.setVisible(visible) - self.btn_delete_season.setVisible(visible) - self.hb.setVisible(visible) - - - class TextBox(xbmcgui.WindowXMLDialog): - """ Create a skinned textbox window """ - - def __init__(self, *args, **kwargs): - self.title = kwargs.get('title') - self.text = kwargs.get('text') - self.doModal() - - def onInit(self): - try: - self.getControl(5).setText(self.text) - self.getControl(1).setLabel(self.title) - except: - pass - - def onClick(self, control_id): - pass - - def onFocus(self, control_id): - pass - - def onAction(self, action): - self.close() - - # TODO mirar retro-compatiblidad - # class ControlEdit(xbmcgui.ControlButton): - # def __new__(self, *args, **kwargs): - # del kwargs["isPassword"] - # del kwargs["window"] - # args = list(args) - # return xbmcgui.ControlButton.__new__(self, *args, **kwargs) - # - # def __init__(self, *args, **kwargs): - # self.isPassword = kwargs["isPassword"] - # self.window = kwargs["window"] - # self.label = "" - # self.text = "" - # self.textControl = xbmcgui.ControlLabel(self.getX(), self.getY(), self.getWidth(), self.getHeight(), - # self.text, - # font=kwargs["font"], textColor=kwargs["textColor"], alignment=4 | 1) - # self.window.addControl(self.textControl) - # - # def setLabel(self, val): - # self.label = val - # xbmcgui.ControlButton.setLabel(self, val) - # - # def getX(self): - # return xbmcgui.ControlButton.getPosition(self)[0] - # - # def getY(self): - # return xbmcgui.ControlButton.getPosition(self)[1] - # - # def setEnabled(self, e): - # xbmcgui.ControlButton.setEnabled(self, e) - # self.textControl.setEnabled(e) - # - # def setWidth(self, w): - # xbmcgui.ControlButton.setWidth(self, w) - # self.textControl.setWidth(w / 2) - # - # def setHeight(self, w): - # xbmcgui.ControlButton.setHeight(self, w) - # self.textControl.setHeight(w) - # - # def setPosition(self, x, y): - # xbmcgui.ControlButton.setPosition(self, x, y) - # self.textControl.setPosition(x + self.getWidth() / 2, y) - # - # def setText(self, text): - # self.text = text - # if self.isPassword: - # self.textControl.setLabel("*" * len(self.text)) - # else: - # self.textControl.setLabel(self.text) - # - # def getText(self): - # return self.text - # - # - # if not hasattr(xbmcgui, "ControlEdit"): - # xbmcgui.ControlEdit = ControlEdit diff --git a/specials/setting.py b/specials/setting.py index 27826152..1a886e70 100644 --- a/specials/setting.py +++ b/specials/setting.py @@ -12,11 +12,9 @@ from builtins import range from past.utils import old_div from channelselector import get_thumb -from core import filetools -from core import servertools +from core import filetools, servertools from core.item import Item -from platformcode import config, logger -from platformcode import platformtools +from platformcode import config, logger, platformtools import xbmcgui CHANNELNAME = "setting" @@ -73,7 +71,7 @@ def menu_channels(item): itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60546) + ":", action="", folder=False, text_bold = True, thumbnail=get_thumb("setting_0.png"))) - # Inicio - Canales configurables + # Home - Configurable channels import channelselector from core import channeltools channel_list = channelselector.filterchannels("all") @@ -85,7 +83,7 @@ def menu_channels(item): itemlist.append(Item(channel=CHANNELNAME, title=". " + config.get_localized_string(60547) % channel.title, action="channel_config", config=channel.channel, folder=False, thumbnail=channel.thumbnail)) - # Fin - Canales configurables + # End - Configurable channels itemlist.append(Item(channel=CHANNELNAME, action="", title="", folder=False, thumbnail=get_thumb("setting_0.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60548) + ":", action="", folder=False, text_bold=True, thumbnail=get_thumb("channels.png"))) @@ -98,7 +96,7 @@ def channel_config(item): return platformtools.show_channel_settings(channelpath=filetools.join(config.get_runtime_path(), "channels", item.config)) -def autostart(item): # item necessario launcher.py linea 265 +def autostart(item): # item required launcher.py line 265 if config.enable_disable_autorun(AUTOSTART): logger.info('AUTOSTART ENABLED') # xbmcgui.Dialog().ok(config.get_localized_string(20000), config.get_localized_string(70709)) @@ -126,7 +124,7 @@ def autostart(item): # item necessario launcher.py linea 265 # torrent_options = [config.get_localized_string(30006), config.get_localized_string(70254), config.get_localized_string(70255)] # torrent_options.extend(platformtools.torrent_client_installed()) - + # list_controls = [ # { @@ -257,7 +255,7 @@ def menu_servers(item): itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60552), action="", folder=False, text_bold = True, thumbnail=get_thumb("setting_0.png"))) - # Inicio - Servidores configurables + # Home - Configurable servers server_list = list(servertools.get_debriders_list().keys()) for server in server_list: @@ -280,7 +278,7 @@ def menu_servers(item): Item(channel=CHANNELNAME, title=". " + config.get_localized_string(60553) % server_parameters["name"], action="server_config", config=server, folder=False, thumbnail="")) - # Fin - Servidores configurables + # End - Configurable servers return itemlist @@ -332,13 +330,13 @@ def cb_servers_blacklist(item, dict_values): config.set_setting('filter_servers', v) else: config.set_setting("black_list", v, server=k) - if v: # Si el servidor esta en la lista negra no puede estar en la de favoritos + if v: # If the server is blacklisted it cannot be in the favorites list config.set_setting("favorites_servers_list", 100, server=k) f = True progreso.update(old_div((i * 100), n), config.get_localized_string(60559) % k) i += 1 - if not f: # Si no hay ningun servidor en la lista, desactivarla + if not f: # If there is no server in the list, deactivate it config.set_setting('filter_servers', False) progreso.close() @@ -406,7 +404,7 @@ def cb_servers_favorites(server_names, dict_values): progreso.update(old_div((i * 100), n), config.get_localized_string(60559) % server_parameters['name']) i += 1 - if not dict_name: # Si no hay ningun servidor en lalista desactivarla + if not dict_name: # If there is no server in the list, deactivate it config.set_setting("favorites_servers", False) progreso.close() @@ -420,7 +418,7 @@ def submenu_tools(item): logger.info() itemlist = list() - # Herramientas personalizadas + # Custom tools import os channel_custom = os.path.join(config.get_runtime_path(), 'channels', 'custom.py') if not filetools.exists(channel_custom): @@ -483,9 +481,9 @@ def check_quickfixes(item): def conf_tools(item): logger.info() - # Activar o desactivar canales + # Enable or disable channels if item.extra == "channels_onoff": - if config.get_platform(True)['num_version'] >= 17.0: # A partir de Kodi 16 se puede usar multiselect, y de 17 con preselect + if config.get_platform(True)['num_version'] >= 17.0: # From Kodi 16 you can use multiselect, and from 17 with preselect return channels_onoff(item) import channelselector @@ -515,14 +513,14 @@ def conf_tools(item): config.get_localized_string(60593)]}) for channel in channel_list: - # Si el canal esta en la lista de exclusiones lo saltamos + # If the channel is on the exclusion list, we skip it if channel.channel not in excluded_channels: channel_parameters = channeltools.get_channel_parameters(channel.channel) status_control = "" status = config.get_setting("enabled", channel.channel) - # si status no existe es que NO HAY valor en _data.json + # if status does not exist, there is NO value in _data.json if status is None: status = channel_parameters["active"] logger.debug("%s | Status (XML): %s" % (channel.channel, status)) @@ -552,15 +550,14 @@ def conf_tools(item): callback="channel_status", custom_button={"visible": False}) - # Comprobacion de archivos channel_data.json + # Checking channel_data.json files elif item.extra == "lib_check_datajson": itemlist = [] import channelselector from core import channeltools channel_list = channelselector.filterchannels("allchannelstatus") - # Tener una lista de exclusion no tiene mucho sentido por que se comprueba si channel.json tiene "settings", - # pero por si acaso se deja + # Having an exclusion list doesn't make much sense because it checks if channel.json has "settings", but just in case it is left excluded_channels = ['url', 'setting', 'help'] @@ -573,9 +570,9 @@ def conf_tools(item): list_status = None default_settings = None - # Se comprueba si el canal esta en la lista de exclusiones + # It is checked if the channel is in the exclusion list if channel.channel not in excluded_channels: - # Se comprueba que tenga "settings", sino se salta + # It is checked that it has "settings", otherwise it skips list_controls, dict_settings = channeltools.get_channel_controls_settings(channel.channel) if not list_controls: @@ -586,23 +583,22 @@ def conf_tools(item): continue # logger.info(channel.channel + " SALTADO!") - # Se cargan los ajustes del archivo json del canal - file_settings = os.path.join(config.get_data_path(), "settings_channels", - channel.channel + "_data.json") + # The json file settings of the channel are loaded + file_settings = os.path.join(config.get_data_path(), "settings_channels", channel.channel + "_data.json") dict_settings = {} dict_file = {} if filetools.exists(file_settings): - # logger.info(channel.channel + " Tiene archivo _data.json") + # logger.info(channel.channel + " Has _data.json file") channeljson_exists = True - # Obtenemos configuracion guardada de ../settings/channel_data.json + # We get saved settings from ../settings/channel_data.json try: dict_file = jsontools.load(filetools.read(file_settings)) if isinstance(dict_file, dict) and 'settings' in dict_file: dict_settings = dict_file['settings'] except EnvironmentError: - logger.error("ERROR al leer el archivo: %s" % file_settings) + logger.error("ERROR when reading the file: %s" % file_settings) else: - # logger.info(channel.channel + " No tiene archivo _data.json") + # logger.info(channel.channel + " No _data.json file") channeljson_exists = False if channeljson_exists: @@ -614,12 +610,12 @@ def conf_tools(item): else: datajson_size = None - # Si el _data.json esta vacio o no existe... + # If the _data.json is empty or does not exist ... if (len(dict_settings) and datajson_size) == 0 or not channeljson_exists: - # Obtenemos controles del archivo ../channels/channel.json + # We get controls from the file ../channels/channel.json needsfix = True try: - # Se cargan los ajustes por defecto + # Default settings are loaded list_controls, default_settings = channeltools.get_channel_controls_settings( channel.channel) # logger.info(channel.title + " | Default: %s" % default_settings) @@ -628,26 +624,26 @@ def conf_tools(item): logger.error(channel.title + config.get_localized_string(60570) % traceback.format_exc()) # default_settings = {} - # Si _data.json necesita ser reparado o no existe... + # If _data.json needs to be repaired or doesn't exist ... if needsfix or not channeljson_exists: if default_settings is not None: - # Creamos el channel_data.json + # We create the channel_data.json default_settings.update(dict_settings) dict_settings = default_settings dict_file['settings'] = dict_settings - # Creamos el archivo ../settings/channel_data.json + # We create the file ../settings/channel_data.json if not filetools.write(file_settings, jsontools.dump(dict_file), silent=True): - logger.error("ERROR al salvar el archivo: %s" % file_settings) + logger.error("ERROR saving file: %s" % file_settings) list_status = config.get_localized_string(60560) else: if default_settings is None: list_status = config.get_localized_string(60571) else: - # logger.info(channel.channel + " - NO necesita correccion!") + # logger.info(channel.channel + " - NO correction needed!") needsfix = False - # Si se ha establecido el estado del canal se añade a la lista + # If the channel status has been set it is added to the list if needsfix is not None: if needsfix: if not channeljson_exists: @@ -657,8 +653,7 @@ def conf_tools(item): list_status = config.get_localized_string(60589) list_colour = "green" else: - # Si "needsfix" es "false" y "datjson_size" es None habra - # ocurrido algun error + # If "needsfix" is "false" and "datjson_size" is None, an error will have occurred if datajson_size is None: list_status = config.get_localized_string(60590) list_colour = "red" @@ -673,9 +668,9 @@ def conf_tools(item): thumbnail=channel.thumbnail, text_color=list_colour)) else: - logger.error("Algo va mal con el canal %s" % channel.channel) + logger.error("Something is wrong with the channel %s" % channel.channel) - # Si el canal esta en la lista de exclusiones lo saltamos + # If the channel is on the exclusion list, we skip it else: continue except: @@ -689,7 +684,7 @@ def channels_onoff(item): import channelselector, xbmcgui from core import channeltools - # Cargar lista de opciones + # Load list of options # ------------------------ lista = []; ids = [] channels_list = channelselector.filterchannels('allchannelstatus') @@ -704,11 +699,11 @@ def channels_onoff(item): lista.append(it) ids.append(channel.channel) - # Diálogo para pre-seleccionar + # Dialog to pre-select # ---------------------------- preselecciones = [config.get_localized_string(70517), config.get_localized_string(70518), config.get_localized_string(70519)] ret = platformtools.dialog_select(config.get_localized_string(60545), preselecciones) - if ret == -1: return False # pedido cancel + if ret == -1: return False # order cancel if ret == 2: preselect = [] elif ret == 1: preselect = list(range(len(ids))) else: @@ -719,13 +714,13 @@ def channels_onoff(item): if channel_status: preselect.append(i) - # Diálogo para seleccionar + # Dialog to select # ------------------------ ret = xbmcgui.Dialog().multiselect(config.get_localized_string(60545), lista, preselect=preselect, useDetails=True) - if ret == None: return False # pedido cancel + if ret == None: return False # order cancel seleccionados = [ids[i] for i in ret] - # Guardar cambios en canales activados + # Save changes to activated channels # ------------------------------------ for canal in ids: channel_status = config.get_setting('enabled', canal) @@ -744,7 +739,7 @@ def channel_status(item, dict_values): for k in dict_values: if k == "all_channels": - logger.info("Todos los canales | Estado seleccionado: %s" % dict_values[k]) + logger.info("All channels | Selected state: %s" % dict_values[k]) if dict_values[k] != 0: excluded_channels = ['url', 'search', 'videolibrary', 'setting', @@ -759,25 +754,25 @@ def channel_status(item, dict_values): new_status_all = None new_status_all_default = channel_parameters["active"] - # Opcion Activar todos + # Option Activate all if dict_values[k] == 1: new_status_all = True - # Opcion Desactivar todos + # Option Deactivate all if dict_values[k] == 2: new_status_all = False - # Opcion Recuperar estado por defecto + # Retrieve default status option if dict_values[k] == 3: - # Si tiene "enabled" en el _data.json es porque el estado no es el del channel.json + # If you have "enabled" in the _data.json, it is because the state is not that of the channel.json if config.get_setting("enabled", channel.channel): new_status_all = new_status_all_default - # Si el canal no tiene "enabled" en el _data.json no se guarda, se pasa al siguiente + # If the channel does not have "enabled" in the _data.json it is not saved, it goes to the next else: continue - # Se guarda el estado del canal + # Channel status is saved if new_status_all is not None: config.set_setting("enabled", new_status_all, channel.channel) break @@ -785,15 +780,15 @@ def channel_status(item, dict_values): continue else: - logger.info("Canal: %s | Estado: %s" % (k, dict_values[k])) + logger.info("Channel: %s | State: %s" % (k, dict_values[k])) config.set_setting("enabled", dict_values[k], k) - logger.info("el valor esta como %s " % config.get_setting("enabled", k)) + logger.info("the value is like %s " % config.get_setting("enabled", k)) platformtools.itemlist_update(Item(channel=CHANNELNAME, action="mainlist")) except: import traceback - logger.error("Detalle del error: %s" % traceback.format_exc()) + logger.error("Error detail: %s" % traceback.format_exc()) platformtools.dialog_notification(config.get_localized_string(60579), config.get_localized_string(60580)) @@ -823,15 +818,15 @@ def restore_tools(item): path = filetools.dirname(tvshow_file) if not serie.active: - # si la serie no esta activa descartar + # if the series is not active discard continue - # Eliminamos la carpeta con la serie ... + # We delete the folder with the series ... if tvshow_file.endswith('.strm') or tvshow_file.endswith('.json') or tvshow_file.endswith('.nfo'): os.remove(os.path.join(path, tvshow_file)) # filetools.rmdirtree(path) - # ... y la volvemos a añadir + # ... and we add it again service.update(path, p_dialog, i, t, serie, 3) p_dialog.close() @@ -855,7 +850,7 @@ def restore_tools(item): path = filetools.dirname(movie_json) movie = Item().fromjson(filetools.read(movie_json)) - # Eliminamos la carpeta con la pelicula ... + # We delete the folder with the movie ... filetools.rmdirtree(path) import math @@ -863,69 +858,69 @@ def restore_tools(item): p_dialog2.update(int(math.ceil((i + 1) * t)), heading, config.get_localized_string(60389) % (movie.contentTitle, movie.channel.capitalize())) - # ... y la volvemos a añadir + # ... and we add it again videolibrarytools.save_movie(movie) except Exception as ex: - logger.error("Error al crear de nuevo la película") + logger.error("Error creating movie again") template = "An exception of type %s occured. Arguments:\n%r" message = template % (type(ex).__name__, ex.args) logger.error(message) p_dialog2.close() - + def report_menu(item): logger.info('URL: ' + item.url) - + from channelselector import get_thumb - + thumb_debug = get_thumb("update.png") thumb_error = get_thumb("error.png") thumb_next = get_thumb("next.png") itemlist = [] paso = 1 - # Crea un menú de opciones para permitir al usuario reportar un fallo de Alfa a través de un servidor "pastebin" - # Para que el informe sea completo el usuario debe tener la opción de DEBUG=ON - # Los servidores "pastbin" gratuitos tienen limitación de capacidad, por lo que el tamaño del log es importante - # Al final de la operación de upload, se pasa al usuario la dirección de log en el servidor para que los reporte - + # Create a menu of options to allow the user to report an Alpha failure through a "pastebin" server + # For the report to be complete, the user must have the option DEBUG = ON + # Free pastbin servers have capacity limitations, so the size of the log is important + # At the end of the upload operation, the user is passed the log address on the server to report them + itemlist.append(Item(channel=item.channel, action="", title=config.get_localized_string(707418), thumbnail=thumb_next, folder=False)) - #if not config.get_setting('debug'): - itemlist.append(Item(channel=item.channel, action="activate_debug", extra=True, + # if not config.get_setting('debug'): + itemlist.append(Item(channel=item.channel, action="activate_debug", extra=True, title=config.get_localized_string(707419) % str(paso), thumbnail=thumb_debug, folder=False)) paso += 1 - itemlist.append(Item(channel="channelselector", action="getmainlist", + itemlist.append(Item(channel="channelselector", action="getmainlist", title=config.get_localized_string(707420) % str(paso), thumbnail=thumb_debug)) paso += 1 - itemlist.append(Item(channel=item.channel, action="report_send", + itemlist.append(Item(channel=item.channel, action="report_send", title=config.get_localized_string(707421) % str(paso), thumbnail=thumb_error, folder=False)) paso += 1 - #if config.get_setting('debug'): - itemlist.append(Item(channel=item.channel, action="activate_debug", extra=False, + # if config.get_setting('debug'): + itemlist.append(Item(channel=item.channel, action="activate_debug", extra=False, title=config.get_localized_string(707422) % str(paso), thumbnail=thumb_debug, folder=False)) paso += 1 - + if item.url: itemlist.append(Item(channel=item.channel, action="", title="", folder=False)) - - itemlist.append(Item(channel=item.channel, action="", + + itemlist.append(Item(channel=item.channel, action="", title=config.get_localized_string(707423), thumbnail=thumb_next, folder=False)) - + if item.one_use: action = '' url = '' else: action = 'call_browser' url = item.url - itemlist.append(Item(channel=item.channel, action=action, - title="**- LOG: [COLOR gold]%s[/COLOR] -**" % item.url, url=url, + itemlist.append(Item(channel=item.channel, action=action, + title="**- LOG: [COLOR gold]%s[/COLOR] -**" % item.url, url=url, thumbnail=thumb_next, unify=False, folder=False)) itemlist.append(Item(channel=item.channel, action="call_browser", @@ -935,24 +930,24 @@ def report_menu(item): itemlist.append(Item(channel=item.channel, action="call_browser", url='https://t.me/kodiondemand', title="Su telegram", thumbnail=thumb_next, unify=False, folder=False)) - + if item.one_use: - itemlist.append(Item(channel=item.channel, action="", - title="[COLOR orange]NO ACCEDA al INFORME: se BORRARÁ[/COLOR]", + itemlist.append(Item(channel=item.channel, action="", + title="[COLOR orange]NO ACCEDA al INFORME: se BORRARÁ[/COLOR]", thumbnail=thumb_next, folder=False)) - itemlist.append(Item(channel=item.channel, action="", - title="[COLOR orange]ya que es de un solo uso[/COLOR]", + itemlist.append(Item(channel=item.channel, action="", + title="[COLOR orange]ya que es de un solo uso[/COLOR]", thumbnail=thumb_next, folder=False)) - + return itemlist - - + + def activate_debug(item): logger.info(item.extra) from platformcode import platformtools - - # Activa/Desactiva la opción de DEBUB en settings.xml - + + #Enable / disable DEBUB option in settings.xml + if isinstance(item.extra, str): return report_menu(item) if item.extra: @@ -961,20 +956,20 @@ def activate_debug(item): else: config.set_setting('debug', False) platformtools.dialog_notification(config.get_localized_string(707430), config.get_localized_string(707432)) - - + + def report_send(item, description='', fatal=False): import xbmc import random import traceback if PY3: - #from future import standard_library - #standard_library.install_aliases() - import urllib.parse as urlparse # Es muy lento en PY2. En PY3 es nativo + # from future import standard_library + # standard_library.install_aliases() + import urllib.parse as urlparse # It is very slow in PY2. In PY3 it is native import urllib.parse as urllib else: - import urllib # Usamos el nativo de PY2 que es más rápido + import urllib # We use the native of PY2 which is faster import urlparse try: @@ -983,149 +978,149 @@ def report_send(item, description='', fatal=False): except: requests_status = False logger.error(traceback.format_exc()) - + from core import jsontools, httptools, scrapertools from platformcode import envtal - - # Esta función realiza la operación de upload del LOG. El tamaño del archivo es de gran importacia porque - # los servicios de "pastebin" gratuitos tienen limitaciones, a veces muy bajas. - # Hay un ervicio, File.io, que permite subida directa de "achivos binarios" a través de la función "request" - # Esto aumenta dráticamente la capacidad del envío del log, muy por encima de lo necesitado - # Por ello es necesario contar con una lista de servicios "pastebin" que puedan realizar la operación de upload, - # ya sea por capacidad disponible o por disponibilidad. - # Para poder usar los servidores "pastebin" con un código común, se ha creado un diccionario con los servidores - # y sus características. En cada entrada se recogen las peculiaridades de cada servidor, tanto para formar - # la petición consu POST como para la forma de recibir el código del upload en la respuesta (json, header, regex - # en datos,...). - # Al iniciar este método se aleatoriza la lista de servidores "pastebin" para evitar que todos los usuarios hagan - # uploads contra el mismo servidor y puedan ocasionar sobrecargas. - # Se lee el arcivo de log y se compara su tamaño con la capacidad del servidor (parámetro 10 de cada entrada - # (empezando desde 0), expresado en MB, hasta que se encuentra uno capacitado. Si el upload falla se sigue intentado - # con los siguientes servidores que tengan la capacidad requerida. - # Si no se encuentra ningun servidor disponible se pide al usuario que lo intente más tarde, o que suba el log - # directamente en el foro. Si es un problema de tamaño, se le pide que reicinie Kodi y reporducza el fallo, para - # que el LOG sea más pequeño. - - + + # This function performs the LOG upload operation. The file size is of great importance because + # Free pastebin services have limitations, sometimes very low. + # There is an ervice, File.io, that allows direct upload of "binary files" through the "request" function + # This dramatically increases the ability to send the log, well above what is needed. + # Therefore it is necessary to have a list of "pastebin" services that can perform the upload operation, + # either by available capacity or by availability. + # In order to use the "pastebin" servers with a common code, a dictionary has been created with the servers + # and their characteristics. In each entry the peculiarities of each server are collected, both to form + # the request with POST as for the way to receive the upload code in the response (json, header, regex + # in data, ...). + # Starting this method randomizes the list of "pastebin" servers to prevent all users from doing + # uploads against the same server and may cause overloads. + # The log file is read and its size is compared with the server capacity (parameter 10 of each entry + # (starting from 0), expressed in MB, until a qualified one is found. If the upload fails, it continues trying + # with the following servers that have the required capacity. + # If no available server is found, the user is asked to try again later, or to upload the log. + # directly on the forum. If it is a size problem, you are asked to reset Kodi and redo the fault, to + # that the LOG is smaller. + + pastebin_list = { - 'hastebin': ('1', 'https://hastebin.com/', 'documents', 'random', '', '', - 'data', 'json', 'key', '', '0.29', '10', True, 'raw/', '', ''), - 'dpaste': ('1', 'http://dpaste.com/', 'api/v2/', 'random', 'content=', - '&syntax=text&title=%s&poster=alfa&expiry_days=7', + 'hastebin': ('1', 'https://hastebin.com/', 'documents', 'random', '', '', + 'data', 'json', 'key', '', '0.29', '10', True, 'raw/', '', ''), + 'dpaste': ('1', 'http://dpaste.com/', 'api/v2/', 'random', 'content=', + '&syntax=text&title=%s&poster=alfa&expiry_days=7', 'headers', '', '', 'location', '0.23', '15', True, '', '.txt', ''), - 'ghostbin': ('1', 'https://ghostbin.com/', 'paste/new', 'random', 'lang=text&text=', - '&expire=2d&password=&title=%s', - 'data', 'regex', '(.*?)\s*-\s*Ghostbin<\/title>', '', + 'ghostbin': ('1', 'https://ghostbin.com/', 'paste/new', 'random', 'lang=text&text=', + '&expire=2d&password=&title=%s', + 'data', 'regex', '<title>(.*?)\s*-\s*Ghostbin<\/title>', '', '0.49', '15', False, 'paste/', '', ''), - 'write.as': ('1', 'https://write.as/', 'api/posts', 'random', 'body=', '&title=%s', + 'write.as': ('1', 'https://write.as/', 'api/posts', 'random', 'body=', '&title=%s', 'data', 'json', 'data', 'id', '0.018', '15', True, '', '', ''), - 'oneclickpaste': ('1', 'http://oneclickpaste.com/', 'index.php', 'random', 'paste_data=', - '&title=%s&format=text&paste_expire_date=1W&visibility=0&pass=&submit=Submit', - 'data', 'regex', '<a class="btn btn-primary" href="[^"]+\/(\d+\/)">\s*View\s*Paste\s*<\/a>', + 'oneclickpaste': ('1', 'http://oneclickpaste.com/', 'index.php', 'random', 'paste_data=', + '&title=%s&format=text&paste_expire_date=1W&visibility=0&pass=&submit=Submit', + 'data', 'regex', '<a class="btn btn-primary" href="[^"]+\/(\d+\/)">\s*View\s*Paste\s*<\/a>', '', '0.060', '5', True, '', '', ''), - 'bpaste': ('1', 'https://bpaste.net/', '', 'random', 'code=', '&lexer=text&expiry=1week', - 'data', 'regex', 'View\s*<a\s*href="[^*]+/(.*?)">raw<\/a>', '', + 'bpaste': ('1', 'https://bpaste.net/', '', 'random', 'code=', '&lexer=text&expiry=1week', + 'data', 'regex', 'View\s*<a\s*href="[^*]+/(.*?)">raw<\/a>', '', '0.79', '15', True, 'raw/', '', ''), - 'dumpz': ('0', 'http://dumpz.org/', 'api/dump', 'random', 'code=', '&lexer=text&comment=%s&password=', + 'dumpz': ('0', 'http://dumpz.org/', 'api/dump', 'random', 'code=', '&lexer=text&comment=%s&password=', 'headers', '', '', 'location', '0.99', '15', False, '', '', ''), - 'file.io': ('1', 'https://file.io/', '', 'random', '', 'expires=1w', - 'requests', 'json', 'key', '', '99.0', '30', False, '', '.log', ''), - 'uploadfiles': ('1', 'https://up.uploadfiles.io/upload', '', 'random', '', '', - 'requests', 'json', 'url', '', '99.0', '30', False, None, '', '') + 'file.io': ('1', 'https://file.io/', '', 'random', '', 'expires=1w', + 'requests', 'json', 'key', '', '99.0', '30', False, '', '.log', ''), + 'uploadfiles': ('1', 'https://up.uploadfiles.io/upload', '', 'random', '', '', + 'requests', 'json', 'url', '', '99.0', '30', False, None, '', '') } - pastebin_list_last = ['hastebin', 'ghostbin', 'file.io'] # Estos servicios los dejamos los últimos - pastebin_one_use = ['file.io'] # Servidores de un solo uso y se borra + pastebin_list_last = ['hastebin', 'ghostbin', 'file.io'] # We leave these services the last + pastebin_one_use = ['file.io'] # Single-use servers and deletes pastebin_dir = [] paste_file = {} paste_params = () paste_post = '' status = False msg = config.get_localized_string(707424) - - # Se verifica que el DEBUG=ON, si no está se rechaza y se pide al usuario que lo active y reproduzca el fallo + + # DEBUG = ON is verified, if it is not it is rejected and the user is asked to activate it and reproduce the fault if not config.get_setting('debug'): platformtools.dialog_notification(config.get_localized_string(707425), config.get_localized_string(707426)) return report_menu(item) - - # De cada al futuro se permitira al usuario que introduzca una breve descripción del fallo que se añadirá al LOG + + # From each to the future the user will be allowed to enter a brief description of the fault that will be added to the LOG if description == 'OK': description = platformtools.dialog_input('', 'Introduzca una breve descripción del fallo') - # Escribimos en el log algunas variables de Kodi y Alfa que nos ayudarán en el diagnóstico del fallo + # We write in the log some Kodi and Alpha variables that will help us diagnose the failure environment = envtal.list_env() if not environment['log_path']: environment['log_path'] = str(filetools.join(xbmc.translatePath("special://logpath/"), 'kodi.log')) environment['log_size_bytes'] = str(filetools.getsize(environment['log_path'])) environment['log_size'] = str(round(float(environment['log_size_bytes']) / (1024*1024), 3)) - - # Se lee el archivo de LOG + + # LOG file is read log_path = environment['log_path'] if filetools.exists(log_path): - log_size_bytes = int(environment['log_size_bytes']) # Tamaño del archivivo en Bytes - log_size = float(environment['log_size']) # Tamaño del archivivo en MB - log_data = filetools.read(log_path) # Datos del archivo - if not log_data: # Algún error? + log_size_bytes = int(environment['log_size_bytes']) # File size in Bytes + log_size = float(environment['log_size']) # File size in MB + log_data = filetools.read(log_path) # File data + if not log_data: # Some mistake? platformtools.dialog_notification(config.get_localized_string(707427), '', 2) return report_menu(item) - else: # Log no existe o path erroneo? + else: # Log no existe or erroneous path? platformtools.dialog_notification(config.get_localized_string(707427), '', 2) return report_menu(item) - # Si se ha introducido la descripción del fallo, se inserta la principio de los datos del LOG - # log_title = '***** DESCRIPCIÓN DEL FALLO *****' + # If the fault description has been entered, the beginning of the LOG data is inserted + # log_title = '***** FAULT DESCRIPTION *****' # if description: # log_data = '%s\n%s\n\n%s' %(log_title, description, log_data) - - # Se aleatorizan los nombre de los servidores "patebin" + + # Server names "patebin" are scrambled for label_a, value_a in list(pastebin_list.items()): if label_a not in pastebin_list_last: pastebin_dir.append(label_a) random.shuffle(pastebin_dir) - pastebin_dir.extend(pastebin_list_last) # Estos servicios los dejamos los últimos - - #pastebin_dir = ['uploadfiles'] # Para pruebas de un servicio - #log_data = 'TEST PARA PRUEBAS DEL SERVICIO' - - # Se recorre la lista de servidores "pastebin" hasta localizar uno activo, con capacidad y disponibilidad + pastebin_dir.extend(pastebin_list_last) # We leave these services the last + + #pastebin_dir = ['uploadfiles'] # For testing a service + #log_data = 'TEST FOR SERVICE TESTS' + + # The list of "pastebin" servers is scrolled to locate an active one, with capacity and availability for paste_name in pastebin_dir: - if pastebin_list[paste_name][0] != '1': # Si no esta activo el servidore, pasamos + if pastebin_list[paste_name][0] != '1': # If the server is not active, we pass continue - if pastebin_list[paste_name][6] == 'requests' and not requests_status: # Si "requests" no esta activo, pasamos + if pastebin_list[paste_name][6] == 'requests' and not requests_status: # If "requests" is not active, we pass continue - paste_host = pastebin_list[paste_name][1] # URL del servidor "pastebin" - paste_sufix = pastebin_list[paste_name][2] # sufijo del API para el POST + paste_host = pastebin_list[paste_name][1] # Server URL "pastebin" + paste_sufix = pastebin_list[paste_name][2] # API suffix for POST paste_title = '' if pastebin_list[paste_name][3] == 'random': - paste_title = "LOG" + str(random.randrange(1, 999999999)) # Título del LOG - paste_post1 = pastebin_list[paste_name][4] # Parte inicial del POST - paste_post2 = pastebin_list[paste_name][5] # Parte secundaria del POST - paste_type = pastebin_list[paste_name][6] # Tipo de downloadpage: DATA o HEADERS - paste_resp = pastebin_list[paste_name][7] # Tipo de respuesta: JSON o datos con REGEX - paste_resp_key = pastebin_list[paste_name][8] # Si es JSON, etiqueta `primaria con la CLAVE - paste_url = pastebin_list[paste_name][9] # Etiqueta primaria para HEADER y sec. para JSON - paste_file_size = float(pastebin_list[paste_name][10]) # Capacidad en MB del servidor - if paste_file_size > 0: # Si es 0, la capacidad es ilimitada - if log_size > paste_file_size: # Verificación de capacidad y tamaño - msg = 'Archivo de log demasiado grande. Reinicie Kodi y reinténtelo' + paste_title = "LOG" + str(random.randrange(1, 999999999)) # LOG title + paste_post1 = pastebin_list[paste_name][4] # Initial part of the POST + paste_post2 = pastebin_list[paste_name][5] # Secondary part of POST + paste_type = pastebin_list[paste_name][6] # Type of downloadpage: DATE HEADERS + paste_resp = pastebin_list[paste_name][7] # Response type: JSON or data with REGEX + paste_resp_key = pastebin_list[paste_name][8] # If JSON, label `primary with KEY + paste_url = pastebin_list[paste_name][9] # Primary label for HEADER and sec. for JSON + paste_file_size = float(pastebin_list[paste_name][10]) # Server capacity in MB + if paste_file_size > 0: # If it is 0, the capacity is unlimited + if log_size > paste_file_size: # Capacity and size verification + msg = 'Log file too large. Restart Kodi and retry' continue - paste_timeout = int(pastebin_list[paste_name][11]) # Timeout para el servidor - paste_random_headers = pastebin_list[paste_name][12] # Utiliza RAMDOM headers para despistar el serv.? - paste_host_return = pastebin_list[paste_name][13] # Parte de url para componer la clave para usuario - paste_host_return_tail = pastebin_list[paste_name][14] # Sufijo de url para componer la clave para usuario + paste_timeout = int(pastebin_list[paste_name][11]) # Timeout for the server + paste_random_headers = pastebin_list[paste_name][12] # Do you use RAMDOM headers to mislead the serv? + paste_host_return = pastebin_list[paste_name][13] # Part of url to compose the key for user + paste_host_return_tail = pastebin_list[paste_name][14] # Url suffix to compose user key paste_headers = {} - if pastebin_list[paste_name][15]: # Headers requeridas por el servidor + if pastebin_list[paste_name][15]: # Headers required by the server paste_headers.update(jsontools.load((pastebin_list[paste_name][15]))) if paste_name in pastebin_one_use: - pastebin_one_use_msg = '[COLOR red]NO ACCEDA al INFORME: se BORRARÁ[/COLOR]' + pastebin_one_use_msg = 'DO NOT ACCESS THE REPORT: it will be DELETED' item.one_use = True else: pastebin_one_use_msg = '' - + try: - # Se crea el POST con las opciones del servidor "pastebin" - # Se trata el formato de "requests" + # POST is created with server options "pastebin" + # This is the "requests" format if paste_type == 'requests': paste_file = {'file': (paste_title+'.log', log_data)} if paste_post1: @@ -1135,14 +1130,14 @@ def report_send(item, description='', fatal=False): paste_params = paste_post2 % (paste_title+'.log', log_size_bytes) else: paste_params = paste_post2 - - #Se trata el formato de downloads + + # This is the download format else: - #log_data = 'Test de Servidor para ver su viabilidad (áéíóúñ¿?)' - if paste_name in ['hastebin']: # Hay algunos servicios que no necesitan "quote" + # log_data = 'Server Test to see its viability (áéíóúñ¿?)' + if paste_name in ['hastebin']: # There are some services that do not need "quote" paste_post = log_data else: - paste_post = urllib.quote_plus(log_data) # Se hace un "quote" de los datos del LOG + paste_post = urllib.quote_plus(log_data) # A "quote" is made from the LOG data if paste_post1: paste_post = '%s%s' % (paste_post1, paste_post) if paste_post2: @@ -1151,104 +1146,101 @@ def report_send(item, description='', fatal=False): else: paste_post += paste_post2 - # Se hace la petición en downloadpage con HEADERS o DATA, con los parámetros del servidor + # Request is made on downloadpage with HEADERS or DATA, with server parameters if paste_type == 'headers': - data = httptools.downloadpage(paste_host+paste_sufix, post=paste_post, - timeout=paste_timeout, random_headers=paste_random_headers, + data = httptools.downloadpage(paste_host+paste_sufix, post=paste_post, + timeout=paste_timeout, random_headers=paste_random_headers, headers=paste_headers).headers elif paste_type == 'data': - data = httptools.downloadpage(paste_host+paste_sufix, post=paste_post, - timeout=paste_timeout, random_headers=paste_random_headers, + data = httptools.downloadpage(paste_host+paste_sufix, post=paste_post, + timeout=paste_timeout, random_headers=paste_random_headers, headers=paste_headers).data - - # Si la petición es con formato REQUESTS, se realiza aquí + + # If the request is in REQUESTS format, it is made here elif paste_type == 'requests': - #data = requests.post(paste_host, params=paste_params, files=paste_file, + #data = requests.post(paste_host, params=paste_params, files=paste_file, # timeout=paste_timeout) - data = httptools.downloadpage(paste_host, params=paste_params, file=log_data, - file_name=paste_title+'.log', timeout=paste_timeout, + data = httptools.downloadpage(paste_host, params=paste_params, file=log_data, + file_name=paste_title+'.log', timeout=paste_timeout, random_headers=paste_random_headers, headers=paste_headers) except: msg = 'Inténtelo más tarde' - logger.error('Fallo al guardar el informe. ' + msg) + logger.error('Failed to save report. ' + msg) logger.error(traceback.format_exc()) continue - # Se analiza la respuesta del servidor y se localiza la clave del upload para formar la url a pasar al usuario + # The server response is analyzed and the upload key is located to form the url to pass to the user if data: paste_host_resp = paste_host - if paste_host_return == None: # Si devuelve la url completa, no se compone + if paste_host_return == None: # If you return the full url, it is not composed paste_host_resp = '' paste_host_return = '' - - # Respuestas a peticiones REQUESTS - if paste_type == 'requests': # Respuesta de petición tipo "requests"? - if paste_resp == 'json': # Respuesta en formato JSON? + + # Responses to REQUESTS requests + if paste_type == 'requests': # Response of request type "requests"? + if paste_resp == 'json': # Answer in JSON format? if paste_resp_key in data.data: if not paste_url: - key = jsontools.load(data.data)[paste_resp_key] # con una etiqueta + key = jsontools.load(data.data)[paste_resp_key] # with a label else: - key = jsontools.load(data.data)[paste_resp_key][paste_url] # con dos etiquetas anidadas - item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, + key = jsontools.load(data.data)[paste_resp_key][paste_url] # with two nested tags + item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, paste_host_return_tail) else: - logger.error('ERROR en formato de retorno de datos. data.data=' + - str(data.data)) + logger.error('ERROR in data return format. data.data=' + str(data.data)) continue - - # Respuestas a peticiones DOWNLOADPAGE - elif paste_resp == 'json': # Respuesta en formato JSON? + + # Responses to DOWNLOADPAGE requests + elif paste_resp == 'json': # Answer in JSON format? if paste_resp_key in data: if not paste_url: - key = jsontools.load(data)[paste_resp_key] # con una etiqueta + key = jsontools.load(data)[paste_resp_key] # with a label else: - key = jsontools.load(data)[paste_resp_key][paste_url] # con dos etiquetas anidadas - item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, + key = jsontools.load(data)[paste_resp_key][paste_url] # con two nested tags + item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, paste_host_return_tail) else: - logger.error('ERROR en formato de retorno de datos. data=' + str(data)) + logger.error('ERROR in data return format. data=' + str(data)) continue - elif paste_resp == 'regex': # Respuesta en DATOS, a buscar con un REGEX? + elif paste_resp == 'regex': # Answer in DATA, to search with a REGEX? key = scrapertools.find_single_match(data, paste_resp_key) if key: - item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, + item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, paste_host_return_tail) else: - logger.error('ERROR en formato de retorno de datos. data=' + str(data)) + logger.error('ERROR in data return format. data=' + str(data)) continue - elif paste_type == 'headers': # Respuesta en HEADERS, a buscar en "location"? + elif paste_type == 'headers': # Answer in HEADERS, to search in "location"? if paste_url in data: - item.url = data[paste_url] # Etiqueta de retorno de la clave - item.url = urlparse.urljoin(paste_host_resp + paste_host_return, + item.url = data[paste_url] # Key return label + item.url = urlparse.urljoin(paste_host_resp + paste_host_return, item.url + paste_host_return_tail) else: - logger.error('ERROR en formato de retorno de datos. response.headers=' + - str(data)) + logger.error('ERROR in data return format. response.headers=' + str(data)) continue else: - logger.error('ERROR en formato de retorno de datos. paste_type=' + - str(paste_type) + ' / DATA: ' + data) + logger.error('ERROR in data return format. paste_type=' + str(paste_type) + ' / DATA: ' + data) continue - status = True # Operación de upload terminada con éxito - logger.info('Report created: ' + str(item.url)) #Se guarda la URL del informe a usuario - # if fatal: # De uso futuro, para logger.crash - # platformtools.dialog_ok('Informe de ERROR en Alfa CREADO', 'Repórtelo en el foro agregando ERROR FATAL y esta URL: ', '[COLOR gold]%s[/COLOR]' % item.url, pastebin_one_use_msg) - # else: # Se pasa la URL del informe a usuario - # platformtools.dialog_ok('Informe de Fallo en Alfa CREADO', 'Repórtelo en el foro agregando una descripcion del fallo y esta URL: ', '[COLOR gold]%s[/COLOR]' % item.url, pastebin_one_use_msg) + status = True # Upload operation completed successfully + logger.info('Report created: ' + str(item.url)) # The URL of the user report is saved + # if fatal: # For future use, for logger.crash + # platformtools.dialog_ok('KoD CREATED ERROR report', 'Report it in the forum by adding FATAL ERROR and this URL: ', '[COLOR gold]%s[/COLOR]' % item.url, pastebin_one_use_msg) + # else: # Report URL passed to user + # platformtools.dialog_ok('KoD Crash Report CREATED', 'Report it on the forum by adding a bug description and this URL: ', '[COLOR gold]%s[/COLOR]' % item.url, pastebin_one_use_msg) - break # Operación terminado, no seguimos buscando - - if not status and not fatal: # Operación fracasada... - platformtools.dialog_notification(config.get_localized_string(707428), msg) #... se notifica la causa + break # Operation finished, we don't keep looking + + if not status and not fatal: # Operation failed ... + platformtools.dialog_notification(config.get_localized_string(707428), msg) #... cause is reported logger.error(config.get_localized_string(707428) + msg) - - # Se devuelve control con item.url actualizado, así aparecerá en el menú la URL del informe + + # Control is returned with updated item.url, so the report URL will appear in the menu item.action = 'report_menu' platformtools.itemlist_update(item, True) # return report_menu(item) - - + + def call_browser(item): import webbrowser if not webbrowser.open(item.url): diff --git a/specials/side_menu.py b/specials/side_menu.py index b0da3e4d..1ede1c95 100644 --- a/specials/side_menu.py +++ b/specials/side_menu.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------ -#from builtins import str +# from builtins import str import sys PY3 = False if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int @@ -9,8 +9,7 @@ if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int import os from core.item import Item from core import jsontools -from platformcode import config, logger -from platformcode import launcher +from platformcode import config, logger, launcher import xbmc, xbmcgui, xbmcplugin, xbmcaddon media_path = os.path.join(config.get_runtime_path(), "resources/skins/Default/media/side_menu/") @@ -113,7 +112,7 @@ class Main(xbmcgui.WindowXMLDialog): self.items = [] def onInit(self): - #### Compatibilidad con Kodi 18 #### + #### Kodi 18 compatibility #### if config.get_platform(True)['num_version'] < 18: self.setCoordinateResolution(2) diff --git a/specials/trailertools.py b/specials/trailertools.py index 7db64695..a240cea3 100644 --- a/specials/trailertools.py +++ b/specials/trailertools.py @@ -16,10 +16,10 @@ from past.utils import old_div if PY3: #from future import standard_library #standard_library.install_aliases() - import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo + import urllib.parse as urllib # It is very slow in PY2. In PY3 it is native import urllib.parse as urlparse else: - import urllib # Usamos el nativo de PY2 que es más rápido + import urllib # We use the native of PY2 which is faster import urlparse import re @@ -37,7 +37,7 @@ def_lang = info_language[config.get_setting("info_language", "videolibrary")] result = None window_select = [] -# Para habilitar o no la opción de búsqueda manual +# To enable or disable the manual search option if config.get_platform() != "plex": keyboard = True else: @@ -47,14 +47,14 @@ else: def buscartrailer(item, trailers=[]): logger.info() - # Lista de acciones si se ejecuta desde el menú contextual + # List of actions if run from context menu if item.action == "manual_search" and item.contextual: itemlist = manual_search(item) item.contentTitle = itemlist[0].contentTitle elif 'search' in item.action and item.contextual: itemlist = globals()[item.action](item) else: - # Se elimina la opción de Buscar Trailer del menú contextual para evitar redundancias + # Remove Trailer Search option from context menu to avoid redundancies if isinstance(item.context, str) and "buscar_trailer" in item.context: item.context = item.context.replace("buscar_trailer", "") elif isinstance(item.context, list) and "buscar_trailer" in item.context: @@ -80,8 +80,8 @@ def buscartrailer(item, trailers=[]): item.year = item.infoLabels['year'] - logger.info("Búsqueda: %s" % item.contentTitle) - logger.info("Año: %s" % item.year) + logger.info("Search: %s" % item.contentTitle) + logger.info("Year: %s" % item.year) if item.infoLabels['trailer'] and not trailers: url = item.infoLabels['trailer'] if "youtube" in url: @@ -98,8 +98,7 @@ def buscartrailer(item, trailers=[]): itemlist.extend(tmdb_trailers(item, tipo)) else: for trailer in trailers: - title = trailer['name'] + " [" + trailer['size'] + "p] (" + trailer['language'].replace("en", "ING") \ - .replace("it", "ITA") + ") [tmdb/youtube]" + title = trailer['name'] + " [" + trailer['size'] + "p] (" + trailer['language'].replace("en", "ING").replace("it", "ITA") + ") [tmdb/youtube]" itemlist.append(item.clone(action="play", title=title, url=trailer['url'], server="youtube")) except: import traceback @@ -111,7 +110,7 @@ def buscartrailer(item, trailers=[]): title = "%s" itemlist.append(item.clone(title=title % config.get_localized_string(70507), action="youtube_search")) itemlist.append(item.clone(title=title % config.get_localized_string(70024), action="filmaffinity_search")) - # Si se trata de una serie, no se incluye la opción de buscar en Abandomoviez + # If it is a series, the option to search in Abandomoviez is not included if not item.show and not item.infoLabels['tvshowtitle']: itemlist.append(item.clone(title=title % config.get_localized_string(70508), action="abandomoviez_search")) @@ -152,8 +151,7 @@ def tmdb_trailers(item, tipo="movie"): if tmdb_search: for result in tmdb_search.get_videos(): - title = result['name'] + " [" + result['size'] + "p] (" + result['language'].replace("en", "ING") \ - .replace("it", "ITA") + ") [tmdb/youtube]" + title = result['name'] + " [" + result['size'] + "p] (" + result['language'].replace("en", "ING").replace("it", "ITA") + ") [tmdb/youtube]" itemlist.append(item.clone(action="play", title=title, url=result['url'], server="youtube")) return itemlist @@ -165,7 +163,7 @@ def youtube_search(item): titulo = item.contentTitle if item.extra != "youtube": titulo += " trailer" - # Comprueba si es una búsqueda de cero o viene de la opción Siguiente + # Check if it is a zero search or comes from the Next option if item.page != "": data = httptools.downloadpage(item.page).data else: @@ -183,8 +181,7 @@ def youtube_search(item): if item.contextual: scrapedtitle = "%s" % scrapedtitle url = urlparse.urljoin('https://www.youtube.com/', scrapedurl) - itemlist.append(item.clone(title=scrapedtitle, action="play", server="youtube", url=url, - thumbnail=scrapedthumbnail)) + itemlist.append(item.clone(title=scrapedtitle, action="play", server="youtube", url=url, thumbnail=scrapedthumbnail)) next_page = scrapertools.find_single_match(data, '<a href="([^"]+)"[^>]+><span class="yt-uix-button-content">' 'Siguiente') if next_page != "": @@ -207,7 +204,7 @@ def youtube_search(item): def abandomoviez_search(item): logger.info() - # Comprueba si es una búsqueda de cero o viene de la opción Siguiente + # Check if it is a zero search or comes from the Next option if item.page != "": data = httptools.downloadpage(item.page).data else: @@ -226,7 +223,7 @@ def abandomoviez_search(item): patron = '(?:<td width="85"|<div class="col-md-2 col-sm-2 col-xs-3">).*?<img src="([^"]+)"' \ '.*?href="([^"]+)">(.*?)(?:<\/td>|<\/small>)' matches = scrapertools.find_multiple_matches(data, patron) - # Si solo hay un resultado busca directamente los trailers, sino lista todos los resultados + # If there is only one result, search directly for the trailers, but list all the results if len(matches) == 1: item.url = urlparse.urljoin("http://www.abandomoviez.net/%s" % item.prefix, matches[0][1]) item.thumbnail = matches[0][0] @@ -235,26 +232,22 @@ def abandomoviez_search(item): for scrapedthumbnail, scrapedurl, scrapedtitle in matches: scrapedurl = urlparse.urljoin("http://www.abandomoviez.net/%s" % item.prefix, scrapedurl) scrapedtitle = scrapertools.htmlclean(scrapedtitle) - itemlist.append(item.clone(title=scrapedtitle, action="search_links_abando", - url=scrapedurl, thumbnail=scrapedthumbnail)) + itemlist.append(item.clone(title=scrapedtitle, action="search_links_abando", url=scrapedurl, thumbnail=scrapedthumbnail)) next_page = scrapertools.find_single_match(data, '<a href="([^"]+)">Siguiente') if next_page != "": next_page = urlparse.urljoin("http://www.abandomoviez.net/%s" % item.prefix, next_page) - itemlist.append(item.clone(title=config.get_localized_string(70502), action="abandomoviez_search", page=next_page, thumbnail="", - text_color="")) + itemlist.append(item.clone(title=config.get_localized_string(70502), action="abandomoviez_search", page=next_page, thumbnail="", text_color="")) if not itemlist: - itemlist.append(item.clone(title=config.get_localized_string(70501), action="", thumbnail="", - text_color="")) + itemlist.append(item.clone(title=config.get_localized_string(70501), action="", thumbnail="", text_color="")) if keyboard: if item.contextual: title = "%s" else: title = "%s" - itemlist.append(item.clone(title=title % config.get_localized_string(70511), - action="manual_search", thumbnail="", extra="abandomoviez")) + itemlist.append(item.clone(title=title % config.get_localized_string(70511), action="manual_search", thumbnail="", extra="abandomoviez")) return itemlist @@ -321,7 +314,7 @@ def filmaffinity_search(item): item.url = item.filmaffinity return search_links_filmaff(item) - # Comprueba si es una búsqueda de cero o viene de la opción Siguiente + # Check if it is a zero search or comes from the Next option if item.page != "": data = httptools.downloadpage(item.page).data else: @@ -334,7 +327,7 @@ def filmaffinity_search(item): patron = '<div class="mc-poster">.*?<img.*?src="([^"]+)".*?' \ '<div class="mc-title"><a href="/es/film(\d+).html"[^>]+>(.*?)<img' matches = scrapertools.find_multiple_matches(data, patron) - # Si solo hay un resultado, busca directamente los trailers, sino lista todos los resultados + # If there is only one result, search directly for the trailers, but list all the results if len(matches) == 1: item.url = "http://www.filmaffinity.com/es/evideos.php?movie_id=%s" % matches[0][1] item.thumbnail = matches[0][0] @@ -349,26 +342,22 @@ def filmaffinity_search(item): if PY3: scrapedtitle = unicode(scrapedtitle, encoding="utf-8", errors="ignore") scrapedtitle = scrapertools.htmlclean(scrapedtitle) - itemlist.append(item.clone(title=scrapedtitle, url=scrapedurl, - action="search_links_filmaff", thumbnail=scrapedthumbnail)) + itemlist.append(item.clone(title=scrapedtitle, url=scrapedurl, action="search_links_filmaff", thumbnail=scrapedthumbnail)) next_page = scrapertools.find_single_match(data, '<a href="([^"]+)">>></a>') if next_page != "": next_page = urlparse.urljoin("http://www.filmaffinity.com/es/", next_page) - itemlist.append(item.clone(title=config.get_localized_string(70502), page=next_page, action="filmaffinity_search", thumbnail="", - text_color="")) + itemlist.append(item.clone(title=config.get_localized_string(70502), page=next_page, action="filmaffinity_search", thumbnail="", text_color="")) if not itemlist: - itemlist.append(item.clone(title=config.get_localized_string(70501) % item.contentTitle, - action="", thumbnail="", text_color="")) + itemlist.append(item.clone(title=config.get_localized_string(70501) % item.contentTitle, action="", thumbnail="", text_color="")) if keyboard: if item.contextual: title = "%s" else: title = "%s" - itemlist.append(item.clone(title=title % config.get_localized_string(70513), - action="manual_search", thumbnail="", extra="filmaffinity")) + itemlist.append(item.clone(title=title % config.get_localized_string(70513), action="manual_search", thumbnail="", extra="filmaffinity")) return itemlist @@ -400,8 +389,7 @@ def search_links_filmaff(item): scrapedtitle += " [" + server + "]" if item.contextual: scrapedtitle = "%s" % scrapedtitle - itemlist.append(item.clone(title=scrapedtitle, url=trailer_url, server=server, action="play", - thumbnail=thumbnail)) + itemlist.append(item.clone(title=scrapedtitle, url=trailer_url, server=server, action="play", thumbnail=thumbnail)) itemlist = servertools.get_servers_itemlist(itemlist) if keyboard: @@ -409,8 +397,7 @@ def search_links_filmaff(item): title = "%s" else: title = "%s" - itemlist.append(item.clone(title=title % config.get_localized_string(70513), - action="manual_search", thumbnail="", extra="filmaffinity")) + itemlist.append(item.clone(title=title % config.get_localized_string(70513), action="manual_search", thumbnail="", extra="filmaffinity")) return itemlist @@ -451,7 +438,7 @@ try: self.control_list.addItems(self.items) self.setFocus(self.control_list) def onClick(self, id): - # Boton Cancelar y [X] + # Cancel button y [X] if id == 5: global window_select, result self.result = "_no_video" diff --git a/specials/tvmoviedb.py b/specials/tvmoviedb.py index dc6263b2..03250850 100644 --- a/specials/tvmoviedb.py +++ b/specials/tvmoviedb.py @@ -1,22 +1,14 @@ # -*- coding: utf-8 -*- -import re -import urllib +import re, urllib, xbmcaddon from base64 import b64decode as bdec -import xbmcaddon - from channelselector import get_thumb -from core import filetools -from core import httptools -from core import jsontools -from core import scrapertools +from core import filetools, httptools, jsontools, scrapertools, trakt_tools from core.item import Item from core.support import typo from core.tmdb import Tmdb -from core import trakt_tools -from platformcode import config, logger -from platformcode import platformtools +from platformcode import config, logger, platformtools info_language = ["de", "en", "es", "fr", "it", "pt"] # from videolibrary.json def_lang = info_language[config.get_setting("info_language", "videolibrary")] @@ -102,8 +94,7 @@ def search_(item): return listado_fa(item) if "myanimelist" in item.url: item.url += texto.replace(" ", "%20") - item.url += "&type=0&score=0&status=0&p=0&r=0&sm=0&sd=0&sy=0&em=0&ed=0&ey=0&c[0]=a" \ - "&c[1]=b&c[2]=c&c[3]=d&c[4]=f&gx=0" + item.url += "&type=0&score=0&status=0&p=0&r=0&sm=0&sd=0&sy=0&em=0&ed=0&ey=0&c[0]=a&c[1]=b&c[2]=c&c[3]=d&c[4]=f&gx=0" item.action = "busqueda_mal" return busqueda_mal(item) @@ -125,8 +116,7 @@ def search_(item): def busqueda(item): logger.info() - new_item = Item(title=item.contentTitle, text=item.contentTitle.replace("+", " "), mode=item.contentType, - infoLabels=item.infoLabels) + new_item = Item(title=item.contentTitle, text=item.contentTitle.replace("+", " "), mode=item.contentType, infoLabels=item.infoLabels) from specials import search return search.channel_search(new_item) @@ -278,35 +268,26 @@ def trakt(item): itemlist.append(item.clone(title=typo(config.get_localized_string(70048), 'color kod bold'), extra="cuenta")) else: item.extra = "movie" - # Se comprueba si existe un token guardado y sino se ejecuta el proceso de autentificación + # A saved token is checked and the authentication process is executed if not token_auth: - #folder = (config.get_platform() == "plex") + # folder = (config.get_platform() == "plex") itemlist.append(item.clone(title=config.get_localized_string(70054), action="auth_trakt", folder=folder)) else: itemlist.append(item.clone(title=config.get_localized_string(70055), action="", )) itemlist.append( - item.clone(title=config.get_localized_string(60651), action="acciones_trakt", url="users/me/watchlist/movies%s" % page, - order="added", how="desc")) + item.clone(title=config.get_localized_string(60651), action="acciones_trakt", url="users/me/watchlist/movies%s" % page, order="added", how="desc")) itemlist.append( - item.clone(title=config.get_localized_string(60652), action="acciones_trakt", url="users/me/watchlist/shows%s" % page, - extra="show", - order="added", how="desc")) + item.clone(title=config.get_localized_string(60652), action="acciones_trakt", url="users/me/watchlist/shows%s" % page, extra="show", order="added", how="desc")) itemlist.append(item.clone(title=config.get_localized_string(70056), action="", )) itemlist.append( - item.clone(title=config.get_localized_string(60651), action="acciones_trakt", url="users/me/watched/movies%s" % page, - order="added", how="desc")) + item.clone(title=config.get_localized_string(60651), action="acciones_trakt", url="users/me/watched/movies%s" % page, order="added", how="desc")) itemlist.append( - item.clone(title=config.get_localized_string(60652), action="acciones_trakt", url="users/me/watched/shows%s" % page, - extra="show", - order="added", how="desc")) + item.clone(title=config.get_localized_string(60652), action="acciones_trakt", url="users/me/watched/shows%s" % page, extra="show", order="added", how="desc")) itemlist.append(item.clone(title=config.get_localized_string(70068), action="", )) itemlist.append( - item.clone(title=config.get_localized_string(60651), action="acciones_trakt", url="users/me/collection/movies%s" % page, - order="added", how="desc")) + item.clone(title=config.get_localized_string(60651), action="acciones_trakt", url="users/me/collection/movies%s" % page, order="added", how="desc")) itemlist.append( - item.clone(title=config.get_localized_string(60652), action="acciones_trakt", url="users/me/collection/shows%s" % page, - extra="show", - order="added", how="desc")) + item.clone(title=config.get_localized_string(60652), action="acciones_trakt", url="users/me/collection/shows%s" % page, extra="show", order="added", how="desc")) itemlist.append( item.clone(title=config.get_localized_string(70057), action="acciones_trakt", url="users/me/lists", )) @@ -320,24 +301,17 @@ def mal(item): item.login = True itemlist.append( - item.clone(title=config.get_localized_string(70058), url="https://myanimelist.net/topanime.php?type=tv&limit=0", action="top_mal", - contentType="tvshow", extra="tv")) - itemlist.append(item.clone(title=config.get_localized_string(70059), url="https://myanimelist.net/topanime.php?type=movie&limit=0", - action="top_mal", - contentType="movie", extra="movie")) + item.clone(title=config.get_localized_string(70058), url="https://myanimelist.net/topanime.php?type=tv&limit=0", action="top_mal", contentType="tvshow", extra="tv")) + itemlist.append(item.clone(title=config.get_localized_string(70059), url="https://myanimelist.net/topanime.php?type=movie&limit=0", action="top_mal", contentType="movie", extra="movie")) itemlist.append( - item.clone(title=config.get_localized_string(70061), url="https://myanimelist.net/topanime.php?type=ova&limit=0", action="top_mal", - contentType="tvshow", extra="tv", tipo="ova")) + item.clone(title=config.get_localized_string(70061), url="https://myanimelist.net/topanime.php?type=ova&limit=0", action="top_mal", contentType="tvshow", extra="tv", tipo="ova")) itemlist.append( - item.clone(title=config.get_localized_string(70028), url="https://myanimelist.net/topanime.php?type=bypopularity&limit=0", - action="top_mal")) - itemlist.append(item.clone(title=config.get_localized_string(70060), url="https://myanimelist.net/topanime.php?type=upcoming&limit=0", - action="top_mal")) + item.clone(title=config.get_localized_string(70028), url="https://myanimelist.net/topanime.php?type=bypopularity&limit=0", action="top_mal")) + itemlist.append(item.clone(title=config.get_localized_string(70060), url="https://myanimelist.net/topanime.php?type=upcoming&limit=0", action="top_mal")) itemlist.append(item.clone(title=config.get_localized_string(70062), url="", action="indices_mal")) itemlist.append(item.clone(title=config.get_localized_string(70063), url="", action="indices_mal")) if config.get_platform() != "plex": - itemlist.append(item.clone(title=config.get_localized_string(70064), url="https://myanimelist.net/anime.php?q=", - action="search_")) + itemlist.append(item.clone(title=config.get_localized_string(70064), url="https://myanimelist.net/anime.php?q=", action="search_")) itemlist.append(item.clone(title=typo(config.get_localized_string(70038), 'bold submenu'), action="filtro_mal")) itemlist.append(item.clone(title=typo(config.get_localized_string(70057), 'bold submenu'), action="cuenta_mal")) @@ -345,15 +319,15 @@ def mal(item): return itemlist -##-------------------- SECCION TMDB ------------------------## +##-------------------- SECTION TMDB ------------------------## def listado_tmdb(item): - # Listados principales de la categoría Tmdb (Más populares, más vistas, etc...) + # Main listings of the Tmdb category (Most popular, Most viewed, etc ...) itemlist = [] item.fanart = default_fan if not item.pagina: item.pagina = 1 - # Listado de actores + # List of actors if 'nm' in item.infoLabels['imdb_id']: try: @@ -370,7 +344,7 @@ def listado_tmdb(item): else: ob_tmdb = Tmdb(discover=item.search, tipo=item.extra, idioma_busqueda=langt) - # Sagas y colecciones + # Sagas and collections if "collection" in item.search["url"]: try: new_item = item.clone(action="", url='') @@ -394,7 +368,7 @@ def listado_tmdb(item): else: try: orden = False - # Si se hace una búsqueda por actores o directores, se extraen esos resultados + # If you do a search for actors or directors, those results are extracted if "cast" in ob_tmdb.result and not item.crew: ob_tmdb.results = ob_tmdb.result["cast"] orden = True @@ -404,7 +378,7 @@ def listado_tmdb(item): for i in range(0, len(ob_tmdb.results)): new_item = item.clone(action="detalles", url='', infoLabels={'mediatype': item.contentType}) new_item.infoLabels = ob_tmdb.get_infoLabels(new_item.infoLabels, origen=ob_tmdb.results[i]) - # Si no hay sinopsis en idioma elegido, buscar en el alternativo + # If there is no synopsis in the chosen language, search in the alternative if not new_item.infoLabels["plot"] and not 'person' in item.search["url"]: ob_tmdb2 = Tmdb(id_Tmdb=new_item.infoLabels["tmdb_id"], tipo=item.extra, idioma_busqueda=langt_alt) new_item.infoLabels["plot"] = ob_tmdb2.get_sinopsis() @@ -443,7 +417,7 @@ def listado_tmdb(item): % (typo(new_item.contentTitle,'bold'), typo(new_item.infoLabels['rating'].replace("0.0", ""),'color kod bold')) else: - # Si es una búsqueda de personas se incluye en el título y fanart una película por la que es conocido + # If it is a search for people, a film for which it is known is included in the title and fanart known_for = ob_tmdb.results[i].get("known_for") type=item.type if known_for: @@ -475,7 +449,7 @@ def detalles(item): itemlist = [] images = {} data = "" - # Si viene de seccion imdb + # If it comes from imdb section if not item.infoLabels["tmdb_id"]: headers = [['Accept-Language', langi]] #data = httptools.downloadpage("http://www.imdb.com/title/" + item.infoLabels['imdb_id'], headers=headers, @@ -483,7 +457,7 @@ def detalles(item): data = httptools.downloadpage("http://www.imdb.com/title/" + item.infoLabels['imdb_id'], headers=headers).data pics = scrapertools.find_single_match(data, 'showAllVidsAndPics.*?href=".*?(tt\d+)') - # Imágenes imdb + # Imdb images if pics: images["imdb"] = {'url': 'http://www.imdb.com/_json/title/%s/mediaviewer' % pics} @@ -495,7 +469,7 @@ def detalles(item): try: item.infoLabels = ob_tmdb.get_infoLabels(item.infoLabels) - # Si no hay sinopsis en idioma elegido, buscar en el alternativo + # If there is no synopsis in the chosen language, search in the alternative if not item.infoLabels["plot"]: item.infoLabels["plot"] = ob_tmdb.get_sinopsis(idioma_alternativo=langt_alt) except: @@ -505,7 +479,7 @@ def detalles(item): if item.infoLabels['thumbnail']: item.thumbnail = item.infoLabels['thumbnail'] - # Sinopsis, votos de imdb + # Synopsis, votes from imdb if data: plot = scrapertools.find_single_match(data, 'class="inline canwrap" itemprop="description">(.*?)</div>') plot = scrapertools.htmlclean(plot) @@ -525,29 +499,24 @@ def detalles(item): itemlist.append(item.clone(title="--- %s ---" % item.infoLabels['tagline'], action="")) title = item.contentType.replace("movie", config.get_localized_string(70283)).replace("tvshow", "serie") - # Búsqueda por títulos idioma elegido y/o versión original y español + # Search by titles chosen language and / or original version and Spanish itemlist.append(item.clone(action="busqueda", title=config.get_localized_string(70069) % (title, item.contentTitle))) if item.infoLabels['originaltitle'] and item.contentTitle != item.infoLabels['originaltitle']: - itemlist.append(item.clone(action="busqueda", contentTitle=item.infoLabels['originaltitle'], - title=config.get_localized_string(70070) % item.infoLabels['originaltitle'])) + itemlist.append(item.clone(action="busqueda", contentTitle=item.infoLabels['originaltitle'], title=config.get_localized_string(70070) % item.infoLabels['originaltitle'])) if langt != "es" and langt != "en" and item.infoLabels["tmdb_id"]: tmdb_lang = Tmdb(id_Tmdb=item.infoLabels["tmdb_id"], tipo=item.extra, idioma_busqueda=def_lang) if tmdb_lang.result.get("title") and tmdb_lang.result["title"] != item.contentTitle \ and tmdb_lang.result["title"] != item.infoLabels['originaltitle']: tmdb_lang = tmdb_lang.result["title"] - itemlist.append(item.clone(action="busqueda", title=config.get_localized_string(70066) % tmdb_lang, - contentTitle=tmdb_lang)) + itemlist.append(item.clone(action="busqueda", title=config.get_localized_string(70066) % tmdb_lang, contentTitle=tmdb_lang)) - # En caso de serie, opción de info por temporadas + # In case of series, option of info by seasons if item.contentType == "tvshow" and item.infoLabels['tmdb_id']: - itemlist.append(item.clone(action="info_seasons", - title=config.get_localized_string(70067) % item.infoLabels["number_of_seasons"])) - # Opción de ver el reparto y navegar por sus películas/series + itemlist.append(item.clone(action="info_seasons", title=config.get_localized_string(70067) % item.infoLabels["number_of_seasons"])) + # Option to watch the cast and browse their movies / series if item.infoLabels['tmdb_id']: - itemlist.append(item.clone(action="reparto", title=config.get_localized_string(70071), - infoLabels={'tmdb_id': item.infoLabels['tmdb_id'], - 'mediatype': item.contentType})) + itemlist.append(item.clone(action="reparto", title=config.get_localized_string(70071), infoLabels={'tmdb_id': item.infoLabels['tmdb_id'], 'mediatype': item.contentType})) if config.is_xbmc(): item.contextual = True @@ -556,8 +525,7 @@ def detalles(item): try: images['tmdb'] = ob_tmdb.result["images"] - itemlist.append(item.clone(action="imagenes", title=config.get_localized_string(70316), images=images, - extra="menu")) + itemlist.append(item.clone(action="imagenes", title=config.get_localized_string(70316), images=images, extra="menu")) except: pass @@ -580,9 +548,7 @@ def detalles(item): url_album = scrapertools.find_single_match(data_music, 'album(?:|s) on request.*?href="([^"]+)"') if url_album: url_album = "https://nl.hideproxy.me" + url_album - itemlist.append( - item.clone(action="musica_movie", title=config.get_localized_string(70317), url=url_album, - )) + itemlist.append(item.clone(action="musica_movie", title=config.get_localized_string(70317), url=url_album)) except: pass @@ -591,7 +557,7 @@ def detalles(item): itemlist.append(item.clone(title=config.get_localized_string(70318), action="menu_trakt")) itemlist.append(item.clone(title="", action="")) - # Es parte de una colección + # It is part of a collection try: if ob_tmdb.result.get("belongs_to_collection"): new_item = item.clone(search='', infoLabels={'mediatype': item.contentType}) @@ -602,12 +568,11 @@ def detalles(item): if saga["backdrop_path"]: new_item.fanart = 'http://image.tmdb.org/t/p/original' + saga["backdrop_path"] new_item.search = {'url': 'collection/%s' % saga['id'], 'language': langt} - itemlist.append(new_item.clone(title=config.get_localized_string(70327) % saga["name"], action="listado_tmdb", - )) + itemlist.append(new_item.clone(title=config.get_localized_string(70327) % saga["name"], action="listado_tmdb")) except: pass - # Películas/Series similares y recomendaciones + # Similar Movies / Series and Recommendations if item.infoLabels['tmdb_id']: item.extra = item.contentType.replace('tvshow', 'tv') title = title.replace("película", config.get_localized_string(70137)).replace("serie", config.get_localized_string(30123)) @@ -624,7 +589,7 @@ def detalles(item): def reparto(item): - # Actores y equipo de rodaje de una película/serie + # Actors and film crew for a movie / series itemlist = [] item.extra=item.contentType.replace('tvshow','tv') item.search = {'url': '%s/%s/credits' % (item.extra, item.infoLabels['tmdb_id'])} @@ -672,7 +637,7 @@ def reparto(item): def info_seasons(item): - # Info de temporadas y episodios + # Season and episode info itemlist = [] ob_tmdb = Tmdb(id_Tmdb=item.infoLabels["tmdb_id"], tipo="tv", idioma_busqueda=langt) @@ -719,7 +684,7 @@ def info_seasons(item): def indices_tmdb(item): - # Indices por genero y año + # Indices by gender and year itemlist = [] from datetime import datetime if config.get_localized_string(70032) in item.title: @@ -835,7 +800,7 @@ def filtro(item): def filtrado(item, values): values_copy = values.copy() - # Guarda el filtro para que sea el que se cargue por defecto + # Save the filter to be the one loaded by default if "save" in values and values["save"]: values_copy.pop("save") config.set_setting("filtro_defecto_" + item.extra, values_copy, item.channel) @@ -882,24 +847,24 @@ def musica_movie(item): return itemlist -##-------------------- SECCION IMDB ------------------------## +##-------------------- SECTION IMDB ------------------------## def listado_imdb(item): - # Método principal para secciones de imdb + # Main method for imdb sections itemlist = [] headers = [['Accept-Language', langi]] if "www.imdb.com" in item.url: - #data = httptools.downloadpage(item.url, headers=headers, replace_headers=True).data + # data = httptools.downloadpage(item.url, headers=headers, replace_headers=True).data data = httptools.downloadpage(item.url, headers=headers).data else: url = 'http://www.imdb.com/search/title?' + item.url - #data = httptools.downloadpage(url, headers=headers, replace_headers=True).data + # data = httptools.downloadpage(url, headers=headers, replace_headers=True).data data = httptools.downloadpage(url, headers=headers).data data = re.sub(r"\n|\r|\t| ", "", data) data = re.sub(r"\s{2}", " ", data) - # Listado de actores + # List of actors if 'search/name' in item.url: patron = '<td class="image">.*?src="([^"]+)".*?href="/name/(nm\d+).*?>([^<]+)<.*?href.*?>([^<]+)</a>' \ '</span>(.*?)</td>' @@ -996,7 +961,7 @@ def filtro_imdb(item): valores = {} dict_values = None - # Se utilizan los valores por defecto/guardados + # Default / saved values ​​are used valores_guardados = config.get_setting("filtro_defecto_imdb_" + item.extra, item.channel) if valores_guardados: dict_values = valores_guardados @@ -1086,7 +1051,7 @@ def filtro_imdb(item): def filtrado_imdb(item, values): values_copy = values.copy() - # Guarda el filtro para que sea el que se cargue por defecto + # Save the filter to be the one loaded by default if "save" in values and values["save"]: values_copy.pop("save") config.set_setting("filtro_defecto_imdb_" + item.extra, values_copy, item.channel) @@ -1119,7 +1084,7 @@ def filtrado_imdb(item, values): def indices_imdb(item): - # Índices imdb por año y genero + # Imdb indices by year and gender itemlist = [] from datetime import datetime if config.get_localized_string(70032) in item.title: @@ -1149,12 +1114,12 @@ def indices_imdb(item): return itemlist -##-------------------- SECCION FILMAFFINITY ------------------------## +##-------------------- FILMAFFINITY SECTION ------------------------## def listado_fa(item): - # Método para listados principales de filmaffinity + # Filmaffinity main listing method itemlist = [] - # Listados con paginación por post + # Listings with pagination per post if item.extra == "top": if item.page_fa: post = "from=%s" % item.page_fa @@ -1176,7 +1141,7 @@ def listado_fa(item): data = re.sub(r"\s{2}", " ", data) votaciones = [] - # Si es la sección de estrenos cambia la estructura del scraper + # If it is the premiere section, change the structure of the scraper if item.extra == "estrenos": patron = '<i class="fa fa-calendar"></i>\s*(\d+[^<]+)<(.*?)(?:<div class="panel panel-default">|' \ '<div class="text-center")' @@ -1269,7 +1234,7 @@ def listado_fa(item): def indices_fa(item): - # Índices por genero, año, temas y sagas/colecciones + # Indexes by gender, year, themes and sagas / collections itemlist = [] if item.url: data = httptools.downloadpage(item.url).data @@ -1357,7 +1322,7 @@ def indices_fa(item): def temas_fa(item): - # Películas y series por temas + # Movies and series by themes itemlist = [] data = httptools.downloadpage(item.url).data @@ -1402,7 +1367,7 @@ def detalles_fa(item): data = re.sub(r"\n|\r|\t| ", "", data) data = re.sub(r"\s{2}", " ", data) - # Se extrae el título original para posibles búsquedas en tmdb posteriores + # The original title is extracted for possible searches in later tmdb orig_title = scrapertools.find_single_match(data, 'itemprop="datePublished">.*?<dd>([^<]+)</dd>').strip() if item.contentType == "movie": item.infoLabels['originaltitle'] = re.sub(r"(?i)\(TV Series\)|\(S\)|\(TV\)", "", orig_title) @@ -1426,11 +1391,11 @@ def detalles_fa(item): ob_tmdb = Tmdb(id_Tmdb=ob_tmdb.get_id(), tipo=item_tmdb.extra, idioma_busqueda=langt) item.infoLabels = ob_tmdb.get_infoLabels(item.infoLabels) - # Si no hay sinopsis en idioma elegido, buscar en el alternativo + # If there is no synopsis in the chosen language, search in the alternative if not item.infoLabels["plot"]: item.infoLabels["plot"] = ob_tmdb.get_sinopsis(idioma_alternativo=langt_alt) - # Se concatena el plot de filmaffinity al de tmdb si lo hay + # The filmaffinity plot is concatenated to the tmdb plot if any plot = scrapertools.find_single_match(data, '<dd itemprop="description">(.*?)</dd>') plot = plot.replace("<br><br />", "\n") plot = scrapertools.decodeHtmlentities(plot).replace(" (FILMAFFINITY)", "") @@ -1439,7 +1404,7 @@ def detalles_fa(item): elif plot and not item.infoLabels['plot']: item.infoLabels['plot'] = plot - # Se busca y rellena con la info de filmaffinity para diferenciarla de tmdb + # It is searched and filled with the filmaffinity info to differentiate it from tmdb if not item.infoLabels['duration']: duration = scrapertools.find_single_match(data, '<dd itemprop="duration">(\d+)') if duration: @@ -1544,7 +1509,7 @@ def detalles_fa(item): token_auth = config.get_setting("token_trakt", "trakt") if token_auth and ob_tmdb.result: itemlist.append(item.clone(title=config.get_localized_string(70323), action="menu_trakt")) - # Acciones si se configura cuenta en FA (Votar y añadir/quitar en listas) + # Actions if account is configured in FA (Vote and add / remove in lists) mivoto = scrapertools.find_single_match(data, 'bg-my-rating.*?>\s*(\d+)') itk = scrapertools.find_single_match(data, 'data-itk="([^"]+)"') folder = not config.is_xbmc() @@ -1568,7 +1533,7 @@ def detalles_fa(item): new_item.infoLabels["duration"] = "" itemlist.append(new_item) - # Si pertenece a una saga/colección + # If you belong to a saga / collection if ob_tmdb.result: itemlist.append(item.clone(title="", action="", infoLabels={})) if ob_tmdb.result.get("belongs_to_collection"): @@ -1603,7 +1568,7 @@ def filtro_fa(item): valores = {} dict_values = None - # Se utilizan los valores por defecto/guardados + # Default / saved values ​​are used valores_guardados = config.get_setting("filtro_defecto_filmaf_" + item.extra, item.channel) if valores_guardados: dict_values = valores_guardados @@ -1675,7 +1640,7 @@ def filtro_fa(item): def filtrado_fa(item, values): values_copy = values.copy() - # Guarda el filtro para que sea el que se cargue por defecto + # Save the filter to be the one loaded by default if "save" in values and values["save"]: values_copy.pop("save") config.set_setting("filtro_defecto_filmaf_" + item.extra, values_copy, item.channel) @@ -1732,7 +1697,7 @@ def login_fa(): def cuenta_fa(item): - # Menú de cuenta filmaffinity + # Filmaffinity account menu itemlist = [] login, message = login_fa() if not login: @@ -1748,7 +1713,7 @@ def cuenta_fa(item): def acciones_fa(item): - # Acciones cuenta filmaffinity, votar, ver listas o añadir/quitar de lista + # Actions account filmaffinity, vote, view lists or add / remove from list itemlist = [] if item.accion == "votos" or item.accion == "lista": @@ -1847,7 +1812,7 @@ def acciones_fa(item): def votar_fa(item): - # Ventana para seleccionar el voto + # Window to select the vote logger.info() list_controls = [] @@ -1889,7 +1854,7 @@ def callback_voto(item, values): def newlist(item): - # Creación de nueva lista en filmaffinity + # Creation of new list in filmaffinity itemlist = [] if item.accion == "lista": location = httptools.downloadpage(item.url, only_headers=True).headers["location"] @@ -1910,7 +1875,7 @@ def newlist(item): return itemlist -##-------------------- LISTADOS DE IMAGENES ------------------------## +##-------------------- IMAGE LISTINGS ------------------------## def imagenes(item): itemlist = [] @@ -2055,13 +2020,13 @@ def fanartv(item): return item, resultado -##-------------------- SECCION TRAKT.TV ------------------------## +##-------------------- SECTION TRAKT.TV ------------------------## def auth_trakt(item): return trakt_tools.auth_trakt() def menu_trakt(item): - # Menú con acciones de cuenta trakt (vistas, watchlist, coleccion) + # Menu with trakt account actions (views, watchlist, collection) itemlist = [] token_auth = config.get_setting("token_trakt", "trakt") tipo = item.extra.replace("tv", "show") + "s" @@ -2279,9 +2244,9 @@ def order_trakt(item, values): return acciones_trakt(item) -##-------------------- SECCION MYANIMELIST ------------------------## +##-------------------- MYANIMELIST SECTION ------------------------## def top_mal(item): - # Para los menús principales de tops pelícuas/series/ovas + # For the main menus of movie tops / series / ova itemlist = [] data = httptools.downloadpage(item.url, cookies=False).data data = re.sub(r"\n|\r|\t| ", "", data) @@ -2388,7 +2353,7 @@ def detalles_mal(item): ob_tmdb = Tmdb(id_Tmdb=ob_tmdb.get_id(), tipo=item_tmdb.extra, idioma_busqueda=langt) item.infoLabels = ob_tmdb.get_infoLabels(item.infoLabels) - # Se concatena sinopsis myanimelist con la de tmdb si la hubiese + # Myanimelist synopsis is concatenated with that of tmdb if any plot = scrapertools.find_single_match(data, '<span itemprop="description">(.*?)</span>') plot = plot.replace("<br />", "\n").replace("<i>", "[I]").replace("</i>", "[/I]") plot = scrapertools.decodeHtmlentities(plot) @@ -2411,7 +2376,7 @@ def detalles_mal(item): except: pass - # Se sobreescribe la info de myanimelist sobre la de tmdb + # Myanimelist info overwrites tmdb info generos = scrapertools.find_single_match(data, 'Genres:</span>(.*?)</div>') if generos: item.infoLabels['genre'] = scrapertools.htmlclean(generos) @@ -2445,7 +2410,7 @@ def detalles_mal(item): itemlist.append(item.clone(action="videos_mal", title=config.get_localized_string(70353), url=item.url + "/video")) - # Opción para ver la info de personajes y dobladores/equipo de rodaje + # Option to see the info of characters and voiceovers / filming equipment if not "No characters or voice actors" in data and not "No staff for this anime" in data: itemlist.append(item.clone(action="staff_mal", title=config.get_localized_string(70354), url=item.url + "/characters")) @@ -2497,7 +2462,7 @@ def detalles_mal(item): if token_auth and ob_tmdb.result: itemlist.append(item.clone(title=config.get_localized_string(70323), action="menu_trakt")) - # Se listan precuelas, secuelas y series alternativas + # Prequels, sequels and alternative series are listed prequel = scrapertools.find_single_match(data, 'Prequel:</td>(.*?)</td>') if prequel: matches = scrapertools.find_multiple_matches(prequel, 'href="([^"]+)">(.*?)</a>') @@ -2550,7 +2515,7 @@ def detalles_mal(item): search={'url': '%s/%s/recommendations' % (item.extra, item.infoLabels['tmdb_id']), 'language': langt, 'page': 1}, )) - # Recomendaciones myanimelist y búsqueda de info en anidb (fansubs en español) + # Myanimelist recommendations and info search on anidb (fansubs in Spanish) itemlist.append(item.clone(title=config.get_localized_string(70359), action="reco_mal")) anidb_link = scrapertools.find_single_match(data, '<a href="(http://anidb.info/perl-bin/animedb.pl\?show=anime&aid=\d+)') @@ -2562,7 +2527,7 @@ def detalles_mal(item): def videos_mal(item): - # Método para episodios en crunchyroll y trailer/promocionales + # Method for crunchyroll and trailer / promotional episodes itemlist = [] data = httptools.downloadpage(item.url, cookies=False).data @@ -2604,7 +2569,7 @@ def videos_mal(item): def reco_mal(item): - # Recomendaciones de myanimelist + # Myanimelist recommendations itemlist = [] data = httptools.downloadpage(item.url + "/userrecs", cookies=False).data @@ -2628,7 +2593,7 @@ def reco_mal(item): def indices_mal(item): - # Índices por temporadas y generos + # Seasonal and gender indices itemlist = [] url_base = "" if "Temporadas" in item.title: @@ -2664,7 +2629,7 @@ def indices_mal(item): def season_mal(item): - # Scraper para temporadas de anime + # Scraper for anime seasons itemlist = [] cookie_session = get_cookie_value() @@ -2758,7 +2723,7 @@ def season_mal(item): def staff_mal(item): - # Dobladores/Equipo de rodaje + # Benders / Filming Equipment itemlist = [] data = httptools.downloadpage(item.url, cookies=False).data data = re.sub(r"\n|\r|\t| ", "", data) @@ -2869,7 +2834,7 @@ def detail_staff(item): def busqueda_mal(item): - # Scraper para búsquedas en myanimelist + # Scraper for myanimelist searches itemlist = [] cookie_session = get_cookie_value() @@ -2942,7 +2907,7 @@ def busqueda_mal(item): def info_anidb(item, itemlist, url): - # Extrae info, puntuación y fansubs en anidb + # Extract info, score and fansubs on anidb data = httptools.downloadpage(url).data data = re.sub(r"\n|\r|\t| ", "", data) data = re.sub(r"\s{2}", " ", data) @@ -2994,7 +2959,7 @@ def filtro_mal(item): list_controls = [] valores = {} dict_values = None - # Se utilizan los valores por defecto/guardados + # Default / saved values ​​are used valores_guardados = config.get_setting("filtro_defecto_mal", item.channel) if valores_guardados: dict_values = valores_guardados @@ -3044,7 +3009,7 @@ def filtro_mal(item): def callback_mal(item, values): values_copy = values.copy() - # Guarda el filtro para que sea el que se cargue por defecto + # Save the filter to be the one loaded by default if "save" in values and values["save"]: values_copy.pop("save") config.set_setting("filtro_defecto_mal", values_copy, item.channel) @@ -3072,7 +3037,7 @@ def callback_mal(item, values): def musica_anime(item): - # Lista los animes y canciones disponibles similares al título del anime + # List available anime and songs similar to the anime title logger.info() itemlist = [] @@ -3145,7 +3110,7 @@ def login_mal(from_list=False): def cuenta_mal(item): - # Menú de cuenta myanimelist + # Myanimelist account menu itemlist = [] login, message, user = login_mal(True) if not login: @@ -3167,7 +3132,7 @@ def cuenta_mal(item): def items_mal(item): - # Scraper para las listas personales + # Scraper for personal lists logger.info() itemlist = [] data = httptools.downloadpage(item.url).data @@ -3213,7 +3178,7 @@ def items_mal(item): def menu_mal(item): - # Opciones cuenta MAL, añadir a lista/votar + # Options BAD account, add to list / vote itemlist = [] data = httptools.downloadpage(item.url).data @@ -3271,7 +3236,7 @@ def addlist_mal(item): url = "https://myanimelist.net/ownlist/anime/add.json" if item.lista: url = "https://myanimelist.net/ownlist/anime/edit.json" - #data = httptools.downloadpage(url, post=jsontools.dump(post), headers=headers_mal, replace_headers=True).data + # data = httptools.downloadpage(url, post=jsontools.dump(post), headers=headers_mal, replace_headers=True).data data = httptools.downloadpage(url, post=jsontools.dump(post), headers=headers_mal).data item.title = "En tu lista" if config.is_xbmc(): diff --git a/specials/url.py b/specials/url.py index 1fdd9db3..d808747e 100644 --- a/specials/url.py +++ b/specials/url.py @@ -17,7 +17,7 @@ def mainlist(item): return itemlist -# Al llamarse "search" la función, el launcher pide un text a buscar y lo añade como parámetro +# When the function "search" is called, the launcher asks for a text to search for and adds it as a parameter def search(item, text): log(text)