Files
addon/plugin.video.alfa/channels/filtertools.py
2017-07-28 19:37:39 -04:00

622 lines
22 KiB
Python
Executable File

# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# filtertools - se encarga de filtrar resultados
# ------------------------------------------------------------
from core import config
from core import filetools
from core import jsontools
from core import logger
from core.item import Item
from platformcode import platformtools
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": "FILTRO: Configurar", "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": "FILTRO: Añadir '%s'" % item.language, "action": "save_from_context",
"channel": "filtertools", "from_channel": item.channel})
else:
_context.append({"title": "FILTRO: Borrar '%s'" % 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="[COLOR %s]Configurar filtro para series...[/COLOR]" %
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 = " (desactivado)"
if dict_series[tvshow][TAG_ACTIVE]:
activo = ""
title = "Configurar [COLOR %s][%s][/COLOR]%s" % (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="No existen filtros, busca una serie y "
"pulsa en menú contextual 'FILTRO: Configurar'"))
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()
lang_selected = dict_series.get(tvshow, {}).get(TAG_LANGUAGE, 'Español')
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()