Traduzioni Specials

This commit is contained in:
Alhaziel01
2020-05-27 16:31:06 +02:00
parent 2e8d950775
commit cd0c280cb5
13 changed files with 786 additions and 1913 deletions

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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("&quot", "")
url = scrapertools.find_single_match(data, 'plugin://plugin.video.%s/\?([^;]*)' % config.PLUGIN_NAME).replace("&quot", "")
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,&quot;plugin://plugin.video.kod/?" + item.tourl() + "&quot;,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

View File

@@ -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)

View File

@@ -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[^"]*)"><b>([^<]*)')
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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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', '<title>(.*?)\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):

View File

@@ -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)

View File

@@ -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="([^"]+)">&gt;&gt;</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"

View File

@@ -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|&nbsp;", "", 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|&nbsp;", "", 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|&nbsp;", "", 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&amp;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|&nbsp;", "", 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|&nbsp;", "", 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():

View File

@@ -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)