483 lines
18 KiB
Python
Executable File
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
|