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

483 lines
18 KiB
Python
Executable File

# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------------
# Item is the object we use for representing data
# --------------------------------------------------------------------------------
import base64
import copy
import os
import urllib
from HTMLParser import HTMLParser
from core import jsontools as json
class InfoLabels(dict):
def __str__(self):
return self.tostring(separador=',\r\t')
def __setitem__(self, name, value):
if name in ["season", "episode"]:
# forzamos int() en season y episode
try:
super(InfoLabels, self).__setitem__(name, int(value))
except:
pass
elif name in ['IMDBNumber', 'imdb_id']:
# Por compatibilidad hemos de guardar el valor en los tres campos
super(InfoLabels, self).__setitem__('IMDBNumber', str(value))
# super(InfoLabels, self).__setitem__('code', value)
super(InfoLabels, self).__setitem__('imdb_id', str(value))
elif name == "mediatype" and value not in ["list", "movie", "tvshow", "season", "episode"]:
super(InfoLabels, self).__setitem__('mediatype', 'list')
elif name in ['tmdb_id', 'tvdb_id', 'noscrap_id']:
super(InfoLabels, self).__setitem__(name, str(value))
else:
super(InfoLabels, self).__setitem__(name, value)
# Python 2.4
def __getitem__(self, key):
try:
return super(InfoLabels, self).__getitem__(key)
except:
return self.__missing__(key)
def __missing__(self, key):
"""
Valores por defecto en caso de que la clave solicitada no exista.
El parametro 'default' en la funcion obj_infoLabels.get(key,default) tiene preferencia sobre los aqui definidos.
"""
if key in ['rating']:
# Ejemplo de clave q devuelve un str formateado como float por defecto
return '0.0'
elif key == 'code':
code = []
# Añadir imdb_id al listado de codigos
if 'imdb_id' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('imdb_id'):
code.append(super(InfoLabels, self).__getitem__('imdb_id'))
# Completar con el resto de codigos
for scr in ['tmdb_id', 'tvdb_id', 'noscrap_id']:
if scr in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__(scr):
value = "%s%s" % (scr[:-2], super(InfoLabels, self).__getitem__(scr))
code.append(value)
# Opcion añadir un code del tipo aleatorio
if not code:
import time
value = time.strftime("%Y%m%d%H%M%S", time.gmtime())
code.append(value)
super(InfoLabels, self).__setitem__('noscrap_id', value)
return code
elif key == 'mediatype':
# "list", "movie", "tvshow", "season", "episode"
if 'tvshowtitle' in super(InfoLabels, self).keys() \
and super(InfoLabels, self).__getitem__('tvshowtitle') != "":
if 'episode' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('episode') != "":
return 'episode'
if 'episodeName' in super(InfoLabels, self).keys() \
and super(InfoLabels, self).__getitem__('episodeName') != "":
return 'episode'
if 'season' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('season') != "":
return 'season'
else:
return 'tvshow'
elif 'title' in super(InfoLabels, self).keys() and super(InfoLabels, self).__getitem__('title') != "":
return 'movie'
else:
return 'list'
else:
# El resto de claves devuelven cadenas vacias por defecto
return ""
def tostring(self, separador=', '):
ls = []
dic = dict(super(InfoLabels, self).items())
for i in sorted(dic.items()):
i_str = str(i)[1:-1]
if isinstance(i[0], str):
old = i[0] + "',"
new = i[0] + "':"
else:
old = str(i[0]) + ","
new = str(i[0]) + ":"
ls.append(i_str.replace(old, new, 1))
return "{%s}" % separador.join(ls)
class Item(object):
def __init__(self, **kwargs):
"""
Inicializacion del item
"""
# Creamos el atributo infoLabels
self.__dict__["infoLabels"] = InfoLabels()
if "infoLabels" in kwargs:
if isinstance(kwargs["infoLabels"], dict):
self.__dict__["infoLabels"].update(kwargs["infoLabels"])
del kwargs["infoLabels"]
if "parentContent" in kwargs:
self.set_parent_content(kwargs["parentContent"])
del kwargs["parentContent"]
kw = copy.copy(kwargs)
for k in kw:
if k in ["contentTitle", "contentPlot", "contentSerieName", "show", "contentType", "contentEpisodeTitle",
"contentSeason", "contentEpisodeNumber", "contentThumbnail", "plot", "duration", "contentQuality",
"quality"]:
self.__setattr__(k, kw[k])
del kwargs[k]
self.__dict__.update(kwargs)
self.__dict__ = self.toutf8(self.__dict__)
def __contains__(self, m):
"""
Comprueba si un atributo existe en el item
"""
return m in self.__dict__
def __setattr__(self, name, value):
"""
Función llamada al modificar cualquier atributo del item, modifica algunos atributos en función de los datos
modificados.
"""
value = self.toutf8(value)
if name == "__dict__":
for key in value:
self.__setattr__(key, value[key])
return
# Descodificamos los HTML entities
if name in ["title", "plot", "fulltitle", "contentPlot", "contentTitle"]:
value = self.decode_html(value)
# Al modificar cualquiera de estos atributos content...
if name in ["contentTitle", "contentPlot", "plot", "contentSerieName", "contentType", "contentEpisodeTitle",
"contentSeason", "contentEpisodeNumber", "contentThumbnail", "show", "contentQuality", "quality"]:
# ... marcamos hasContentDetails como "true"...
self.__dict__["hasContentDetails"] = True
# ...y actualizamos infoLables
if name == "contentTitle":
self.__dict__["infoLabels"]["title"] = value
elif name == "contentPlot" or name == "plot":
self.__dict__["infoLabels"]["plot"] = value
elif name == "contentSerieName" or name == "show":
self.__dict__["infoLabels"]["tvshowtitle"] = value
elif name == "contentType":
self.__dict__["infoLabels"]["mediatype"] = value
elif name == "contentEpisodeTitle":
self.__dict__["infoLabels"]["episodeName"] = value
elif name == "contentSeason":
self.__dict__["infoLabels"]["season"] = value
elif name == "contentEpisodeNumber":
self.__dict__["infoLabels"]["episode"] = value
elif name == "contentThumbnail":
self.__dict__["infoLabels"]["thumbnail"] = value
elif name == "contentQuality" or name == "quality":
self.__dict__["infoLabels"]["quality"] = value
elif name == "duration":
# String q representa la duracion del video en segundos
self.__dict__["infoLabels"]["duration"] = str(value)
elif name == "viewcontent" and value not in ["files", "movies", "tvshows", "seasons", "episodes"]:
super(Item, self).__setattr__("viewcontent", "files")
# Al asignar un valor a infoLables
elif name == "infoLabels":
if isinstance(value, dict):
value_defaultdict = InfoLabels(value)
self.__dict__["infoLabels"] = value_defaultdict
else:
super(Item, self).__setattr__(name, value)
def __getattr__(self, name):
"""
Devuelve los valores por defecto en caso de que el atributo solicitado no exista en el item
"""
if name.startswith("__"):
return super(Item, self).__getattribute__(name)
# valor por defecto para folder
if name == "folder":
return True
# valor por defecto para contentChannel
elif name == "contentChannel":
return "list"
# valor por defecto para viewcontent
elif name == "viewcontent":
# intentamos fijarlo segun el tipo de contenido...
if self.__dict__["infoLabels"]["mediatype"] == 'movie':
viewcontent = 'movies'
elif self.__dict__["infoLabels"]["mediatype"] in ["tvshow", "season", "episode"]:
viewcontent = "episodes"
else:
viewcontent = "files"
self.__dict__["viewcontent"] = viewcontent
return viewcontent
# Valor por defecto para hasContentDetails
elif name == "hasContentDetails":
return False
# valores guardados en infoLabels
elif name in ["contentTitle", "contentPlot", "contentSerieName", "show", "contentType", "contentEpisodeTitle",
"contentSeason", "contentEpisodeNumber", "contentThumbnail", "plot", "duration",
"contentQuality", "quality"]:
if name == "contentTitle":
return self.__dict__["infoLabels"]["title"]
elif name == "contentPlot" or name == "plot":
return self.__dict__["infoLabels"]["plot"]
elif name == "contentSerieName" or name == "show":
return self.__dict__["infoLabels"]["tvshowtitle"]
elif name == "contentType":
ret = self.__dict__["infoLabels"]["mediatype"]
if ret == 'list' and self.__dict__.get("fulltitle", None): # retrocompatibilidad
ret = 'movie'
self.__dict__["infoLabels"]["mediatype"] = ret
return ret
elif name == "contentEpisodeTitle":
return self.__dict__["infoLabels"]["episodeName"]
elif name == "contentSeason":
return self.__dict__["infoLabels"]["season"]
elif name == "contentEpisodeNumber":
return self.__dict__["infoLabels"]["episode"]
elif name == "contentThumbnail":
return self.__dict__["infoLabels"]["thumbnail"]
elif name == "contentQuality" or name == "quality":
return self.__dict__["infoLabels"]["quality"]
else:
return self.__dict__["infoLabels"][name]
# valor por defecto para el resto de atributos
else:
return ""
def __str__(self):
return '\r\t' + self.tostring('\r\t')
def set_parent_content(self, parentContent):
"""
Rellena los campos contentDetails con la informacion del item "padre"
@param parentContent: item padre
@type parentContent: item
"""
# Comprueba que parentContent sea un Item
if not type(parentContent) == type(self):
return
# Copia todos los atributos que empiecen por "content" y esten declarados y los infoLabels
for attr in parentContent.__dict__:
if attr.startswith("content") or attr == "infoLabels":
self.__setattr__(attr, parentContent.__dict__[attr])
def tostring(self, separator=", "):
"""
Genera una cadena de texto con los datos del item para el log
Uso: logger.info(item.tostring())
@param separator: cadena que se usará como separador
@type separator: str
'"""
dic = self.__dict__.copy()
# Añadimos los campos content... si tienen algun valor
for key in ["contentTitle", "contentPlot", "contentSerieName", "contentEpisodeTitle",
"contentSeason", "contentEpisodeNumber", "contentThumbnail"]:
value = self.__getattr__(key)
if value:
dic[key] = value
if 'mediatype' in self.__dict__["infoLabels"]:
dic["contentType"] = self.__dict__["infoLabels"]['mediatype']
ls = []
for var in sorted(dic):
if isinstance(dic[var], str):
valor = "'%s'" % dic[var]
elif isinstance(dic[var], InfoLabels):
if separator == '\r\t':
valor = dic[var].tostring(',\r\t\t')
else:
valor = dic[var].tostring()
else:
valor = str(dic[var])
ls.append(var + "= " + valor)
return separator.join(ls)
def tourl(self):
"""
Genera una cadena de texto con los datos del item para crear una url, para volver generar el Item usar
item.fromurl().
Uso: url = item.tourl()
"""
dump = json.dump(self.__dict__)
# if empty dict
if not dump:
# set a str to avoid b64encode fails
dump = ""
return urllib.quote(base64.b64encode(dump))
def fromurl(self, url):
"""
Genera un item a partir de una cadena de texto. La cadena puede ser creada por la funcion tourl() o tener
el formato antiguo: plugin://plugin.video.alfa/?channel=... (+ otros parametros)
Uso: item.fromurl("cadena")
@param url: url
@type url: str
"""
if "?" in url:
url = url.split("?")[1]
decoded = False
try:
str_item = base64.b64decode(urllib.unquote(url))
json_item = json.load(str_item, object_hook=self.toutf8)
if json_item is not None and len(json_item) > 0:
self.__dict__.update(json_item)
decoded = True
except:
pass
if not decoded:
url = urllib.unquote_plus(url)
dct = dict([[param.split("=")[0], param.split("=")[1]] for param in url.split("&") if "=" in param])
self.__dict__.update(dct)
self.__dict__ = self.toutf8(self.__dict__)
if 'infoLabels' in self.__dict__ and not isinstance(self.__dict__['infoLabels'], InfoLabels):
self.__dict__['infoLabels'] = InfoLabels(self.__dict__['infoLabels'])
return self
def tojson(self, path=""):
"""
Crea un JSON a partir del item, para guardar archivos de favoritos, lista de descargas, etc...
Si se especifica un path, te lo guarda en la ruta especificada, si no, devuelve la cadena json
Usos: item.tojson(path="ruta\archivo\json.json")
file.write(item.tojson())
@param path: ruta
@type path: str
"""
if path:
open(path, "wb").write(json.dump(self.__dict__))
else:
return json.dump(self.__dict__)
def fromjson(self, json_item=None, path=""):
"""
Genera un item a partir de un archivo JSON
Si se especifica un path, lee directamente el archivo, si no, lee la cadena de texto pasada.
Usos: item = Item().fromjson(path="ruta\archivo\json.json")
item = Item().fromjson("Cadena de texto json")
@param json_item: item
@type json_item: json
@param path: ruta
@type path: str
"""
if path:
if os.path.exists(path):
json_item = open(path, "rb").read()
else:
json_item = {}
if json_item is None:
json_item = {}
item = json.load(json_item, object_hook=self.toutf8)
self.__dict__.update(item)
if 'infoLabels' in self.__dict__ and not isinstance(self.__dict__['infoLabels'], InfoLabels):
self.__dict__['infoLabels'] = InfoLabels(self.__dict__['infoLabels'])
return self
def clone(self, **kwargs):
"""
Genera un nuevo item clonando el item actual
Usos: NuevoItem = item.clone()
NuevoItem = item.clone(title="Nuevo Titulo", action = "Nueva Accion")
"""
newitem = copy.deepcopy(self)
if "infoLabels" in kwargs:
kwargs["infoLabels"] = InfoLabels(kwargs["infoLabels"])
for kw in kwargs:
newitem.__setattr__(kw, kwargs[kw])
newitem.__dict__ = newitem.toutf8(newitem.__dict__)
return newitem
@staticmethod
def decode_html(value):
"""
Descodifica las HTML entities
@param value: valor a decodificar
@type value: str
"""
try:
unicode_title = unicode(value, "utf8", "ignore")
return HTMLParser().unescape(unicode_title).encode("utf8")
except:
return value
def toutf8(self, *args):
"""
Pasa el item a utf8
"""
if len(args) > 0:
value = args[0]
else:
value = self.__dict__
if type(value) == unicode:
return value.encode("utf8")
elif type(value) == str:
return unicode(value, "utf8", "ignore").encode("utf8")
elif type(value) == list:
for x, key in enumerate(value):
value[x] = self.toutf8(value[x])
return value
elif isinstance(value, dict):
newdct = {}
for key in value:
v = self.toutf8(value[key])
if type(key) == unicode:
key = key.encode("utf8")
newdct[key] = v
if len(args) > 0:
if isinstance(value, InfoLabels):
return InfoLabels(newdct)
else:
return newdct
else:
return value