Reorganized channels and support moved to core

This commit is contained in:
Alhaziel
2019-05-23 18:30:04 +02:00
parent c14cf65f36
commit 4c4f618255
79 changed files with 716 additions and 184 deletions
+12
View File
@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
import os
import sys
# Appends the main plugin dir to the PYTHONPATH if an internal package cannot be imported.
# Examples: In Plex Media Server all modules are under "Code.*" package, and in Enigma2 under "Plugins.Extensions.*"
try:
# from core import logger
import core
except:
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
+724
View File
@@ -0,0 +1,724 @@
# -*- coding: utf-8 -*-
import os
from core import channeltools
from core import jsontools
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
from platformcode import launcher
from time import sleep
from platformcode.config import get_setting
__channel__ = "autoplay"
PLAYED = False
autoplay_node = {}
def context():
'''
Agrega la opcion Configurar AutoPlay al menu contextual
:return:
'''
_context = ""
if config.is_xbmc():
_context = [{"title": config.get_localized_string(60071),
"action": "autoplay_config",
"channel": "autoplay"}]
return _context
context = context()
def show_option(channel, itemlist, text_color='yellow', thumbnail=None, fanart=None):
'''
Agrega la opcion Configurar AutoPlay en la lista recibida
: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)
:return:
'''
from channelselector import get_thumb
logger.info()
if not config.is_xbmc():
return itemlist
if thumbnail == None:
thumbnail = get_thumb('autoplay.png')
if fanart == None:
fanart = get_thumb('autoplay.png')
plot_autoplay = config.get_localized_string(60399)
itemlist.append(
Item(channel=__channel__,
title=config.get_localized_string(60071),
action="autoplay_config",
text_color=text_color,
thumbnail=thumbnail,
fanart=fanart,
plot=plot_autoplay,
from_channel=channel
))
return itemlist
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.
: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
'''
logger.info()
global PLAYED
global autoplay_node
PLAYED = False
base_item = item
if not config.is_xbmc():
#platformtools.dialog_notification('AutoPlay ERROR', 'Sólo disponible para XBMC/Kodi')
return itemlist
if not autoplay_node:
# Obtiene el nodo AUTOPLAY desde el json
autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY')
channel_id = item.channel
if item.channel == 'videolibrary':
autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY')
channel_id = item.contentChannel
try:
active = autoplay_node['status']
except:
active = is_active(item.channel)
if not channel_id in autoplay_node or not active:
return itemlist
# Agrega servidores y calidades que no estaban listados a autoplay_node
new_options = check_value(channel_id, itemlist)
# Obtiene el nodo del canal desde autoplay_node
channel_node = autoplay_node.get(channel_id, {})
# Obtiene los ajustes des autoplay para este canal
settings_node = channel_node.get('settings', {})
if get_setting('autoplay') or settings_node['active']:
url_list_valid = []
autoplay_list = []
autoplay_b = []
favorite_servers = []
favorite_quality = []
# Guarda el valor actual de "Accion y Player Mode" en preferencias
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)
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)
# Prioridades a la hora de ordenar itemlist:
# 0: Servidores y calidades
# 1: Calidades y servidores
# 2: Solo servidores
# 3: Solo calidades
# 4: No ordenar
if (settings_node['custom_servers'] and settings_node['custom_quality']):
priority = settings_node['priority'] # 0: Servidores y calidades o 1: Calidades y servidores
elif settings_node['custom_servers']:
priority = 2 # Solo servidores
elif settings_node['custom_quality']:
priority = 3 # Solo calidades
else:
priority = 4 # No ordenar
# Obtiene las listas servidores, calidades disponibles desde el nodo del json de AutoPlay
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 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']
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
for item in itemlist:
autoplay_elem = dict()
b_dict = dict()
# Comprobamos q se trata de un item de video
if 'server' not in item:
continue
# Agrega la opcion configurar AutoPlay al menu contextual
if 'context' not in item:
item.context = list()
if not filter(lambda x: x['action'] == 'autoplay_config', context):
item.context.append({"title": config.get_localized_string(60071),
"action": "autoplay_config",
"channel": "autoplay",
"from_channel": channel_id})
# Si no tiene calidad definida le asigna calidad 'default'
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
# si el servidor y la calidad no se encuentran en las listas de favoritos o la url esta repetida,
# descartamos el 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
b_dict['videoitem']= item
autoplay_b.append(b_dict)
continue
autoplay_elem["indice_server"] = favorite_servers.index(item.server.lower())
autoplay_elem["indice_quality"] = favorite_quality.index(item.quality)
elif priority == 2: # Solo servidores
# si el servidor no se encuentra en la lista de favoritos o la url esta repetida,
# descartamos el item
if item.server.lower() not in favorite_servers or item.url in url_list_valid:
item.type_b = True
b_dict['videoitem'] = item
autoplay_b.append(b_dict)
continue
autoplay_elem["indice_server"] = favorite_servers.index(item.server.lower())
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 item.quality not in favorite_quality or item.url in url_list_valid:
item.type_b = True
b_dict['videoitem'] = item
autoplay_b.append(b_dict)
continue
autoplay_elem["indice_quality"] = favorite_quality.index(item.quality)
else: # No ordenar
# si la url esta repetida, descartamos el 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
url_list_valid.append(item.url)
item.plan_b=True
autoplay_elem['videoitem'] = item
# autoplay_elem['server'] = item.server
# autoplay_elem['quality'] = item.quality
autoplay_list.append(autoplay_elem)
# Ordenamos segun la prioridad
if priority == 0: # Servidores y calidades
autoplay_list.sort(key=lambda orden: (orden['indice_server'], orden['indice_quality']))
elif priority == 1: # Calidades y servidores
autoplay_list.sort(key=lambda orden: (orden['indice_quality'], orden['indice_server']))
elif priority == 2: # Solo servidores
autoplay_list.sort(key=lambda orden: orden['indice_server'])
elif priority == 3: # Solo calidades
autoplay_list.sort(key=lambda orden: orden['indice_quality'])
# Se prepara el plan b, en caso de estar activo se agregan los elementos no favoritos al final
try:
plan_b = settings_node['plan_b']
except:
plan_b = True
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 autoplay_list or (plan_b and autoplay_b):
#played = False
max_intentos = 5
max_intentos_servers = {}
# Si se esta reproduciendo algo detiene la reproduccion
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 autoplay_elem['videoitem'].type_b:
text_b = '(Plan B)'
if not platformtools.is_playing() and not PLAYED:
videoitem = autoplay_elem['videoitem']
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 max_intentos_servers[videoitem.server.lower()] == 0:
continue
lang = " "
if hasattr(videoitem, 'language') and videoitem.language != "":
lang = " '%s' " % videoitem.language
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!!!
# Intenta reproducir los enlaces
# Si el canal tiene metodo play propio lo utiliza
channel = __import__('channels.%s' % channel_id, None, None, ["channels.%s" % channel_id])
if hasattr(channel, 'play'):
resolved_item = getattr(channel, 'play')(videoitem)
if len(resolved_item) > 0:
if isinstance(resolved_item[0], list):
videoitem.video_urls = resolved_item
else:
videoitem = resolved_item[0]
# Si no directamente reproduce y marca como visto
# Verifica si el item viene de la videoteca
try:
if base_item.contentChannel =='videolibrary':
# Marca como visto
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_auto_as_watched(base_item)
# Rellena el video con los datos del item principal y reproduce
play_item = base_item.clone(url=videoitem)
platformtools.play_video(play_item.url, autoplay=True)
else:
# Si no viene de la videoteca solo reproduce
platformtools.play_video(videoitem, autoplay=True)
except:
pass
sleep(3)
try:
if platformtools.is_playing():
PLAYED = True
break
except:
logger.debug(str(len(autoplay_list)))
# Si hemos llegado hasta aqui es por q no se ha podido reproducir
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 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)):
max_intentos_servers[videoitem.server.lower()] = max_intentos
# Si no quedan elementos en la lista se informa
if autoplay_elem == autoplay_list[-1]:
platformtools.dialog_notification('AutoPlay', config.get_localized_string(60072))
else:
platformtools.dialog_notification(config.get_localized_string(60074), config.get_localized_string(60075))
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
if user_config_setting_action != 2:
config.set_setting("default_action", user_config_setting_action)
if user_config_setting_player != 0:
config.set_setting("player_mode", user_config_setting_player)
return itemlist
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.
: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.
'''
logger.info()
change = False
result = True
if not config.is_xbmc():
# platformtools.dialog_notification('AutoPlay ERROR', 'Sólo disponible para XBMC/Kodi')
result = False
else:
autoplay_path = os.path.join(config.get_data_path(), "settings_channels", 'autoplay_data.json')
if os.path.exists(autoplay_path):
autoplay_node = jsontools.get_node_from_file('autoplay', "AUTOPLAY")
else:
change = True
autoplay_node = {"AUTOPLAY": {}}
if channel not in autoplay_node or reset:
change = True
# Se comprueba que no haya calidades ni servidores duplicados
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
channel_node = {"servers": list_servers,
"quality": list_quality,
"settings": {
"active": False,
"plan_b": True,
"custom_servers": False,
"custom_quality": False,
"priority": 0}}
for n in range(1, 4):
s = c = 0
if len(list_servers) >= n:
s = n - 1
if len(list_quality) >= n:
c = n - 1
channel_node["settings"]["server_%s" % n] = s
channel_node["settings"]["quality_%s" % n] = c
autoplay_node[channel] = channel_node
if change:
result, json_data = jsontools.update_node(autoplay_node, 'autoplay', 'AUTOPLAY')
if not result:
heading = config.get_localized_string(60077)
msj = config.get_localized_string(60078)
icon = 1
platformtools.dialog_notification(heading, msj, icon, sound=False)
return result
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
:param channel: str
:param values: list (una de servidores o calidades)
:param value_type: str (server o quality)
:return: list
'''
logger.info()
global autoplay_node
change = False
if not autoplay_node:
# Obtiene el nodo AUTOPLAY desde el json
autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY')
channel_node = autoplay_node.get(channel)
server_list = channel_node.get('servers')
if not server_list:
server_list = channel_node['servers'] = list()
quality_list = channel_node.get('quality')
if not quality_list:
quality_list = channel_node['quality'] = list()
for item in itemlist:
if item.server.lower() not in server_list and item.server !='':
server_list.append(item.server.lower())
change = True
if item.quality not in quality_list and item.quality !='':
quality_list.append(item.quality)
change = True
if change:
change, json_data = jsontools.update_node(autoplay_node, 'autoplay', 'AUTOPLAY')
return change
def autoplay_config(item):
logger.info()
global autoplay_node
dict_values = {}
list_controls = []
channel_parameters = channeltools.get_channel_parameters(item.from_channel)
channel_name = channel_parameters['title']
if not autoplay_node:
# Obtiene el nodo AUTOPLAY desde el json
autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY')
channel_node = autoplay_node.get(item.from_channel, {})
settings_node = channel_node.get('settings', {})
allow_option = True
active_settings = {"id": "active", "label": config.get_localized_string(60079),
"color": "0xffffff99", "type": "bool", "default": False, "enabled": allow_option,
"visible": allow_option}
list_controls.append(active_settings)
dict_values['active'] = settings_node.get('active', False)
# Idioma
status_language = config.get_setting("filter_languages", item.from_channel)
if not status_language:
status_language = 0
set_language = {"id": "language", "label": config.get_localized_string(60080), "color": "0xffffff99",
"type": "list", "default": 0, "enabled": "eq(-1,true)", "visible": True,
"lvalues": get_languages(item.from_channel)}
list_controls.append(set_language)
dict_values['language'] = status_language
separador = {"id": "label", "label": " "
"_________________________________________________________________________________________",
"type": "label", "enabled": True, "visible": True}
list_controls.append(separador)
# Seccion servidores favoritos
server_list = channel_node.get("servers", [])
if not server_list:
enabled = False
server_list = ["No disponible"]
else:
enabled = "eq(-3,true)"
custom_servers_settings = {"id": "custom_servers", "label": config.get_localized_string(60081), "color": "0xff66ffcc",
"type": "bool", "default": False, "enabled": enabled, "visible": True}
list_controls.append(custom_servers_settings)
if dict_values['active'] and enabled:
dict_values['custom_servers'] = settings_node.get('custom_servers', False)
else:
dict_values['custom_servers'] = False
for num in range(1, 4):
pos1 = num + 3
default = num - 1
if default > len(server_list) - 1:
default = 0
set_servers = {"id": "server_%s" % num, "label": u" \u2665" + config.get_localized_string(60082) % num,
"color": "0xfffcab14", "type": "list", "default": default,
"enabled": "eq(-%s,true)+eq(-%s,true)" % (pos1, num), "visible": True,
"lvalues": server_list}
list_controls.append(set_servers)
dict_values["server_%s" % num] = settings_node.get("server_%s" % num, 0)
if settings_node.get("server_%s" % num, 0) > len(server_list) - 1:
dict_values["server_%s" % num] = 0
# Seccion Calidades favoritas
quality_list = channel_node.get("quality", [])
if not quality_list:
enabled = False
quality_list = ["No disponible"]
else:
enabled = "eq(-7,true)"
custom_quality_settings = {"id": "custom_quality", "label": config.get_localized_string(60083), "color": "0xff66ffcc",
"type": "bool", "default": False, "enabled": enabled, "visible": True}
list_controls.append(custom_quality_settings)
if dict_values['active'] and enabled:
dict_values['custom_quality'] = settings_node.get('custom_quality', False)
else:
dict_values['custom_quality'] = False
for num in range(1, 4):
pos1 = num + 7
default = num - 1
if default > len(quality_list) - 1:
default = 0
set_quality = {"id": "quality_%s" % num, "label": u" \u2665 Calidad Favorita %s" % num,
"color": "0xfff442d9", "type": "list", "default": default,
"enabled": "eq(-%s,true)+eq(-%s,true)" % (pos1, num), "visible": True,
"lvalues": quality_list}
list_controls.append(set_quality)
dict_values["quality_%s" % num] = settings_node.get("quality_%s" % num, 0)
if settings_node.get("quality_%s" % num, 0) > len(quality_list) - 1:
dict_values["quality_%s" % num] = 0
# Plan B
dict_values['plan_b'] = settings_node.get('plan_b', False)
enabled = "eq(-4,true)|eq(-8,true)"
plan_b = {"id": "plan_b", "label": config.get_localized_string(70172),
"color": "0xffffff99",
"type": "bool", "default": False, "enabled": enabled, "visible": True}
list_controls.append(plan_b)
# Seccion Prioridades
priority_list = [config.get_localized_string(70174), config.get_localized_string(70175)]
set_priority = {"id": "priority", "label": config.get_localized_string(60085),
"color": "0xffffff99", "type": "list", "default": 0,
"enabled": True, "visible": "eq(-5,true)+eq(-9,true)+eq(-12,true)", "lvalues": priority_list}
list_controls.append(set_priority)
dict_values["priority"] = settings_node.get("priority", 0)
# Abrir cuadro de dialogo
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,
'function': "reset",
'close': True,
'label': 'Reset'})
def save(item, dict_data_saved):
'''
Guarda los datos de la ventana de configuracion
:param item: item
:param dict_data_saved: dict
:return:
'''
logger.info()
global autoplay_node
if not autoplay_node:
# Obtiene el nodo AUTOPLAY desde el json
autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY')
new_config = dict_data_saved
if not new_config['active']:
new_config['language']=0
channel_node = autoplay_node.get(item.from_channel)
config.set_setting("filter_languages", dict_data_saved.pop("language"), item.from_channel)
channel_node['settings'] = dict_data_saved
result, json_data = jsontools.update_node(autoplay_node, 'autoplay', 'AUTOPLAY')
return result
def get_languages(channel):
'''
Obtiene los idiomas desde el json del canal
:param channel: str
:return: list
'''
logger.info()
list_language = ['No filtrar']
list_controls, dict_settings = channeltools.get_channel_controls_settings(channel)
for control in list_controls:
try:
if control["id"] == 'filter_languages':
list_language = control["lvalues"]
except:
pass
return list_language
def is_active(channel):
'''
Devuelve un booleano q indica si esta activo o no autoplay en el canal desde el que se llama
:return: True si esta activo autoplay para el canal desde el que se llama, False en caso contrario.
'''
logger.info()
global autoplay_node
if not config.is_xbmc():
return False
if not autoplay_node:
# Obtiene el nodo AUTOPLAY desde el json
autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY')
# Obtine el canal desde el q se hace la llamada
#import inspect
#module = inspect.getmodule(inspect.currentframe().f_back)
#canal = module.__name__.split('.')[1]
canal = channel
# Obtiene el nodo del canal desde autoplay_node
channel_node = autoplay_node.get(canal, {})
# Obtiene los ajustes des autoplay para este canal
settings_node = channel_node.get('settings', {})
return settings_node.get('active', False) or get_setting('autoplay')
def reset(item, dict):
channel_name = item.from_channel
channel = __import__('channels.%s' % channel_name, fromlist=["channels.%s" % channel_name])
list_servers = channel.list_servers
list_quality = channel.list_quality
init(channel_name, list_servers, list_quality, reset=True)
platformtools.dialog_notification('AutoPlay', config.get_localized_string(70523) % item.category)
return
def set_status(status):
logger.info()
# Obtiene el nodo AUTOPLAY desde el json
autoplay_node = jsontools.get_node_from_file('autoplay', 'AUTOPLAY')
autoplay_node['status'] = status
result, json_data = jsontools.update_node(autoplay_node, 'autoplay', 'AUTOPLAY')
def play_multi_channel(item, itemlist):
logger.info()
global PLAYED
actual_channel = ''
channel_videos = []
video_dict = dict()
set_status(True)
for video_item in itemlist:
if video_item.contentChannel != actual_channel:
actual_channel = video_item.contentChannel
elif is_active(actual_channel):
channel_videos.append(video_item)
video_dict[actual_channel] = channel_videos
for channel, videos in video_dict.items():
item.contentChannel = channel
if not PLAYED:
start(videos, item)
else:
break
+167
View File
@@ -0,0 +1,167 @@
# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------------
# autorenumber - Rinomina Automaticamente gli Episodi
# --------------------------------------------------------------------------------
import os
try:
import xbmcgui
except:
xbmcgui = None
from platformcode import config
from core import jsontools, tvdb
from core.item import Item
from platformcode import platformtools
from core.support import typo, log
TAG_TVSHOW_RENUMERATE = "TVSHOW_AUTORENUMBER"
TAG_SEASON_EPISODE = "season_episode"
__channel__ = "autorenumber"
def access():
allow = False
if config.is_xbmc():
allow = True
return allow
def context():
if access():
_context = [{"title": config.get_localized_string(70585),
"action": "config_item",
"channel": "autorenumber"}]
return _context
def config_item(item):
log(item)
tvdb.find_and_set_infoLabels(item)
data = ''
data = add_season(data)
title = item.show
count = 0
while not item.infoLabels['tvdb_id']:
try:
item.show = platformtools.dialog_input(default=item.show, heading=config.get_localized_string(30112))
tvdb.find_and_set_infoLabels(item)
count = count + 1
except:
heading = config.get_localized_string(70704)
item.infoLabels['tvdb_id'] = platformtools.dialog_numeric(0, heading)
data.append(item.infoLabels['tvdb_id'])
if item.infoLabels['tvdb_id'] != 0:
write_data(item.from_channel, title, data)
else:
message = config.get_localized_string(60444)
heading = item.show.strip()
platformtools.dialog_notification(heading, message)
def add_season(data=None):
log("data= ", data)
heading = config.get_localized_string(70686)
season = platformtools.dialog_numeric(0, heading)
if season != "":
heading = config.get_localized_string(70687)
episode = platformtools.dialog_numeric(0, heading)
if episode == "0":
heading = config.get_localized_string(70688)
special = platformtools.dialog_numeric(0, heading)
return [int(season), int(episode), int(special)]
elif episode != '':
return [int(season), int(episode), '']
def write_data(channel, show, data):
log()
dict_series = jsontools.get_node_from_file(channel, TAG_TVSHOW_RENUMERATE)
tvshow = show.strip()
# list_season_episode = dict_series.get(tvshow, {}).get(TAG_SEASON_EPISODE, [])
if data:
dict_renumerate = {TAG_SEASON_EPISODE: data}
dict_series[tvshow] = dict_renumerate
else:
dict_series.pop(tvshow, None)
result = jsontools.update_node(dict_series, channel, TAG_TVSHOW_RENUMERATE)[0]
if result:
if data:
message = config.get_localized_string(60446)
else:
message = config.get_localized_string(60444)
else:
message = config.get_localized_string(70593)
heading = show.strip()
platformtools.dialog_notification(heading, message)
def renumber(itemlist, item='', typography=''):
log()
if item:
try:
dict_series = jsontools.get_node_from_file(item.channel, TAG_TVSHOW_RENUMERATE)
SERIES = dict_series[item.show.rstrip()]['season_episode']
S = SERIES[0]
E = SERIES[1]
SP = SERIES[2]
ID = SERIES[3]
page = 1
epList = []
exist = True
item.infoLabels['tvdb_id'] = ID
tvdb.set_infoLabels_item(item)
while exist:
data = tvdb.otvdb_global.get_list_episodes(ID,page)
if data:
for episodes in data['data']:
if episodes['airedSeason'] >= S:
if E == 0:
epList.append([0, SP])
E = 1
if episodes['airedEpisodeNumber'] >= E or episodes['airedSeason'] > S:
epList.append([episodes['airedSeason'], episodes['airedEpisodeNumber']])
page = page + 1
else:
exist = False
epList.sort()
ep = 0
for item in itemlist:
s = str(epList[ep][0])
e = str(epList[ep][1])
item.title = typo(s + 'x'+ e + ' - ', typography) + item.title
ep = ep + 1
except:
return itemlist
else:
for item in itemlist:
if item.contentType != 'movie':
if item.context:
context2 = item.context
item.context = context() + context2
else:
item.context = context()
return itemlist
+174
View File
@@ -0,0 +1,174 @@
{
"id": "downloads",
"name": "Descargas",
"active": false,
"adult": false,
"language": ["*"],
"categories": [
"movie"
],
"settings": [
{
"type": "label",
"label": "@70229",
"enabled": true,
"visible": true
},
{
"id": "library_add",
"type": "bool",
"label": "@70230",
"default": false,
"enabled": true,
"visible": true
},
{
"id": "library_move",
"type": "bool",
"label": "@70231",
"default": false,
"enabled": "eq(-1,true)",
"visible": true
},
{
"id": "browser",
"type": "bool",
"label": "@70232",
"default": true,
"enabled": true,
"visible": true
},
{
"type": "label",
"label": "@70243",
"enabled": true,
"visible": true
},
{
"id": "block_size",
"type": "list",
"label": "@70233",
"lvalues": [
"128 KB",
"256 KB",
"512 KB",
"1 MB",
"2 MB"
],
"default": 1,
"enabled": true,
"visible": true
},
{
"id": "part_size",
"type": "list",
"label": "@70234",
"lvalues": [
"1 MB",
"2 MB",
"4 MB",
"8 MB",
"16 MB",
"32 MB"
],
"default": 1,
"enabled": true,
"visible": true
},
{
"id": "max_connections",
"type": "list",
"label": "@70235",
"lvalues": [
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10"
],
"default": 4,
"enabled": true,
"visible": true
},
{
"id": "max_buffer",
"type": "list",
"label": "@70236",
"lvalues": [
"0",
"2",
"4",
"6",
"8",
"10",
"12",
"14",
"16",
"18",
"20"
],
"default": 5,
"enabled": true,
"visible": true
},
{
"type": "label",
"label": "@70237",
"enabled": true,
"visible": true
},
{
"id": "server_reorder",
"type": "list",
"label": "@70238",
"lvalues": [
"@70244",
"@70245"
],
"default": 0,
"enabled": true,
"visible": true
},
{
"id": "language",
"type": "list",
"label": "@70246",
"lvalues": [
"Ita, Sub, Eng, Vos, Vosi",
"Eng, Ita, Sub, Vos, Vosi",
"Sub, Ita, Eng, Vos, Vosi",
"Eng, Sub, Ita, Vos, Vosi"
],
"default": 0,
"enabled": "eq(-1,'@70245')",
"visible": true
},
{
"id": "quality",
"type": "list",
"label": "@70240",
"lvalues": [
"@70241",
"HD 1080",
"HD 720",
"SD"
],
"default": 0,
"enabled": "eq(-2,'Reordenar')",
"visible": true
},
{
"id": "server_speed",
"type": "bool",
"label": "@70242",
"default": true,
"enabled": "eq(-3,'Reordenar')",
"visible": true
}
]
}
+863
View File
@@ -0,0 +1,863 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Gestor de descargas
# ------------------------------------------------------------
import os
import re
import time
import unicodedata
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
STATUS_COLORS = {0: "orange", 1: "orange", 2: "green", 3: "red"}
STATUS_CODES = type("StatusCode", (), {"stoped": 0, "canceled": 1, "completed": 2, "error": 3})
DOWNLOAD_LIST_PATH = config.get_setting("downloadlistpath")
DOWNLOAD_PATH = config.get_setting("downloadpath")
STATS_FILE = os.path.join(config.get_data_path(), "servers.json")
TITLE_FILE = "[COLOR %s][%i%%][/COLOR] %s"
TITLE_TVSHOW = "[COLOR %s][%i%%][/COLOR] %s [%s]"
def mainlist(item):
logger.info()
itemlist = []
# Lista de archivos
for file in sorted(filetools.listdir(DOWNLOAD_LIST_PATH)):
# Saltamos todos los que no sean JSON
if not file.endswith(".json"): continue
# cargamos el item
file = os.path.join(DOWNLOAD_LIST_PATH, file)
i = Item(path=file).fromjson(filetools.read(file))
i.thumbnail = i.contentThumbnail
# Listado principal
if not item.contentType == "tvshow":
# Series
if i.contentType == "episode":
# Comprobamos que la serie no este ya en el itemlist
if not filter(
lambda x: x.contentSerieName == i.contentSerieName and x.contentChannel == i.contentChannel,
itemlist):
title = TITLE_TVSHOW % (
STATUS_COLORS[i.downloadStatus], i.downloadProgress, i.contentSerieName, i.contentChannel)
itemlist.append(Item(title=title, channel="downloads", action="mainlist", contentType="tvshow",
contentSerieName=i.contentSerieName, contentChannel=i.contentChannel,
downloadStatus=i.downloadStatus, downloadProgress=[i.downloadProgress],
fanart=i.fanart, thumbnail=i.thumbnail))
else:
s = \
filter(
lambda x: x.contentSerieName == i.contentSerieName and x.contentChannel == i.contentChannel,
itemlist)[0]
s.downloadProgress.append(i.downloadProgress)
downloadProgress = sum(s.downloadProgress) / len(s.downloadProgress)
if not s.downloadStatus in [STATUS_CODES.error, STATUS_CODES.canceled] and not i.downloadStatus in [
STATUS_CODES.completed, STATUS_CODES.stoped]:
s.downloadStatus = i.downloadStatus
s.title = TITLE_TVSHOW % (
STATUS_COLORS[s.downloadStatus], downloadProgress, i.contentSerieName, i.contentChannel)
# Peliculas
elif i.contentType == "movie" or i.contentType == "video":
i.title = TITLE_FILE % (STATUS_COLORS[i.downloadStatus], i.downloadProgress, i.contentTitle)
itemlist.append(i)
# Listado dentro de una serie
else:
if i.contentType == "episode" and i.contentSerieName == item.contentSerieName and i.contentChannel == item.contentChannel:
i.title = TITLE_FILE % (STATUS_COLORS[i.downloadStatus], i.downloadProgress,
"%dx%0.2d: %s" % (i.contentSeason, i.contentEpisodeNumber, i.contentTitle))
itemlist.append(i)
estados = [i.downloadStatus for i in itemlist]
# Si hay alguno completado
if 2 in estados:
itemlist.insert(0, Item(channel=item.channel, action="clean_ready", title=config.get_localized_string(70218),
contentType=item.contentType, contentChannel=item.contentChannel,
contentSerieName=item.contentSerieName, text_color="sandybrown"))
# Si hay alguno con error
if 3 in estados:
itemlist.insert(0, Item(channel=item.channel, action="restart_error", title=config.get_localized_string(70219),
contentType=item.contentType, contentChannel=item.contentChannel,
contentSerieName=item.contentSerieName, text_color="orange"))
# Si hay alguno pendiente
if 1 in estados or 0 in estados:
itemlist.insert(0, Item(channel=item.channel, action="download_all", title=support.typo(config.get_localized_string(70220),'bold'),
contentType=item.contentType, contentChannel=item.contentChannel,
contentSerieName=item.contentSerieName))
if len(itemlist):
itemlist.insert(0, Item(channel=item.channel, action="clean_all", title=support.typo(config.get_localized_string(70221),'bold'),
contentType=item.contentType, contentChannel=item.contentChannel,
contentSerieName=item.contentSerieName))
if not item.contentType == "tvshow" and config.get_setting("browser", "downloads") == True:
itemlist.insert(0, Item(channel=item.channel, action="browser", title=support.typo(config.get_localized_string(70222),'bold'),url=DOWNLOAD_PATH))
if not item.contentType == "tvshow":
itemlist.insert(0, Item(channel=item.channel, action="settings", title= support.typo(config.get_localized_string(70223),'bold color kod')))
return itemlist
def settings(item):
ret = platformtools.show_channel_settings(caption=config.get_localized_string(70224))
platformtools.itemlist_refresh()
return ret
def browser(item):
logger.info()
itemlist = []
for file in filetools.listdir(item.url):
if file == "list": continue
if filetools.isdir(filetools.join(item.url, file)):
itemlist.append(
Item(channel=item.channel, title=file, action=item.action, url=filetools.join(item.url, file)))
else:
itemlist.append(Item(channel=item.channel, title=file, action="play", url=filetools.join(item.url, file)))
return itemlist
def clean_all(item):
logger.info()
for fichero in sorted(filetools.listdir(DOWNLOAD_LIST_PATH)):
if fichero.endswith(".json"):
download_item = Item().fromjson(filetools.read(os.path.join(DOWNLOAD_LIST_PATH, fichero)))
if not item.contentType == "tvshow" or (
item.contentSerieName == download_item.contentSerieName and item.contentChannel == download_item.contentChannel):
filetools.remove(os.path.join(DOWNLOAD_LIST_PATH, fichero))
platformtools.itemlist_refresh()
def clean_ready(item):
logger.info()
for fichero in sorted(filetools.listdir(DOWNLOAD_LIST_PATH)):
if fichero.endswith(".json"):
download_item = Item().fromjson(filetools.read(os.path.join(DOWNLOAD_LIST_PATH, fichero)))
if not item.contentType == "tvshow" or (
item.contentSerieName == download_item.contentSerieName and item.contentChannel == download_item.contentChannel):
if download_item.downloadStatus == STATUS_CODES.completed:
filetools.remove(os.path.join(DOWNLOAD_LIST_PATH, fichero))
platformtools.itemlist_refresh()
def restart_error(item):
logger.info()
for fichero in sorted(filetools.listdir(DOWNLOAD_LIST_PATH)):
if fichero.endswith(".json"):
download_item = Item().fromjson(filetools.read(os.path.join(DOWNLOAD_LIST_PATH, fichero)))
if not item.contentType == "tvshow" or (
item.contentSerieName == download_item.contentSerieName and item.contentChannel == download_item.contentChannel):
if download_item.downloadStatus == STATUS_CODES.error:
if filetools.isfile(
os.path.join(config.get_setting("downloadpath"), download_item.downloadFilename)):
filetools.remove(
os.path.join(config.get_setting("downloadpath"), download_item.downloadFilename))
update_json(item.path,
{"downloadStatus": STATUS_CODES.stoped, "downloadComplete": 0, "downloadProgress": 0})
platformtools.itemlist_refresh()
def download_all(item):
time.sleep(0.5)
for fichero in sorted(filetools.listdir(DOWNLOAD_LIST_PATH)):
if fichero.endswith(".json"):
download_item = Item(path=os.path.join(DOWNLOAD_LIST_PATH, fichero)).fromjson(
filetools.read(os.path.join(DOWNLOAD_LIST_PATH, fichero)))
if not item.contentType == "tvshow" or (
item.contentSerieName == download_item.contentSerieName and item.contentChannel == download_item.contentChannel):
if download_item.downloadStatus in [STATUS_CODES.stoped, STATUS_CODES.canceled]:
res = start_download(download_item)
platformtools.itemlist_refresh()
# Si se ha cancelado paramos
if res == STATUS_CODES.canceled: break
def menu(item):
logger.info()
if item.downloadServer:
servidor = item.downloadServer.get("server", "Auto")
else:
servidor = "Auto"
# Opciones disponibles para el menu
op = [config.get_localized_string(70225), config.get_localized_string(70226), config.get_localized_string(70227),
"Modificar servidor: %s" % (servidor.capitalize())]
opciones = []
# Opciones para el menu
if item.downloadStatus == 0: # Sin descargar
opciones.append(op[0]) # Descargar
if not item.server: opciones.append(op[3]) # Elegir Servidor
opciones.append(op[1]) # Eliminar de la lista
if item.downloadStatus == 1: # descarga parcial
opciones.append(op[0]) # Descargar
if not item.server: opciones.append(op[3]) # Elegir Servidor
opciones.append(op[2]) # Reiniciar descarga
opciones.append(op[1]) # Eliminar de la lista
if item.downloadStatus == 2: # descarga completada
opciones.append(op[1]) # Eliminar de la lista
opciones.append(op[2]) # Reiniciar descarga
if item.downloadStatus == 3: # descarga con error
opciones.append(op[2]) # Reiniciar descarga
opciones.append(op[1]) # Eliminar de la lista
# Mostramos el dialogo
seleccion = platformtools.dialog_select(config.get_localized_string(30163), opciones)
# -1 es cancelar
if seleccion == -1: return
logger.info("opcion=%s" % (opciones[seleccion]))
# Opcion Eliminar
if opciones[seleccion] == op[1]:
filetools.remove(item.path)
# Opcion inicaiar descarga
if opciones[seleccion] == op[0]:
start_download(item)
# Elegir Servidor
if opciones[seleccion] == op[3]:
select_server(item)
# Reiniciar descarga
if opciones[seleccion] == op[2]:
if filetools.isfile(os.path.join(config.get_setting("downloadpath"), item.downloadFilename)):
filetools.remove(os.path.join(config.get_setting("downloadpath"), item.downloadFilename))
update_json(item.path, {"downloadStatus": STATUS_CODES.stoped, "downloadComplete": 0, "downloadProgress": 0,
"downloadServer": {}})
platformtools.itemlist_refresh()
def move_to_libray(item):
download_path = filetools.join(config.get_setting("downloadpath"), item.downloadFilename)
library_path = filetools.join(config.get_videolibrary_path(), *filetools.split(item.downloadFilename))
final_path = download_path
if config.get_setting("library_add", "downloads") == True and config.get_setting("library_move", "downloads") == True:
if not filetools.isdir(filetools.dirname(library_path)):
filetools.mkdir(filetools.dirname(library_path))
if filetools.isfile(library_path) and filetools.isfile(download_path):
filetools.remove(library_path)
if filetools.isfile(download_path):
if filetools.move(download_path, library_path):
final_path = library_path
if len(filetools.listdir(filetools.dirname(download_path))) == 0:
filetools.rmdir(filetools.dirname(download_path))
if config.get_setting("library_add", "downloads") == True:
if filetools.isfile(final_path):
if item.contentType == "movie" and item.infoLabels["tmdb_id"]:
library_item = Item(title=config.get_localized_string(70228) % item.downloadFilename, channel="downloads",
action="findvideos", infoLabels=item.infoLabels, url=final_path)
videolibrarytools.save_movie(library_item)
elif item.contentType == "episode" and item.infoLabels["tmdb_id"]:
library_item = Item(title=config.get_localized_string(70228) % item.downloadFilename, channel="downloads",
action="findvideos", infoLabels=item.infoLabels, url=final_path)
tvshow = Item(channel="downloads", contentType="tvshow",
infoLabels={"tmdb_id": item.infoLabels["tmdb_id"]})
videolibrarytools.save_tvshow(tvshow, [library_item])
def update_json(path, params):
item = Item().fromjson(filetools.read(path))
item.__dict__.update(params)
filetools.write(path, item.tojson())
def save_server_statistics(server, speed, success):
if os.path.isfile(STATS_FILE):
servers = jsontools.load(open(STATS_FILE, "rb").read())
else:
servers = {}
if not server in servers:
servers[server] = {"success": [], "count": 0, "speeds": [], "last": 0}
servers[server]["count"] += 1
servers[server]["success"].append(bool(success))
servers[server]["success"] = servers[server]["success"][-5:]
servers[server]["last"] = time.time()
if success:
servers[server]["speeds"].append(speed)
servers[server]["speeds"] = servers[server]["speeds"][-5:]
open(STATS_FILE, "wb").write(jsontools.dump(servers))
return
def get_server_position(server):
if os.path.isfile(STATS_FILE):
servers = jsontools.load(open(STATS_FILE, "rb").read())
else:
servers = {}
if server in servers:
pos = [s for s in sorted(servers, key=lambda x: (sum(servers[x]["speeds"]) / (len(servers[x]["speeds"]) or 1),
float(sum(servers[x]["success"])) / (
len(servers[x]["success"]) or 1)), reverse=True)]
return pos.index(server) + 1
else:
return 0
def get_match_list(data, match_list, order_list=None, only_ascii=False, ignorecase=False):
"""
Busca coincidencias en una cadena de texto, con un diccionario de "ID" / "Listado de cadenas de busqueda":
{ "ID1" : ["Cadena 1", "Cadena 2", "Cadena 3"],
"ID2" : ["Cadena 4", "Cadena 5", "Cadena 6"]
}
El diccionario no pude contener una misma cadena de busqueda en varías IDs.
La busqueda se realiza por orden de tamaño de cadena de busqueda (de mas larga a mas corta) si una cadena coincide,
se elimina de la cadena a buscar para las siguientes, para que no se detecten dos categorias si una cadena es parte de otra:
por ejemplo: "Idioma Español" y "Español" si la primera aparece en la cadena "Pablo sabe hablar el Idioma Español"
coincidira con "Idioma Español" pero no con "Español" ya que la coincidencia mas larga tiene prioridad.
"""
match_dict = dict()
matches = []
# Pasamos la cadena a unicode
data = unicode(data, "utf8")
# Pasamos el diccionario a {"Cadena 1": "ID1", "Cadena 2", "ID1", "Cadena 4", "ID2"} y los pasamos a unicode
for key in match_list:
if order_list and not key in order_list:
raise Exception("key '%s' not in match_list" % key)
for value in match_list[key]:
if value in match_dict:
raise Exception("Duplicate word in list: '%s'" % value)
match_dict[unicode(value, "utf8")] = key
# Si ignorecase = True, lo pasamos todo a mayusculas
if ignorecase:
data = data.upper()
match_dict = dict((key.upper(), match_dict[key]) for key in match_dict)
# Si ascii = True, eliminamos todos los accentos y Ñ
if only_ascii:
data = ''.join((c for c in unicodedata.normalize('NFD', data) if unicodedata.category(c) != 'Mn'))
match_dict = dict((''.join((c for c in unicodedata.normalize('NFD', key) if unicodedata.category(c) != 'Mn')),
match_dict[key]) for key in match_dict)
# Ordenamos el listado de mayor tamaño a menor y buscamos.
for match in sorted(match_dict, key=lambda x: len(x), reverse=True):
s = data
for a in matches:
s = s.replace(a, "")
if match in s:
matches.append(match)
if matches:
if order_list:
return type("Mtch_list", (),
{"key": match_dict[matches[-1]], "index": order_list.index(match_dict[matches[-1]])})
else:
return type("Mtch_list", (), {"key": match_dict[matches[-1]], "index": None})
else:
if order_list:
return type("Mtch_list", (), {"key": None, "index": len(order_list)})
else:
return type("Mtch_list", (), {"key": None, "index": None})
def sort_method(item):
"""
Puntua cada item en funcion de varios parametros:
@type item: item
@param item: elemento que se va a valorar.
@return: puntuacion otenida
@rtype: int
"""
lang_orders = {}
lang_orders[0] = ["ES", "LAT", "SUB", "ENG", "VOSE"]
lang_orders[1] = ["ES", "SUB", "LAT", "ENG", "VOSE"]
lang_orders[2] = ["ENG", "SUB", "VOSE", "ESP", "LAT"]
lang_orders[3] = ["VOSE", "ENG", "SUB", "ESP", "LAT"]
quality_orders = {}
quality_orders[0] = ["BLURAY", "FULLHD", "HD", "480P", "360P", "240P"]
quality_orders[1] = ["FULLHD", "HD", "480P", "360P", "240P", "BLURAY"]
quality_orders[2] = ["HD", "480P", "360P", "240P", "FULLHD", "BLURAY"]
quality_orders[3] = ["480P", "360P", "240P", "BLURAY", "FULLHD", "HD"]
order_list_idiomas = lang_orders[int(config.get_setting("language", "downloads"))]
match_list_idimas = {"ES": ["CAST", "ESP", "Castellano", "Español", "Audio Español"],
"LAT": ["LAT", "Latino"],
"SUB": ["Subtitulo Español", "Subtitulado", "SUB"],
"ENG": ["EN", "ENG", "Inglés", "Ingles", "English"],
"VOSE": ["VOSE"]}
order_list_calidad = ["BLURAY", "FULLHD", "HD", "480P", "360P", "240P"]
order_list_calidad = quality_orders[int(config.get_setting("quality", "downloads"))]
match_list_calidad = {"BLURAY": ["BR", "BLURAY"],
"FULLHD": ["FULLHD", "FULL HD", "1080", "HD1080", "HD 1080"],
"HD": ["HD", "HD REAL", "HD 720", "720", "HDTV"],
"480P": ["SD", "480P"],
"360P": ["360P"],
"240P": ["240P"]}
value = (get_match_list(item.title, match_list_idimas, order_list_idiomas, ignorecase=True, only_ascii=True).index, \
get_match_list(item.title, match_list_calidad, order_list_calidad, ignorecase=True, only_ascii=True).index)
if config.get_setting("server_speed", "downloads"):
value += tuple([get_server_position(item.server)])
return value
def download_from_url(url, item):
logger.info("Intentando descargar: %s" % (url))
if url.lower().endswith(".m3u8") or url.lower().startswith("rtmp"):
save_server_statistics(item.server, 0, False)
return {"downloadStatus": STATUS_CODES.error}
# Obtenemos la ruta de descarga y el nombre del archivo
item.downloadFilename = item.downloadFilename.replace('/','-')
download_path = filetools.dirname(filetools.join(DOWNLOAD_PATH, item.downloadFilename))
file_name = filetools.basename(filetools.join(DOWNLOAD_PATH, item.downloadFilename))
# Creamos la carpeta si no existe
if not filetools.exists(download_path):
filetools.mkdir(download_path)
# Lanzamos la descarga
d = Downloader(url, download_path, file_name,
max_connections=1 + int(config.get_setting("max_connections", "downloads")),
block_size=2 ** (17 + int(config.get_setting("block_size", "downloads"))),
part_size=2 ** (20 + int(config.get_setting("part_size", "downloads"))),
max_buffer=2 * int(config.get_setting("max_buffer", "downloads")))
d.start_dialog(config.get_localized_string(60332))
# Descarga detenida. Obtenemos el estado:
# Se ha producido un error en la descarga
if d.state == d.states.error:
logger.info("Error al intentar descargar %s" % (url))
status = STATUS_CODES.error
# La descarga se ha detenifdo
elif d.state == d.states.stopped:
logger.info("Descarga detenida")
status = STATUS_CODES.canceled
# La descarga ha finalizado
elif d.state == d.states.completed:
logger.info("Descargado correctamente")
status = STATUS_CODES.completed
if item.downloadSize and item.downloadSize != d.size[0]:
status = STATUS_CODES.error
save_server_statistics(item.server, d.speed[0], d.state != d.states.error)
dir = os.path.dirname(item.downloadFilename)
file = filetools.join(dir, d.filename)
if status == STATUS_CODES.completed:
move_to_libray(item.clone(downloadFilename=file))
return {"downloadUrl": d.download_url, "downloadStatus": status, "downloadSize": d.size[0],
"downloadProgress": d.progress, "downloadCompleted": d.downloaded[0], "downloadFilename": file}
def download_from_server(item):
logger.info(item.tostring())
unsupported_servers = ["torrent"]
progreso = platformtools.dialog_progress(config.get_localized_string(30101), config.get_localized_string(70178) % item.server)
channel = __import__('channels.%s' % item.contentChannel, None, None, ["channels.%s" % item.contentChannel])
if hasattr(channel, "play") and not item.play_menu:
progreso.update(50, config.get_localized_string(70178) % item.server, config.get_localized_string(60003) % item.contentChannel)
try:
itemlist = getattr(channel, "play")(item.clone(channel=item.contentChannel, action=item.contentAction))
except:
logger.error("Error en el canal %s" % item.contentChannel)
else:
if len(itemlist) and isinstance(itemlist[0], Item):
download_item = item.clone(**itemlist[0].__dict__)
download_item.contentAction = download_item.action
download_item.infoLabels = item.infoLabels
item = download_item
elif len(itemlist) and isinstance(itemlist[0], list):
item.video_urls = itemlist
if not item.server: item.server = "directo"
else:
logger.info("No hay nada que reproducir")
return {"downloadStatus": STATUS_CODES.error}
progreso.close()
logger.info("contentAction: %s | contentChannel: %s | server: %s | url: %s" % (
item.contentAction, item.contentChannel, item.server, item.url))
if not item.server or not item.url or not item.contentAction == "play" or item.server in unsupported_servers:
logger.error("El Item no contiene los parametros necesarios.")
return {"downloadStatus": STATUS_CODES.error}
if not item.video_urls:
video_urls, puedes, motivo = servertools.resolve_video_urls_for_playing(item.server, item.url, item.password,
True)
else:
video_urls, puedes, motivo = item.video_urls, True, ""
# Si no esta disponible, salimos
if not puedes:
logger.info("El vídeo **NO** está disponible")
return {"downloadStatus": STATUS_CODES.error}
else:
logger.info("El vídeo **SI** está disponible")
result = {}
# Recorre todas las opciones hasta que consiga descargar una correctamente
for video_url in reversed(video_urls):
result = download_from_url(video_url[1], item)
if result["downloadStatus"] in [STATUS_CODES.canceled, STATUS_CODES.completed]:
break
# Error en la descarga, continuamos con la siguiente opcion
if result["downloadStatus"] == STATUS_CODES.error:
continue
# Devolvemos el estado
return result
def download_from_best_server(item):
logger.info(
"contentAction: %s | contentChannel: %s | url: %s" % (item.contentAction, item.contentChannel, item.url))
result = {"downloadStatus": STATUS_CODES.error}
progreso = platformtools.dialog_progress(config.get_localized_string(30101), config.get_localized_string(70179))
channel = __import__('channels.%s' % item.contentChannel, None, None, ["channels.%s" % item.contentChannel])
progreso.update(50, config.get_localized_string(70184), config.get_localized_string(70180) % item.contentChannel)
if hasattr(channel, item.contentAction):
play_items = getattr(channel, item.contentAction)(
item.clone(action=item.contentAction, channel=item.contentChannel))
else:
play_items = servertools.find_video_items(item.clone(action=item.contentAction, channel=item.contentChannel))
play_items = filter(lambda x: x.action == "play" and not "trailer" in x.title.lower(), play_items)
progreso.update(100, config.get_localized_string(70183), config.get_localized_string(70181) % len(play_items),
config.get_localized_string(70182))
if config.get_setting("server_reorder", "downloads") == 1:
play_items.sort(key=sort_method)
if progreso.iscanceled():
return {"downloadStatus": STATUS_CODES.canceled}
progreso.close()
# Recorremos el listado de servers, hasta encontrar uno que funcione
for play_item in play_items:
play_item = item.clone(**play_item.__dict__)
play_item.contentAction = play_item.action
play_item.infoLabels = item.infoLabels
result = download_from_server(play_item)
if progreso.iscanceled():
result["downloadStatus"] = STATUS_CODES.canceled
# Tanto si se cancela la descarga como si se completa dejamos de probar mas opciones
if result["downloadStatus"] in [STATUS_CODES.canceled, STATUS_CODES.completed]:
result["downloadServer"] = {"url": play_item.url, "server": play_item.server}
break
return result
def select_server(item):
logger.info(
"contentAction: %s | contentChannel: %s | url: %s" % (item.contentAction, item.contentChannel, item.url))
progreso = platformtools.dialog_progress(config.get_localized_string(30101), config.get_localized_string(70179))
channel = __import__('channels.%s' % item.contentChannel, None, None, ["channels.%s" % item.contentChannel])
progreso.update(50, config.get_localized_string(70184), config.get_localized_string(70180) % item.contentChannel)
if hasattr(channel, item.contentAction):
play_items = getattr(channel, item.contentAction)(
item.clone(action=item.contentAction, channel=item.contentChannel))
else:
play_items = servertools.find_video_items(item.clone(action=item.contentAction, channel=item.contentChannel))
play_items = filter(lambda x: x.action == "play" and not "trailer" in x.title.lower(), play_items)
progreso.update(100, config.get_localized_string(70183), config.get_localized_string(70181) % len(play_items),
config.get_localized_string(70182))
for x, i in enumerate(play_items):
if not i.server and hasattr(channel, "play"):
play_items[x] = getattr(channel, "play")(i)
seleccion = platformtools.dialog_select(config.get_localized_string(70192), ["Auto"] + [s.title for s in play_items])
if seleccion > 1:
update_json(item.path, {
"downloadServer": {"url": play_items[seleccion - 1].url, "server": play_items[seleccion - 1].server}})
elif seleccion == 0:
update_json(item.path, {"downloadServer": {}})
platformtools.itemlist_refresh()
def start_download(item):
logger.info(
"contentAction: %s | contentChannel: %s | url: %s" % (item.contentAction, item.contentChannel, item.url))
# Ya tenemnos server, solo falta descargar
if item.contentAction == "play":
ret = download_from_server(item)
update_json(item.path, ret)
return ret["downloadStatus"]
elif item.downloadServer and item.downloadServer.get("server"):
ret = download_from_server(
item.clone(server=item.downloadServer.get("server"), url=item.downloadServer.get("url"),
contentAction="play"))
update_json(item.path, ret)
return ret["downloadStatus"]
# No tenemos server, necesitamos buscar el mejor
else:
ret = download_from_best_server(item)
update_json(item.path, ret)
return ret["downloadStatus"]
def get_episodes(item):
logger.info("contentAction: %s | contentChannel: %s | contentType: %s" % (
item.contentAction, item.contentChannel, item.contentType))
# El item que pretendemos descargar YA es un episodio
if item.contentType == "episode":
episodes = [item.clone()]
# El item es uma serie o temporada
elif item.contentType in ["tvshow", "season"]:
# importamos el canal
channel = __import__('channels.%s' % item.contentChannel, None, None, ["channels.%s" % item.contentChannel])
# Obtenemos el listado de episodios
episodes = getattr(channel, item.contentAction)(item)
itemlist = []
# Tenemos las lista, ahora vamos a comprobar
for episode in episodes:
# Si partiamos de un item que ya era episodio estos datos ya están bien, no hay que modificarlos
if item.contentType != "episode":
episode.contentAction = episode.action
episode.contentChannel = episode.channel
# Si el resultado es una temporada, no nos vale, tenemos que descargar los episodios de cada temporada
if episode.contentType == "season":
itemlist.extend(get_episodes(episode))
# Si el resultado es un episodio ya es lo que necesitamos, lo preparamos para añadirlo a la descarga
if episode.contentType == "episode":
# Pasamos el id al episodio
if not episode.infoLabels["tmdb_id"]:
episode.infoLabels["tmdb_id"] = item.infoLabels["tmdb_id"]
# Episodio, Temporada y Titulo
if not episode.contentSeason or not episode.contentEpisodeNumber:
season_and_episode = scrapertools.get_season_and_episode(episode.title)
if season_and_episode:
episode.contentSeason = season_and_episode.split("x")[0]
episode.contentEpisodeNumber = season_and_episode.split("x")[1]
# Buscamos en tmdb
if item.infoLabels["tmdb_id"]:
scraper.find_and_set_infoLabels(episode)
# Episodio, Temporada y Titulo
if not episode.contentTitle:
episode.contentTitle = re.sub("\[[^\]]+\]|\([^\)]+\)|\d*x\d*\s*-", "", episode.title).strip()
episode.downloadFilename = filetools.validate_path(os.path.join(item.downloadFilename, "%dx%0.2d - %s" % (
episode.contentSeason, episode.contentEpisodeNumber, episode.contentTitle.strip())))
itemlist.append(episode)
# Cualquier otro resultado no nos vale, lo ignoramos
else:
logger.info("Omitiendo item no válido: %s" % episode.tostring())
return itemlist
def write_json(item):
logger.info()
item.action = "menu"
item.channel = "downloads"
item.downloadStatus = STATUS_CODES.stoped
item.downloadProgress = 0
item.downloadSize = 0
item.downloadCompleted = 0
if not item.contentThumbnail:
item.contentThumbnail = item.thumbnail
for name in ["text_bold", "text_color", "text_italic", "context", "totalItems", "viewmode", "title", "fulltitle",
"thumbnail"]:
if item.__dict__.has_key(name):
item.__dict__.pop(name)
path = os.path.join(config.get_setting("downloadlistpath"), str(time.time()) + ".json")
filetools.write(path, item.tojson())
item.path = path
time.sleep(0.1)
def save_download(item):
logger.info()
# Menu contextual
if item.from_action and item.from_channel:
item.channel = item.from_channel
item.action = item.from_action
del item.from_action
del item.from_channel
item.contentChannel = item.channel
item.contentAction = item.action
if item.contentType in ["tvshow", "episode", "season"]:
save_download_tvshow(item)
elif item.contentType == "movie":
save_download_movie(item)
else:
save_download_video(item)
def save_download_video(item):
logger.info("contentAction: %s | contentChannel: %s | contentTitle: %s" % (
item.contentAction, item.contentChannel, item.contentTitle))
set_movie_title(item)
item.downloadFilename = filetools.validate_path("%s [%s]" % (item.contentTitle.strip(), item.contentChannel))
write_json(item)
if not platformtools.dialog_yesno(config.get_localized_string(30101), config.get_localized_string(70189)):
platformtools.dialog_ok(config.get_localized_string(30101), item.contentTitle,
config.get_localized_string(30109))
else:
start_download(item)
def save_download_movie(item):
logger.info("contentAction: %s | contentChannel: %s | contentTitle: %s" % (
item.contentAction, item.contentChannel, item.contentTitle))
progreso = platformtools.dialog_progress(config.get_localized_string(30101), config.get_localized_string(70191))
set_movie_title(item)
result = scraper.find_and_set_infoLabels(item)
if not result:
progreso.close()
return save_download_video(item)
progreso.update(0, config.get_localized_string(60062))
item.downloadFilename = filetools.validate_path("%s [%s]" % (item.contentTitle.strip(), item.contentChannel))
write_json(item)
progreso.close()
if not platformtools.dialog_yesno(config.get_localized_string(30101), config.get_localized_string(70189)):
platformtools.dialog_ok(config.get_localized_string(30101), item.contentTitle,
config.get_localized_string(30109))
else:
start_download(item)
def save_download_tvshow(item):
logger.info("contentAction: %s | contentChannel: %s | contentType: %s | contentSerieName: %s" % (
item.contentAction, item.contentChannel, item.contentType, item.contentSerieName))
progreso = platformtools.dialog_progress(config.get_localized_string(30101), config.get_localized_string(70188))
scraper.find_and_set_infoLabels(item)
item.downloadFilename = filetools.validate_path("%s [%s]" % (item.contentSerieName, item.contentChannel))
progreso.update(0, config.get_localized_string(70186), config.get_localized_string(70187) % item.contentChannel)
episodes = get_episodes(item)
progreso.update(0, config.get_localized_string(70190), " ")
for x, i in enumerate(episodes):
progreso.update(x * 100 / len(episodes),
"%dx%0.2d: %s" % (i.contentSeason, i.contentEpisodeNumber, i.contentTitle))
write_json(i)
progreso.close()
if not platformtools.dialog_yesno(config.get_localized_string(30101), config.get_localized_string(70189)):
platformtools.dialog_ok(config.get_localized_string(30101),
str(len(episodes)) + " capitulos de: " + item.contentSerieName,
config.get_localized_string(30109))
else:
for i in episodes:
res = start_download(i)
if res == STATUS_CODES.canceled:
break
def set_movie_title(item):
if not item.contentTitle:
item.contentTitle = re.sub("\[[^\]]+\]|\([^\)]+\)", "", item.fulltitle).strip()
if not item.contentTitle:
item.contentTitle = re.sub("\[[^\]]+\]|\([^\)]+\)", "", item.title).strip()
+242
View File
@@ -0,0 +1,242 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Lista de vídeos favoritos
# ------------------------------------------------------------
import os
import time
from core import filetools
from core import scrapertools
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
try:
# Fijamos la ruta a favourites.xml
if config.is_xbmc():
import xbmc
FAVOURITES_PATH = xbmc.translatePath("special://profile/favourites.xml")
else:
FAVOURITES_PATH = os.path.join(config.get_data_path(), "favourites.xml")
except:
import traceback
logger.error(traceback.format_exc())
def mainlist(item):
logger.info()
itemlist = []
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", "")
item = Item().fromurl(url)
item.title = name
item.thumbnail = thumb
item.isFavourite = True
if type(item.context) == str:
item.context = item.context.split("|")
elif type(item.context) != list:
item.context = []
item.context.extend([{"title": config.get_localized_string(30154), # "Quitar de favoritos"
"action": "delFavourite",
"channel": "favorites",
"from_title": item.title},
{"title": "Renombrar",
"action": "renameFavourite",
"channel": "favorites",
"from_title": item.title}
])
# logger.debug(item.tostring('\n'))
itemlist.append(item)
return itemlist
def read_favourites():
favourites_list = []
if filetools.exists(FAVOURITES_PATH):
data = filetools.read(FAVOURITES_PATH)
matches = scrapertools.find_multiple_matches(data, "<favourite([^<]*)</favourite>")
for match in matches:
name = scrapertools.find_single_match(match, 'name="([^"]*)')
thumb = scrapertools.find_single_match(match, 'thumb="([^"]*)')
data = scrapertools.find_single_match(match, '[^>]*>([^<]*)')
favourites_list.append((name, thumb, data))
return favourites_list
def save_favourites(favourites_list):
raw = '<favourites>' + chr(10)
for name, thumb, data in favourites_list:
raw += ' <favourite name="%s" thumb="%s">%s</favourite>' % (name, thumb, data) + chr(10)
raw += '</favourites>' + chr(10)
return filetools.write(FAVOURITES_PATH, raw)
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 item.from_action:
item.__dict__["action"] = item.__dict__.pop("from_action")
if item.from_channel:
item.__dict__["channel"] = item.__dict__.pop("from_channel")
favourites_list = read_favourites()
data = "ActivateWindow(10025,&quot;plugin://plugin.video.%s/?" % config.PLUGIN_NAME + item.tourl() + "&quot;,return)"
titulo = item.title.replace('"', "'")
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'
def delFavourite(item):
logger.info()
# logger.debug(item.tostring('\n'))
if item.from_title:
item.title = item.from_title
favourites_list = read_favourites()
for fav in favourites_list[:]:
if fav[0] == item.title:
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.itemlist_refresh()
break
def renameFavourite(item):
logger.info()
# logger.debug(item.tostring('\n'))
# Buscar el item q queremos renombrar en favourites.xml
favourites_list = read_favourites()
for i, fav in enumerate(favourites_list):
if fav[0] == item.from_title:
# abrir el teclado
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.itemlist_refresh()
##################################################
# Funciones para migrar favoritos antiguos (.txt)
def readbookmark(filepath):
logger.info()
import urllib
bookmarkfile = filetools.file_open(filepath)
lines = bookmarkfile.readlines()
try:
titulo = urllib.unquote_plus(lines[0].strip())
except:
titulo = lines[0].strip()
try:
url = urllib.unquote_plus(lines[1].strip())
except:
url = lines[1].strip()
try:
thumbnail = urllib.unquote_plus(lines[2].strip())
except:
thumbnail = lines[2].strip()
try:
server = urllib.unquote_plus(lines[3].strip())
except:
server = lines[3].strip()
try:
plot = urllib.unquote_plus(lines[4].strip())
except:
plot = lines[4].strip()
# Campos fulltitle y canal añadidos
if len(lines) >= 6:
try:
fulltitle = urllib.unquote_plus(lines[5].strip())
except:
fulltitle = lines[5].strip()
else:
fulltitle = titulo
if len(lines) >= 7:
try:
canal = urllib.unquote_plus(lines[6].strip())
except:
canal = lines[6].strip()
else:
canal = ""
bookmarkfile.close()
return canal, titulo, thumbnail, plot, server, url, fulltitle
def check_bookmark(readpath):
# Crea un listado con las entradas de favoritos
itemlist = []
if readpath.startswith("special://") and config.is_xbmc():
import xbmc
readpath = xbmc.translatePath(readpath)
for fichero in sorted(filetools.listdir(readpath)):
# Ficheros antiguos (".txt")
if fichero.endswith(".txt"):
# Esperamos 0.1 segundos entre ficheros, para que no se solapen los nombres de archivo
time.sleep(0.1)
# Obtenemos el item desde el .txt
canal, titulo, thumbnail, plot, server, url, fulltitle = readbookmark(filetools.join(readpath, fichero))
if canal == "":
canal = "favorites"
item = Item(channel=canal, action="play", url=url, server=server, title=fulltitle, thumbnail=thumbnail,
plot=plot, fanart=thumbnail, fulltitle=fulltitle, folder=False)
filetools.rename(filetools.join(readpath, fichero), fichero[:-4] + ".old")
itemlist.append(item)
# Si hay Favoritos q guardar
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")
# Esto solo funcionara al migrar de versiones anteriores, ya no existe "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")
except:
pass
+12
View File
@@ -0,0 +1,12 @@
{
"id": "filmontv",
"name": "Film in tv",
"language": ["ita"],
"active": false,
"adult": false,
"thumbnail": null,
"banner": null,
"categories": [],
"settings": [],
"channel": false
}
+88
View File
@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Canale film in tv
# Ringraziamo Icarus crew
# ------------------------------------------------------------
import re
import urllib
from core import httptools, scrapertools, tmdb, support
from core.item import Item
from platformcode import logger
host = "https://www.comingsoon.it"
TIMEOUT_TOTAL = 60
def mainlist(item):
logger.info(" mainlist")
itemlist = [Item(channel=item.channel,
title=support.typo("IN ONDA ADESSO bold"),
action="tvoggi",
url="%s/filmtv/" % host,
thumbnail=item.thumbnail),
Item(channel=item.channel,
title="Mattina",
action="tvoggi",
url="%s/filmtv/oggi/mattina/" % host,
thumbnail=item.thumbnail),
Item(channel=item.channel,
title="Pomeriggio",
action="tvoggi",
url="%s/filmtv/oggi/pomeriggio/" % host,
thumbnail=item.thumbnail),
Item(channel=item.channel,
title="Sera",
action="tvoggi",
url="%s/filmtv/oggi/sera/" % host,
thumbnail=item.thumbnail),
Item(channel=item.channel,
title="Notte",
action="tvoggi",
url="%s/filmtv/oggi/notte/" % host,
thumbnail=item.thumbnail)]
return itemlist
def tvoggi(item):
logger.info("filmontv tvoggi")
itemlist = []
# Carica la pagina
data = httptools.downloadpage(item.url).data
# Estrae i contenuti
patron = r'<div class="col-xs-5 box-immagine">[^<]+<img src="([^"]+)[^<]+<[^<]+<[^<]+<[^<]+<[^<]+<.*?titolo">(.*?)<[^<]+<[^<]+<[^<]+<[^>]+><br />(.*?)<[^<]+</div>[^<]+<[^<]+<[^<]+<[^>]+>[^<]+<[^<]+<[^<]+<[^>]+><[^<]+<[^>]+>:\s*([^<]+)[^<]+<[^<]+[^<]+<[^<]+[^<]+<[^<]+[^<]+[^>]+>:\s*([^<]+)'
# patron = r'<div class="col-xs-5 box-immagine">[^<]+<img src="([^"]+)[^<]+<[^<]+<[^<]+<[^<]+<[^<]+<.*?titolo">(.*?)<[^<]+<[^<]+<[^<]+<[^>]+><br />(.*?)<[^<]+</div>'
matches = re.compile(patron, re.DOTALL).findall(data)
for scrapedthumbnail, scrapedtitle, scrapedtv, scrapedgender, scrapedyear in matches:
# for scrapedthumbnail, scrapedtitle, scrapedtv in matches:
scrapedurl = ""
scrapedtitle = scrapertools.decodeHtmlentities(scrapedtitle).strip()
infoLabels = {}
infoLabels["year"] = scrapedyear
itemlist.append(
Item(channel=item.channel,
action="do_search",
extra=urllib.quote_plus(scrapedtitle) + '{}' + 'movie',
title=scrapedtitle + "[COLOR yellow] " + scrapedtv + "[/COLOR]",
fulltitle=scrapedtitle,
url=scrapedurl,
thumbnail=scrapedthumbnail,
contentTitle=scrapedtitle,
contentType='movie',
infoLabels=infoLabels,
folder=True))
tmdb.set_infoLabels_itemlist(itemlist, seekTmdb=True)
return itemlist
def do_search(item):
from specials import search
return search.do_search(item)
+632
View File
@@ -0,0 +1,632 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# filtertools - se encarga de filtrar resultados
# ------------------------------------------------------------
from core import jsontools
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
from core import channeltools
TAG_TVSHOW_FILTER = "TVSHOW_FILTER"
TAG_NAME = "name"
TAG_ACTIVE = "active"
TAG_LANGUAGE = "language"
TAG_QUALITY_ALLOWED = "quality_allowed"
COLOR = {"parent_item": "yellow", "error": "red", "striped_even_active": "blue",
"striped_even_inactive": "0xff00bfff", "striped_odd_active": "0xff008000",
"striped_odd_inactive": "0xff00fa9a", "selected": "blue"
}
filter_global = None
__channel__ = "filtertools"
# TODO echar un ojo a https://pyformat.info/, se puede formatear el estilo y hacer referencias directamente a elementos
class ResultFilter:
def __init__(self, dict_filter):
self.active = dict_filter[TAG_ACTIVE]
self.language = dict_filter[TAG_LANGUAGE]
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)
class Filter:
def __init__(self, item, global_filter_lang_id):
self.result = None
self.__get_data(item, global_filter_lang_id)
def __get_data(self, item, global_filter_lang_id):
dict_filtered_shows = jsontools.get_node_from_file(item.channel, TAG_TVSHOW_FILTER)
tvshow = item.show.lower().strip()
global_filter_language = config.get_setting(global_filter_lang_id, item.channel)
if tvshow in dict_filtered_shows.keys():
self.result = ResultFilter({TAG_ACTIVE: dict_filtered_shows[tvshow][TAG_ACTIVE],
TAG_LANGUAGE: dict_filtered_shows[tvshow][TAG_LANGUAGE],
TAG_QUALITY_ALLOWED: dict_filtered_shows[tvshow][TAG_QUALITY_ALLOWED]})
# opcion general "no filtrar"
elif global_filter_language != 0:
from core import channeltools
list_controls, dict_settings = channeltools.get_channel_controls_settings(item.channel)
for control in list_controls:
if control["id"] == global_filter_lang_id:
try:
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))
break
self.result = ResultFilter({TAG_ACTIVE: True, TAG_LANGUAGE: language, TAG_QUALITY_ALLOWED: []})
break
def __str__(self):
return "{'%s'}" % self.result
def access():
"""
Devuelve si se puede usar o no filtertools
"""
allow = False
if config.is_xbmc() or config.get_platform() == "mediaserver":
allow = True
return allow
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-.
@param item: elemento para obtener la información y ver que contexto añadir
@type item: item
param list_language: listado de idiomas posibles
@type list_language: list[str]
@param list_quality: listado de calidades posibles
@type list_quality: list[str]
@param exist: si existe el filtro
@type exist: bool
@return: lista de opciones a mostrar en el menú contextual
@rtype: list
"""
# Dependiendo de como sea el contexto lo guardamos y añadimos las opciones de filtertools.
if type(item.context) == str:
_context = item.context.split("|")
elif type(item.context) == list:
_context = item.context
else:
_context = []
if access():
dict_data = {"title": config.get_localized_string(60426), "action": "config_item", "channel": "filtertools"}
if list_language:
dict_data["list_language"] = list_language
if list_quality:
dict_data["list_quality"] = list_quality
added = False
if type(_context) == list:
for x in _context:
if x and type(x) == dict:
if x["channel"] == "filtertools":
added = True
break
if not added:
_context.append(dict_data)
if item.action == "play":
if not exist:
_context.append({"title": config.get_localized_string(60427) % item.language, "action": "save_from_context",
"channel": "filtertools", "from_channel": item.channel})
else:
_context.append({"title": config.get_localized_string(60428) % item.language, "action": "delete_from_context",
"channel": "filtertools", "from_channel": item.channel})
return _context
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))
return itemlist
def load(item):
return mainlist(channel=item.from_channel, list_language=item.list_language, list_quality=item.list_quality)
def check_conditions(_filter, list_item, item, list_language, list_quality, quality_count=0, language_count=0):
is_language_valid = True
if _filter.language:
# logger.debug("title es %s" % item.title)
# viene de episodios
if isinstance(item.language, list):
if _filter.language in item.language:
language_count += 1
else:
is_language_valid = False
# viene de findvideos
else:
if item.language.lower() == _filter.language.lower():
language_count += 1
else:
is_language_valid = False
is_quality_valid = True
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 item.quality.lower() in _filter.quality_allowed:
quality = item.quality.lower()
quality_count += 1
else:
is_quality_valid = False
if is_language_valid and is_quality_valid:
item.list_language = list_language
if list_quality:
item.list_quality = list_quality
item.context = context(item, exist=True)
list_item.append(item)
# logger.debug("{0} | context: {1}".format(item.title, item.context))
# logger.debug(" -Enlace añadido")
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
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.
@param list_item: lista de enlaces
@type list_item: list[Item]
@param item: elemento a filtrar
@type item: Item
@param list_language: listado de idiomas posibles
@type list_language: list[str]
@param list_quality: listado de calidades posibles
@type list_quality: list[str]
@param global_filter_lang_id: id de la variable de filtrado por idioma que está en settings
@type global_filter_lang_id: str
@return: lista de Item
@rtype: list[Item]
"""
logger.info()
# si los campos obligatorios son None salimos
if list_item is None or item is None:
return []
logger.debug("total de items : %s" % len(list_item))
global filter_global
if not filter_global:
filter_global = Filter(item, global_filter_lang_id).result
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)
else:
item.context = context(item)
list_item.append(item)
return list_item
def get_links(list_item, item, list_language, list_quality=None, global_filter_lang_id="filter_languages"):
"""
Devuelve una lista de enlaces filtrados.
@param list_item: lista de enlaces
@type list_item: list[Item]
@param item: elemento a filtrar
@type item: item
@param list_language: listado de idiomas posibles
@type list_language: list[str]
@param list_quality: listado de calidades posibles
@type list_quality: list[str]
@param global_filter_lang_id: id de la variable de filtrado por idioma que está en settings
@type global_filter_lang_id: str
@return: lista de Item
@rtype: list[Item]
"""
logger.info()
# si los campos obligatorios son None salimos
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 len(list_item) == 0:
return list_item
logger.debug("total de items : %s" % len(list_item))
new_itemlist = []
quality_count = 0
language_count = 0
_filter = Filter(item, global_filter_lang_id).result
logger.debug("filter: '%s' datos: '%s'" % (item.show, _filter))
if _filter and _filter.active:
for item in list_item:
new_itemlist, quality_count, language_count = check_conditions(_filter, new_itemlist, item, list_language,
list_quality, quality_count, language_count)
logger.info("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))
if len(new_itemlist) == 0:
list_item_all = []
for i in list_item:
list_item_all.append(i.tourl())
_context = [{"title": "FILTRO: Borrar '%s'" % _filter.language, "action": "delete_from_context",
"channel": "filtertools", "to_channel": "seriesdanko"}]
if _filter.quality_allowed:
msg_quality_allowed = " y calidad %s" % _filter.quality_allowed
else:
msg_quality_allowed = ""
new_itemlist.append(Item(channel=__channel__, action="no_filter", list_item_all=list_item_all,
show=item.show,
title="[COLOR %s]No hay elementos con idioma '%s'%s, pulsa para mostrar "
"sin filtro[/COLOR]"
% (COLOR.get("error", "auto"), _filter.language, msg_quality_allowed),
context=_context))
else:
for item in list_item:
item.list_language = list_language
if list_quality:
item.list_quality = list_quality
item.context = context(item)
new_itemlist = list_item
return new_itemlist
def no_filter(item):
"""
Muestra los enlaces sin filtrar
@param item: item
@type item: Item
@return: lista de enlaces
@rtype: list[Item]
"""
logger.info()
itemlist = []
for i in item.list_item_all:
itemlist.append(Item().fromurl(i))
return itemlist
def mainlist(channel, list_language, list_quality):
"""
Muestra una lista de las series filtradas
@param channel: nombre del canal para obtener las series filtradas
@type channel: str
@param list_language: lista de idiomas del canal
@type list_language: list[str]
@param list_quality: lista de calidades del canal
@type list_quality: list[str]
@return: lista de Item
@rtype: list[Item]
"""
logger.info()
itemlist = []
dict_series = jsontools.get_node_from_file(channel, TAG_TVSHOW_FILTER)
idx = 0
for tvshow in sorted(dict_series):
if idx % 2 == 0:
if dict_series[tvshow][TAG_ACTIVE]:
tag_color = COLOR.get("striped_even_active", "auto")
else:
tag_color = COLOR.get("striped_even_inactive", "auto")
else:
if dict_series[tvshow][TAG_ACTIVE]:
tag_color = COLOR.get("striped_odd_active", "auto")
else:
tag_color = COLOR.get("striped_odd_inactive", "auto")
idx += 1
name = dict_series.get(tvshow, {}).get(TAG_NAME, tvshow)
activo = config.get_localized_string(60433)
if dict_series[tvshow][TAG_ACTIVE]:
activo = ""
title = config.get_localized_string(60434) % (tag_color, name, activo)
itemlist.append(Item(channel=__channel__, action="config_item", title=title, show=name,
list_language=list_language, list_quality=list_quality, from_channel=channel))
if len(itemlist) == 0:
itemlist.append(Item(channel=channel, action="mainlist", title=config.get_localized_string(60435)))
return itemlist
def config_item(item):
"""
muestra una serie filtrada para su configuración
@param item: item
@type item: Item
"""
logger.info()
logger.info("item %s" % item.tostring())
# OBTENEMOS LOS DATOS DEL JSON
dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_FILTER)
tvshow = item.show.lower().strip()
default_lang = ''
channel_parameters = channeltools.get_channel_parameters(item.from_channel)
list_language = channel_parameters["filter_languages"]
try:
if channel_parameters["filter_languages"] != '' and len(list_language) > 0:
default_lang = list_language[1]
except:
pass
if default_lang == '':
platformtools.dialog_notification("FilterTools", "No hay idiomas definidos")
return
else:
lang_selected = dict_series.get(tvshow, {}).get(TAG_LANGUAGE, default_lang)
list_quality = dict_series.get(tvshow, {}).get(TAG_QUALITY_ALLOWED, [x.lower() for x in item.list_quality])
# logger.info("lang selected {}".format(lang_selected))
# logger.info("list quality {}".format(list_quality))
active = True
custom_button = {'visible': False}
allow_option = False
if item.show.lower().strip() in dict_series:
allow_option = True
active = dict_series.get(item.show.lower().strip(), {}).get(TAG_ACTIVE, False)
custom_button = {'label': 'Borrar', 'function': 'delete', 'visible': True, 'close': True}
list_controls = []
if allow_option:
active_control = {
"id": "active",
"type": "bool",
"label": "¿Activar/Desactivar filtro?",
"color": "",
"default": active,
"enabled": allow_option,
"visible": allow_option,
}
list_controls.append(active_control)
language_option = {
"id": "language",
"type": "list",
"label": "Idioma",
"color": "0xFFee66CC",
"default": item.list_language.index(lang_selected),
"enabled": True,
"visible": True,
"lvalues": item.list_language
}
list_controls.append(language_option)
if item.list_quality:
list_controls_calidad = [
{
"id": "textoCalidad",
"type": "label",
"label": "Calidad permitida",
"color": "0xffC6C384",
"enabled": True,
"visible": True,
},
]
for element in sorted(item.list_quality, key=str.lower):
list_controls_calidad.append({
"id": element,
"type": "bool",
"label": element,
"default": (False, True)[element.lower() in list_quality],
"enabled": True,
"visible": True,
})
# concatenamos list_controls con list_controls_calidad
list_controls.extend(list_controls_calidad)
title = "Filtrado de enlaces para: [COLOR %s]%s[/COLOR]" % (COLOR.get("selected", "auto"), item.show)
platformtools.show_channel_settings(list_controls=list_controls, callback='save', item=item,
caption=title, custom_button=custom_button)
def delete(item, dict_values):
logger.info()
if item:
dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_FILTER)
tvshow = item.show.strip().lower()
heading = "¿Está seguro que desea eliminar el filtro?"
line1 = "Pulse 'Si' para eliminar el filtro de [COLOR %s]%s[/COLOR], pulse 'No' o cierre la ventana para " \
"no hacer nada." % (COLOR.get("selected", "auto"), item.show.strip())
if platformtools.dialog_yesno(heading, line1) == 1:
lang_selected = dict_series.get(tvshow, {}).get(TAG_LANGUAGE, "")
dict_series.pop(tvshow, None)
result, json_data = jsontools.update_node(dict_series, item.from_channel, TAG_TVSHOW_FILTER)
sound = False
if result:
message = "FILTRO ELIMINADO"
else:
message = "Error al guardar en disco"
sound = True
heading = "%s [%s]" % (item.show.strip(), lang_selected)
platformtools.dialog_notification(heading, message, sound=sound)
if item.action in ["findvideos", "play"]:
platformtools.itemlist_refresh()
def save(item, dict_data_saved):
"""
Guarda los valores configurados en la ventana
@param item: item
@type item: Item
@param dict_data_saved: diccionario con los datos salvados
@type dict_data_saved: dict
"""
logger.info()
if item and dict_data_saved:
logger.debug('item: %s\ndatos: %s' % (item.tostring(), dict_data_saved))
if item.from_channel == "videolibrary":
item.from_channel = item.contentChannel
dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_FILTER)
tvshow = item.show.strip().lower()
logger.info("Se actualiza los datos")
list_quality = []
for _id, value in dict_data_saved.items():
if _id in item.list_quality and value:
list_quality.append(_id.lower())
lang_selected = item.list_language[dict_data_saved[TAG_LANGUAGE]]
dict_filter = {TAG_NAME: item.show, TAG_ACTIVE: dict_data_saved.get(TAG_ACTIVE, True),
TAG_LANGUAGE: lang_selected, TAG_QUALITY_ALLOWED: list_quality}
dict_series[tvshow] = dict_filter
result, json_data = jsontools.update_node(dict_series, item.from_channel, TAG_TVSHOW_FILTER)
sound = False
if result:
message = "FILTRO GUARDADO"
else:
message = "Error al guardar en disco"
sound = True
heading = "%s [%s]" % (item.show.strip(), lang_selected)
platformtools.dialog_notification(heading, message, sound=sound)
if item.from_action in ["findvideos", "play"]:
platformtools.itemlist_refresh()
def save_from_context(item):
"""
Salva el filtro a través del menú contextual
@param item: item
@type item: item
"""
logger.info()
dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_FILTER)
tvshow = item.show.strip().lower()
dict_filter = {TAG_NAME: item.show, TAG_ACTIVE: True, TAG_LANGUAGE: item.language, TAG_QUALITY_ALLOWED: []}
dict_series[tvshow] = dict_filter
result, json_data = jsontools.update_node(dict_series, item.from_channel, TAG_TVSHOW_FILTER)
sound = False
if result:
message = "FILTRO GUARDADO"
else:
message = "Error al guardar en disco"
sound = True
heading = "%s [%s]" % (item.show.strip(), item.language)
platformtools.dialog_notification(heading, message, sound=sound)
if item.from_action in ["findvideos", "play"]:
platformtools.itemlist_refresh()
def delete_from_context(item):
"""
Elimina el filtro a través del menú contextual
@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
if item.to_channel != "":
item.from_channel = item.to_channel
dict_series = jsontools.get_node_from_file(item.from_channel, TAG_TVSHOW_FILTER)
tvshow = item.show.strip().lower()
lang_selected = dict_series.get(tvshow, {}).get(TAG_LANGUAGE, "")
dict_series.pop(tvshow, None)
result, json_data = jsontools.update_node(dict_series, item.from_channel, TAG_TVSHOW_FILTER)
sound = False
if result:
message = "FILTRO ELIMINADO"
else:
message = "Error al guardar en disco"
sound = True
heading = "%s [%s]" % (item.show.strip(), lang_selected)
platformtools.dialog_notification(heading, message, sound=sound)
if item.from_action in ["findvideos", "play", "no_filter"]: # 'no_filter' es el mismo caso que L#601
platformtools.itemlist_refresh()
+921
View File
@@ -0,0 +1,921 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Alfa favoritos
# ==============
# - 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: alfavorites-default.json
# - Se puede copiar alfavorites-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 alfavorites- y terminar en .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
# ------------------------------------------------------------
import os, re
from datetime import datetime
from core.item import Item
from platformcode import config, logger, platformtools
from core import filetools, jsontools
def fechahora_actual():
return datetime.now().strftime('%Y-%m-%d %H:%M')
# Helpers para listas
# -------------------
PREFIJO_LISTA = 'kodfavorites-'
# Devuelve el nombre de la lista activa (Ej: alfavorites-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: alfavorites-Prueba.json => Prueba)
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 => alfavorites-Prueba.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
def save_log_lista_shared(msg):
msg = fechahora_actual() + ': ' + msg + os.linesep
fullfilename = os.path.join(config.get_data_path(), 'alfavorites_shared.log')
with open(fullfilename, 'a') as f: f.write(msg); f.close()
# Limpiar texto para usar como nombre de fichero
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
pass
txt = unicodedata.normalize('NFKD', txt).encode('ascii', 'ignore')
txt = txt.decode('utf-8').strip()
if blank_char != ' ': txt = txt.replace(' ', blank_char)
txt = re.sub(disallowed_chars, '', txt)
return str(txt)
# Clase para cargar y guardar en el fichero de Alfavoritos
# --------------------------------------------------------
class AlfavoritesData:
def __init__(self, filename = None):
# Si no se especifica ningún fichero se usa la lista_activa (si no la hay se crea)
if filename == None:
filename = get_lista_activa()
self.user_favorites_file = os.path.join(config.get_data_path(), filename)
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)
jsondata = jsontools.load(filetools.read(fichero_anterior))
self.user_favorites = jsondata
self.info_lista = {}
self.save()
filetools.remove(fichero_anterior)
else:
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
self.user_favorites = []
else:
self.user_favorites = jsondata['user_favorites']
self.info_lista = jsondata['info_lista']
if len(self.user_favorites) == 0:
self.info_lista = {}
# Crear algunas carpetas por defecto
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': [] })
self.save()
def save(self):
if 'created' not in self.info_lista:
self.info_lista['created'] = fechahora_actual()
self.info_lista['updated'] = fechahora_actual()
jsondata = {}
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))
# ============================
# Añadir desde menú contextual
# ============================
def addFavourite(item):
logger.info()
alfav = AlfavoritesData()
# Si se llega aquí mediante el menú contextual, hay que recuperar los parámetros action y channel
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
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
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
for enlace in alfav.user_favorites[i_perfil]['items']:
it = Item().fromurl(enlace)
repe = True
for prop in campos:
if prop in it.__dict__ and prop in item.__dict__ and it.__dict__[prop] != item.__dict__[prop]:
repe = False
break
if repe:
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 (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, ...)
# Añadir fecha en que se guarda
item.date_added = fechahora_actual()
# Guardar
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
# ====================
def mainlist(item):
logger.info()
alfav = AlfavoritesData()
item.category = get_name_from_filename(os.path.basename(alfav.user_favorites_file))
itemlist = []
last_i = len(alfav.user_favorites) - 1
for i_perfil, perfil in enumerate(alfav.user_favorites):
context = []
context.append({'title': config.get_localized_string(70533), 'channel': item.channel, 'action': 'editar_perfil_titulo',
'i_perfil': i_perfil})
context.append({'title': config.get_localized_string(70534), 'channel': item.channel, 'action': 'eliminar_perfil',
'i_perfil': i_perfil})
if i_perfil > 0:
context.append({'title': config.get_localized_string(70535), 'channel': item.channel, 'action': 'mover_perfil',
'i_perfil': i_perfil, 'direccion': 'top'})
context.append({'title': config.get_localized_string(70536), 'channel': item.channel, 'action': 'mover_perfil',
'i_perfil': i_perfil, 'direccion': 'arriba'})
if i_perfil < last_i:
context.append({'title': config.get_localized_string(70537), 'channel': item.channel, 'action': 'mover_perfil',
'i_perfil': i_perfil, 'direccion': 'abajo'})
context.append({'title': config.get_localized_string(70538), 'channel': item.channel, 'action': 'mover_perfil',
'i_perfil': i_perfil, 'direccion': 'bottom'})
plot = '%d enlaces en la carpeta' % len(perfil['items'])
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
def mostrar_perfil(item):
logger.info()
alfav = AlfavoritesData()
itemlist = []
i_perfil = item.i_perfil
if not alfav.user_favorites[i_perfil]: return itemlist
last_i = len(alfav.user_favorites[i_perfil]['items']) - 1
ruta_runtime = config.get_runtime_path()
for i_enlace, enlace in enumerate(alfav.user_favorites[i_perfil]['items']):
it = Item().fromurl(enlace)
it.context = [ {'title': '[COLOR blue]'+config.get_localized_string(70617)+'[/COLOR]', 'channel': item.channel, 'action': 'acciones_enlace',
'i_enlace': i_enlace, 'i_perfil': i_perfil} ]
it.plot += '[CR][CR][COLOR blue]Canal:[/COLOR] ' + it.channel + ' [COLOR blue]Action:[/COLOR] ' + it.action
if it.extra != '': it.plot += ' [COLOR blue]Extra:[/COLOR] ' + it.extra
it.plot += '[CR][COLOR blue]Url:[/COLOR] ' + it.url if isinstance(it.url, str) else '...'
if it.date_added != '': it.plot += '[CR][COLOR blue]Added:[/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.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
ruta, fichero = filetools.split(it.thumbnail.replace('\\','/'))
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:
it.thumbnail = filetools.join(ruta_runtime, 'resources', 'media', 'themes', 'default', fichero)
itemlist.append(it)
return itemlist
# Rutinas internas compartidas
# ----------------------------
# Diálogo para seleccionar/crear una carpeta. Devuelve índice de la carpeta en user_favorites (-1 si 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
ret = platformtools.dialog_select(titulo, acciones)
if ret == -1: return -1 # pedido cancel
if ret < len(alfav.user_favorites):
i_perfil = ret
else: # crear nueva carpeta
if _crea_perfil(alfav):
i_perfil = len(alfav.user_favorites) - 1
return i_perfil
# Diálogo para crear una carpeta
def _crea_perfil(alfav):
titulo = platformtools.dialog_input(default='', heading=config.get_localized_string(70551))
if titulo is None or titulo == '':
return False
alfav.user_favorites.append({'title': titulo, 'items': []})
alfav.save()
return True
# Gestión de perfiles y enlaces
# -----------------------------
def crear_perfil(item):
logger.info()
alfav = AlfavoritesData()
if not _crea_perfil(alfav): return False
platformtools.itemlist_refresh()
return True
def editar_perfil_titulo(item):
logger.info()
alfav = AlfavoritesData()
if not alfav.user_favorites[item.i_perfil]: return False
titulo = platformtools.dialog_input(default=alfav.user_favorites[item.i_perfil]['title'], heading=config.get_localized_string(70533))
if titulo is None or titulo == '' or titulo == alfav.user_favorites[item.i_perfil]['title']:
return False
alfav.user_favorites[item.i_perfil]['title'] = titulo
alfav.save()
platformtools.itemlist_refresh()
return True
def eliminar_perfil(item):
logger.info()
alfav = AlfavoritesData()
if not alfav.user_favorites[item.i_perfil]: return False
# Pedir confirmación
if not platformtools.dialog_yesno(config.get_localized_string(70618), config.get_localized_string(70619)): return False
del alfav.user_favorites[item.i_perfil]
alfav.save()
platformtools.itemlist_refresh()
return True
def acciones_enlace(item):
logger.info()
acciones = [config.get_localized_string(70620), config.get_localized_string(70621), config.get_localized_string(70622), config.get_localized_string(70623),
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
elif ret == 0:
return editar_enlace_titulo(item)
elif ret == 1:
return editar_enlace_color(item)
elif ret == 2:
return editar_enlace_thumbnail(item)
elif ret == 3:
return editar_enlace_carpeta(item)
elif ret == 4:
return editar_enlace_lista(item)
elif ret == 5:
return eliminar_enlace(item)
elif ret == 6:
return mover_enlace(item.clone(direccion='top'))
elif ret == 7:
return mover_enlace(item.clone(direccion='arriba'))
elif ret == 8:
return mover_enlace(item.clone(direccion='abajo'))
elif ret == 9:
return mover_enlace(item.clone(direccion='bottom'))
def editar_enlace_titulo(item):
logger.info()
alfav = AlfavoritesData()
if not alfav.user_favorites[item.i_perfil]: return False
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')
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()
alfav.save()
platformtools.itemlist_refresh()
return True
def editar_enlace_color(item):
logger.info()
alfav = AlfavoritesData()
if not alfav.user_favorites[item.i_perfil]: return False
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)
if ret == -1: return False # pedido cancel
it.text_color = colores[ret]
alfav.user_favorites[item.i_perfil]['items'][item.i_enlace] = it.tourl()
alfav.save()
platformtools.itemlist_refresh()
return True
def editar_enlace_thumbnail(item):
logger.info()
alfav = AlfavoritesData()
if not alfav.user_favorites[item.i_perfil]: return False
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)
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)
opciones = []
ids = []
try:
from core import channeltools
channel_parameters = channeltools.get_channel_parameters(it.channel)
if channel_parameters['thumbnail'] != '':
nombre = 'Canal %s' % it.channel
if is_kodi17:
it_thumb = xbmcgui.ListItem(nombre)
it_thumb.setArt({ 'thumb': channel_parameters['thumbnail'] })
opciones.append(it_thumb)
else:
opciones.append(nombre)
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':
nombre = f.replace('thumb_', '').replace('_', ' ').replace('.png', '')
if is_kodi17:
it_thumb = xbmcgui.ListItem(nombre)
it_thumb.setArt({ 'thumb': os.path.join(resource_path, f) })
opciones.append(it_thumb)
else:
opciones.append(nombre)
ids.append(os.path.join(resource_path, f))
if is_kodi17:
ret = xbmcgui.Dialog().select('Seleccionar thumbnail:', opciones, useDetails=True)
else:
ret = platformtools.dialog_select('Seleccionar thumbnail:', opciones)
if ret == -1: return False # pedido cancel
it.thumbnail = ids[ret]
alfav.user_favorites[item.i_perfil]['items'][item.i_enlace] = it.tourl()
alfav.save()
platformtools.itemlist_refresh()
return True
def editar_enlace_carpeta(item):
logger.info()
alfav = AlfavoritesData()
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)
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])
del alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]
alfav.save()
platformtools.itemlist_refresh()
return True
def editar_enlace_lista(item):
logger.info()
alfav = AlfavoritesData()
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
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
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')
return False
ret = platformtools.dialog_select('Seleccionar lista destino', opciones)
if ret == -1:
return False # pedido cancel
alfav_destino = AlfavoritesData(opciones[ret])
# Diálogo para escoger/crear carpeta en la lista de destino
i_perfil = _selecciona_perfil(alfav_destino, 'Seleccionar carpeta destino', -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])
del alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]
alfav_destino.save()
alfav.save()
platformtools.itemlist_refresh()
return True
def eliminar_enlace(item):
logger.info()
alfav = AlfavoritesData()
if not alfav.user_favorites[item.i_perfil]: return False
if not alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]: return False
del alfav.user_favorites[item.i_perfil]['items'][item.i_enlace]
alfav.save()
platformtools.itemlist_refresh()
return True
# Mover perfiles y enlaces (arriba, abajo, top, bottom)
# ------------------------
def mover_perfil(item):
logger.info()
alfav = AlfavoritesData()
alfav.user_favorites = _mover_item(alfav.user_favorites, item.i_perfil, item.direccion)
alfav.save()
platformtools.itemlist_refresh()
return True
def mover_enlace(item):
logger.info()
alfav = AlfavoritesData()
if not alfav.user_favorites[item.i_perfil]: return False
alfav.user_favorites[item.i_perfil]['items'] = _mover_item(alfav.user_favorites[item.i_perfil]['items'], item.i_enlace, item.direccion)
alfav.save()
platformtools.itemlist_refresh()
return True
# Mueve un item determinado (numérico) de una lista (arriba, abajo, top, bottom) y devuelve la lista modificada
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 direccion == 'arriba':
if i_selected == 0: # Ya está arriba de todo
return lista
lista.insert(i_selected - 1, lista.pop(i_selected))
elif direccion == 'abajo':
if i_selected == last_i: # Ya está abajo de todo
return lista
lista.insert(i_selected + 1, lista.pop(i_selected))
elif direccion == 'top':
if i_selected == 0: # Ya está arriba de todo
return lista
lista.insert(0, lista.pop(i_selected))
elif direccion == 'bottom':
if i_selected == last_i: # Ya está abajo de todo
return lista
lista.insert(last_i, lista.pop(i_selected))
return lista
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Gestionar diferentes listas de alfavoritos
# ------------------------------------------
def mainlist_listas(item):
logger.info()
itemlist = []
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
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
def acciones_lista(item):
logger.info()
acciones = [config.get_localized_string(70604), config.get_localized_string(70629),
config.get_localized_string(70605), config.get_localized_string(70606), config.get_localized_string(70607)]
ret = platformtools.dialog_select(item.lista, acciones)
if ret == -1:
return False # pedido cancel
elif ret == 0:
return activar_lista(item)
elif ret == 1:
return renombrar_lista(item)
elif ret == 2:
return compartir_lista(item)
elif ret == 3:
return eliminar_lista(item)
elif ret == 4:
return informacion_lista(item)
def activar_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)
return False
config.set_setting('lista_activa', item.lista)
from channelselector import get_thumb
item_inicio = Item(title=config.get_localized_string(70527), channel="alfavorites", action="mainlist",
thumbnail=get_thumb("mylink.png"),
category=config.get_localized_string(70527), viewmode="thumbnails")
platformtools.itemlist_update(item_inicio, replace=True)
return True
def renombrar_lista(item):
logger.info()
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)
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:
return False
titulo = text_clean(titulo, blank_char='_')
filename = get_filename_from_name(titulo)
fullfilename = os.path.join(config.get_data_path(), filename)
# Comprobar que el nuevo nombre no exista
if os.path.exists(fullfilename):
platformtools.dialog_ok('Alfa', config.get_localized_string(70613), fullfilename)
return False
# Rename del fichero
if not filetools.rename(fullfilename_current, filename):
platformtools.dialog_ok('Alfa', config.get_localized_string(70631), fullfilename)
return False
# Update settings si es la lista activa
if item.lista == get_lista_activa():
config.set_setting('lista_activa', filename)
platformtools.itemlist_refresh()
return True
def eliminar_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)
return False
if item.lista == get_lista_activa():
platformtools.dialog_ok('Alfa', 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
filetools.remove(fullfilename)
platformtools.itemlist_refresh()
return True
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)
return False
alfav = AlfavoritesData(item.lista)
txt = 'Lista: [COLOR gold]%s[/COLOR]' % 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:
txt += '[CR]' + config.get_localized_string(70636) + ' ' + alfav.info_lista['downloaded_date'] + ' ' + alfav.info_lista['downloaded_from'] + ' ' + config.get_localized_string(70637)
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) + ' ' + 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))
platformtools.dialog_textviewer(config.get_localized_string(70607), txt)
return True
def compartir_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), 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
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
from lib import MultipartPostHandler
import urllib2
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)
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))
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)
return False
# Apuntar código en fichero de log y dentro de la lista
save_log_lista_shared(config.get_localized_string(70648) + ' ' + item.lista + ' ' + codigo + ' ' + config.get_localized_string(70649))
alfav = AlfavoritesData(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)
return True
def acciones_nueva_lista(item):
logger.info()
acciones = [config.get_localized_string(70651),
config.get_localized_string(70652),
config.get_localized_string(70653),
config.get_localized_string(70654)]
ret = platformtools.dialog_select(config.get_localized_string(70608), acciones)
if ret == -1:
return False # pedido cancel
elif ret == 0:
return crear_lista(item)
elif ret == 1:
codigo = platformtools.dialog_input(default='', heading=config.get_localized_string(70609)) # 05370382084539519168
if codigo is None or codigo == '':
return False
return descargar_lista(item, 'http://s000.tinyupload.com/?file_id=' + codigo)
elif ret == 2:
url = platformtools.dialog_input(default='https://', heading=config.get_localized_string(70610))
if url is None or url == '':
return False
return descargar_lista(item, url)
elif ret == 3:
txt = config.get_localized_string(70611)
platformtools.dialog_textviewer(config.get_localized_string(70607), txt)
return False
def crear_lista(item):
logger.info()
titulo = platformtools.dialog_input(default='', heading=config.get_localized_string(70612))
if titulo is None or titulo == '':
return False
titulo = text_clean(titulo, blank_char='_')
filename = get_filename_from_name(titulo)
fullfilename = os.path.join(config.get_data_path(), filename)
# Comprobar que el fichero no exista ya
if os.path.exists(fullfilename):
platformtools.dialog_ok('Alfa', config.get_localized_string(70613), fullfilename)
return False
# Provocar que se guarde con las carpetas vacías por defecto
alfav = AlfavoritesData(filename)
platformtools.itemlist_refresh()
return True
def descargar_lista(item, url):
logger.info()
from core import httptools, scrapertools
if 'tinyupload.com/' in url:
try:
from urlparse import urlparse
data = httptools.downloadpage(url).data
logger.debug(data)
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)
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)
return False
url_json = video_urls[0][1] # https://www58.zippyshare.com/d/qPzzQ0UM/25460/alfavorites-testeanding.json
url_name = url_json[url_json.rfind('/')+1:]
elif 'friendpaste.com/' in url:
url_json = url if url.endswith('/raw') else url + '/raw'
url_name = 'friendpaste'
else:
url_json = url
url_name = url[url.rfind('/')+1:]
# Download json
data = httptools.downloadpage(url_json).data
# Verificar formato json de alfavorites y añadir info de la descarga
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))
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
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 == '':
return False
titulo = text_clean(titulo, blank_char='_')
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 os.path.exists(fullfilename):
if not platformtools.dialog_yesno('Alfa', 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)
platformtools.itemlist_refresh()
return True
+48
View File
@@ -0,0 +1,48 @@
{
"id": "news",
"name": "Novedades",
"active": false,
"adult": false,
"language": ["*"],
"categories": [
"movie"
],
"settings": [
{
"id": "multithread",
"type": "bool",
"label": "@60656",
"default": true,
"enabled": true,
"visible": true
},
{
"id": "result_mode",
"type": "list",
"label": "@60657",
"default": 2,
"enabled": true,
"visible": true,
"lvalues": [
"@60658",
"@60659",
"@60660"
]
},
{
"id": "perfil",
"type": "list",
"label": "@60666",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": [
"@60667",
"@60668",
"@60669",
"@60670",
"@60671"
]
}
]
}
+656
View File
@@ -0,0 +1,656 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Channel for recent videos on several channels
# ------------------------------------------------------------
import glob
import os
import re
import time
from threading import Thread
from channelselector import get_thumb, auto_filter
from core import channeltools
from core import scrapertools
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
from core import jsontools
THUMBNAILS = {'0': 'posters', '1': 'banners', '2': 'squares'}
__perfil__ = config.get_setting('perfil', "news")
# Fijar perfil de color
perfil = [['0xFF0B7B92', '0xFF89FDFB', '0xFFACD5D4'],
['0xFFB31313', '0xFFFF9000', '0xFFFFEE82'],
['0xFF891180', '0xFFCB22D7', '0xFFEEA1EB'],
['0xFFA5DEE5', '0xFFE0F9B5', '0xFFFEFDCA'],
['0xFFF23557', '0xFF22B2DA', '0xFFF0D43A']]
#color1, color2, color3 = ["white", "white", "white"]
color1, color2, color3 = perfil[__perfil__]
list_newest = []
list_newest_tourl = []
channels_id_name = {}
menu_cache_path = os.path.join(config.get_data_path(), "settings_channels", 'menu_cache_data.json')
menu_settings_path = os.path.join(config.get_data_path(), "settings_channels", 'menu_settings_data.json')
def mainlist(item):
logger.info()
itemlist = []
list_canales, any_active = get_channels_list()
channel_language = config.get_setting("channel_language", default="all")
#if list_canales['peliculas']:
thumbnail = get_thumb("channels_movie.png")
new_item = Item(channel=item.channel, action="novedades", extra="peliculas", title=config.get_localized_string(30122),
thumbnail=thumbnail)
set_category_context(new_item)
itemlist.append(new_item)
# thumbnail = get_thumb("channels_movie_4k.png")
# new_item = Item(channel=item.channel, action="novedades", extra="4k", title=config.get_localized_string(70208), thumbnail=thumbnail)
#
# set_category_context(new_item)
# itemlist.append(new_item)
#if list_canales['terror']:
# thumbnail = get_thumb("channels_horror.png")
# new_item = Item(channel=item.channel, action="novedades", extra="terror", title=config.get_localized_string(70209),
# thumbnail=thumbnail)
# set_category_context(new_item)
# itemlist.append(new_item)
#if list_canales['infantiles']:
# thumbnail = get_thumb("channels_children.png")
# new_item = Item(channel=item.channel, action="novedades", extra="infantiles", title=config.get_localized_string(60510),
# thumbnail=thumbnail)
# set_category_context(new_item)
# itemlist.append(new_item)
#if list_canales['series']:
thumbnail = get_thumb("channels_tvshow.png")
new_item = Item(channel=item.channel, action="novedades", extra="series", title=config.get_localized_string(60511),
thumbnail=thumbnail)
set_category_context(new_item)
itemlist.append(new_item)
#if list_canales['anime']:
thumbnail = get_thumb("channels_anime.png")
new_item = Item(channel=item.channel, action="novedades", extra="anime", title=config.get_localized_string(60512),
thumbnail=thumbnail)
set_category_context(new_item)
itemlist.append(new_item)
if channel_language == "all" or channel_language == "esp":
# if list_canales['Castellano']:
thumbnail = get_thumb("channels_spanish.png")
new_item = Item(channel=item.channel, action="novedades", extra="castellano", title=config.get_localized_string(70014),
thumbnail=thumbnail)
set_category_context(new_item)
itemlist.append(new_item)
# if list_canales['Latino']:
thumbnail = get_thumb("channels_latino.png")
new_item = Item(channel=item.channel, action="novedades", extra="latino", title=config.get_localized_string(59976),
thumbnail=thumbnail)
set_category_context(new_item)
itemlist.append(new_item)
if channel_language == "all":
# if list_canales['Italiano']:
thumbnail = get_thumb("channels_italian.png")
new_item = Item(channel=item.channel, action="novedades", extra="italiano", title=config.get_localized_string(70563),
thumbnail=thumbnail)
set_category_context(new_item)
itemlist.append(new_item)
# if list_canales['Torrent']:
# thumbnail = get_thumb("channels_torrent.png")
# new_item = Item(channel=item.channel, action="novedades", extra="torrent", title=config.get_localized_string(70171), thumbnail=thumbnail)
# set_category_context(new_item)
# itemlist.append(new_item)
#if list_canales['documentales']:
thumbnail = get_thumb("channels_documentary.png")
new_item = Item(channel=item.channel, action="novedades", extra="documentales", title=config.get_localized_string(60513),
thumbnail=thumbnail)
set_category_context(new_item)
itemlist.append(new_item)
return itemlist
def set_category_context(item):
item.context = [{"title": config.get_localized_string(60514) % item.title,
"extra": item.extra,
"action": "setting_channel",
"channel": item.channel}]
item.category = config.get_localized_string(60679) % item.extra
def get_channels_list():
logger.info()
list_canales = {'peliculas': [], '4k': [], 'terror': [], 'infantiles': [], 'series': [], 'anime': [],
'castellano': [], 'latino':[], 'italiano':[], 'torrent':[], 'documentales': []}
any_active = False
# Rellenar listas de canales disponibles
channels_path = os.path.join(config.get_runtime_path(), "channels", '*.json')
channel_language = config.get_setting("channel_language", default="all")
if channel_language =="auto":
channel_language = auto_filter()
for infile in sorted(glob.glob(channels_path)):
channel_id = os.path.basename(infile)[:-5]
channel_parameters = channeltools.get_channel_parameters(channel_id)
# No incluir si es un canal inactivo
if not channel_parameters["active"]:
continue
# No incluir si es un canal para adultos, y el modo adulto está desactivado
if channel_parameters["adult"] and config.get_setting("adult_mode") == 0:
continue
# No incluir si el canal es en un idioma filtrado
if channel_language != "all" and channel_language not in 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
for categoria in list_canales:
include_in_newest = config.get_setting("include_in_newest_" + categoria, channel_id)
if include_in_newest:
channels_id_name[channel_id] = channel_parameters["title"]
list_canales[categoria].append((channel_id, channel_parameters["title"]))
any_active = True
return list_canales, any_active
def set_cache(item):
logger.info()
item.mode = 'set_cache'
t = Thread(target=novedades, args=[item])
t.start()
#t.join()
def get_from_cache(item):
logger.info()
itemlist=[]
cache_node = jsontools.get_node_from_file('menu_cache_data.json', 'cached')
first=item.last
last = first+40
#if last >=len(cache_node[item.extra]):
# last = len(cache_node[item.extra])
for cached_item in cache_node[item.extra][first:last]:
new_item= Item()
new_item = new_item.fromurl(cached_item)
itemlist.append(new_item)
if item.mode == 'silent':
set_cache(item)
if last >= len(cache_node[item.extra]):
item.mode='finish'
itemlist = add_menu_items(item, itemlist)
else:
item.mode='get_cached'
item.last =last
itemlist = add_menu_items(item, itemlist)
return itemlist
def add_menu_items(item, itemlist):
logger.info()
menu_icon = get_thumb('menu.png')
menu = Item(channel="channelselector", action="getmainlist", viewmode="movie", thumbnail=menu_icon, title='Menu')
itemlist.insert(0, menu)
if item.mode != 'finish':
if item.mode == 'get_cached':
last=item.last
else:
last = len(itemlist)
refresh_icon = get_thumb('more.png')
refresh = item.clone(thumbnail=refresh_icon, mode='get_cached',title='Mas', last=last)
itemlist.insert(len(itemlist), refresh)
return itemlist
def novedades(item):
logger.info()
global list_newest
threads = []
list_newest = []
start_time = time.time()
mode = item.mode
if mode == '':
mode = 'normal'
if mode=='get_cached':
if os.path.exists(menu_cache_path):
return get_from_cache(item)
multithread = config.get_setting("multithread", "news")
logger.info("multithread= " + str(multithread))
if not multithread:
if platformtools.dialog_yesno(config.get_localized_string(60515),
config.get_localized_string(60516),
config.get_localized_string(60517),
config.get_localized_string(60518)):
if config.set_setting("multithread", True, "news"):
multithread = True
if mode == 'normal':
progreso = platformtools.dialog_progress(item.category, config.get_localized_string(60519))
list_canales, any_active = get_channels_list()
if config.is_xbmc():
from specials import side_menu
if mode=='silent' and any_active and len(list_canales[item.extra]) > 0:
side_menu.set_menu_settings(item)
aux_list=[]
for canal in list_canales[item.extra]:
if len(aux_list)<2:
aux_list.append(canal)
list_canales[item.extra]=aux_list
if mode == 'set_cache':
list_canales[item.extra] = list_canales[item.extra][2:]
if any_active and len(list_canales[item.extra])>0:
import math
# fix float porque la division se hace mal en python 2.x
number_of_channels = float(100) / len(list_canales[item.extra])
for index, channel in enumerate(list_canales[item.extra]):
channel_id, channel_title = channel
percentage = int(math.ceil((index + 1) * number_of_channels))
# if progreso.iscanceled():
# progreso.close()
# logger.info("Búsqueda cancelada")
# return itemlist
# Modo Multi Thread
if multithread:
t = Thread(target=get_newest, args=[channel_id, item.extra], name=channel_title)
t.start()
threads.append(t)
if mode == 'normal':
progreso.update(percentage, "", config.get_localized_string(60520) % channel_title)
# Modo single Thread
else:
if mode == 'normal':
logger.info("Obteniendo novedades de channel_id=" + channel_id)
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
if multithread:
pendent = [a for a in threads if a.isAlive()]
t = float(100) / len(pendent)
while pendent:
index = (len(threads) - len(pendent)) + 1
percentage = int(math.ceil(index * t))
list_pendent_names = [a.getName() for a in pendent]
if mode == 'normal':
mensaje = config.get_localized_string(30994) % (", ".join(list_pendent_names))
progreso.update(percentage, config.get_localized_string(60521) % (len(threads) - len(pendent), len(threads)),
mensaje)
logger.debug(mensaje)
if progreso.iscanceled():
logger.info("Busqueda de novedades cancelada")
break
time.sleep(0.5)
pendent = [a for a in threads if a.isAlive()]
if mode == 'normal':
mensaje = config.get_localized_string(60522) % (len(list_newest), time.time() - start_time)
progreso.update(100, mensaje, " ", " ")
logger.info(mensaje)
start_time = time.time()
# logger.debug(start_time)
result_mode = config.get_setting("result_mode", "news")
if mode != 'normal':
result_mode=0
if result_mode == 0: # Agrupados por contenido
ret = group_by_content(list_newest)
elif result_mode == 1: # Agrupados por canales
ret = group_by_channel(list_newest)
else: # Sin agrupar
ret = no_group(list_newest)
while time.time() - start_time < 2:
# mostrar cuadro de progreso con el tiempo empleado durante almenos 2 segundos
time.sleep(0.5)
if mode == 'normal':
progreso.close()
if mode == 'silent':
set_cache(item)
item.mode = 'set_cache'
ret = add_menu_items(item, ret)
if mode != 'set_cache':
return ret
else:
if mode != 'set_cache':
no_channels = platformtools.dialog_ok(config.get_localized_string(30130) + ' - ' + item.extra, config.get_localized_string(70661), config.get_localized_string(70662))
return
def get_newest(channel_id, categoria):
logger.info("channel_id=" + channel_id + ", categoria=" + 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
try:
puede = True
try:
modulo = __import__('channels.%s' % channel_id, fromlist=["channels.%s" % channel_id])
except:
try:
exec "import channels." + channel_id + " as modulo"
except:
puede = False
if not puede:
return
logger.info("running channel " + modulo.__name__ + " " + modulo.__file__)
list_result = modulo.newest(categoria)
logger.info("canal= %s %d resultados" % (channel_id, len(list_result)))
exist=False
if os.path.exists(menu_cache_path):
cache_node = jsontools.get_node_from_file('menu_cache_data.json', 'cached')
exist=True
else:
cache_node = {}
#logger.debug('cache node: %s' % cache_node)
for item in list_result:
# logger.info("item="+item.tostring())
item.channel = channel_id
list_newest.append(item)
list_newest_tourl.append(item.tourl())
cache_node[categoria] = list_newest_tourl
jsontools.update_node(cache_node, 'menu_cache_data.json', "cached")
except:
logger.error("No se pueden recuperar novedades de: " + channel_id)
import traceback
logger.error(traceback.format_exc())
def get_title(item):
if item.contentSerieName: # Si es una serie
title = item.contentSerieName
if not scrapertools.get_season_and_episode(title) and item.contentEpisodeNumber:
if not item.contentSeason:
item.contentSeason = '1'
title = "%s - %sx%s" % (title, item.contentSeason, str(item.contentEpisodeNumber).zfill(2))
elif item.contentTitle: # Si es una pelicula con el canal adaptado
title = item.contentTitle
elif item.fulltitle: # Si el canal no esta adaptado
title = item.fulltitle
else: # Como ultimo recurso
title = item.title
# Limpiamos el titulo de etiquetas de formato anteriores
title = re.compile("\[/*COLO.*?\]", re.DOTALL).sub("", title)
title = re.compile("\[/*B\]", re.DOTALL).sub("", title)
title = re.compile("\[/*I\]", re.DOTALL).sub("", title)
return title
def no_group(list_result_canal):
itemlist = []
global channels_id_name
for i in list_result_canal:
i.title = get_title(i) + " [" + channels_id_name[i.channel] + "]"
i.text_color = color3
itemlist.append(i.clone())
return sorted(itemlist, key=lambda it: it.title.lower())
def group_by_channel(list_result_canal):
global channels_id_name
dict_canales = {}
itemlist = []
for i in list_result_canal:
if i.channel not in dict_canales:
dict_canales[i.channel] = []
# Formatear titulo
i.title = get_title(i)
# Añadimos el contenido al listado de cada canal
dict_canales[i.channel].append(i)
# Añadimos el contenido encontrado en la lista list_result
for c in sorted(dict_canales):
itemlist.append(Item(channel="news", title=channels_id_name[c] + ':', text_color=color1, text_bold=True))
for i in dict_canales[c]:
if i.contentQuality:
i.title += ' (%s)' % i.contentQuality
if i.language:
i.title += ' [%s]' % i.language
i.title = ' %s' % i.title
i.text_color = color3
itemlist.append(i.clone())
return itemlist
def group_by_content(list_result_canal):
global channels_id_name
dict_contenidos = {}
list_result = []
for i in list_result_canal:
# Formatear titulo
i.title = get_title(i)
# Eliminar tildes y otros caracteres especiales para la key
import unicodedata
try:
new_key = i.title.lower().strip().decode("UTF-8")
new_key = ''.join((c for c in unicodedata.normalize('NFD', new_key) if unicodedata.category(c) != 'Mn'))
except:
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...
dict_contenidos[new_key].append(i)
else: # ...sino añadirlo al diccionario
dict_contenidos[new_key] = [i]
# Añadimos el contenido encontrado en la lista list_result
for v in dict_contenidos.values():
title = v[0].title
if len(v) > 1:
# Eliminar de la lista de nombres de canales los q esten duplicados
canales_no_duplicados = []
for i in v:
if i.channel not in canales_no_duplicados:
canales_no_duplicados.append(channels_id_name[i.channel])
if len(canales_no_duplicados) > 1:
canales = ', '.join([i for i in canales_no_duplicados[:-1]])
title += config.get_localized_string(70210) % (canales, canales_no_duplicados[-1])
else:
title += config.get_localized_string(70211) % (', '.join([i for i in canales_no_duplicados]))
new_item = v[0].clone(channel="news", title=title, action="show_channels",
sub_list=[i.tourl() for i in v], extra=channels_id_name)
else:
new_item = v[0].clone(title=title)
new_item.text_color = color3
list_result.append(new_item)
return sorted(list_result, key=lambda it: it.title.lower())
def show_channels(item):
logger.info()
global channels_id_name
channels_id_name = item.extra
itemlist = []
for i in item.sub_list:
new_item = Item()
new_item = new_item.fromurl(i)
# logger.debug(new_item.tostring())
if new_item.contentQuality:
new_item.title += ' (%s)' % new_item.contentQuality
if new_item.language:
new_item.title += ' [%s]' % new_item.language
new_item.title += ' (%s)' % channels_id_name[new_item.channel]
new_item.text_color = color1
itemlist.append(new_item.clone())
return itemlist
def menu_opciones(item):
itemlist = list()
itemlist.append(Item(channel=item.channel, title=config.get_localized_string(60525),
text_bold = True, thumbnail=get_thumb("setting_0.png"),
folder=False))
itemlist.append(Item(channel=item.channel, action="setting_channel", extra="peliculas", title=config.get_localized_string(60526),
thumbnail=get_thumb("channels_movie.png"),
folder=False))
# itemlist.append(Item(channel=item.channel, action="setting_channel", extra="4K", title=config.get_localized_string(70207),
# thumbnail=get_thumb("channels_movie.png"), folder=False))
# itemlist.append(Item(channel=item.channel, action="setting_channel", extra="infantiles", title=config.get_localized_string(60527),
# thumbnail=get_thumb("channels_children.png"),
# folder=False))
itemlist.append(Item(channel=item.channel, action="setting_channel", extra="series",
title=config.get_localized_string(60528),
thumbnail=get_thumb("channels_tvshow.png"),
folder=False))
itemlist.append(Item(channel=item.channel, action="setting_channel", extra="anime",
title=config.get_localized_string(60529),
thumbnail=get_thumb("channels_anime.png"),
folder=False))
# itemlist.append(
# Item(channel=item.channel, action="setting_channel", extra="castellano", title=config.get_localized_string(70212),
# thumbnail=get_thumb("channels_documentary.png"), folder=False))
# itemlist.append(Item(channel=item.channel, action="setting_channel", extra="latino", title=config.get_localized_string(70213),
# thumbnail=get_thumb("channels_documentary.png"), folder=False))
itemlist.append(Item(channel=item.channel, action="setting_channel", extra="Torrent", title=config.get_localized_string(70214),
thumbnail=get_thumb("channels_documentary.png"), folder=False))
itemlist.append(Item(channel=item.channel, action="setting_channel", extra="documentales",
title=config.get_localized_string(60530),
thumbnail=get_thumb("channels_documentary.png"),
folder=False))
itemlist.append(Item(channel=item.channel, action="settings", title=config.get_localized_string(60531),
thumbnail=get_thumb("setting_0.png"),
folder=False))
return itemlist
def settings(item):
return platformtools.show_channel_settings(caption=config.get_localized_string(60532))
def setting_channel(item):
channels_path = os.path.join(config.get_runtime_path(), "channels", '*.json')
channel_language = config.get_setting("channel_language", default="all")
list_controls = []
for infile in sorted(glob.glob(channels_path)):
channel_id = os.path.basename(infile)[:-5]
channel_parameters = channeltools.get_channel_parameters(channel_id)
# No incluir si es un canal inactivo
if not channel_parameters["active"]:
continue
# No incluir si es un canal para adultos, y el modo adulto está desactivado
if channel_parameters["adult"] and config.get_setting("adult_mode") == 0:
continue
# No incluir si el canal es en un idioma filtrado
if channel_language != "all" and channel_language not in channel_parameters["language"] \
and "*" not in channel_parameters["language"]:
continue
# No incluir si en su configuracion el canal no existe 'include_in_newest'
include_in_newest = config.get_setting("include_in_newest_" + item.extra, channel_id)
if include_in_newest is None:
continue
control = {'id': channel_id,
'type': "bool",
'label': channel_parameters["title"],
'default': include_in_newest,
'enabled': True,
'visible': True}
list_controls.append(control)
caption = config.get_localized_string(60533) + item.title.replace(config.get_localized_string(60525), "- ").strip()
if config.get_setting("custom_button_value_news", item.channel):
custom_button_label = config.get_localized_string(59992)
else:
custom_button_label = config.get_localized_string(59991)
return platformtools.show_channel_settings(list_controls=list_controls,
caption=caption,
callback="save_settings", item=item,
custom_button={'visible': True,
'function': "cb_custom_button",
'close': False,
'label': custom_button_label})
def save_settings(item, dict_values):
for v in dict_values:
config.set_setting("include_in_newest_" + item.extra, dict_values[v], v)
def cb_custom_button(item, dict_values):
value = config.get_setting("custom_button_value_news", item.channel)
if value == "":
value = False
for v in dict_values.keys():
dict_values[v] = not value
if config.set_setting("custom_button_value_news", not value, item.channel) == True:
return {"label": "Ninguno"}
else:
return {"label": "Todos"}
+44
View File
@@ -0,0 +1,44 @@
{
"id": "search",
"name": "@60672",
"active": false,
"adult": false,
"language": ["ita"],
"categories": ["movie"],
"settings": [
{
"id": "multithread",
"type": "bool",
"label": "@60673",
"default": true,
"enabled": true,
"visible": true
},
{
"id": "result_mode",
"type": "list",
"label": "@60674",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": ["@60675","@60676"]
},
{
"id": "saved_searches_limit",
"type": "list",
"label": "@60677",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": ["10","20","30","40"]
},
{
"id": "last_search",
"type": "bool",
"label": "@60678",
"default": true,
"enabled": true,
"visible": true
}
]
}
+783
View File
@@ -0,0 +1,783 @@
# -*- coding: utf-8 -*-
import glob
import os
import re
import time
from threading import Thread
from channelselector import get_thumb, auto_filter
from core import channeltools
from core import scrapertools
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
from core import tmdb
import xbmc, xbmcaddon
addon = xbmcaddon.Addon('metadata.themoviedb.org')
def_lang = addon.getSetting('language')
link_list = []
max_links = 30
def mainlist(item):
logger.info()
item.channel = "search"
itemlist = []
context = [{"title": config.get_localized_string(60412), "action": "setting_channel", "channel": item.channel}]
itemlist.append(Item(channel=item.channel, action="sub_menu", title=config.get_localized_string(70305), context=context,
thumbnail=get_thumb("search.png")))
itemlist.append(Item(channel=item.channel, action='genres_menu', title=config.get_localized_string(70306), type='movie',
thumbnail=get_thumb("genres.png")))
itemlist.append (Item(channel=item.channel, action='discover_list', title=config.get_localized_string(70307),
context=context, search_type='list', list_type='movie/popular',
thumbnail=get_thumb("popular.png")))
itemlist.append(Item(channel=item.channel, action='discover_list', title=config.get_localized_string(70308),
context=context, search_type='list', list_type='movie/top_rated',
thumbnail=get_thumb("top_rated.png")))
itemlist.append(
Item(channel=item.channel, action='discover_list', title=config.get_localized_string(70309), context=context,
search_type='list', list_type='movie/now_playing',
thumbnail=get_thumb("now_playing.png")))
itemlist.append(Item(channel=item.channel, action='genres_menu', title=config.get_localized_string(70310), type='tv',
thumbnail=get_thumb("genres.png")))
itemlist.append(
Item(channel=item.channel, action='discover_list', title=config.get_localized_string(70311), context=context,
search_type='list',list_type='tv/popular', thumbnail=get_thumb("popular.png")))
itemlist.append(Item(channel=item.channel, action='discover_list', title=config.get_localized_string(70312), context=context,
search_type='list', list_type='tv/on_the_air', thumbnail=get_thumb("on_the_air.png")))
itemlist.append(Item(channel=item.channel, action='discover_list', title=config.get_localized_string(70313), context=context,
search_type='list', list_type='tv/top_rated', thumbnail=get_thumb("top_rated.png")))
itemlist.append(Item(channel="filmontv", action="mainlist", title=config.get_localized_string(50001),
thumbnail=get_thumb("on_the_air.png"), viewmode="thumbnails"))
return itemlist
def genres_menu(item):
itemlist = []
genres = tmdb.get_genres(item.type)
logger.debug(genres)
logger.debug(genres[item.type])
for key, value in genres[item.type].items():
itemlist.append(item.clone(title=value, action='discover_list', search_type='discover',
list_type=key, page='1'))
return sorted(itemlist, key=lambda it: it.title)
def sub_menu(item):
logger.info()
item.channel = "search"
itemlist = list()
context = [{"title": config.get_localized_string(70273),
"action": "setting_channel",
"channel": item.channel}]
itemlist.append(Item(channel=item.channel, action="search",
title=config.get_localized_string(30980), context=context,
thumbnail=get_thumb("search.png")))
thumbnail = get_thumb("search_star.png")
itemlist.append(Item(channel='tvmoviedb', title=config.get_localized_string(70036), action="search_",
search={'url': 'search/person', 'language': def_lang, 'page': 1}, star=True,
thumbnail=thumbnail))
itemlist.append(Item(channel=item.channel, action="search",
title=config.get_localized_string(59998), extra="categorias",
context=context,
thumbnail=get_thumb("search.png")))
itemlist.append(Item(channel=item.channel, action="opciones", title=config.get_localized_string(59997),
thumbnail=get_thumb("search.png")))
itemlist.append(Item(channel="tvmoviedb", action="mainlist", title=config.get_localized_string(70274),
thumbnail=get_thumb("search.png")))
saved_searches_list = get_saved_searches()
context2 = context[:]
context2.append({"title": config.get_localized_string(59996),
"action": "clear_saved_searches",
"channel": item.channel})
logger.info("saved_searches_list=%s" % saved_searches_list)
if saved_searches_list:
itemlist.append(Item(channel=item.channel, action="",
title=config.get_localized_string(59995), context=context2,
thumbnail=get_thumb("search.png")))
for saved_search_text in saved_searches_list:
itemlist.append(Item(channel=item.channel, action="do_search",
title=' "' + saved_search_text + '"',
extra=saved_search_text, context=context2,
category=saved_search_text,
thumbnail=get_thumb("search.png")))
return itemlist
def opciones(item):
itemlist = list()
itemlist.append(Item(channel=item.channel, action="setting_channel",
title=config.get_localized_string(59994), folder=False,
thumbnail=get_thumb("search.png")))
itemlist.append(Item(channel=item.channel, action="clear_saved_searches", title=config.get_localized_string(59996),
folder=False, thumbnail=get_thumb("search.png")))
itemlist.append(Item(channel=item.channel, action="settings", title=config.get_localized_string(60531), folder=False,
thumbnail=get_thumb("search.png")))
return itemlist
def settings(item):
return platformtools.show_channel_settings(caption=config.get_localized_string(59993))
def setting_channel(item):
if config.get_platform(True)['num_version'] >= 17.0: # A partir de Kodi 16 se puede usar multiselect, y de 17 con preselect
return setting_channel_new(item)
else:
return setting_channel_old(item)
def setting_channel_new(item):
import channelselector, xbmcgui
from core import channeltools
# Cargar lista de opciones (canales activos del usuario y que permitan búsqueda global)
# ------------------------
lista = []; ids = []; lista_lang = []; lista_ctgs = []
channels_list = channelselector.filterchannels('all')
for channel in channels_list:
channel_parameters = channeltools.get_channel_parameters(channel.channel)
# No incluir si en la configuracion del canal no existe "include_in_global_search"
if not channel_parameters['include_in_global_search']:
continue
lbl = '%s' % channel_parameters['language']
lbl += ' %s' % ', '.join(config.get_localized_category(categ) for categ in channel_parameters['categories'])
it = xbmcgui.ListItem(channel.title, lbl)
it.setArt({ 'thumb': channel.thumbnail, 'fanart': channel.fanart })
lista.append(it)
ids.append(channel.channel)
lista_lang.append(channel_parameters['language'])
lista_ctgs.append(channel_parameters['categories'])
# Diálogo para pre-seleccionar
# ----------------------------
preselecciones = [
config.get_localized_string(70570),
config.get_localized_string(70571),
config.get_localized_string(70572),
config.get_localized_string(70573),
# config.get_localized_string(70574),
# config.get_localized_string(70575),
config.get_localized_string(70576)
]
presel_values = ['skip', 'actual', 'all', 'none', 'cast', 'lat', 'ita']
categs = ['movie', 'tvshow', 'documentary', 'anime', 'vos', 'direct', 'torrent']
if config.get_setting('adult_mode') > 0: categs.append('adult')
for c in categs:
preselecciones.append(config.get_localized_string(70577) + config.get_localized_category(c))
presel_values.append(c)
if item.action == 'setting_channel': # Configuración de los canales incluídos en la búsqueda
del preselecciones[0]
del presel_values[0]
#else: # Llamada desde "buscar en otros canales" (se puede saltar la selección e ir directo a la búsqueda)
ret = platformtools.dialog_select(config.get_localized_string(59994), preselecciones)
if ret == -1: return False # pedido cancel
if presel_values[ret] == 'skip': return True # continuar sin modificar
elif presel_values[ret] == 'none': preselect = []
elif presel_values[ret] == 'all': preselect = range(len(ids))
elif presel_values[ret] in ['cast', 'lat']:
preselect = []
for i, lg in enumerate(lista_lang):
if presel_values[ret] in lg or '*' in lg:
preselect.append(i)
elif presel_values[ret] in ['ita']:
preselect = []
for i, lg in enumerate(lista_lang):
if presel_values[ret] in lg or '*' in lg:
preselect.append(i)
elif presel_values[ret] == 'actual':
preselect = []
for i, canal in enumerate(ids):
channel_status = config.get_setting('include_in_global_search', canal)
if channel_status:
preselect.append(i)
else:
preselect = []
for i, ctgs in enumerate(lista_ctgs):
if presel_values[ret] in ctgs:
preselect.append(i)
# Diálogo para seleccionar
# ------------------------
ret = xbmcgui.Dialog().multiselect(config.get_localized_string(59994), lista, preselect=preselect, useDetails=True)
if ret == None: return False # pedido cancel
seleccionados = [ids[i] for i in ret]
# Guardar cambios en canales para la búsqueda
# -------------------------------------------
for canal in ids:
channel_status = config.get_setting('include_in_global_search', canal)
if channel_status is None: channel_status = True
if channel_status and canal not in seleccionados:
config.set_setting('include_in_global_search', False, canal)
elif not channel_status and canal in seleccionados:
config.set_setting('include_in_global_search', True, canal)
return True
def setting_channel_old(item):
channels_path = os.path.join(config.get_runtime_path(), "channels", '*.json')
# channel_language = config.get_setting("channel_language", default="all")
channel_language = auto_filter()
list_controls = []
for infile in sorted(glob.glob(channels_path)):
channel_name = os.path.basename(infile)[:-5]
channel_parameters = channeltools.get_channel_parameters(channel_name)
# No incluir si es un canal inactivo
if not channel_parameters["active"]:
continue
# No incluir si es un canal para adultos, y el modo adulto está desactivado
if channel_parameters["adult"] and config.get_setting("adult_mode") == 0:
continue
# No incluir si el canal es en un idioma filtrado
if channel_language != "all" and channel_language not in channel_parameters["language"] \
and "*" not in channel_parameters["language"]:
continue
# No incluir si en la configuracion del canal no existe "include_in_global_search"
include_in_global_search = channel_parameters["include_in_global_search"]
if not include_in_global_search:
continue
else:
# Se busca en la configuración del canal el valor guardado
include_in_global_search = config.get_setting("include_in_global_search", channel_name)
control = {'id': channel_name,
'type': "bool",
'label': channel_parameters["title"],
'default': include_in_global_search,
'enabled': True,
'visible': True}
list_controls.append(control)
if config.get_setting("custom_button_value", item.channel):
custom_button_label = config.get_localized_string(59992)
else:
custom_button_label = config.get_localized_string(59991)
return platformtools.show_channel_settings(list_controls=list_controls,
caption=config.get_localized_string(59990),
callback="save_settings", item=item,
custom_button={'visible': True,
'function': "cb_custom_button",
'close': False,
'label': custom_button_label})
def save_settings(item, dict_values):
progreso = platformtools.dialog_progress(config.get_localized_string(59988), config.get_localized_string(59989))
n = len(dict_values)
for i, v in enumerate(dict_values):
progreso.update((i * 100) / n, config.get_localized_string(59988))
config.set_setting("include_in_global_search", dict_values[v], v)
progreso.close()
return True
def cb_custom_button(item, dict_values):
value = config.get_setting("custom_button_value", item.channel)
if value == "":
value = False
for v in dict_values.keys():
dict_values[v] = not value
if config.set_setting("custom_button_value", not value, item.channel) == True:
return {"label": config.get_localized_string(59992)}
else:
return {"label": config.get_localized_string(59991)}
def searchbycat(item):
# Only in xbmc/kodi
# Abre un cuadro de dialogo con las categorías en las que hacer la búsqueda
categories = [config.get_localized_string(30122), config.get_localized_string(30123), config.get_localized_string(30124), config.get_localized_string(30125), config.get_localized_string(59975), config.get_localized_string(59976)]
categories_id = ["movie", "tvshow", "anime", "documentary", "vos", "latino"]
list_controls = []
for i, category in enumerate(categories):
control = {'id': categories_id[i],
'type': "bool",
'label': category,
'default': False,
'enabled': True,
'visible': True}
list_controls.append(control)
control = {'id': "separador",
'type': "label",
'label': '',
'default': "",
'enabled': True,
'visible': True}
list_controls.append(control)
control = {'id': "torrent",
'type': "bool",
'label': config.get_localized_string(70275),
'default': True,
'enabled': True,
'visible': True}
list_controls.append(control)
return platformtools.show_channel_settings(list_controls=list_controls, caption=config.get_localized_string(59974),
callback="search_cb", item=item)
def search_cb(item, values=""):
cat = []
for c in values:
if values[c]:
cat.append(c)
if not len(cat):
return None
else:
logger.info(item.tostring())
logger.info(str(cat))
return do_search(item, cat)
# Al llamar a esta función, el sistema pedirá primero el texto a buscar
# y lo pasará en el parámetro "tecleado"
def search(item, tecleado):
logger.info()
tecleado = tecleado.replace("+", " ")
item.category = tecleado
if tecleado != "":
save_search(tecleado)
if item.extra == "categorias":
item.extra = tecleado
itemlist = searchbycat(item)
else:
item.extra = tecleado
itemlist = do_search(item, [])
return itemlist
def show_result(item):
tecleado = None
if item.adult and config.get_setting("adult_request_password"):
# Solicitar contraseña
tecleado = platformtools.dialog_input("", config.get_localized_string(60334), True)
if tecleado is None or tecleado != config.get_setting("adult_password"):
return []
item.channel = item.__dict__.pop('from_channel')
item.action = item.__dict__.pop('from_action')
if item.__dict__.has_key('tecleado'):
tecleado = item.__dict__.pop('tecleado')
try:
channel = __import__('channels.%s' % item.channel, fromlist=["channels.%s" % item.channel])
except:
import traceback
logger.error(traceback.format_exc())
return []
if tecleado:
# Mostrar resultados: agrupados por canales
return channel.search(item, tecleado)
else:
# Mostrar resultados: todos juntos
if item.infoPlus: #Si viene de una ventana de InfoPlus, hay que salir de esta forma...
del item.infoPlus #si no, se mete en un bucle mostrando la misma pantalla,
item.title = item.title.strip() #dando error en "handle -1"
return getattr(channel, item.action)(item)
try:
from platformcode import launcher
launcher.run(item)
except ImportError:
return getattr(channel, item.action)(item)
def channel_search(search_results, channel_parameters, tecleado):
try:
exec("from specials import " + channel_parameters["channel"] + " as module")
mainlist = module.mainlist(Item(channel=channel_parameters["channel"]))
search_items = [item for item in mainlist if item.action == "search"]
if not search_items:
search_items = [Item(channel=channel_parameters["channel"], action="search")]
for item in search_items:
result = module.search(item.clone(), tecleado)
if result is None:
result = []
if len(result):
if not channel_parameters["title"].capitalize() in search_results:
search_results[channel_parameters["title"].capitalize()] = []
search_results[channel_parameters["title"].capitalize()].append({"item": item,
"itemlist": result,
"adult": channel_parameters["adult"]})
except:
logger.error("No se puede buscar en: %s" % channel_parameters["title"])
import traceback
logger.error(traceback.format_exc())
# Esta es la función que realmente realiza la búsqueda
def do_search(item, categories=None):
logger.info("blaa categorias %s" % categories)
if item.contextual==True:
categories = ["Películas"]
setting_item = Item(channel=item.channel, title=config.get_localized_string(59994), folder=False,
thumbnail=get_thumb("search.png"))
if not setting_channel(setting_item):
return False
if categories is None:
categories = []
multithread = config.get_setting("multithread", "search")
result_mode = config.get_setting("result_mode", "search")
if item.wanted!='':
tecleado=item.wanted
else:
tecleado = item.extra
itemlist = []
channels_path = os.path.join(config.get_runtime_path(), "channels", '*.json')
logger.info("channels_path=%s" % channels_path)
# channel_language = config.get_setting("channel_language", default="all")
channel_language = auto_filter()
logger.info("channel_language=%s" % channel_language)
# Para Kodi es necesario esperar antes de cargar el progreso, de lo contrario
# el cuadro de progreso queda "detras" del cuadro "cargando..." y no se le puede dar a cancelar
time.sleep(0.5)
progreso = platformtools.dialog_progress(config.get_localized_string(30993) % tecleado, "")
channel_files = sorted(glob.glob(channels_path), key=lambda x: os.path.basename(x))
import math
threads = []
search_results = {}
start_time = time.time()
list_channels_search = []
# Extrae solo los canales a buscar
for index, infile in enumerate(channel_files):
try:
basename = os.path.basename(infile)
basename_without_extension = basename[:-5]
logger.info("%s..." % basename_without_extension)
channel_parameters = channeltools.get_channel_parameters(basename_without_extension)
# No busca si es un canal inactivo
if not channel_parameters["active"]:
logger.info("%s -no activo-" % basename_without_extension)
continue
# En caso de búsqueda por categorias
if categories:
# Si no se ha seleccionado torrent no se muestra
#if "torrent" not in categories and "infoPlus" not in categories:
# if "torrent" in channel_parameters["categories"]:
# logger.info("%s -torrent-" % basename_without_extension)
# continue
for cat in categories:
if cat not in channel_parameters["categories"]:
logger.info("%s -no en %s-" % (basename_without_extension, cat))
continue
# No busca si es un canal para adultos, y el modo adulto está desactivado
if channel_parameters["adult"] and config.get_setting("adult_mode") == 0:
logger.info("%s -adulto-" % basename_without_extension)
continue
# No busca si el canal es en un idioma filtrado
if channel_language != "all" and channel_language not in channel_parameters["language"] \
and "*" not in channel_parameters["language"]:
logger.info("%s -idioma no válido-" % basename_without_extension)
continue
# No busca si es un canal excluido de la búsqueda global
include_in_global_search = channel_parameters["include_in_global_search"]
if include_in_global_search:
# Buscar en la configuracion del canal
include_in_global_search = config.get_setting("include_in_global_search", basename_without_extension)
if not include_in_global_search:
logger.info("%s -no incluido en lista a buscar-" % basename_without_extension)
continue
list_channels_search.append(infile)
except:
logger.error("No se puede buscar en: %s" % channel_parameters["title"])
import traceback
logger.error(traceback.format_exc())
continue
for index, infile in enumerate(list_channels_search):
try:
# fix float porque la division se hace mal en python 2.x
percentage = int(float((index+1))/len(list_channels_search)*float(100))
basename = os.path.basename(infile)
basename_without_extension = basename[:-5]
logger.info("%s..." % basename_without_extension)
channel_parameters = channeltools.get_channel_parameters(basename_without_extension)
# Movido aqui el progreso, para que muestre el canal exacto que está buscando
progreso.update(percentage,
config.get_localized_string(60520) % (channel_parameters["title"]))
# Modo Multi Thread
if progreso.iscanceled():
progreso.close()
logger.info("Búsqueda cancelada")
return itemlist
if multithread:
t = Thread(target=channel_search, args=[search_results, channel_parameters, tecleado],
name=channel_parameters["title"])
t.setDaemon(True)
t.start()
threads.append(t)
# Modo single Thread
else:
logger.info("Intentado búsqueda en %s de %s " % (basename_without_extension, tecleado))
channel_search(search_results, channel_parameters, tecleado)
except:
logger.error("No se puede buscar en: %s" % channel_parameters["title"])
import traceback
logger.error(traceback.format_exc())
continue
# Modo Multi Thread
# Usando isAlive() no es necesario try-except,
# ya que esta funcion (a diferencia de is_alive())
# es compatible tanto con versiones antiguas de python como nuevas
if multithread:
pendent = [a for a in threads if a.isAlive()]
if len(pendent) > 0: t = float(100) / len(pendent)
while len(pendent) > 0:
index = (len(threads) - len(pendent)) + 1
percentage = int(math.ceil(index * t))
list_pendent_names = [a.getName() for a in pendent]
mensaje = config.get_localized_string(70282) % (", ".join(list_pendent_names))
progreso.update(percentage, config.get_localized_string(60521) % (len(threads) - len(pendent) + 1, len(threads)),
mensaje)
if progreso.iscanceled():
logger.info("Búsqueda cancelada")
break
time.sleep(0.5)
pendent = [a for a in threads if a.isAlive()]
total = 0
for channel in sorted(search_results.keys()):
for element in search_results[channel]:
total += len(element["itemlist"])
title = channel
# resultados agrupados por canales
if item.contextual == True or item.action == 'search_tmdb':
result_mode = 1
if result_mode == 0:
if len(search_results[channel]) > 1:
title += " -%s" % element["item"].title.strip()
title += " (%s)" % len(element["itemlist"])
title = re.sub("\[COLOR [^\]]+\]", "", title)
title = re.sub("\[/COLOR]", "", title)
itemlist.append(Item(title=title, channel="search", action="show_result", url=element["item"].url,
extra=element["item"].extra, folder=True, adult=element["adult"],
from_action="search", from_channel=element["item"].channel, tecleado=tecleado))
# todos los resultados juntos, en la misma lista
else:
title = config.get_localized_string(70697) % channel
itemlist.append(Item(title=title, channel="search", action="",
folder=False, text_bold=True, from_channel=channel))
for i in element["itemlist"]:
if i.action:
title = " " + i.title
if "infoPlus" in categories: #Se manrca vi viene de una ventana de InfoPlus
i.infoPlus = True
itemlist.append(i.clone(title=title, from_action=i.action, from_channel=i.channel,
channel="search", action="show_result", adult=element["adult"]))
title = config.get_localized_string(59972) % (
tecleado, total, time.time() - start_time)
itemlist.insert(0, Item(title=title, text_color='yellow'))
progreso.close()
#Para opcion Buscar en otros canales
if item.contextual == True:
return exact_results(itemlist, tecleado)
else:
return itemlist
def exact_results(results, wanted):
logger.info()
itemlist =[]
for item in results:
if item.action=='':
channel=item.from_channel
if item.action != '' and item.contentTitle==wanted:
item.title = '%s [%s]' % (item.title, channel)
itemlist.append(item)
return itemlist
def save_search(text):
saved_searches_limit = int((10, 20, 30, 40,)[int(config.get_setting("saved_searches_limit", "search"))])
current_saved_searches_list = config.get_setting("saved_searches_list", "search")
if current_saved_searches_list is None:
saved_searches_list = []
else:
saved_searches_list = list(current_saved_searches_list)
if text in saved_searches_list:
saved_searches_list.remove(text)
saved_searches_list.insert(0, text)
config.set_setting("saved_searches_list", saved_searches_list[:saved_searches_limit], "search")
def clear_saved_searches(item):
config.set_setting("saved_searches_list", list(), "search")
platformtools.dialog_ok(config.get_localized_string(60329), config.get_localized_string(60424))
def get_saved_searches():
current_saved_searches_list = config.get_setting("saved_searches_list", "search")
if current_saved_searches_list is None:
saved_searches_list = []
else:
saved_searches_list = list(current_saved_searches_list)
return saved_searches_list
def discover_list(item):
from platformcode import unify
itemlist = []
result = tmdb.discovery(item)
tvshow = False
logger.debug(item)
for elem in result:
elem['tmdb_id']=elem['id']
if 'title' in elem:
title = unify.normalize(elem['title']).capitalize()
elem['year'] = scrapertools.find_single_match(elem['release_date'], '(\d{4})-\d+-\d+')
else:
title = unify.normalize(elem['name']).capitalize()
tvshow = True
new_item = Item(channel='search', title=title, infoLabels=elem, action='do_search', extra=title,
category=config.get_localized_string(70695), context ='')
if tvshow:
new_item.contentSerieName = title
else:
new_item.contentTitle = title
itemlist.append(new_item)
tmdb.set_infoLabels_itemlist(itemlist, seekTmdb=True)
if item.page != '' and len(itemlist)>0:
next_page = str(int(item.page)+1)
#if not 'similar' in item.list_type:
# itemlist.append(item.clone(title='Pagina Siguente', page=next_page))
#else:
itemlist.append(Item(channel=item.channel, action='discover_list', title=config.get_localized_string(70065),
search_type=item.search_type, list_type=item.list_type, type=item.type, page=next_page))
return itemlist
def search_tmdb(item):
logger.debug(item)
itemlist = []
threads = []
logger.debug(item)
wanted = item.contentTitle
search = do_search(item)
if item.contentSerieName == '':
results = exact_results(search, wanted)
for result in results:
logger.debug(result)
t = Thread(target=get_links, args=[result])
t.start()
threads.append(t)
for thread in threads:
thread.join()
# try:
# get_links(result)
# except:
# pass
for link in link_list:
if link.action == 'play' and not 'trailer' in link.title.lower() and len(itemlist) < max_links:
itemlist.append(link)
return sorted(itemlist, key=lambda it: it.server)
else:
for item in search:
if item.contentSerieName != '' and item.contentSerieName == wanted:
logger.debug(item)
itemlist.append(item)
return itemlist
def get_links (item):
logger.info()
results =[]
channel = __import__('channels.%s' % item.from_channel, None, None, ["channels.%s" % item.from_channel])
if len(link_list) <= max_links:
link_list.extend(getattr(channel, item.from_action)(item))
+866
View File
@@ -0,0 +1,866 @@
# -*- coding: utf-8 -*-
import Queue
import datetime
import glob
import os
import re
import threading
import time
import urllib
from threading import Thread
from unicodedata import normalize
import xbmc
from core import channeltools, httptools, tmdb, servertools
from lib.fuzzywuzzy import fuzz
from platformcode import platformtools
try:
import json
except:
import simplejson as json
from platformcode import config
from platformcode import logger
from core.item import Item
TMDB_KEY = tmdb.tmdb_auth_key ######TMDB_KEY = '92db8778ccb39d825150332b0a46061d'
# TMDB_KEY = '92db8778ccb39d825150332b0a46061d'
TMDB_URL_BASE = 'http://api.themoviedb.org/3/'
TMDB_IMAGES_BASEURL = 'http://image.tmdb.org/t/p/'
INCLUDE_ADULT = True if config.get_setting("enableadultmode") else False
LANGUAGE_ID = 'it'
DTTIME = (datetime.datetime.utcnow() - datetime.timedelta(hours=5))
SYSTIME = DTTIME.strftime('%Y%m%d%H%M%S%f')
TODAY_TIME = DTTIME.strftime('%Y-%m-%d')
MONTH_TIME = (DTTIME - datetime.timedelta(days=30)).strftime('%Y-%m-%d')
MONTH2_TIME = (DTTIME - datetime.timedelta(days=60)).strftime('%Y-%m-%d')
YEAR_DATE = (DTTIME - datetime.timedelta(days=365)).strftime('%Y-%m-%d')
TIMEOUT_TOTAL = config.get_setting("timeout")
MAX_THREADS = config.get_setting("maxthreads")
# TIMEOUT_TOTAL = config.get_setting("timeout", default=90)
# MAX_THREADS = config.get_setting("maxthreads", default=24)
NLS_Search_by_Channel = config.get_localized_string(30974)
NLS_Alternative_Search = config.get_localized_string(70021)
NLS_Search_by_Title = config.get_localized_string(30980)
NLS_Search_by_Person = config.get_localized_string(30981)
NLS_Search_by_Company = config.get_localized_string(30982)
NLS_Now_Playing = config.get_localized_string(30983)
NLS_Popular = config.get_localized_string(30984)
NLS_Top_Rated = config.get_localized_string(30985)
NLS_Search_by_Collection = config.get_localized_string(30986)
NLS_List_by_Genre = config.get_localized_string(30987)
NLS_Search_by_Year = config.get_localized_string(30988)
NLS_Search_Similar_by_Title = config.get_localized_string(30989)
NLS_Search_Tvshow_by_Title = config.get_localized_string(30990)
NLS_Most_Voted = config.get_localized_string(30996)
NLS_Oscar = config.get_localized_string(30997)
NLS_Last_2_months = config.get_localized_string(60534)
NLS_Library = config.get_localized_string(30991)
NLS_Next_Page = config.get_localized_string(30992)
NLS_Looking_For = config.get_localized_string(30993)
NLS_Searching_In = config.get_localized_string(30994)
NLS_Found_So_Far = config.get_localized_string(30995)
NLS_Info_Title = config.get_localized_string(30975)
NLS_Info_Person = config.get_localized_string(30979)
NLS_New_TVShow = config.get_localized_string(30978)
NLS_TVShow_onair = config.get_localized_string(30977)
NLS_TVShow_airing_today = config.get_localized_string(30976)
TMDb_genres = {}
def mainlist(item):
logger.info(" mainlist")
itemlist = [Item(channel="search",
title="[COLOR lightgreen]%s[/COLOR]" % NLS_Search_by_Channel,
action="mainlist",
thumbnail="http://i.imgur.com/pE5WSZp.png"),
Item(channel="tvmoviedb",
title="[COLOR yellow]%s[/COLOR]" % NLS_Alternative_Search,
action="mainlist",
url="search_movie_by_title",
thumbnail="https://s6.postimg.cc/6lll9b8c1/searching.png"),
Item(channel=item.channel,
title="[COLOR yellow]%s[/COLOR]" % NLS_Search_by_Title,
action="search",
url="search_movie_by_title",
thumbnail="http://i.imgur.com/B1H1G8U.png"),
Item(channel=item.channel,
title="[COLOR yellow]%s[/COLOR]" % NLS_Search_by_Person,
action="search",
url="search_person_by_name",
thumbnail="http://i.imgur.com/efuEeNu.png"),
Item(channel=item.channel,
title="[COLOR yellow]%s[/COLOR]" % NLS_Search_by_Year,
action="search_movie_by_year",
url="search_movie_by_year",
thumbnail="https://d1kz0yd1invg7i.cloudfront.net/uploads/app/icon/1/calendar-icon-big.png"),
Item(channel=item.channel,
title="[COLOR yellow]%s[/COLOR]" % NLS_Search_by_Collection,
action="search",
url="search_collection_by_name",
thumbnail="http://i.imgur.com/JmcvZDL.png"),
Item(channel=item.channel,
title="[COLOR yellow]%s[/COLOR]" % NLS_Search_Similar_by_Title,
action="search",
url="search_similar_movie_by_title",
thumbnail="http://i.imgur.com/JmcvZDL.png"),
Item(channel=item.channel,
title="[COLOR lime]%s[/COLOR]" % NLS_Search_Tvshow_by_Title,
action="search",
url="search_tvshow_by_title",
thumbnail="https://i.imgur.com/2ZWjLn5.jpg?1"),
Item(channel=item.channel,
title="(TV Shows) [COLOR lime]%s[/COLOR]" % NLS_New_TVShow,
action="list_tvshow",
url='discover/tv?sort_by=popularity.desc&first_air_date.gte=%s&first_air_date.lte=%s&' % (
MONTH2_TIME, TODAY_TIME),
plot="1",
type="tvshow",
thumbnail="https://i.imgur.com/2ZWjLn5.jpg?1"),
Item(channel=item.channel,
title="(TV Shows) [COLOR lime]%s[/COLOR]" % NLS_TVShow_onair,
action="list_tvshow",
url="tv/on_the_air?",
plot="1",
type="tvshow",
thumbnail="https://i.imgur.com/2ZWjLn5.jpg?1"),
Item(channel=item.channel,
title="(TV Shows) [COLOR lime]%s[/COLOR]" % NLS_Popular,
action="list_tvshow",
url="tv/popular?",
plot="1",
type="tvshow",
thumbnail="https://i.imgur.com/2ZWjLn5.jpg?1"),
Item(channel=item.channel,
title="(TV Shows) [COLOR lime]%s[/COLOR]" % NLS_Top_Rated,
action="list_tvshow",
url="tv/top_rated?",
plot="1",
type="tvshow",
thumbnail="https://i.imgur.com/2ZWjLn5.jpg?1"),
Item(channel=item.channel,
title="(TV Shows) [COLOR lime]%s[/COLOR]" % NLS_TVShow_airing_today,
action="list_tvshow",
url="tv/airing_today?",
plot="1",
type="tvshow",
thumbnail="https://i.imgur.com/2ZWjLn5.jpg?1"),
Item(channel=item.channel,
title="(Movies) [COLOR yellow]%s[/COLOR]" % NLS_Now_Playing,
action="list_movie",
url="movie/now_playing?",
plot="1",
type="movie",
thumbnail="http://i.imgur.com/B16HnVh.png"),
Item(channel=item.channel,
title="(Movies) [COLOR yellow]%s[/COLOR]" % NLS_Popular,
action="list_movie",
url="movie/popular?",
plot="1",
type="movie",
thumbnail="http://i.imgur.com/8IBjyzw.png"),
Item(channel=item.channel,
title="(Movies) [COLOR yellow]%s[/COLOR]" % NLS_Top_Rated,
action="list_movie",
url="movie/top_rated?",
plot="1",
type="movie",
thumbnail="http://www.clipartbest.com/cliparts/RiG/6qn/RiG6qn79T.png"),
Item(channel=item.channel,
title="(Movies) [COLOR yellow]%s[/COLOR]" % NLS_Most_Voted,
action="list_movie",
url='discover/movie?certification_country=US&sort_by=vote_count.desc&',
plot="1",
type="movie",
thumbnail="http://i.imgur.com/5ShnO8w.png"),
Item(channel=item.channel,
title="(Movies) [COLOR yellow]%s[/COLOR]" % NLS_Oscar,
action="list_movie",
url='list/509ec17b19c2950a0600050d?',
plot="1",
type="movie",
thumbnail="http://i.imgur.com/5ShnO8w.png"),
Item(channel=item.channel,
title="(Movies) [COLOR yellow]%s[/COLOR]" % NLS_Last_2_months,
action="list_movie",
url='discover/movie?primary_release_date.gte=%s&primary_release_date.lte=%s&' % (
YEAR_DATE, MONTH2_TIME),
plot="1",
type="movie",
thumbnail="http://i.imgur.com/CsizqUI.png"),
Item(channel=item.channel,
title="(Movies) [COLOR yellow]%s[/COLOR]" % NLS_List_by_Genre,
action="list_genres",
type="movie",
thumbnail="http://i.imgur.com/uotyBbU.png")]
return itemlist
def list_movie(item):
logger.info(" list_movie '%s/%s'" % (item.url, item.plot))
results = [0, 0]
page = int(item.plot)
itemlist = build_movie_list(item, tmdb_get_data('%s&page=%d&' % (item.url, page), results=results))
if page < results[0]:
itemlist.append(Item(
channel=item.channel,
title="[COLOR orange]%s (%d/%d)[/COLOR]" % (NLS_Next_Page, page * len(itemlist), results[1]),
action="list_movie",
url=item.url,
plot="%d" % (page + 1),
type=item.type,
viewmode="" if page <= 1 else "paged_list"))
return itemlist
def list_tvshow(item):
logger.info(" list_tvshow '%s/%s'" % (item.url, item.plot))
results = [0, 0]
page = int(item.plot)
itemlist = build_movie_list(item, tmdb_get_data('%spage=%d&' % (item.url, page), results=results))
if page < results[0]:
itemlist.append(Item(
channel=item.channel,
title="[COLOR orange]%s (%d/%d)[/COLOR]" % (NLS_Next_Page, page * len(itemlist), results[1]),
action="list_tvshow",
url=item.url,
plot="%d" % (page + 1),
type=item.type,
viewmode="" if page <= 1 else "paged_list"))
return itemlist
def list_genres(item):
logger.info(" list_genres")
tmdb_genre(1)
itemlist = []
for genre_id, genre_name in TMDb_genres.iteritems():
itemlist.append(
Item(channel=item.channel,
title=genre_name,
action="list_movie",
url='genre/%d/movies?primary_release_date.gte=%s&primary_release_date.lte=%s&language=it' % (
genre_id, YEAR_DATE, TODAY_TIME),
plot="1"))
return itemlist
def discover_list(item):
from platformcode import unify
from core import scrapertools
itemlist = []
result = tmdb.discovery(item)
tvshow = False
logger.debug(item)
for elem in result:
elem['tmdb_id'] = elem['id']
if 'title' in elem:
title = unify.normalize(elem['title']).capitalize()
elem['year'] = scrapertools.find_single_match(elem['release_date'], '(\d{4})-\d+-\d+')
else:
title = unify.normalize(elem['name']).capitalize()
tvshow = True
new_item = Item(channel='searchall', title=title, infoLabels=elem, action='search_tmdb', extra=title,
category=config.get_localized_string(70695), context='')
if tvshow:
new_item.contentSerieName = title
else:
new_item.contentTitle = title
itemlist.append(new_item)
tmdb.set_infoLabels_itemlist(itemlist, seekTmdb=True)
if item.page != '' and len(itemlist) > 0:
next_page = str(int(item.page) + 1)
# if not 'similar' in item.list_type:
# itemlist.append(item.clone(title='Pagina Siguente', page=next_page))
# else:
itemlist.append(Item(channel=item.channel, action='discover_list', title=config.get_localized_string(30992),
text_color="orange", search_type=item.search_type, list_type=item.list_type,
type=item.type, page=next_page))
return itemlist
# Do not change the name of this function otherwise launcher.py won't create the keyboard dialog required to enter the search terms
def search(item, search_terms):
if item.url == '': return []
return globals()[item.url](item, search_terms) if item.url in globals() else []
def search_tvshow_by_title(item, search_terms):
logger.info(" search_tvshow_by_title '%s'" % (search_terms))
return list_movie(
Item(channel=item.channel,
url='search/tv?query=%s&' % search_terms,
plot="1",
type="tvshow"))
def search_movie_by_title(item, search_terms):
logger.info(" search_movie_by_title '%s'" % (search_terms))
return list_movie(
Item(channel=item.channel,
url='search/movie?query=%s&' % search_terms,
plot="1",
type="movie"))
def search_similar_movie_by_title(item, search_terms):
logger.info(" search_similar_movie_by_title '%s'" % (search_terms))
return list_movie(
Item(channel=item.channel,
url='search/movie?append_to_response=similar_movies,alternative_title&query=%s&' % search_terms,
plot="1",
type='movie'))
def search_movie_by_year(item):
logger.info(" search_movie_by_year")
now = datetime.datetime.now()
year = int(now.year)
result = []
for i in range(150):
year_to_search = year - i
result.append(Item(channel=item.channel,
url='discover/movie?primary_release_year=%s&' % year_to_search,
plot="1",
type="movie",
title="%s" % year_to_search,
action="list_movie"))
return result
def search_person_by_name(item, search_terms):
logger.info(" search_person_by_name '%s'" % (search_terms))
persons = tmdb_get_data("search/person?query=%s&" % search_terms)
itemlist = []
for person in persons:
name = normalize_unicode(tmdb_tag(person, 'name'))
poster = tmdb_image(person, 'profile_path')
fanart = ''
for movie in tmdb_tag(person, 'known_for', []):
if tmdb_tag_exists(movie, 'backdrop_path'):
fanart = tmdb_image(movie, 'backdrop_path', 'w1280')
break
# extracmds = [
# (NLS_Info_Person, "RunScript(script.extendedinfo,info=extendedactorinfo,id=%s)" % str(tmdb_tag(person, 'id')))] \
# if xbmc.getCondVisibility('System.HasAddon(script.extendedinfo)') else []
itemlist.append(Item(
channel=item.channel,
action='search_movie_by_person',
extra=str(tmdb_tag(person, 'id')),
title=name,
thumbnail=poster,
viewmode='list',
fanart=fanart,
type='movie'
# extracmds=extracmds
))
return itemlist
def search_movie_by_person(item):
logger.info(" search_movie_by_person '%s'" % (item.extra))
# return list_movie(
# Item(channel=item.channel,
# url="discover/movie?with_people=%s&primary_release_date.lte=%s&sort_by=primary_release_date.desc&" % (
# item.extra, TODAY_TIME),
# plot="1"))
person_movie_credits = tmdb_get_data(
"person/%s/movie_credits?primary_release_date.lte=%s&sort_by=primary_release_date.desc&" % (
item.extra, TODAY_TIME))
movies = []
if person_movie_credits:
movies.extend(tmdb_tag(person_movie_credits, 'cast', []))
movies.extend(tmdb_tag(person_movie_credits, 'crew', []))
# Movie person list is not paged
return build_movie_list(item, movies)
def search_collection_by_name(item, search_terms):
logger.info(" search_collection_by_name '%s'" % (search_terms))
collections = tmdb_get_data("search/collection?query=%s&" % search_terms)
itemlist = []
for collection in collections:
name = normalize_unicode(tmdb_tag(collection, 'name'))
poster = tmdb_image(collection, 'poster_path')
fanart = tmdb_image(collection, 'backdrop_path', 'w1280')
itemlist.append(Item(
channel=item.channel,
action='search_movie_by_collection',
extra=str(tmdb_tag(collection, 'id')),
title=name,
thumbnail=poster,
viewmode='list',
fanart=fanart,
type='movie'
))
return itemlist
def search_movie_by_collection(item):
logger.info(" search_movie_by_collection '%s'" % (item.extra))
collection = tmdb_get_data("collection/%s?" % item.extra)
# Movie collection list is not paged
return build_movie_list(item, collection['parts']) if 'parts' in collection else []
def build_movie_list(item, movies):
if movies is None: return []
itemlist = []
for movie in movies:
t = tmdb_tag(movie, 'title')
if t == '':
t = re.sub('\s(|[(])(UK|US|AU|\d{4})(|[)])$', '', tmdb_tag(movie, 'name'))
title = normalize_unicode(t)
title_search = normalize_unicode(t, encoding='ascii')
if not all(ord(char) < 128 for char in title): continue
poster = tmdb_image(movie, 'poster_path')
fanart = tmdb_image(movie, 'backdrop_path', 'w1280')
jobrole = normalize_unicode(
' [COLOR yellow][' + tmdb_tag(movie, 'job') + '][/COLOR]' if tmdb_tag_exists(movie, 'job') else '')
genres = normalize_unicode(
' / '.join([tmdb_genre(genre).upper() for genre in tmdb_tag(movie, 'genre_ids', [])]))
year = tmdb_tag(movie, 'release_date')[0:4] if tmdb_tag_exists(movie, 'release_date') else ''
plot = normalize_unicode(tmdb_tag(movie, 'overview'))
rating = tmdb_tag(movie, 'vote_average')
votes = tmdb_tag(movie, 'vote_count')
extrameta = {'plot': plot}
if year != "": extrameta["Year"] = year
if genres != "": extrameta["Genre"] = genres
if votes:
extrameta["Rating"] = rating
extrameta["Votes"] = "%d" % votes
# extracmds = [(NLS_Info_Title, "RunScript(script.extendedinfo,info=extendedinfo,id=%s)" % str(tmdb_tag(movie, 'id')))] \
# if xbmc.getCondVisibility('System.HasAddon(script.extendedinfo)') else [('Movie/Show Info', 'XBMC.Action(Info)')]
found = False
kodi_db_movies = kodi_database_movies(title)
for kodi_db_movie in kodi_db_movies:
logger.info('Kod.database set for local playing(%s):\n%s' % (title, str(kodi_db_movie)))
if year == str(kodi_db_movie["year"]):
found = True
# If some, less relevant, keys are missing locally
# try to get them through TMDB anyway.
try:
poster = kodi_db_movie["art"]["poster"]
fanart = kodi_db_movie["art"]["fanart"]
except KeyError:
poster = poster
fanart = fanart
itemlist.append(Item(
channel=item.channel,
action='play',
url=kodi_db_movie["file"],
title='[COLOR orange][%s][/COLOR] ' % NLS_Library + kodi_db_movie["title"] + jobrole,
thumbnail=poster,
category=genres,
plot=plot,
viewmode='movie_with_plot',
fanart=fanart,
infoLabels=extrameta,
folder=False,
))
if not found:
logger.info('Kod.database set for channels search(%s)' % title)
itemlist.append(Item(
channel=item.channel,
action='do_channels_search',
extra=url_quote_plus(title_search) + '{}' + item.type + '{}' + year,
title=title + jobrole,
thumbnail=poster,
category=genres,
plot=plot,
viewmode='movie_with_plot',
fanart=fanart,
infoLabels=extrameta,
))
return itemlist
def normalize_unicode(string, encoding='utf-8'):
if string is None: string = ''
return normalize('NFKD', string if isinstance(string, unicode) else unicode(string, encoding, 'ignore')).encode(
encoding, 'ignore')
def tmdb_get_data(url="", results=[0, 0], language=True):
url = TMDB_URL_BASE + "%sinclude_adult=%s&api_key=%s" % (url, INCLUDE_ADULT, TMDB_KEY)
# Temporary fix until tmdb fixes the issue with getting the genres by language!
if language: url += "&language=%s" % LANGUAGE_ID
response = get_json_response(url)
results[0] = response['total_pages'] if 'total_pages' in response else 0
results[1] = response['total_results'] if 'total_results' in response else 0
if response:
if "results" in response:
return response["results"]
elif "items" in response:
return response["items"]
elif "tv_credits" in response:
return response["tv_credits"]["cast"]
else:
return response
def tmdb_tag_exists(entry, tag):
return isinstance(entry, dict) and tag in entry and entry[tag] is not None
def tmdb_tag(entry, tag, default=""):
return entry[tag] if isinstance(entry, dict) and tag in entry else default
def tmdb_image(entry, tag, width='original'):
return TMDB_IMAGES_BASEURL + width + '/' + tmdb_tag(entry, tag) if tmdb_tag_exists(entry, tag) else ''
def tmdb_genre(id):
if id not in TMDb_genres:
genres = tmdb_get_data("genre/list?", language="it")
for genre in tmdb_tag(genres, 'genres', []):
TMDb_genres[tmdb_tag(genre, 'id')] = tmdb_tag(genre, 'name')
return TMDb_genres[id] if id in TMDb_genres and TMDb_genres[id] != None else str(id)
def kodi_database_movies(title):
json_query = \
'{"jsonrpc": "2.0",\
"params": {\
"sort": {"order": "ascending", "method": "title"},\
"filter": {"operator": "is", "field": "title", "value": "%s"},\
"properties": ["title", "art", "file", "year"]\
},\
"method": "VideoLibrary.GetMovies",\
"id": "libMovies"\
}' % title
response = get_xbmc_jsonrpc_response(json_query)
return response["result"]["movies"] if response and "result" in response and "movies" in response["result"] else []
def get_xbmc_jsonrpc_response(json_query=""):
try:
response = xbmc.executeJSONRPC(json_query)
response = unicode(response, 'utf-8', errors='ignore')
response = json.loads(response)
logger.info(" jsonrpc %s" % response)
except Exception, e:
logger.info(" jsonrpc error: %s" % str(e))
response = None
return response
def url_quote_plus(input_string):
try:
return urllib.quote_plus(input_string.encode('utf8', 'ignore'))
except:
return urllib.quote_plus(unicode(input_string, "utf-8").encode("utf-8"))
def get_json_response(url=""):
response = httptools.downloadpage(url).data
try:
results = json.loads(response)
except:
logger.info(" Exception: Could not get new JSON data from %s" % url)
results = []
return results
def channel_search(queue, channel_parameters, category, title_year, tecleado):
try:
search_results = []
title_search = urllib.unquote_plus(tecleado)
exec "from specials import " + channel_parameters["channel"] + " as module"
mainlist = module.mainlist(Item(channel=channel_parameters["channel"]))
for item in mainlist:
if item.action != "search" or category and item.extra != category:
continue
for res_item in module.search(item.clone(), tecleado):
title = res_item.fulltitle
# If the release year is known, check if it matches the year found in the title
if title_year > 0:
year_match = re.search('\(.*(\d{4}).*\)', title)
if year_match and abs(int(year_match.group(1)) - title_year) > 1:
continue
# Clean up a bit the returned title to improve the fuzzy matching
title = re.sub(r'\(.*\)', '', title) # Anything within ()
title = re.sub(r'\[.*\]', '', title) # Anything within []
# Check if the found title fuzzy matches the searched one
if fuzz.token_sort_ratio(title_search, title) > 85:
res_item.title = "[COLOR azure]" + res_item.title + "[/COLOR][COLOR orange] su [/COLOR][COLOR green]" + \
channel_parameters["title"] + "[/COLOR]"
search_results.append(res_item)
queue.put(search_results)
except:
logger.error("No se puede buscar en: " + channel_parameters["title"])
import traceback
logger.error(traceback.format_exc())
def do_channels_search(item):
logger.info(" do_channels_search")
tecleado, category, title_year = item.extra.split('{}')
try:
title_year = int(title_year)
except:
title_year = 0
itemlist = []
channels_path = os.path.join(config.get_runtime_path(), "channels", '*.json')
logger.info(" channels_path=" + channels_path)
# channel_language = config.get_setting("channel_language")
channel_language = auto_filter()
logger.info(" channel_language=" + channel_language)
if channel_language == "":
channel_language = "all"
logger.info(" channel_language=" + channel_language)
progreso = platformtools.dialog_progress_bg(NLS_Looking_For % urllib.unquote_plus(tecleado))
channel_files = sorted(glob.glob(channels_path))
search_results = Queue.Queue()
completed_channels = 0
number_of_channels = 0
start_time = int(time.time())
for infile in channel_files:
basename_without_extension = os.path.basename(infile)[:-5]
channel_parameters = channeltools.get_channel_parameters(basename_without_extension)
# No busca si es un canal inactivo
if channel_parameters["active"] != True:
continue
# En caso de busqueda por categorias
if category and category not in channel_parameters["categories"]:
continue
# No busca si el canal es en un idioma filtrado
if channel_language != "all" and channel_parameters["language"] != channel_language:
continue
# No busca si es un canal excluido de la busqueda global
include_in_global_search = channel_parameters["include_in_global_search"]
if include_in_global_search == True:
# Buscar en la configuracion del canal
include_in_global_search = config.get_setting("include_in_global_search", basename_without_extension)
if include_in_global_search == False:
continue
t = Thread(target=channel_search, args=[search_results, channel_parameters, category, title_year, tecleado])
t.setDaemon(True)
t.start()
number_of_channels += 1
while threading.active_count() >= MAX_THREADS:
delta_time = int(time.time()) - start_time
if len(itemlist) <= 0:
timeout = None # No result so far,lets the thread to continue working until a result is returned
elif delta_time >= TIMEOUT_TOTAL:
progreso.close()
itemlist = sorted(itemlist, key=lambda item: item.fulltitle)
return itemlist
else:
timeout = TIMEOUT_TOTAL - delta_time # Still time to gather other results
progreso.update(completed_channels * 100 / number_of_channels)
try:
itemlist.extend(search_results.get(timeout=timeout))
completed_channels += 1
except:
progreso.close()
itemlist = sorted(itemlist, key=lambda item: item.fulltitle)
return itemlist
while completed_channels < number_of_channels:
delta_time = int(time.time()) - start_time
if len(itemlist) <= 0:
timeout = None # No result so far,lets the thread to continue working until a result is returned
elif delta_time >= TIMEOUT_TOTAL:
break # At least a result matching the searched title has been found, lets stop the search
else:
timeout = TIMEOUT_TOTAL - delta_time # Still time to gather other results
progreso.update(completed_channels * 100 / number_of_channels)
try:
itemlist.extend(search_results.get(timeout=timeout))
completed_channels += 1
except:
# Expired timeout raise an exception
break
progreso.close()
# todo 1 : impostare una visualizzazione % di avanzamento (serve?)
# todo 2 : verificare la formattazione dei titoli estratti
# todo 3 : gestione numero threads e timeout
if config.get_setting("findlinks") == True and "{}movie{}" in item.extra:
itemlist = links_list(itemlist)
itemlist = sorted(itemlist, key=lambda item: item.title.lower())
else:
itemlist = sorted(itemlist, key=lambda item: item.fulltitle)
return itemlist
def links_list(itemlist):
logger.info(" links_list")
itemlistresults = []
itemlistlist = []
allthreads = []
global_search_results = Queue.Queue()
# create and collect threads
for item in itemlist:
t = Thread(target=list_single_site, args=[global_search_results, item])
t.setDaemon(True)
allthreads.append(t)
# start threads
for thread in allthreads:
try:
thread.start()
except:
logger.error("thread error !")
# join threads, to wait all threads end before going on
for thread in allthreads:
thread.join()
# collect results
while not global_search_results.empty():
for item in global_search_results.get():
if not item_url_in_itemlist(item, itemlistresults):
channelformatteditem = rewrite_item_title(item)
if channelformatteditem is not None:
itemlistresults.append(channelformatteditem)
return itemlistresults
def list_single_site(queue, item):
logger.info(" list_single_site")
channelitemlist = []
try:
# logger.info(item.channel + " start channel search " + time.strftime("%Y-%m-%d %H:%M:%S"))
module_to_call = getattr(__import__("channels"), item.channel)
channelitemlist = module_to_call.findvideos(item)
queue.put(channelitemlist)
# logger.info(item.channel + " end channel search " + time.strftime("%Y-%m-%d %H:%M:%S"))
except:
try:
# logger.info(item.channel + " start servertools search " + time.strftime("%Y-%m-%d %H:%M:%S"))
# logger.info("no findvideos defined in channel functions, calling servertools.findvideos to find links")
servertools_itemlist = []
headers = [['Referer', item.channel]]
data = httptools.downloadpage(item.url, headers=headers).data
list_servertools = servertools.findvideos(data)
for item_servertools in list_servertools:
servertools_itemlist.append(Item(channel=item.channel,
action="play",
fulltitle=item.title,
server=item_servertools[0],
thumbnail=item_servertools[3],
title=item.title,
url=item_servertools[1]))
queue.put(servertools_itemlist)
# logger.info(item.channel + " end servertools search " + time.strftime("%Y-%m-%d %H:%M:%S"))
except Exception, e:
logger.error('exception in list_single_site: ' + str(e))
return channelitemlist
# utility function
def item_url_in_itemlist(item, itemlist):
logger.info(" item_url_in_itemlist")
i = 0
while i < len(itemlist):
if itemlist[i].url == item.url:
# logger.info("elemento eliminato : " + item.url)
return True
i = i + 1
return False
# utility function, optional
def rewrite_item_title(item):
logger.info(" rewrite_item_title")
if "download" not in item.title.lower():
item.title = "[COLOR yellow][%s][/COLOR][COLOR orange][%s][/COLOR] %s" % (
item.server, item.channel, item.fulltitle)
else:
return None
return item
+745
View File
@@ -0,0 +1,745 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Configuracion
# ------------------------------------------------------------
from channelselector import get_thumb
from core import filetools
from core import servertools
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
CHANNELNAME = "setting"
def mainlist(item):
logger.info()
itemlist = list()
itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60535), action="settings", folder=False,
thumbnail=get_thumb("setting_0.png")))
itemlist.append(Item(channel=CHANNELNAME, title="", action="", folder=False, thumbnail=get_thumb("setting_0.png")))
itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60536) + ":", text_bold=True, action="", folder=False,
thumbnail=get_thumb("setting_0.png")))
itemlist.append(Item(channel=CHANNELNAME, title=" " + config.get_localized_string(60537), action="menu_channels", folder=True,
thumbnail=get_thumb("channels.png")))
itemlist.append(Item(channel=CHANNELNAME, title=" " + config.get_localized_string(60538), action="menu_servers", folder=True,
thumbnail=get_thumb("channels.png")))
itemlist.append(Item(channel="news", title=" " + config.get_localized_string(60539), action="menu_opciones",
folder=True, thumbnail=get_thumb("news.png")))
itemlist.append(Item(channel="search", title=" " + config.get_localized_string(60540), action="opciones", folder=True,
thumbnail=get_thumb("search.png")))
itemlist.append(Item(channel=CHANNELNAME, title=" " + config.get_localized_string(60541), action="channel_config",
config="downloads", folder=True, thumbnail=get_thumb("downloads.png")))
if config.get_videolibrary_support():
itemlist.append(Item(channel="videolibrary", title=" " + config.get_localized_string(60542), action="channel_config",
folder=True, thumbnail=get_thumb("videolibrary.png")))
if config.is_xbmc():
itemlist.append(Item(channel=CHANNELNAME, title=" " + config.get_localized_string(70253), action="setting_torrent",
folder=True, thumbnail=get_thumb("channels_torrent.png")))
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(60544), action="submenu_tools", folder=True,
thumbnail=get_thumb("setting_0.png")))
return itemlist
def menu_channels(item):
logger.info()
itemlist = list()
itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60545), action="conf_tools", folder=False,
extra="channels_onoff", thumbnail=get_thumb("setting_0.png")))
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
import channelselector
from core import channeltools
channel_list = channelselector.filterchannels("all")
for channel in channel_list:
channel_parameters = channeltools.get_channel_parameters(channel.channel)
if channel_parameters["has_settings"]:
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
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")))
itemlist.append(Item(channel=CHANNELNAME, title=". " + config.get_localized_string(60549), action="conf_tools",
folder=True, extra="lib_check_datajson", thumbnail=get_thumb("channels.png")))
return itemlist
def channel_config(item):
return platformtools.show_channel_settings(channelpath=filetools.join(config.get_runtime_path(), "channels",
item.config))
def setting_torrent(item):
logger.info()
default = config.get_setting("torrent_client", server="torrent", default=0)
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 = [
{
"id": "list_torrent",
"type": "list",
"label": config.get_localized_string(70256),
"default": default,
"enabled": True,
"visible": True,
"lvalues": torrent_options
}
]
platformtools.show_channel_settings(list_controls=list_controls, callback='save_setting_torrent', item=item,
caption=config.get_localized_string(70257), custom_button={'visible': False})
def save_setting_torrent(item, dict_data_saved):
if dict_data_saved and "list_torrent" in dict_data_saved:
config.set_setting("torrent_client", dict_data_saved["list_torrent"], server="torrent")
def menu_servers(item):
logger.info()
itemlist = list()
itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60550), action="servers_blacklist", folder=False,
thumbnail=get_thumb("setting_0.png")))
itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60551),
action="servers_favorites", folder=False, thumbnail=get_thumb("setting_0.png")))
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
server_list = servertools.get_debriders_list().keys()
for server in server_list:
server_parameters = servertools.get_server_parameters(server)
if server_parameters["has_settings"]:
itemlist.append(
Item(channel=CHANNELNAME, title = ". " + config.get_localized_string(60553) % server_parameters["name"],
action="server_config", config=server, folder=False, thumbnail=""))
itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60554),
action="", folder=False, text_bold = True, thumbnail=get_thumb("setting_0.png")))
server_list = servertools.get_servers_list().keys()
for server in sorted(server_list):
server_parameters = servertools.get_server_parameters(server)
logger.info(server_parameters)
if server_parameters["has_settings"] and filter(lambda x: x["id"] not in ["black_list", "white_list"],
server_parameters["settings"]):
itemlist.append(
Item(channel=CHANNELNAME, title=". " + config.get_localized_string(60553) % server_parameters["name"],
action="server_config", config=server, folder=False, thumbnail=""))
# Fin - Servidores configurables
return itemlist
def server_config(item):
return platformtools.show_channel_settings(channelpath=filetools.join(config.get_runtime_path(), "servers",
item.config))
def servers_blacklist(item):
server_list = servertools.get_servers_list()
dict_values = {}
list_controls = [{"id": "filter_servers",
"type": "bool",
"label": "@30068",
"default": False,
"enabled": True,
"visible": True}]
dict_values['filter_servers'] = config.get_setting('filter_servers')
if dict_values['filter_servers'] == None:
dict_values['filter_servers'] = False
for i, server in enumerate(sorted(server_list.keys())):
server_parameters = server_list[server]
controls, defaults = servertools.get_server_controls_settings(server)
dict_values[server] = config.get_setting("black_list", server=server)
control = {"id": server,
"type": "bool",
"label": ' %s' % server_parameters["name"],
"default": defaults.get("black_list", False),
"enabled": "eq(-%s,True)" % (i + 1),
"visible": True}
list_controls.append(control)
return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values,
caption=config.get_localized_string(60550), callback="cb_servers_blacklist")
def cb_servers_blacklist(item, dict_values):
f = False
progreso = platformtools.dialog_progress(config.get_localized_string(60557), config.get_localized_string(60558))
n = len(dict_values)
i = 1
for k, v in dict_values.items():
if k == 'filter_servers':
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
config.set_setting("favorites_servers_list", 100, server=k)
f = True
progreso.update((i * 100) / n, config.get_localized_string(60559) % k)
i += 1
if not f: # Si no hay ningun servidor en la lista, desactivarla
config.set_setting('filter_servers', False)
progreso.close()
def servers_favorites(item):
server_list = servertools.get_servers_list()
dict_values = {}
list_controls = [{'id': 'favorites_servers',
'type': "bool",
'label': config.get_localized_string(60577),
'default': False,
'enabled': True,
'visible': True}]
dict_values['favorites_servers'] = config.get_setting('favorites_servers')
if dict_values['favorites_servers'] == None:
dict_values['favorites_servers'] = False
server_names = [config.get_localized_string(59992)]
for server in sorted(server_list.keys()):
if config.get_setting("black_list", server=server):
continue
server_names.append(server_list[server]['name'])
orden = config.get_setting("favorites_servers_list", server=server)
if orden > 0:
dict_values[orden] = len(server_names) - 1
for x in range(1, 6):
control = {'id': x,
'type': "list",
'label': config.get_localized_string(60597) % x,
'lvalues': server_names,
'default': 0,
'enabled': "eq(-%s,True)" % x,
'visible': True}
list_controls.append(control)
return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, item=server_names,
caption=config.get_localized_string(60551), callback="cb_servers_favorites")
def cb_servers_favorites(server_names, dict_values):
dict_name = {}
progreso = platformtools.dialog_progress(config.get_localized_string(60557), config.get_localized_string(60558))
for i, v in dict_values.items():
if i == "favorites_servers":
config.set_setting("favorites_servers", v)
elif int(v) > 0:
dict_name[server_names[v]] = int(i)
servers_list = servertools.get_servers_list().items()
n = len(servers_list)
i = 1
for server, server_parameters in servers_list:
if server_parameters['name'] in dict_name.keys():
config.set_setting("favorites_servers_list", dict_name[server_parameters['name']], server=server)
else:
config.set_setting("favorites_servers_list", 0, server=server)
progreso.update((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
config.set_setting("favorites_servers", False)
progreso.close()
def settings(item):
config.open_settings()
def submenu_tools(item):
logger.info()
itemlist = list()
# Herramientas personalizadas
import os
channel_custom = os.path.join(config.get_runtime_path(), 'channels', 'custom.py')
if not filetools.exists(channel_custom):
user_custom = os.path.join(config.get_data_path(), 'custom.py')
if filetools.exists(user_custom):
filetools.copy(user_custom, channel_custom, silent=True)
if filetools.exists(channel_custom):
itemlist.append(Item(channel='custom', action='mainlist', title='Custom Channel'))
#Disabilitato il menu degli aggiornamenti
#itemlist.append(Item(channel=CHANNELNAME, action="check_quickfixes", folder=False,
# title=config.get_localized_string(30001), plot="Versión actual: %s" % config.get_addon_version() ))
itemlist.append(Item(channel=CHANNELNAME, action="update_quasar", folder=False,
title=config.get_localized_string(70569)))
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(60564) + ":", action="", folder=False,
text_bold=True, thumbnail=get_thumb("channels.png")))
itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60565), action="conf_tools",
folder=True, extra="lib_check_datajson", thumbnail=get_thumb("channels.png")))
if config.get_videolibrary_support():
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(60566) + ":", action="", folder=False,
text_bold=True, thumbnail=get_thumb("videolibrary.png")))
itemlist.append(Item(channel=CHANNELNAME, action="overwrite_tools", folder=False,
thumbnail=get_thumb("videolibrary.png"),
title="- " + config.get_localized_string(60567)))
itemlist.append(Item(channel="videolibrary", action="update_videolibrary", folder=False,
thumbnail=get_thumb("videolibrary.png"),
title="- " + config.get_localized_string(60568)))
return itemlist
def check_quickfixes(item):
logger.info()
if not os.path.isfile(config.get_runtime_path() + '/.dev'):
logger.info("DEV MODE OFF")
from platformcode import updater
return updater.check_addon_updates(verbose=True)
else:
logger.info("DEV MODE ON")
def update_quasar(item):
logger.info()
from platformcode import custom_code, platformtools
stat = False
stat = custom_code.update_external_addon("quasar")
if stat:
platformtools.dialog_notification("Actualización Quasar", "Realizada con éxito")
else:
platformtools.dialog_notification("Actualización Quasar", "Ha fallado. Consulte el log")
def conf_tools(item):
logger.info()
# Activar o desactivar canales
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
return channels_onoff(item)
import channelselector
from core import channeltools
channel_list = channelselector.filterchannels("allchannelstatus")
excluded_channels = ['url',
'search',
'videolibrary',
'setting',
'news',
# 'help',
'downloads']
list_controls = []
try:
list_controls.append({'id': "all_channels",
'type': "list",
'label': config.get_localized_string(60594),
'default': 0,
'enabled': True,
'visible': True,
'lvalues': ['',
config.get_localized_string(60591),
config.get_localized_string(60592),
config.get_localized_string(60593)]})
for channel in channel_list:
# Si el canal esta en la lista de exclusiones lo saltamos
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 is None:
status = channel_parameters["active"]
logger.debug("%s | Status (XML): %s" % (channel.channel, status))
if not status:
status_control = config.get_localized_string(60595)
else:
logger.debug("%s | Status: %s" % (channel.channel, status))
control = {'id': channel.channel,
'type': "bool",
'label': channel_parameters["title"] + status_control,
'default': status,
'enabled': True,
'visible': True}
list_controls.append(control)
else:
continue
except:
import traceback
logger.error("Error: %s" % traceback.format_exc())
else:
return platformtools.show_channel_settings(list_controls=list_controls,
item=item.clone(channel_list=channel_list),
caption=config.get_localized_string(60596),
callback="channel_status",
custom_button={"visible": False})
# Comprobacion de archivos channel_data.json
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
excluded_channels = ['url',
'setting',
'help']
try:
import os
from core import jsontools
for channel in channel_list:
list_status = None
default_settings = None
# Se comprueba si el canal esta en la lista de exclusiones
if channel.channel not in excluded_channels:
# Se comprueba que tenga "settings", sino se salta
list_controls, dict_settings = channeltools.get_channel_controls_settings(channel.channel)
if not list_controls:
itemlist.append(Item(channel=CHANNELNAME,
title=channel.title + config.get_localized_string(60569),
action="", folder=False,
thumbnail=channel.thumbnail))
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")
dict_settings = {}
dict_file = {}
if filetools.exists(file_settings):
# logger.info(channel.channel + " Tiene archivo _data.json")
channeljson_exists = True
# Obtenemos configuracion guardada de ../settings/channel_data.json
try:
dict_file = jsontools.load(open(file_settings, "rb").read())
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)
else:
# logger.info(channel.channel + " No tiene archivo _data.json")
channeljson_exists = False
if channeljson_exists:
try:
datajson_size = filetools.getsize(file_settings)
except:
import traceback
logger.error(channel.title + config.get_localized_string(60570) % traceback.format_exc())
else:
datajson_size = None
# Si el _data.json esta vacio o no existe...
if (len(dict_settings) and datajson_size) == 0 or not channeljson_exists:
# Obtenemos controles del archivo ../channels/channel.json
needsfix = True
try:
# Se cargan los ajustes por defecto
list_controls, default_settings = channeltools.get_channel_controls_settings(
channel.channel)
# logger.info(channel.title + " | Default: %s" % default_settings)
except:
import traceback
logger.error(channel.title + config.get_localized_string(60570) % traceback.format_exc())
# default_settings = {}
# Si _data.json necesita ser reparado o no existe...
if needsfix or not channeljson_exists:
if default_settings is not None:
# Creamos el channel_data.json
default_settings.update(dict_settings)
dict_settings = default_settings
dict_file['settings'] = dict_settings
# Creamos el archivo ../settings/channel_data.json
json_data = jsontools.dump(dict_file)
try:
open(file_settings, "wb").write(json_data)
# logger.info(channel.channel + " - Archivo _data.json GUARDADO!")
# El channel_data.json se ha creado/modificado
list_status = config.get_localized_string(60560)
except EnvironmentError:
logger.error("ERROR al salvar el archivo: %s" % file_settings)
else:
if default_settings is None:
list_status = config.get_localized_string(60571)
else:
# logger.info(channel.channel + " - NO necesita correccion!")
needsfix = False
# Si se ha establecido el estado del canal se añade a la lista
if needsfix is not None:
if needsfix:
if not channeljson_exists:
list_status = config.get_localized_string(60588)
list_colour = "red"
else:
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 datajson_size is None:
list_status = config.get_localized_string(60590)
list_colour = "red"
else:
list_status = config.get_localized_string(60589)
list_colour = "green"
if list_status is not None:
itemlist.append(Item(channel=CHANNELNAME,
title=channel.title + list_status,
action="", folder=False,
thumbnail=channel.thumbnail,
text_color=list_colour))
else:
logger.error("Algo va mal con el canal %s" % channel.channel)
# Si el canal esta en la lista de exclusiones lo saltamos
else:
continue
except:
import traceback
logger.error("Error: %s" % traceback.format_exc())
return itemlist
def channels_onoff(item):
import channelselector, xbmcgui
from core import channeltools
# Cargar lista de opciones
# ------------------------
lista = []; ids = []
channels_list = channelselector.filterchannels('allchannelstatus')
for channel in channels_list:
channel_parameters = channeltools.get_channel_parameters(channel.channel)
lbl = '%s' % channel_parameters['language']
# ~ lbl += ' %s' % [config.get_localized_category(categ) for categ in channel_parameters['categories']]
lbl += ' %s' % ', '.join(config.get_localized_category(categ) for categ in channel_parameters['categories'])
it = xbmcgui.ListItem(channel.title, lbl)
it.setArt({ 'thumb': channel.thumbnail, 'fanart': channel.fanart })
lista.append(it)
ids.append(channel.channel)
# Diálogo para pre-seleccionar
# ----------------------------
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 == 2: preselect = []
elif ret == 1: preselect = range(len(ids))
else:
preselect = []
for i, canal in enumerate(ids):
channel_status = config.get_setting('enabled', canal)
if channel_status is None: channel_status = True
if channel_status:
preselect.append(i)
# Diálogo para seleccionar
# ------------------------
ret = xbmcgui.Dialog().multiselect(config.get_localized_string(60545), lista, preselect=preselect, useDetails=True)
if ret == None: return False # pedido cancel
seleccionados = [ids[i] for i in ret]
# Guardar cambios en canales activados
# ------------------------------------
for canal in ids:
channel_status = config.get_setting('enabled', canal)
if channel_status is None: channel_status = True
if channel_status and canal not in seleccionados:
config.set_setting('enabled', False, canal)
elif not channel_status and canal in seleccionados:
config.set_setting('enabled', True, canal)
return False
def channel_status(item, dict_values):
try:
for k in dict_values:
if k == "all_channels":
logger.info("Todos los canales | Estado seleccionado: %s" % dict_values[k])
if dict_values[k] != 0:
excluded_channels = ['url', 'search',
'videolibrary', 'setting',
'news',
'help',
'downloads']
for channel in item.channel_list:
if channel.channel not in excluded_channels:
from core import channeltools
channel_parameters = channeltools.get_channel_parameters(channel.channel)
new_status_all = None
new_status_all_default = channel_parameters["active"]
# Opcion Activar todos
if dict_values[k] == 1:
new_status_all = True
# Opcion Desactivar todos
if dict_values[k] == 2:
new_status_all = False
# Opcion Recuperar estado por defecto
if dict_values[k] == 3:
# Si tiene "enabled" en el _data.json es porque el estado no es el del 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
else:
continue
# Se guarda el estado del canal
if new_status_all is not None:
config.set_setting("enabled", new_status_all, channel.channel)
break
else:
continue
else:
logger.info("Canal: %s | Estado: %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))
platformtools.itemlist_update(Item(channel=CHANNELNAME, action="mainlist"))
except:
import traceback
logger.error("Detalle del error: %s" % traceback.format_exc())
platformtools.dialog_notification(config.get_localized_string(60579), config.get_localized_string(60580))
def overwrite_tools(item):
import videolibrary_service
from core import videolibrarytools
seleccion = platformtools.dialog_yesno(config.get_localized_string(60581),
config.get_localized_string(60582),
config.get_localized_string(60583))
if seleccion == 1:
# tvshows
heading = config.get_localized_string(60584)
p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(60585), heading)
p_dialog.update(0, '')
show_list = []
for path, folders, files in filetools.walk(videolibrarytools.TVSHOWS_PATH):
show_list.extend([filetools.join(path, f) for f in files if f == "tvshow.nfo"])
if show_list:
t = float(100) / len(show_list)
for i, tvshow_file in enumerate(show_list):
head_nfo, serie = videolibrarytools.read_nfo(tvshow_file)
path = filetools.dirname(tvshow_file)
if not serie.active:
# si la serie no esta activa descartar
continue
# Eliminamos la carpeta con la serie ...
filetools.rmdirtree(path)
# ... y la volvemos a añadir
videolibrary_service.update(path, p_dialog, i, t, serie, 3)
p_dialog.close()
# movies
heading = config.get_localized_string(60586)
p_dialog2 = platformtools.dialog_progress_bg(config.get_localized_string(60585), heading)
p_dialog2.update(0, '')
movies_list = []
for path, folders, files in filetools.walk(videolibrarytools.MOVIES_PATH):
movies_list.extend([filetools.join(path, f) for f in files if f.endswith(".json")])
logger.debug("movies_list %s" % movies_list)
if movies_list:
t = float(100) / len(movies_list)
for i, movie_json in enumerate(movies_list):
try:
from core import jsontools
path = filetools.dirname(movie_json)
movie = Item().fromjson(filetools.read(movie_json))
# Eliminamos la carpeta con la pelicula ...
filetools.rmdirtree(path)
import math
heading = config.get_localized_string(60587)
p_dialog2.update(int(math.ceil((i + 1) * t)), heading, "%s: %s" % (movie.contentTitle,
movie.channel.capitalize()))
# ... y la volvemos a añadir
videolibrarytools.save_movie(movie)
except Exception, ex:
logger.error("Error al crear de nuevo la película")
template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex).__name__, ex.args)
logger.error(message)
p_dialog2.close()
+361
View File
@@ -0,0 +1,361 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
import os
from core.item import Item
from core import jsontools
from platformcode import config, logger
from platformcode import launcher
import xbmc, xbmcgui, xbmcplugin, xbmcaddon, channelselector
import xbmc, xbmcaddon
addon = xbmcaddon.Addon('metadata.themoviedb.org')
def_lang = addon.getSetting('language')
media_path = os.path.join(config.get_runtime_path(), "resources/skins/Default/media/side_menu/")
menu_settings_path = os.path.join(config.get_data_path(), "settings_channels", 'menu_settings_data.json')
if os.path.exists(menu_settings_path):
menu_node = jsontools.get_node_from_file('menu_setting_data.json', 'menu')
else:
menu_node = {'categoria actual':config.get_setting('category')}
jsontools.update_node(menu_node, 'menu_settings_data.json', "menu")
ACTION_SHOW_FULLSCREEN = 36
ACTION_GESTURE_SWIPE_LEFT = 511
ACTION_SELECT_ITEM = 7
ACTION_PREVIOUS_MENU = 10
ACTION_MOVE_LEFT = 1
ACTION_MOVE_RIGHT = 2
ACTION_MOVE_DOWN = 4
ACTION_MOVE_UP = 3
def set_menu_settings(item):
if os.path.exists(menu_settings_path):
menu_node = jsontools.get_node_from_file('menu_settings_data.json', 'menu')
else:
menu_node = {}
menu_node['categoria actual'] = item.extra
jsontools.update_node(menu_node, 'menu_settings_data.json', "menu")
def check_user_home(item):
logger.info()
if os.path.exists(menu_settings_path):
menu_node = jsontools.get_node_from_file('menu_settings_data.json', 'menu')
if 'user_home' in menu_node:
item = Item().fromurl(menu_node['user_home'])
else:
item = Item(channel="channelselector", action="getmainlist", viewmode="movie")
from platformcode import platformtools
undefined_start = platformtools.dialog_ok(config.get_localized_string(70664), config.get_localized_string(70665),
config.get_localized_string(70666))
return item
def set_custom_start(item):
logger.info()
if os.path.exists(menu_settings_path):
menu_node = jsontools.get_node_from_file('menu_settings_data.json', 'menu')
else:
menu_node={}
parent_item= Item().fromurl(item.parent)
parent_item.start=True
config.set_setting("custom_start",True)
if config.get_setting("news_start"):
config.set_setting("news_start", False)
menu_node['user_home']=parent_item.tourl()
jsontools.update_node(menu_node, 'menu_settings_data.json', "menu")
def get_start_page():
logger.info()
dictCategory = {
config.get_localized_string(70137): 'peliculas',
config.get_localized_string(30123): 'series',
config.get_localized_string(30124): 'anime',
config.get_localized_string(70018): 'infantiles',
config.get_localized_string(60513): 'documentales',
config.get_localized_string(70013): 'terror',
config.get_localized_string(30124): 'castellano',
config.get_localized_string(59976): 'latino',
config.get_localized_string(70171): 'torrent',
}
category = dictCategory[config.get_setting("category")]
custom_start= config.get_setting("custom_start")
#if category != 'definido':
if custom_start == False:
item = Item(channel="news", action="novedades", extra=category, mode='silent')
else:
from specials import side_menu
item = Item()
item = side_menu.check_user_home(item)
return item
def open_menu(item):
main = Main('side_menu.xml', config.get_runtime_path())
main.doModal()
del main
class Main(xbmcgui.WindowXMLDialog):
def __init__(self, *args, **kwargs):
self.items = []
def onInit(self):
#### Compatibilidad con Kodi 18 ####
if config.get_platform(True)['num_version'] < 18:
self.setCoordinateResolution(2)
self.focus = -1
self.buttons = []
posx= 0
posy= 145
space = 30
selected = 'selected0.png'
width = 260
height = 30
textcolor = "0xFFCCCCCC"
conditional_textcolor = "0xFF0081C2"
shadow = "0x00000000"
offsetx = 30
offsety = 5
font = 'font25_title'
if config.get_setting('start_page'):
label = config.get_localized_string(70663)
self.button_start = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
noFocusTexture='', focusTexture=media_path + selected,
textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
textOffsetY=offsety)
self.addControl(self.button_start)
self.buttons.append(self.button_start)
posy += space * 2
label = config.get_localized_string(70009)
self.button_alfa = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
noFocusTexture='', focusTexture=media_path+selected,
textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
textOffsetY=offsety)
self.addControl(self.button_alfa)
self.buttons.append(self.button_alfa)
posy += space
label = config.get_localized_string(30100)
self.button_config = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
noFocusTexture='', focusTexture=media_path + selected,
textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
textOffsetY=offsety)
self.addControl(self.button_config)
self.buttons.append(self.button_config)
posy += space*2
label = config.get_localized_string(30122)
self.button_peliculas = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
alignment=0x00000000, noFocusTexture='',
focusTexture=media_path+selected, textColor=textcolor,
shadowColor=shadow, textOffsetX=offsetx, textOffsetY=offsety)
self.addControl(self.button_peliculas)
self.buttons.append(self.button_peliculas)
posy += space
label = config.get_localized_string(70017)
self.button_series = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
alignment=0x00000000, noFocusTexture='',
focusTexture=media_path+selected, textColor=textcolor,
shadowColor=shadow, textOffsetX=offsetx, textOffsetY=offsety)
self.addControl(self.button_series)
self.buttons.append(self.button_series)
posy += space
label = config.get_localized_string(30124)
self.button_anime = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
noFocusTexture='', focusTexture=media_path+selected,
textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
textOffsetY=offsety)
self.addControl(self.button_anime)
self.buttons.append(self.button_anime)
# posy += space
# label = config.get_localized_string(70018)
# self.button_infantil = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
# alignment=0x00000000, noFocusTexture='',
# focusTexture=media_path+selected, textColor=textcolor,
# shadowColor=shadow, textOffsetX=offsetx, textOffsetY=offsety)
# self.addControl(self.button_infantil)
# self.buttons.append(self.button_infantil)
posy += space
label = config.get_localized_string(70019)
self.button_docu = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
alignment=0x00000000, noFocusTexture='',
focusTexture=media_path + selected, textColor=textcolor,
shadowColor=shadow, textOffsetX=offsetx, textOffsetY=offsety)
self.addControl(self.button_docu)
self.buttons.append(self.button_docu)
posy += space
# label = config.get_localized_string(70013)
# self.button_terror = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
# alignment=0x00000000, noFocusTexture='',
# focusTexture=media_path+selected, textColor=textcolor,
# shadowColor=shadow, textOffsetX=offsetx, textOffsetY=offsety)
# self.addControl(self.button_terror)
# self.buttons.append(self.button_terror)
# if channelselector.auto_filter() == 'esp':
# posy += space
# label = config.get_localized_string(59981)
# self.button_lat = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
# noFocusTexture='', focusTexture=media_path+selected,
# textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
# textOffsetY=offsety)
# self.addControl(self.button_lat)
# self.buttons.append(self.button_lat)
# posy += space
# label = config.get_localized_string(70014)
# self.button_cast = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
# noFocusTexture='', focusTexture=media_path + selected,
# textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
# textOffsetY=offsety)
# self.addControl(self.button_cast)
# self.buttons.append(self.button_cast)
# posy += space
# label = config.get_localized_string(70015)
# self.button_torrent = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
# alignment=0x00000000, noFocusTexture='',
# focusTexture=media_path+selected, textColor=textcolor,
# shadowColor=shadow, textOffsetX=offsetx, textOffsetY=offsety)
# self.addControl(self.button_torrent)
# self.buttons.append(self.button_torrent)
start_page_item = get_start_page()
if config.get_setting('start_page') and start_page_item.channel == 'news':
posy += space
label = config.get_localized_string(70016)
self.button_config = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
alignment=0x00000000, noFocusTexture='',
focusTexture=media_path+selected, textColor=conditional_textcolor,
shadowColor=shadow, textOffsetX=offsetx, textOffsetY=offsety)
self.addControl(self.button_config)
self.buttons.append(self.button_config)
posy += space*2
label = config.get_localized_string(60423)
self.button_buscar = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
noFocusTexture='', focusTexture=media_path + selected,
textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
textOffsetY=offsety)
self.addControl(self.button_buscar)
self.buttons.append(self.button_buscar)
posy += space
label = config.get_localized_string(70036)
self.button_actor = xbmcgui.ControlButton(posx, posy, width, height, label, font=font, alignment=0x00000000,
noFocusTexture='', focusTexture=media_path + selected,
textColor=textcolor, shadowColor=shadow, textOffsetX=offsetx,
textOffsetY=offsety)
self.addControl(self.button_actor)
self.buttons.append(self.button_actor)
posy += space
label = config.get_localized_string(70010)
self.button_config_search = xbmcgui.ControlButton(posx, posy, width, height, label, font=font,
alignment=0x00000000,
noFocusTexture='', focusTexture=media_path + selected,
textColor=conditional_textcolor, shadowColor=shadow,
textOffsetX=offsetx, textOffsetY=offsety)
self.addControl(self.button_config_search)
self.buttons.append(self.button_config_search)
label=''
self.button_close = xbmcgui.ControlButton(260, 0, 1020, 725, label, noFocusTexture='', focusTexture='')
self.addControl(self.button_close)
def onClick(self, control):
new_item=''
control = self.getControl(control).getLabel()
if control == config.get_localized_string(70663):
new_item = get_start_page()
elif control == config.get_localized_string(70009):
new_item = Item(channel='', action='getmainlist', title='Menú Alfa')
elif control == config.get_localized_string(30100):
new_item = Item(channel='setting', action="settings")
elif control == config.get_localized_string(30122):
new_item = Item(channel='news', action="novedades", extra="peliculas", mode='silent')
elif control == config.get_localized_string(70017):
new_item = Item(channel='news', action="novedades", extra="series", mode='silent')
elif control == config.get_localized_string(30124):
new_item = Item(channel='news', action="novedades", extra="anime", mode='silent')
elif control == config.get_localized_string(70018):
new_item = Item(channel='news', action="novedades", extra="infantiles", mode='silent')
elif control == config.get_localized_string(70019):
new_item = Item(channel='news', action="novedades", extra="documentales", mode='silent')
elif control == config.get_localized_string(70013):
new_item = Item(channel='news', action="novedades", extra="terror", mode='silent')
elif control == config.get_localized_string(59981):
new_item = Item(channel='news', action="novedades", extra="castellano", mode='silent')
elif control == config.get_localized_string(70014):
new_item = Item(channel='news', action="novedades", extra="latino", mode='silent')
elif control == config.get_localized_string(70015):
new_item = Item(channel='news', action="novedades", extra="torrent", mode='silent')
elif control == config.get_localized_string(70016):
menu_node = jsontools.get_node_from_file('menu_settings_data.json', 'menu')
if 'categoria actual' in menu_node:
category = menu_node['categoria actual']
new_item = Item(channel='news', action="setting_channel", extra=category, menu=True)
elif control == config.get_localized_string(60423):
new_item = Item(channel='search', action="search")
elif control == config.get_localized_string(70036):
new_item = Item(channel='tvmoviedb', title="Buscar actor/actriz", action="search_",
search={'url': 'search/person', 'language': def_lang, 'page': 1}, star=True)
elif control == config.get_localized_string(70010):
new_item = Item(channel='search', action="setting_channel")
elif control == '':
self.close()
if new_item !='':
self.run_action(new_item)
def onAction(self, action):
if action == ACTION_PREVIOUS_MENU or action == ACTION_GESTURE_SWIPE_LEFT or action == 110 or action == 92:
self.close()
if action == ACTION_MOVE_RIGHT or action == ACTION_MOVE_DOWN:
self.focus += 1
if self.focus > len(self.buttons)-1:
self.focus = 0
while True:
id_focus = str(self.buttons[self.focus].getId())
if xbmc.getCondVisibility('[Control.IsVisible(' + id_focus + ')]'):
self.setFocus(self.buttons[self.focus])
break
self.focus += 1
if action == ACTION_MOVE_LEFT or action == ACTION_MOVE_UP:
self.focus -= 1
if self.focus < 0:
self.focus = len(self.buttons) - 1
while True:
id_focus = str(self.buttons[self.focus].getId())
if xbmc.getCondVisibility('[Control.IsVisible(' + id_focus + ')]'):
self.setFocus(self.buttons[self.focus])
break
self.focus -= 1
def run_action(self, item):
logger.info()
if item.menu != True:
self.close()
xbmc.executebuiltin("Container.update(%s)"%launcher.run(item))
+181
View File
@@ -0,0 +1,181 @@
{
"id": "tvmoviedb",
"name": "TvMovieDB",
"active": false,
"adult": false,
"language": ["*"],
"thumbnail": "http://i.imgur.com/HA7fvgD.png",
"categories": [
"movie",
"tvshow",
"anime"
],
"settings": [
{
"id": "tmdb",
"type": "list",
"label": "@70418",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": [
"auto",
"@70419",
"@70420",
"@70421",
"@70422",
"@70423",
"@70424",
"@70425",
"@70014"
]
},
{
"id": "tmdb_alternativo",
"type": "list",
"label": "@70426",
"default": 6,
"enabled": true,
"visible": true,
"lvalues": [
"@70419",
"@70420",
"@70421",
"@70422",
"@70423",
"@70424",
"@70425",
"@70014"
]
},
{
"id": "imdb",
"type": "list",
"label": "@70427",
"color": "0xFFE0F04B",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": [
"auto",
"@70419",
"@70420",
"@70421",
"@70422",
"@70423",
"@70424",
"@70425",
"@70014"
]
},
{
"id": "label1",
"type": "label",
"label": "",
"enabled": false,
"visible": true
},
{
"id": "filmaff",
"type": "list",
"label": "@70428",
"color": "0xFF25AA48",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": [
"auto",
"@70429",
"@70430",
"@70431",
"@70432",
"@70433",
"@70434"
]
},
{
"id": "usuariofa",
"type": "text",
"label": "@70435",
"color": "0xFFd50b0b",
"default": "",
"enabled": true,
"visible": true
},
{
"id": "passfa",
"type": "text",
"label": "@70436",
"color": "0xFFd50b0b",
"default": "",
"enabled": "!eq(-1,'')",
"hidden": true,
"visible": true
},
{
"id": "orderfa",
"type": "list",
"label": "@70437",
"color": "0xFF25AA48",
"default": 0,
"enabled": "!eq(-1,'')",
"visible": true,
"lvalues": [
"@70438",
"@60230",
"@70042",
"@70439",
"@70440"
]
},
{
"id": "label2",
"type": "label",
"label": "",
"enabled": false,
"visible": true
},
{
"id": "usuariomal",
"type": "text",
"label": "@70441",
"color": "0xFF25AA48",
"default": "",
"enabled": true,
"visible": true
},
{
"id": "passmal",
"type": "text",
"label": "@70442",
"color": "0xFF25AA48",
"default": "",
"enabled": "!eq(-1,'')",
"hidden": true,
"visible": true
},
{
"id": "adult_mal",
"type": "bool",
"label": "@70443",
"color": "0xFFd50b0b",
"default": false,
"enabled": true,
"visible": true
},
{
"id": "perfil",
"type": "list",
"label": "@60666",
"default": 2,
"enabled": true,
"visible": true,
"lvalues": [
"@70444",
"@70445",
"@70446",
"@59992"
]
}
]
}
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
{
"id": "url",
"name": "URL",
"active": false,
"adult": false,
"thumbnail": "url.png",
"banner": "url.png"
}
+47
View File
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from core import httptools
from core import scrapertools
from core import servertools
from core.item import Item
from platformcode import config, logger
def mainlist(item):
logger.info()
itemlist = []
itemlist.append(Item(channel=item.channel, action="search", title=config.get_localized_string(60089)))
itemlist.append(Item(channel=item.channel, action="search", title=config.get_localized_string(60090)))
itemlist.append(Item(channel=item.channel, action="search", title=config.get_localized_string(60091)))
return itemlist
# Al llamarse "search" la función, el launcher pide un texto a buscar y lo añade como parámetro
def search(item, texto):
logger.info("texto=" + texto)
if not texto.startswith("http"):
texto = "http://" + texto
itemlist = []
if "servidor" in item.title:
itemlist = servertools.find_video_items(data=texto)
for item in itemlist:
item.channel = "url"
item.action = "play"
elif "directo" in item.title:
itemlist.append(Item(channel=item.channel, action="play", url=texto, server="directo", title=config.get_localized_string(60092)))
else:
data = httptools.downloadpage(texto).data
itemlist = servertools.find_video_items(data=data)
for item in itemlist:
item.channel = "url"
item.action = "play"
if len(itemlist) == 0:
itemlist.append(Item(channel=item.channel, action="search", title=config.get_localized_string(60093)))
return itemlist
+326
View File
@@ -0,0 +1,326 @@
{
"id": "videolibrary",
"name": "Videoteca",
"active": false,
"adult": false,
"language": ["*"],
"settings": [
{
"id": "update",
"type": "list",
"label": "@60601",
"default": 1,
"visible": true,
"lvalues": [
"@60602",
"@60603",
"@60604",
"@60605"
]
},
{
"id": "update_wait",
"type": "list",
"label": "@60606",
"default": 0,
"visible": true,
"enabled": "eq(-1,@60603)|eq(-1,@60605)",
"lvalues": [
"No",
"@60609",
"@60610",
"@60611",
"@60612"
]
},
{
"id": "everyday_delay",
"type": "list",
"label": "@60613",
"default": 1,
"visible": true,
"enabled": "eq(-2,@60604)|eq(-2,@60605)",
"lvalues": [
"00:00",
"04:00",
"08:00",
"12:00",
"16:00",
"20:00"
]
},
{
"id": "updatetvshows_interval",
"type": "list",
"label": "@60614",
"default": 0,
"visible": true,
"enabled": "!eq(-3,@60615)",
"lvalues": [
"@60616",
"@60617"
]
},
{
"id": "search_new_content",
"type": "list",
"label": "@60618",
"default": 0,
"enabled": "!eq(-4,@60615)",
"lvalues": [
"@60619",
"@60620"
]
},
{
"id": "addition",
"type": "label",
"label": "@70092",
"enabled": true,
"visible": true
},
{
"id": "enable_filter",
"type": "bool",
"label": "@70090",
"enabled": true,
"visible": true,
"default": false
},
{
"id": "filters",
"type": "text",
"label": "@70091",
"visible": true,
"enabled": "eq(-1,true)"
},
{
"id": "window_type",
"type": "list",
"label": "@60621",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": [
"@60622",
"@60623"
]
},
{
"id": "max_links",
"type": "list",
"label": "@60624",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": [
"@60625",
"30",
"60",
"90",
"120",
"150",
"180",
"210"
]
},
{
"id": "white_list_order",
"type": "bool",
"label": "@60626",
"enabled": true,
"visible": true,
"default": false
},
{
"id": "quit_channel_name",
"type": "bool",
"label": "@60627",
"enabled": true,
"visible": true,
"default": false
},
{
"id": "replace_VD",
"type": "bool",
"label": "@60628",
"enabled": true,
"visible": true,
"default": false
},
{
"id": "db_mode",
"type": "list",
"label": "@60629",
"default": 0,
"enabled": true,
"visible": true,
"lvalues": [
"@60630",
"@60631"
]
},
{
"id": "xbmc_host",
"type": "text",
"label": "@60632",
"visible": true,
"enabled": "eq(-1,Remota)"
},
{
"id": "xbmc_puerto",
"type": "text",
"label": "@60633",
"enabled": "!eq(-1,'')",
"visible": true
},
{
"id": "mark_as_watched",
"type": "bool",
"label": "@60634",
"default": true,
"enabled": true,
"visible": true
},
{
"id": "watched_setting",
"type": "list",
"label": "@60635",
"default": 3,
"visible": true,
"enabled": "eq(-1,true)",
"lvalues": [
"5 min",
"30%",
"50%",
"80%",
"@60636"
]
},
{
"id": "sync_trakt",
"type": "label",
"label": "@60637",
"enabled": true,
"visible": true
},
{
"id": "sync_trakt_watched",
"type": "bool",
"label": "@60638",
"default": false,
"visible": true,
"enabled": "eq(-3,true)"
},
{
"id": "sync_trakt_notification",
"type": "bool",
"label": "@60639",
"default": true,
"visible": true,
"enabled": "eq(-1,true)"
},
{
"id": "sync_trakt_new_tvshow",
"type": "bool",
"label": "@60640",
"default": false,
"enabled": true,
"visible": true
},
{
"id": "sync_trakt_new_tvshow_wait",
"type": "bool",
"label": "@60641",
"default": true,
"visible": true,
"enabled": "eq(-1,true)"
},
{
"id": "show_all_seasons",
"type": "bool",
"label": "@60642",
"default": true
},
{
"id": "no_pile_on_seasons",
"type": "list",
"label": "@60643",
"default": 1,
"lvalues": [
"@60648",
"@60644",
"@60616"
]
},
{
"id": "ask_channel",
"type": "bool",
"label": "@60645",
"default": false
},
{
"id": "original_title_folder",
"type": "list",
"label": "@60646",
"default": 0,
"lvalues": [
"@60647",
"@60649"
]
},
{
"id": "lowerize_title",
"type": "list",
"label": "@70703",
"default": 0,
"lvalues": [
"Si",
"No"
]
},
{
"id": "lab_1",
"type": "label",
"label": "@60650",
"enabled": true,
"visible": true
},
{
"id": "scraper_movies",
"type": "list",
"label": "@60651",
"enabled": false,
"default": 0,
"lvalues": [
"TheMovieDB.org",
"None"
]
},
{
"id": "scraper_tvshows",
"type": "list",
"label": "@60652",
"default": 0,
"lvalues": [
"TheMovieDB.org",
"TheTvDB.com"
]
},
{
"id": "tvdb_retry_eng",
"type": "bool",
"label": "@60653",
"default": true,
"enabled": "eq(-1,TheTvDB.com)",
"visible": true
},
{
"id": "verify_playcount",
"type": "bool",
"label": "@70526",
"default": false
}
]
}
+981
View File
@@ -0,0 +1,981 @@
# -*- coding: utf-8 -*-
import os, traceback
from channelselector import get_thumb
from core import filetools
from core import scrapertools
from core import videolibrarytools
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
from lib import generictools
def mainlist(item):
logger.info()
itemlist = list()
itemlist.append(Item(channel=item.channel, action="list_movies", title=config.get_localized_string(60509),
category=config.get_localized_string(70270),
thumbnail=get_thumb("videolibrary_movie.png")))
itemlist.append(Item(channel=item.channel, action="list_tvshows", title=config.get_localized_string(60600),
category=config.get_localized_string(70271),
thumbnail=get_thumb("videolibrary_tvshow.png")))
return itemlist
def channel_config(item):
return platformtools.show_channel_settings(channelpath=os.path.join(config.get_runtime_path(), "channels",
item.channel),
caption=config.get_localized_string(60598))
def list_movies(item, silent=False):
logger.info()
itemlist = []
dead_list = []
zombie_list = []
for raiz, subcarpetas, ficheros in filetools.walk(videolibrarytools.MOVIES_PATH):
for f in ficheros:
if f.endswith(".nfo"):
nfo_path = filetools.join(raiz, f)
#Sincronizamos las películas vistas desde la videoteca de Kodi con la de Alfa
try:
if config.is_xbmc(): #Si es Kodi, lo hacemos
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_content_as_watched_on_alfa(nfo_path)
except:
logger.error(traceback.format_exc())
head_nfo, new_item = videolibrarytools.read_nfo(nfo_path)
if not new_item: #Si no ha leído bien el .nfo, pasamos a la siguiente
continue
if len(new_item.library_urls) > 1:
multicanal = True
else:
multicanal = False
## verifica la existencia de los canales, en caso de no existir el canal se pregunta si se quieren
## eliminar los enlaces de dicho canal
for canal_org in new_item.library_urls:
canal = generictools.verify_channel(canal_org)
try:
channel_verify = __import__('channels.%s' % canal, fromlist=["channels.%s" % canal])
logger.debug('El canal %s parece correcto' % channel_verify)
except:
dead_item = Item(multicanal=multicanal,
contentType='movie',
dead=canal,
path=raiz,
nfo=nfo_path,
library_urls=new_item.library_urls,
infoLabels={'title': new_item.contentTitle})
if canal not in dead_list and canal not in zombie_list:
confirm = platformtools.dialog_yesno('Videoteca',
'Parece que el canal [COLOR red]%s[/COLOR] ya no existe.' % canal.upper(),
'Deseas eliminar los enlaces de este canal?')
elif canal in zombie_list:
confirm = False
else:
confirm = True
if confirm:
delete(dead_item)
if canal not in dead_list:
dead_list.append(canal)
continue
else:
if canal not in zombie_list:
zombie_list.append(canal)
if len(dead_list) > 0:
for canal in dead_list:
if canal in new_item.library_urls:
del new_item.library_urls[canal]
new_item.nfo = nfo_path
new_item.path = raiz
new_item.thumbnail = new_item.contentThumbnail
new_item.text_color = "blue"
strm_path = new_item.strm_path.replace("\\", "/").rstrip("/")
if '/' in new_item.path:
new_item.strm_path = strm_path
if not filetools.exists(filetools.join(new_item.path, filetools.basename(strm_path))):
# Si se ha eliminado el strm desde la bilbioteca de kodi, no mostrarlo
continue
# Menu contextual: Marcar como visto/no visto
visto = new_item.library_playcounts.get(os.path.splitext(f)[0], 0)
new_item.infoLabels["playcount"] = visto
if visto > 0:
texto_visto = config.get_localized_string(60016)
contador = 0
else:
texto_visto = config.get_localized_string(60017)
contador = 1
# Menu contextual: Eliminar serie/canal
num_canales = len(new_item.library_urls)
if "downloads" in new_item.library_urls:
num_canales -= 1
if num_canales > 1:
texto_eliminar = config.get_localized_string(60018)
else:
texto_eliminar = config.get_localized_string(60019)
new_item.context = [{"title": texto_visto,
"action": "mark_content_as_watched",
"channel": "videolibrary",
"playcount": contador},
{"title": texto_eliminar,
"action": "delete",
"channel": "videolibrary",
"multicanal": multicanal}]
# ,{"title": "Cambiar contenido (PENDIENTE)",
# "action": "",
# "channel": "videolibrary"}]
# logger.debug("new_item: " + new_item.tostring('\n'))
itemlist.append(new_item)
if silent == False:
return sorted(itemlist, key=lambda it: it.title.lower())
else:
return
def list_tvshows(item):
logger.info()
itemlist = []
dead_list = []
zombie_list = []
# Obtenemos todos los tvshow.nfo de la videoteca de SERIES recursivamente
for raiz, subcarpetas, ficheros in filetools.walk(videolibrarytools.TVSHOWS_PATH):
for f in ficheros:
if f == "tvshow.nfo":
tvshow_path = filetools.join(raiz, f)
# logger.debug(tvshow_path)
#Sincronizamos los episodios vistos desde la videoteca de Kodi con la de Alfa
try:
if config.is_xbmc(): #Si es Kodi, lo hacemos
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_content_as_watched_on_alfa(tvshow_path)
except:
logger.error(traceback.format_exc())
head_nfo, item_tvshow = videolibrarytools.read_nfo(tvshow_path)
if len(item_tvshow.library_urls) > 1:
multicanal = True
else:
multicanal = False
## verifica la existencia de los canales, en caso de no existir el canal se pregunta si se quieren
## eliminar los enlaces de dicho canal
for canal in item_tvshow.library_urls:
canal = generictools.verify_channel(canal)
try:
channel_verify = __import__('channels.%s' % canal, fromlist=["channels.%s" % canal])
logger.debug('El canal %s parece correcto' % channel_verify)
except:
dead_item = Item(multicanal=multicanal,
contentType='tvshow',
dead=canal,
path=raiz,
nfo=tvshow_path,
library_urls=item_tvshow.library_urls,
infoLabels={'title': item_tvshow.contentTitle})
if canal not in dead_list and canal not in zombie_list:
confirm = platformtools.dialog_yesno('Videoteca',
'Parece que el canal [COLOR red]%s[/COLOR] ya no existe.' % canal.upper(),
'Deseas eliminar los enlaces de este canal?')
elif canal in zombie_list:
confirm = False
else:
confirm = True
if confirm:
delete(dead_item)
if canal not in dead_list:
dead_list.append(canal)
continue
else:
if canal not in zombie_list:
zombie_list.append(canal)
if len(dead_list) > 0:
for canal in dead_list:
if canal in item_tvshow.library_urls:
del item_tvshow.library_urls[canal]
### continua la carga de los elementos de la videoteca
try: #A veces da errores aleatorios, por no encontrar el .nfo. Probablemente problemas de timing
item_tvshow.title = item_tvshow.contentTitle
item_tvshow.path = raiz
item_tvshow.nfo = tvshow_path
# Menu contextual: Marcar como visto/no visto
visto = item_tvshow.library_playcounts.get(item_tvshow.contentTitle, 0)
item_tvshow.infoLabels["playcount"] = visto
if visto > 0:
texto_visto = config.get_localized_string(60020)
contador = 0
else:
texto_visto = config.get_localized_string(60021)
contador = 1
except:
logger.error('No encuentra: ' + str(tvshow_path))
logger.error(traceback.format_exc())
continue
# Menu contextual: Buscar automáticamente nuevos episodios o no
if item_tvshow.active and int(item_tvshow.active) > 0:
texto_update = config.get_localized_string(60022)
value = 0
item_tvshow.text_color = "green"
else:
texto_update = config.get_localized_string(60023)
value = 1
item_tvshow.text_color = "0xFFDF7401"
# Menu contextual: Eliminar serie/canal
num_canales = len(item_tvshow.library_urls)
if "downloads" in item_tvshow.library_urls:
num_canales -= 1
if num_canales > 1:
texto_eliminar = config.get_localized_string(60024)
else:
texto_eliminar = config.get_localized_string(60025)
item_tvshow.context = [{"title": texto_visto,
"action": "mark_content_as_watched",
"channel": "videolibrary",
"playcount": contador},
{"title": texto_update,
"action": "mark_tvshow_as_updatable",
"channel": "videolibrary",
"active": value},
{"title": texto_eliminar,
"action": "delete",
"channel": "videolibrary",
"multicanal": multicanal},
{"title": config.get_localized_string(70269),
"action": "update_tvshow",
"channel": "videolibrary"}]
# ,{"title": "Cambiar contenido (PENDIENTE)",
# "action": "",
# "channel": "videolibrary"}]
# logger.debug("item_tvshow:\n" + item_tvshow.tostring('\n'))
## verifica la existencia de los canales ##
if len(item_tvshow.library_urls) > 0:
itemlist.append(item_tvshow)
if itemlist:
itemlist = sorted(itemlist, key=lambda it: it.title.lower())
itemlist.append(Item(channel=item.channel, action="update_videolibrary", thumbnail=item.thumbnail,
title=config.get_localized_string(60026), folder=False))
return itemlist
def get_seasons(item):
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
itemlist = []
dict_temp = {}
raiz, carpetas_series, ficheros = filetools.walk(item.path).next()
# Menu contextual: Releer tvshow.nfo
head_nfo, item_nfo = videolibrarytools.read_nfo(item.nfo)
if config.get_setting("no_pile_on_seasons", "videolibrary") == 2: # Siempre
return get_episodes(item)
for f in ficheros:
if f.endswith('.json'):
season = f.split('x')[0]
dict_temp[season] = config.get_localized_string(60027) % season
if config.get_setting("no_pile_on_seasons", "videolibrary") == 1 and len(
dict_temp) == 1: # Sólo si hay una temporada
return get_episodes(item)
else:
# TODO mostrar los episodios de la unica temporada "no vista", en vez de mostrar el Item "temporada X" previo
# si está marcado "ocultar los vistos" en el skin, se ejecutaria esto
# se comprueba cada temporada en dict_temp si está visto.
# si hay una sola temporada y no_pile_on_seasons == 1, se devuelve get(episodios)
# si está todo visto, hacemos como actualmente <-- el else no se hace nada.. CREO
# if config.get_setting("no_pile_on_seasons", "videolibrary") == 1 and len(dict_temp_Visible) == 1: # Sólo si hay una temporada
# Creamos un item por cada temporada
for season, title in dict_temp.items():
new_item = item.clone(action="get_episodes", title=title, contentSeason=season,
filtrar_season=True)
# Menu contextual: Marcar la temporada como vista o no
visto = item_nfo.library_playcounts.get("season %s" % season, 0)
new_item.infoLabels["playcount"] = visto
if visto > 0:
texto = config.get_localized_string(60028)
value = 0
else:
texto = config.get_localized_string(60029)
value = 1
new_item.context = [{"title": texto,
"action": "mark_season_as_watched",
"channel": "videolibrary",
"playcount": value}]
# logger.debug("new_item:\n" + new_item.tostring('\n'))
itemlist.append(new_item)
if len(itemlist) > 1:
itemlist = sorted(itemlist, key=lambda it: int(it.contentSeason))
if config.get_setting("show_all_seasons", "videolibrary"):
new_item = item.clone(action="get_episodes", title=config.get_localized_string(60030))
new_item.infoLabels["playcount"] = 0
itemlist.insert(0, new_item)
return itemlist
def get_episodes(item):
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
itemlist = []
# Obtenemos los archivos de los episodios
raiz, carpetas_series, ficheros = filetools.walk(item.path).next()
# Menu contextual: Releer tvshow.nfo
head_nfo, item_nfo = videolibrarytools.read_nfo(item.nfo)
# Crear un item en la lista para cada strm encontrado
for i in ficheros:
if i.endswith('.strm'):
season_episode = scrapertools.get_season_and_episode(i)
if not season_episode:
# El fichero no incluye el numero de temporada y episodio
continue
season, episode = season_episode.split("x")
# Si hay q filtrar por temporada, ignoramos los capitulos de otras temporadas
if item.filtrar_season and int(season) != int(item.contentSeason):
continue
# Obtener los datos del season_episode.nfo
nfo_path = filetools.join(raiz, i).replace('.strm', '.nfo')
head_nfo, epi = videolibrarytools.read_nfo(nfo_path)
# Fijar el titulo del capitulo si es posible
if epi.contentTitle:
title_episodie = epi.contentTitle.strip()
else:
title_episodie = config.get_localized_string(60031) % \
(epi.contentSeason, str(epi.contentEpisodeNumber).zfill(2))
epi.contentTitle = "%sx%s" % (epi.contentSeason, str(epi.contentEpisodeNumber).zfill(2))
epi.title = "%sx%s - %s" % (epi.contentSeason, str(epi.contentEpisodeNumber).zfill(2), title_episodie)
if item_nfo.library_filter_show:
epi.library_filter_show = item_nfo.library_filter_show
# Menu contextual: Marcar episodio como visto o no
visto = item_nfo.library_playcounts.get(season_episode, 0)
epi.infoLabels["playcount"] = visto
if visto > 0:
texto = config.get_localized_string(60032)
value = 0
else:
texto = config.get_localized_string(60033)
value = 1
epi.context = [{"title": texto,
"action": "mark_content_as_watched",
"channel": "videolibrary",
"playcount": value,
"nfo": item.nfo}]
# logger.debug("epi:\n" + epi.tostring('\n'))
itemlist.append(epi)
return sorted(itemlist, key=lambda it: (int(it.contentSeason), int(it.contentEpisodeNumber)))
def findvideos(item):
from specials import autoplay
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
itemlist = []
list_canales = {}
item_local = None
# Desactiva autoplay
autoplay.set_status(False)
if not item.contentTitle or not item.strm_path:
logger.debug("No se pueden buscar videos por falta de parametros")
return []
content_title = filter(lambda c: c not in ":*?<>|\/", item.contentTitle.strip().lower())
if item.contentType == 'movie':
item.strm_path = filetools.join(videolibrarytools.MOVIES_PATH, item.strm_path)
path_dir = os.path.dirname(item.strm_path)
item.nfo = filetools.join(path_dir, os.path.basename(path_dir) + ".nfo")
else:
item.strm_path = filetools.join(videolibrarytools.TVSHOWS_PATH, item.strm_path)
path_dir = os.path.dirname(item.strm_path)
item.nfo = filetools.join(path_dir, 'tvshow.nfo')
for fd in filetools.listdir(path_dir):
if fd.endswith('.json'):
contenido, nom_canal = fd[:-6].split('[')
if (contenido.startswith(content_title) or item.contentType == 'movie') and nom_canal not in \
list_canales.keys():
list_canales[nom_canal] = filetools.join(path_dir, fd)
num_canales = len(list_canales)
if 'downloads' in list_canales:
json_path = list_canales['downloads']
item_json = Item().fromjson(filetools.read(json_path))
item_json.contentChannel = "local"
# Soporte para rutas relativas en descargas
if filetools.is_relative(item_json.url):
item_json.url = filetools.join(videolibrarytools.VIDEOLIBRARY_PATH, item_json.url)
del list_canales['downloads']
# Comprobar q el video no haya sido borrado
if filetools.exists(item_json.url):
item_local = item_json.clone(action='play')
itemlist.append(item_local)
else:
num_canales -= 1
filtro_canal = ''
if num_canales > 1 and config.get_setting("ask_channel", "videolibrary"):
opciones = [config.get_localized_string(70089) % k.capitalize() for k in list_canales.keys()]
opciones.insert(0, config.get_localized_string(70083))
if item_local:
opciones.append(item_local.title)
from platformcode import platformtools
index = platformtools.dialog_select(config.get_localized_string(30163), opciones)
if index < 0:
return []
elif item_local and index == len(opciones) - 1:
filtro_canal = 'downloads'
platformtools.play_video(item_local)
elif index > 0:
filtro_canal = opciones[index].replace(config.get_localized_string(70078), "").strip()
itemlist = []
for nom_canal, json_path in list_canales.items():
if filtro_canal and filtro_canal != nom_canal.capitalize():
continue
item_canal = Item()
item_canal.channel = nom_canal
nom_canal = item_canal.channel
# Importamos el canal de la parte seleccionada
try:
channel = __import__('channels.%s' % nom_canal, fromlist=["channels.%s" % nom_canal])
except ImportError:
exec "import channels." + nom_canal + " as channel"
item_json = Item().fromjson(filetools.read(json_path))
list_servers = []
try:
# FILTERTOOLS
# si el canal tiene filtro se le pasa el nombre que tiene guardado para que filtre correctamente.
if "list_language" in item_json:
# si se viene desde la videoteca del addon
if "library_filter_show" in item:
item_json.show = item.library_filter_show.get(nom_canal, "")
# Ejecutamos find_videos, del canal o común
item_json.contentChannel = 'videolibrary'
if hasattr(channel, 'findvideos'):
from core import servertools
list_servers = getattr(channel, 'findvideos')(item_json)
list_servers = servertools.filter_servers(list_servers)
else:
from core import servertools
list_servers = servertools.find_video_items(item_json)
except Exception, ex:
logger.error("Ha fallado la funcion findvideos para el canal %s" % nom_canal)
template = "An exception of type %s occured. Arguments:\n%r"
message = template % (type(ex).__name__, ex.args)
logger.error(message)
logger.error(traceback.format_exc())
# Cambiarle el titulo a los servers añadiendoles el nombre del canal delante y
# las infoLabels y las imagenes del item si el server no tiene
for server in list_servers:
#if not server.action: # Ignorar/PERMITIR las etiquetas
# continue
server.contentChannel = server.channel
server.channel = "videolibrary"
server.nfo = item.nfo
server.strm_path = item.strm_path
#### Compatibilidad con Kodi 18: evita que se quede la ruedecedita dando vueltas en enlaces Directos
if server.action == 'play':
server.folder = False
# Se añade el nombre del canal si se desea
if config.get_setting("quit_channel_name", "videolibrary") == 0:
server.title = "%s: %s" % (nom_canal.capitalize(), server.title)
#server.infoLabels = item_json.infoLabels
if not server.thumbnail:
server.thumbnail = item.thumbnail
# logger.debug("server:\n%s" % server.tostring('\n'))
itemlist.append(server)
# return sorted(itemlist, key=lambda it: it.title.lower())
autoplay.play_multi_channel(item, itemlist)
return itemlist
def play(item):
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
if not item.contentChannel == "local":
channel = __import__('channels.%s' % item.contentChannel, fromlist=["channels.%s" % item.contentChannel])
if hasattr(channel, "play"):
itemlist = getattr(channel, "play")(item)
else:
itemlist = [item.clone()]
else:
itemlist = [item.clone(url=item.url, server="local")]
# Para enlaces directo en formato lista
if isinstance(itemlist[0], list):
item.video_urls = itemlist
itemlist = [item]
# Esto es necesario por si el play del canal elimina los datos
for v in itemlist:
if isinstance(v, Item):
v.nfo = item.nfo
v.strm_path = item.strm_path
v.infoLabels = item.infoLabels
if item.contentTitle:
v.title = item.contentTitle
else:
if item.contentType == "episode":
v.title = config.get_localized_string(60036) % item.contentEpisodeNumber
v.thumbnail = item.thumbnail
v.contentThumbnail = item.thumbnail
return itemlist
def update_videolibrary(item):
logger.info()
# Actualizar las series activas sobreescribiendo
import videolibrary_service
videolibrary_service.check_for_update(overwrite=True)
# Eliminar las carpetas de peliculas que no contengan archivo strm
for raiz, subcarpetas, ficheros in filetools.walk(videolibrarytools.MOVIES_PATH):
strm = False
for f in ficheros:
if f.endswith(".strm"):
strm = True
break
if ficheros and not strm:
logger.debug("Borrando carpeta de pelicula eliminada: %s" % raiz)
filetools.rmdirtree(raiz)
# metodos de menu contextual
def update_tvshow(item):
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
heading = config.get_localized_string(60037)
p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(20000), heading)
p_dialog.update(0, heading, item.contentSerieName)
import videolibrary_service
if videolibrary_service.update(item.path, p_dialog, 1, 1, item, False) and config.is_xbmc():
from platformcode import xbmc_videolibrary
xbmc_videolibrary.update(folder=filetools.basename(item.path))
p_dialog.close()
def verify_playcount_series(item, path):
logger.info()
"""
Este método revisa y repara el PlayCount de una serie que se haya desincronizado de la lista real de episodios en su carpeta. Las entradas de episodios, temporadas o serie que falten, son creado con la marca de "no visto". Posteriormente se envia a verificar los contadores de Temporadas y Serie
En el retorno envía de estado de True si se actualizado o False si no, normalmente por error. Con este estado, el caller puede actualizar el estado de la opción "verify_playcount" en "videolibrary.py". La intención de este método es la de dar una pasada que repare todos los errores y luego desactivarse. Se puede volver a activar en el menú de Videoteca de Alfa.
"""
#logger.debug("item:\n" + item.tostring('\n'))
#Si no ha hecho nunca la verificación, lo forzamos
estado = config.get_setting("verify_playcount", "videolibrary")
if not estado or estado == False:
estado = True #Si no ha hecho nunca la verificación, lo forzamos
else:
estado = False
if item.contentType == 'movie': #Esto es solo para Series
return (item, False)
if filetools.exists(path):
nfo_path = filetools.join(path, "tvshow.nfo")
head_nfo, it = videolibrarytools.read_nfo(nfo_path) #Obtenemos el .nfo de la Serie
if not hasattr(it, 'library_playcounts') or not it.library_playcounts: #Si el .nfo no tiene library_playcounts se lo creamos
logger.error('** No tiene PlayCount')
it.library_playcounts = {}
# Obtenemos los archivos de los episodios
raiz, carpetas_series, ficheros = filetools.walk(path).next()
# Crear un item en la lista para cada strm encontrado
estado_update = False
for i in ficheros:
if i.endswith('.strm'):
season_episode = scrapertools.get_season_and_episode(i)
if not season_episode:
# El fichero no incluye el numero de temporada y episodio
continue
season, episode = season_episode.split("x")
if season_episode not in it.library_playcounts: #No está incluido el episodio
it.library_playcounts.update({season_episode: 0}) #actualizamos el playCount del .nfo
estado_update = True #Marcamos que hemos actualizado algo
if 'season %s' % season not in it.library_playcounts: #No está incluida la Temporada
it.library_playcounts.update({'season %s' % season: 0}) #actualizamos el playCount del .nfo
estado_update = True #Marcamos que hemos actualizado algo
if it.contentSerieName not in it.library_playcounts: #No está incluida la Serie
it.library_playcounts.update({item.contentSerieName: 0}) #actualizamos el playCount del .nfo
estado_update = True #Marcamos que hemos actualizado algo
if estado_update:
logger.error('** Estado de actualización: ' + str(estado) + ' / PlayCount: ' + str(it.library_playcounts))
estado = estado_update
# se comprueba que si todos los episodios de una temporada están marcados, se marque tb la temporada
for key, value in it.library_playcounts.iteritems():
if key.startswith("season"):
season = scrapertools.find_single_match(key, 'season (\d+)') #Obtenemos en núm. de Temporada
it = check_season_playcount(it, season)
# Guardamos los cambios en item.nfo
if filetools.write(nfo_path, head_nfo + it.tojson()):
return (it, estado)
return (item, False)
def mark_content_as_watched2(item):
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
if filetools.exists(item.nfo):
head_nfo, it = videolibrarytools.read_nfo(item.nfo)
#logger.debug(it)
if item.contentType == 'movie':
name_file = os.path.splitext(os.path.basename(item.nfo))[0]
if name_file != 'tvshow' :
it.library_playcounts.update({name_file: item.playcount})
if item.contentType == 'episode' or item.contentType == 'list' or name_file == 'tvshow':
# elif item.contentType == 'episode':
name_file = os.path.splitext(os.path.basename(item.strm_path))[0]
num_season = name_file [0]
item.__setattr__('contentType', 'episode')
item.__setattr__('contentSeason', num_season)
#logger.debug(name_file)
else:
name_file = item.contentTitle
# logger.debug(name_file)
if not hasattr(it, 'library_playcounts'):
it.library_playcounts = {}
it.library_playcounts.update({name_file: item.playcount})
# se comprueba que si todos los episodios de una temporada están marcados, se marque tb la temporada
if item.contentType != 'movie':
it = check_season_playcount(it, item.contentSeason)
#logger.debug(it)
# Guardamos los cambios en item.nfo
if filetools.write(item.nfo, head_nfo + it.tojson()):
item.infoLabels['playcount'] = item.playcount
#logger.debug(item.playcount)
# if item.contentType == 'episodesss':
# Actualizar toda la serie
#new_item = item.clone(contentSeason=-1)
#mark_season_as_watched(new_item)
if config.is_xbmc():
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_content_as_watched_on_kodi(item , item.playcount)
# logger.debug(item)
platformtools.itemlist_refresh()
def mark_content_as_watched(item):
logger.info()
#logger.debug("item:\n" + item.tostring('\n'))
if filetools.exists(item.nfo):
head_nfo, it = videolibrarytools.read_nfo(item.nfo)
if item.contentType == 'movie':
name_file = os.path.splitext(os.path.basename(item.nfo))[0]
elif item.contentType == 'episode':
name_file = "%sx%s" % (item.contentSeason, str(item.contentEpisodeNumber).zfill(2))
else:
name_file = item.contentTitle
if not hasattr(it, 'library_playcounts'):
it.library_playcounts = {}
it.library_playcounts.update({name_file: item.playcount})
# se comprueba que si todos los episodios de una temporada están marcados, se marque tb la temporada
if item.contentType != 'movie':
it = check_season_playcount(it, item.contentSeason)
# Guardamos los cambios en item.nfo
if filetools.write(item.nfo, head_nfo + it.tojson()):
item.infoLabels['playcount'] = item.playcount
if item.contentType == 'tvshow' and item.type != 'episode' :
# Actualizar toda la serie
new_item = item.clone(contentSeason=-1)
mark_season_as_watched(new_item)
if config.is_xbmc(): #and item.contentType == 'episode':
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_content_as_watched_on_kodi(item, item.playcount)
platformtools.itemlist_refresh()
def mark_season_as_watched(item):
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
# Obtener el diccionario de episodios marcados
f = filetools.join(item.path, 'tvshow.nfo')
head_nfo, it = videolibrarytools.read_nfo(f)
if not hasattr(it, 'library_playcounts'):
it.library_playcounts = {}
# Obtenemos los archivos de los episodios
raiz, carpetas_series, ficheros = filetools.walk(item.path).next()
# Marcamos cada uno de los episodios encontrados de esta temporada
episodios_marcados = 0
for i in ficheros:
if i.endswith(".strm"):
season_episode = scrapertools.get_season_and_episode(i)
if not season_episode:
# El fichero no incluye el numero de temporada y episodio
continue
season, episode = season_episode.split("x")
if int(item.contentSeason) == -1 or int(season) == int(item.contentSeason):
name_file = os.path.splitext(os.path.basename(i))[0]
it.library_playcounts[name_file] = item.playcount
episodios_marcados += 1
if episodios_marcados:
if int(item.contentSeason) == -1:
# Añadimos todas las temporadas al diccionario item.library_playcounts
for k in it.library_playcounts.keys():
if k.startswith("season"):
it.library_playcounts[k] = item.playcount
else:
# Añadimos la temporada al diccionario item.library_playcounts
it.library_playcounts["season %s" % item.contentSeason] = item.playcount
# se comprueba que si todas las temporadas están vistas, se marque la serie como vista
it = check_tvshow_playcount(it, item.contentSeason)
# Guardamos los cambios en tvshow.nfo
filetools.write(f, head_nfo + it.tojson())
item.infoLabels['playcount'] = item.playcount
if config.is_xbmc():
# Actualizamos la BBDD de Kodi
from platformcode import xbmc_videolibrary
xbmc_videolibrary.mark_season_as_watched_on_kodi(item, item.playcount)
platformtools.itemlist_refresh()
def mark_tvshow_as_updatable(item):
logger.info()
head_nfo, it = videolibrarytools.read_nfo(item.nfo)
it.active = item.active
filetools.write(item.nfo, head_nfo + it.tojson())
platformtools.itemlist_refresh()
def delete(item):
def delete_all(_item):
for file in filetools.listdir(_item.path):
if file.endswith(".strm") or file.endswith(".nfo") or file.endswith(".json")or file.endswith(".torrent"):
filetools.remove(filetools.join(_item.path, file))
raiz, carpeta_serie, ficheros = filetools.walk(_item.path).next()
if ficheros == []:
filetools.rmdir(_item.path)
if config.is_xbmc():
import xbmc
# esperamos 3 segundos para dar tiempo a borrar los ficheros
xbmc.sleep(3000)
# TODO mirar por qué no funciona al limpiar en la videoteca de Kodi al añadirle un path
# limpiamos la videoteca de Kodi
from platformcode import xbmc_videolibrary
xbmc_videolibrary.clean()
logger.info("Eliminados todos los enlaces")
platformtools.itemlist_refresh()
# logger.info(item.contentTitle)
# logger.debug(item.tostring('\n'))
if item.contentType == 'movie':
heading = config.get_localized_string(70084)
else:
heading = config.get_localized_string(70085)
if item.multicanal:
# Obtener listado de canales
if item.dead == '':
opciones = [config.get_localized_string(70086) % k.capitalize() for k in item.library_urls.keys() if
k != "downloads"]
opciones.insert(0, heading)
index = platformtools.dialog_select(config.get_localized_string(30163), opciones)
if index == 0:
# Seleccionado Eliminar pelicula/serie
delete_all(item)
elif index > 0:
# Seleccionado Eliminar canal X
canal = opciones[index].replace(config.get_localized_string(70079), "").lower()
else:
return
else:
canal = item.dead
num_enlaces = 0
for fd in filetools.listdir(item.path):
if fd.endswith(canal + '].json') or scrapertools.find_single_match(fd, '%s]_\d+.torrent' % canal):
if filetools.remove(filetools.join(item.path, fd)):
num_enlaces += 1
if num_enlaces > 0:
# Actualizar .nfo
head_nfo, item_nfo = videolibrarytools.read_nfo(item.nfo)
del item_nfo.library_urls[canal]
if item_nfo.emergency_urls and item_nfo.emergency_urls.get(canal, False):
del item_nfo.emergency_urls[canal]
filetools.write(item.nfo, head_nfo + item_nfo.tojson())
msg_txt = config.get_localized_string(70087) % (num_enlaces, canal)
logger.info(msg_txt)
platformtools.dialog_notification(heading, msg_txt)
platformtools.itemlist_refresh()
else:
if platformtools.dialog_yesno(heading,
config.get_localized_string(70088) % item.infoLabels['title']):
delete_all(item)
def check_season_playcount(item, season):
logger.info()
if season:
episodios_temporada = 0
episodios_vistos_temporada = 0
for key, value in item.library_playcounts.iteritems():
if key.startswith("%sx" % season):
episodios_temporada += 1
if value > 0:
episodios_vistos_temporada += 1
if episodios_temporada == episodios_vistos_temporada:
# se comprueba que si todas las temporadas están vistas, se marque la serie como vista
item.library_playcounts.update({"season %s" % season: 1})
else:
# se comprueba que si todas las temporadas están vistas, se marque la serie como vista
item.library_playcounts.update({"season %s" % season: 0})
return check_tvshow_playcount(item, season)
def check_tvshow_playcount(item, season):
logger.info()
if season:
temporadas_serie = 0
temporadas_vistas_serie = 0
for key, value in item.library_playcounts.iteritems():
#if key.startswith("season %s" % season):
if key.startswith("season" ):
temporadas_serie += 1
if value > 0:
temporadas_vistas_serie += 1
#logger.debug(temporadas_serie)
if temporadas_serie == temporadas_vistas_serie:
item.library_playcounts.update({item.title: 1})
else:
item.library_playcounts.update({item.title: 0})
else:
playcount = item.library_playcounts.get(item.title, 0)
item.library_playcounts.update({item.title: playcount})
return item