folder reorganization
This commit is contained in:
12
platformcode/__init__.py
Normal file
12
platformcode/__init__.py
Normal 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__), "..", "..")))
|
||||
431
platformcode/config.py
Normal file
431
platformcode/config.py
Normal file
@@ -0,0 +1,431 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# Parámetros de configuración (kodi)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
|
||||
PLUGIN_NAME = "alfa"
|
||||
|
||||
__settings__ = xbmcaddon.Addon(id="plugin.video." + PLUGIN_NAME)
|
||||
__language__ = __settings__.getLocalizedString
|
||||
|
||||
|
||||
def get_addon_version(with_fix=True):
|
||||
'''
|
||||
Devuelve el número de versión del addon, y opcionalmente número de fix si lo hay
|
||||
'''
|
||||
if with_fix:
|
||||
return __settings__.getAddonInfo('version') + get_addon_version_fix()
|
||||
else:
|
||||
return __settings__.getAddonInfo('version')
|
||||
|
||||
def get_addon_version_fix():
|
||||
try:
|
||||
last_fix_json = os.path.join(get_runtime_path(), 'last_fix.json') # información de la versión fixeada del usuario
|
||||
if os.path.exists(last_fix_json):
|
||||
with open(last_fix_json, 'r') as f: data=f.read(); f.close()
|
||||
fix = re.findall('"fix_version"\s*:\s*(\d+)', data)
|
||||
if fix:
|
||||
return '.fix%s' % fix[0]
|
||||
except:
|
||||
pass
|
||||
return ''
|
||||
|
||||
def get_platform(full_version=False):
|
||||
"""
|
||||
Devuelve la información la version de xbmc o kodi sobre el que se ejecuta el plugin
|
||||
|
||||
@param full_version: indica si queremos toda la informacion o no
|
||||
@type full_version: bool
|
||||
@rtype: str o dict
|
||||
@return: Si el paramentro full_version es True se retorna un diccionario con las siguientes claves:
|
||||
'num_version': (float) numero de version en formato XX.X
|
||||
'name_version': (str) nombre clave de cada version
|
||||
'video_db': (str) nombre del archivo que contiene la base de datos de videos
|
||||
'plaform': (str) esta compuesto por "kodi-" o "xbmc-" mas el nombre de la version segun corresponda.
|
||||
Si el parametro full_version es False (por defecto) se retorna el valor de la clave 'plaform' del diccionario anterior.
|
||||
"""
|
||||
|
||||
ret = {}
|
||||
codename = {"10": "dharma", "11": "eden", "12": "frodo",
|
||||
"13": "gotham", "14": "helix", "15": "isengard",
|
||||
"16": "jarvis", "17": "krypton", "18": "leia"}
|
||||
code_db = {'10': 'MyVideos37.db', '11': 'MyVideos60.db', '12': 'MyVideos75.db',
|
||||
'13': 'MyVideos78.db', '14': 'MyVideos90.db', '15': 'MyVideos93.db',
|
||||
'16': 'MyVideos99.db', '17': 'MyVideos107.db', '18': 'MyVideos116.db'}
|
||||
|
||||
num_version = xbmc.getInfoLabel('System.BuildVersion')
|
||||
num_version = re.match("\d+\.\d+", num_version).group(0)
|
||||
ret['name_version'] = codename.get(num_version.split('.')[0], num_version)
|
||||
ret['video_db'] = code_db.get(num_version.split('.')[0], "")
|
||||
ret['num_version'] = float(num_version)
|
||||
if ret['num_version'] < 14:
|
||||
ret['platform'] = "xbmc-" + ret['name_version']
|
||||
else:
|
||||
ret['platform'] = "kodi-" + ret['name_version']
|
||||
|
||||
if full_version:
|
||||
return ret
|
||||
else:
|
||||
return ret['platform']
|
||||
|
||||
|
||||
def is_xbmc():
|
||||
return True
|
||||
|
||||
|
||||
def get_videolibrary_support():
|
||||
return True
|
||||
|
||||
|
||||
def get_system_platform():
|
||||
""" fonction: pour recuperer la platform que xbmc tourne """
|
||||
platform = "unknown"
|
||||
if xbmc.getCondVisibility("system.platform.linux"):
|
||||
platform = "linux"
|
||||
elif xbmc.getCondVisibility("system.platform.windows"):
|
||||
platform = "windows"
|
||||
elif xbmc.getCondVisibility("system.platform.osx"):
|
||||
platform = "osx"
|
||||
return platform
|
||||
|
||||
|
||||
def get_all_settings_addon():
|
||||
# Lee el archivo settings.xml y retorna un diccionario con {id: value}
|
||||
from core import scrapertools
|
||||
|
||||
infile = open(os.path.join(get_data_path(), "settings.xml"), "r")
|
||||
data = infile.read()
|
||||
infile.close()
|
||||
|
||||
ret = {}
|
||||
matches = scrapertools.find_multiple_matches(data, '<setting id="([^"]*)" value="([^"]*)')
|
||||
|
||||
for _id, value in matches:
|
||||
ret[_id] = get_setting(_id)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def open_settings():
|
||||
settings_pre = get_all_settings_addon()
|
||||
__settings__.openSettings()
|
||||
settings_post = get_all_settings_addon()
|
||||
|
||||
# cb_validate_config (util para validar cambios realizados en el cuadro de dialogo)
|
||||
if settings_post.get('adult_aux_intro_password', None):
|
||||
# Hemos accedido a la seccion de Canales para adultos
|
||||
from platformcode import platformtools
|
||||
if 'adult_password' not in settings_pre:
|
||||
adult_password = set_setting('adult_password', '0000')
|
||||
else:
|
||||
adult_password = settings_pre['adult_password']
|
||||
|
||||
if settings_post['adult_aux_intro_password'] == adult_password:
|
||||
# La contraseña de acceso es correcta
|
||||
|
||||
# Cambio de contraseña
|
||||
if settings_post['adult_aux_new_password1']:
|
||||
if settings_post['adult_aux_new_password1'] == settings_post['adult_aux_new_password2']:
|
||||
set_setting('adult_password', settings_post['adult_aux_new_password1'])
|
||||
else:
|
||||
platformtools.dialog_ok(get_localized_string(60305),
|
||||
get_localized_string(60306),
|
||||
get_localized_string(60307))
|
||||
|
||||
else:
|
||||
platformtools.dialog_ok(get_localized_string(60305), get_localized_string(60309),
|
||||
get_localized_string(60310))
|
||||
|
||||
# Deshacer cambios
|
||||
set_setting("adult_mode", settings_pre.get("adult_mode", 0))
|
||||
set_setting("adult_request_password", settings_pre.get("adult_request_password", True))
|
||||
|
||||
# Borramos settings auxiliares
|
||||
set_setting('adult_aux_intro_password', '')
|
||||
set_setting('adult_aux_new_password1', '')
|
||||
set_setting('adult_aux_new_password2', '')
|
||||
|
||||
# si se ha cambiado la ruta de la videoteca llamamos a comprobar directorios para que lo cree y pregunte
|
||||
# automaticamente si configurar la videoteca
|
||||
if settings_pre.get("videolibrarypath", None) != settings_post.get("videolibrarypath", None) or \
|
||||
settings_pre.get("folder_movies", None) != settings_post.get("folder_movies", None) or \
|
||||
settings_pre.get("folder_tvshows", None) != settings_post.get("folder_tvshows", None):
|
||||
verify_directories_created()
|
||||
|
||||
else:
|
||||
# si se ha puesto que se quiere autoconfigurar y se había creado el directorio de la videoteca
|
||||
if not settings_pre.get("videolibrary_kodi", None) and settings_post.get("videolibrary_kodi", None) \
|
||||
and settings_post.get("videolibrary_kodi_flag", None) == 1:
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.ask_set_content(2, silent=True)
|
||||
|
||||
|
||||
def get_setting(name, channel="", server="", default=None):
|
||||
"""
|
||||
Retorna el valor de configuracion del parametro solicitado.
|
||||
|
||||
Devuelve el valor del parametro 'name' en la configuracion global, en la configuracion propia del canal 'channel'
|
||||
o en la del servidor 'server'.
|
||||
|
||||
Los parametros channel y server no deben usarse simultaneamente. Si se especifica el nombre del canal se devolvera
|
||||
el resultado de llamar a channeltools.get_channel_setting(name, channel, default). Si se especifica el nombre del
|
||||
servidor se devolvera el resultado de llamar a servertools.get_channel_setting(name, server, default). Si no se
|
||||
especifica ninguno de los anteriores se devolvera el valor del parametro en la configuracion global si existe o
|
||||
el valor default en caso contrario.
|
||||
|
||||
@param name: nombre del parametro
|
||||
@type name: str
|
||||
@param channel: nombre del canal
|
||||
@type channel: str
|
||||
@param server: nombre del servidor
|
||||
@type server: str
|
||||
@param default: valor devuelto en caso de que no exista el parametro name
|
||||
@type default: any
|
||||
|
||||
@return: El valor del parametro 'name'
|
||||
@rtype: any
|
||||
|
||||
"""
|
||||
|
||||
# Specific channel setting
|
||||
if channel:
|
||||
# logger.info("get_setting reading channel setting '"+name+"' from channel json")
|
||||
from core import channeltools
|
||||
value = channeltools.get_channel_setting(name, channel, default)
|
||||
# logger.info("get_setting -> '"+repr(value)+"'")
|
||||
return value
|
||||
|
||||
# Specific server setting
|
||||
elif server:
|
||||
# logger.info("get_setting reading server setting '"+name+"' from server json")
|
||||
from core import servertools
|
||||
value = servertools.get_server_setting(name, server, default)
|
||||
# logger.info("get_setting -> '"+repr(value)+"'")
|
||||
return value
|
||||
|
||||
# Global setting
|
||||
else:
|
||||
# logger.info("get_setting reading main setting '"+name+"'")
|
||||
value = __settings__.getSetting(name)
|
||||
if not value:
|
||||
return default
|
||||
# Translate Path if start with "special://"
|
||||
if value.startswith("special://") and "videolibrarypath" not in name:
|
||||
value = xbmc.translatePath(value)
|
||||
|
||||
# hack para devolver el tipo correspondiente
|
||||
if value == "true":
|
||||
return True
|
||||
elif value == "false":
|
||||
return False
|
||||
else:
|
||||
# special case return as str
|
||||
if name in ["adult_password", "adult_aux_intro_password", "adult_aux_new_password1",
|
||||
"adult_aux_new_password2"]:
|
||||
return value
|
||||
else:
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
return value
|
||||
|
||||
|
||||
def set_setting(name, value, channel="", server=""):
|
||||
"""
|
||||
Fija el valor de configuracion del parametro indicado.
|
||||
|
||||
Establece 'value' como el valor del parametro 'name' en la configuracion global o en la configuracion propia del
|
||||
canal 'channel'.
|
||||
Devuelve el valor cambiado o None si la asignacion no se ha podido completar.
|
||||
|
||||
Si se especifica el nombre del canal busca en la ruta \addon_data\plugin.video.alfa\settings_channels el
|
||||
archivo channel_data.json y establece el parametro 'name' al valor indicado por 'value'. Si el archivo
|
||||
channel_data.json no existe busca en la carpeta channels el archivo channel.json y crea un archivo channel_data.json
|
||||
antes de modificar el parametro 'name'.
|
||||
Si el parametro 'name' no existe lo añade, con su valor, al archivo correspondiente.
|
||||
|
||||
|
||||
Parametros:
|
||||
name -- nombre del parametro
|
||||
value -- valor del parametro
|
||||
channel [opcional] -- nombre del canal
|
||||
|
||||
Retorna:
|
||||
'value' en caso de que se haya podido fijar el valor y None en caso contrario
|
||||
|
||||
"""
|
||||
if channel:
|
||||
from core import channeltools
|
||||
return channeltools.set_channel_setting(name, value, channel)
|
||||
elif server:
|
||||
from core import servertools
|
||||
return servertools.set_server_setting(name, value, server)
|
||||
else:
|
||||
try:
|
||||
if isinstance(value, bool):
|
||||
if value:
|
||||
value = "true"
|
||||
else:
|
||||
value = "false"
|
||||
|
||||
elif isinstance(value, (int, long)):
|
||||
value = str(value)
|
||||
|
||||
__settings__.setSetting(name, value)
|
||||
|
||||
except Exception, ex:
|
||||
from platformcode import logger
|
||||
logger.error("Error al convertir '%s' no se guarda el valor \n%s" % (name, ex))
|
||||
return None
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def get_localized_string(code):
|
||||
dev = __language__(code)
|
||||
|
||||
try:
|
||||
dev = dev.encode("utf-8")
|
||||
except:
|
||||
pass
|
||||
|
||||
return dev
|
||||
|
||||
def get_localized_category(categ):
|
||||
categories = {'movie': get_localized_string(30122), 'tvshow': get_localized_string(30123),
|
||||
'anime': get_localized_string(30124), 'documentary': get_localized_string(30125),
|
||||
'vos': get_localized_string(30136), 'vosi': get_localized_string(70566), 'adult': get_localized_string(30126),
|
||||
'direct': get_localized_string(30137), 'torrent': get_localized_string(70015)}
|
||||
return categories[categ] if categ in categories else categ
|
||||
|
||||
|
||||
|
||||
def get_videolibrary_config_path():
|
||||
value = get_setting("videolibrarypath")
|
||||
if value == "":
|
||||
verify_directories_created()
|
||||
value = get_setting("videolibrarypath")
|
||||
return value
|
||||
|
||||
|
||||
def get_videolibrary_path():
|
||||
return xbmc.translatePath(get_videolibrary_config_path())
|
||||
|
||||
|
||||
def get_temp_file(filename):
|
||||
return xbmc.translatePath(os.path.join("special://temp/", filename))
|
||||
|
||||
|
||||
def get_runtime_path():
|
||||
return xbmc.translatePath(__settings__.getAddonInfo('Path'))
|
||||
|
||||
|
||||
def get_data_path():
|
||||
dev = xbmc.translatePath(__settings__.getAddonInfo('Profile'))
|
||||
|
||||
# Crea el directorio si no existe
|
||||
if not os.path.exists(dev):
|
||||
os.makedirs(dev)
|
||||
|
||||
return dev
|
||||
|
||||
|
||||
def get_cookie_data():
|
||||
import os
|
||||
ficherocookies = os.path.join(get_data_path(), 'cookies.dat')
|
||||
|
||||
cookiedatafile = open(ficherocookies, 'r')
|
||||
cookiedata = cookiedatafile.read()
|
||||
cookiedatafile.close()
|
||||
|
||||
return cookiedata
|
||||
|
||||
|
||||
# Test if all the required directories are created
|
||||
def verify_directories_created():
|
||||
from platformcode import logger
|
||||
from core import filetools
|
||||
from platformcode import xbmc_videolibrary
|
||||
|
||||
config_paths = [["videolibrarypath", "videolibrary"],
|
||||
["downloadpath", "downloads"],
|
||||
["downloadlistpath", "downloads/list"],
|
||||
["settings_path", "settings_channels"]]
|
||||
|
||||
for path, default in config_paths:
|
||||
saved_path = get_setting(path)
|
||||
|
||||
# videoteca
|
||||
if path == "videolibrarypath":
|
||||
if not saved_path:
|
||||
saved_path = xbmc_videolibrary.search_library_path()
|
||||
if saved_path:
|
||||
set_setting(path, saved_path)
|
||||
|
||||
if not saved_path:
|
||||
saved_path = "special://profile/addon_data/plugin.video." + PLUGIN_NAME + "/" + default
|
||||
set_setting(path, saved_path)
|
||||
|
||||
saved_path = xbmc.translatePath(saved_path)
|
||||
if not filetools.exists(saved_path):
|
||||
logger.debug("Creating %s: %s" % (path, saved_path))
|
||||
filetools.mkdir(saved_path)
|
||||
|
||||
config_paths = [["folder_movies", "CINE"],
|
||||
["folder_tvshows", "SERIES"]]
|
||||
|
||||
for path, default in config_paths:
|
||||
saved_path = get_setting(path)
|
||||
|
||||
if not saved_path:
|
||||
saved_path = default
|
||||
set_setting(path, saved_path)
|
||||
|
||||
content_path = filetools.join(get_videolibrary_path(), saved_path)
|
||||
if not filetools.exists(content_path):
|
||||
logger.debug("Creating %s: %s" % (path, content_path))
|
||||
|
||||
# si se crea el directorio
|
||||
filetools.mkdir(content_path)
|
||||
|
||||
try:
|
||||
from core import scrapertools
|
||||
# Buscamos el archivo addon.xml del skin activo
|
||||
skindir = filetools.join(xbmc.translatePath("special://home"), 'addons', xbmc.getSkinDir(),
|
||||
'addon.xml')
|
||||
if not os.path.isdir(skindir): return # No hace falta mostrar error en el log si no existe la carpeta
|
||||
# Extraemos el nombre de la carpeta de resolución por defecto
|
||||
folder = ""
|
||||
data = filetools.read(skindir)
|
||||
res = scrapertools.find_multiple_matches(data, '(<res .*?>)')
|
||||
for r in res:
|
||||
if 'default="true"' in r:
|
||||
folder = scrapertools.find_single_match(r, 'folder="([^"]+)"')
|
||||
break
|
||||
|
||||
# Comprobamos si existe en el addon y sino es así, la creamos
|
||||
default = filetools.join(get_runtime_path(), 'resources', 'skins', 'Default')
|
||||
if folder and not filetools.exists(filetools.join(default, folder)):
|
||||
filetools.mkdir(filetools.join(default, folder))
|
||||
|
||||
# Copiamos el archivo a dicha carpeta desde la de 720p si éste no existe o si el tamaño es diferente
|
||||
if folder and folder != '720p':
|
||||
for root, folders, files in filetools.walk(filetools.join(default, '720p')):
|
||||
for f in files:
|
||||
if not filetools.exists(filetools.join(default, folder, f)) or \
|
||||
(filetools.getsize(filetools.join(default, folder, f)) !=
|
||||
filetools.getsize(filetools.join(default, '720p', f))):
|
||||
filetools.copy(filetools.join(default, '720p', f),
|
||||
filetools.join(default, folder, f),
|
||||
True)
|
||||
except:
|
||||
import traceback
|
||||
logger.error("Al comprobar o crear la carpeta de resolución")
|
||||
logger.error(traceback.format_exc())
|
||||
229
platformcode/custom_code.py
Normal file
229
platformcode/custom_code.py
Normal file
@@ -0,0 +1,229 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# --------------------------------------------------------------------------------
|
||||
# Updater (kodi)
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import json
|
||||
import traceback
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
|
||||
from platformcode import config, logger, platformtools
|
||||
|
||||
from core import jsontools
|
||||
from core import filetools
|
||||
|
||||
json_data_file_name = 'custom_code.json'
|
||||
|
||||
|
||||
def init():
|
||||
logger.info()
|
||||
|
||||
"""
|
||||
Todo el código añadido al add-on se borra con cada actualización. Esta función permite restaurarlo automáticamente con cada actualización.
|
||||
Esto permite al usuario tener su propio código, bajo su responsabilidad, y restaurarlo al add-on cada vez que se actualiza.
|
||||
|
||||
El mecanismo funciona copiando el contenido de la carpeta-arbol ".\userdata\addon_data\plugin.video.alfa\custom_code\..." sobre
|
||||
las carpetas de código del add-on. No verifica el contenido, solo vuelca(reemplaza) el contenido de "custom_code".
|
||||
|
||||
El usuario almacenará en las subcarpetas de "custom_code" su código actualizado y listo para ser copiado en cualquier momento.
|
||||
Si no se desea que copie algo, simplemente se borra de "custom_code" y ya no se copiará en la próxima actualización.
|
||||
|
||||
Los pasos que sigue esta función, son los siguientes:
|
||||
|
||||
1.- La función se llama desde videolibrary_service.py, desde la función inicial:
|
||||
# Copia Custom code a las carpetas de Alfa desde la zona de Userdata
|
||||
from platformcode import custom_code
|
||||
custom_code.init()
|
||||
|
||||
2.- En el inicio de Kodi, comprueba si existe la carpeta "custom_code" en ".\userdata\addon_data\plugin.video.alfa\".
|
||||
Si no existe, la crea y sale sin más, dando al ususario la posibilidad de copiar sobre esa estructura su código,
|
||||
y que la función la vuelque sobre el add-on en el próximo inicio de Kodi.
|
||||
|
||||
3.- En el siguiente inicio de Kodi, comprueba si existe el custom_code.json en la carpeta root del add-on.
|
||||
Si no existe, lo crea con el número de versión del add-on vacío, para permitir que se copien los archivos en esta pasada.
|
||||
|
||||
4.- Verifica que el número de versión del add-on es diferente de el de custom_code.json. Si es la misma versión,
|
||||
se sale porque ya se realizo la copia anteriormente.
|
||||
Si la versión es distinta, se realiza el volcado de todos los archivos de la carpeta-árbol "custom_code" sobre el add-on.
|
||||
Si la carpeta de destino no existe, dará un error y se cancelará la copia. Se considera que no tienen sentido nuevas carpetas.
|
||||
|
||||
5.- Si la copia ha terminado con éxito, se actualiza el custom_code.json con el número de versión del add-on,
|
||||
para que en inicios sucesivos de Kodi no se realicen las copias, hasta que el add-on cambie de versión.
|
||||
En el número de versión del add-on no se considera el número de fix.
|
||||
|
||||
Tiempos: Copiando 7 archivos de prueba, el proceso ha tardado una décima de segundo.
|
||||
"""
|
||||
|
||||
try:
|
||||
#Verifica si Kodi tiene algún achivo de Base de Datos de Vídeo de versiones anteriores, entonces los borra
|
||||
verify_Kodi_video_DB()
|
||||
|
||||
#QUASAR: Preguntamos si se hacen modificaciones a Quasar
|
||||
if not filetools.exists(os.path.join(config.get_data_path(), "quasar.json")) and not config.get_setting('addon_quasar_update', default=False):
|
||||
question_update_external_addon("quasar")
|
||||
|
||||
#QUASAR: Hacemos las modificaciones a Quasar, si está permitido, y si está instalado
|
||||
if config.get_setting('addon_quasar_update', default=False):
|
||||
if not update_external_addon("quasar"):
|
||||
platformtools.dialog_notification("Actualización Quasar", "Ha fallado. Consulte el log")
|
||||
|
||||
#Existe carpeta "custom_code" ? Si no existe se crea y se sale
|
||||
custom_code_dir = os.path.join(config.get_data_path(), 'custom_code')
|
||||
if os.path.exists(custom_code_dir) == False:
|
||||
create_folder_structure(custom_code_dir)
|
||||
return
|
||||
|
||||
else:
|
||||
#Existe "custom_code.json" ? Si no existe se crea
|
||||
custom_code_json_path = config.get_runtime_path()
|
||||
custom_code_json = os.path.join(custom_code_json_path, 'custom_code.json')
|
||||
if os.path.exists(custom_code_json) == False:
|
||||
create_json(custom_code_json_path)
|
||||
|
||||
#Se verifica si la versión del .json y del add-on son iguales. Si es así se sale. Si no se copia "custom_code" al add-on
|
||||
verify_copy_folders(custom_code_dir, custom_code_json_path)
|
||||
except:
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def create_folder_structure(custom_code_dir):
|
||||
logger.info()
|
||||
|
||||
#Creamos todas las carpetas. La importante es "custom_code". Las otras sirven meramente de guía para evitar errores de nombres...
|
||||
os.mkdir(custom_code_dir)
|
||||
os.mkdir(filetools.join(custom_code_dir, 'channels'))
|
||||
os.mkdir(filetools.join(custom_code_dir, 'core'))
|
||||
os.mkdir(filetools.join(custom_code_dir, 'lib'))
|
||||
os.mkdir(filetools.join(custom_code_dir, 'platformcode'))
|
||||
os.mkdir(filetools.join(custom_code_dir, 'resources'))
|
||||
os.mkdir(filetools.join(custom_code_dir, 'servers'))
|
||||
|
||||
return
|
||||
|
||||
|
||||
def create_json(custom_code_json_path, json_name=json_data_file_name):
|
||||
logger.info()
|
||||
|
||||
#Guardamaos el json con la versión de Alfa vacía, para permitir hacer la primera copia
|
||||
json_data_file = filetools.join(custom_code_json_path, json_name)
|
||||
json_file = open(json_data_file, "a+")
|
||||
json_file.write(json.dumps({"addon_version": ""}))
|
||||
json_file.close()
|
||||
|
||||
return
|
||||
|
||||
|
||||
def verify_copy_folders(custom_code_dir, custom_code_json_path):
|
||||
logger.info()
|
||||
|
||||
#verificamos si es una nueva versión de Alfa instalada o era la existente. Si es la existente, nos vamos sin hacer nada
|
||||
json_data_file = filetools.join(custom_code_json_path, json_data_file_name)
|
||||
json_data = jsontools.load(filetools.read(json_data_file))
|
||||
current_version = config.get_addon_version(with_fix=False)
|
||||
if current_version == json_data['addon_version']:
|
||||
return
|
||||
|
||||
#Ahora copiamos los archivos desde el área de Userdata, Custom_code, sobre las carpetas del add-on
|
||||
for root, folders, files in os.walk(custom_code_dir):
|
||||
for file in files:
|
||||
input_file = filetools.join(root, file)
|
||||
output_file = input_file.replace(custom_code_dir, custom_code_json_path)
|
||||
if filetools.copy(input_file, output_file, silent=True) == False:
|
||||
return
|
||||
|
||||
#Guardamaos el json con la versión actual de Alfa, para no volver a hacer la copia hasta la nueva versión
|
||||
json_data['addon_version'] = current_version
|
||||
filetools.write(json_data_file, jsontools.dump(json_data))
|
||||
|
||||
return
|
||||
|
||||
|
||||
def question_update_external_addon(addon_name):
|
||||
logger.info(addon_name)
|
||||
|
||||
#Verificamos que el addon está instalado
|
||||
stat = False
|
||||
if xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % addon_name):
|
||||
#Si es la primera vez que se pregunta por la actualización del addon externo, recogemos la respuesta,
|
||||
# guardaos un .json en userdat/alfa para no volver a preguntar otra vez, y se actualiza el setting en Alfa.
|
||||
stat = platformtools.dialog_yesno('Actualización de %s' % addon_name.capitalize(), '¿Quiere que actualicemos Quasar para que sea compatible con las últimas versiones de Kodi? (recomendado: SÍ)', '', 'Si actualiza Quasar, reinicie Kodi en un par de minutos')
|
||||
|
||||
#Con la respuesta actualizamos la variable en Alfa settings.xml. Se puede cambiar en Ajustes de Alfa, Otros
|
||||
if stat:
|
||||
config.set_setting('addon_quasar_update', True)
|
||||
else:
|
||||
config.set_setting('addon_quasar_update', False)
|
||||
|
||||
#Creamos un .json en userdata para no volver a preguntar otra vez
|
||||
create_json(config.get_data_path(), "%s.json" % addon_name)
|
||||
|
||||
return stat
|
||||
|
||||
def update_external_addon(addon_name):
|
||||
logger.info(addon_name)
|
||||
|
||||
#Verificamos que el addon está instalado
|
||||
if xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % addon_name):
|
||||
#Path de actuali<aciones de Alfa
|
||||
alfa_addon_updates = filetools.join(config.get_runtime_path(), filetools.join("lib", addon_name))
|
||||
|
||||
#Path de destino en addon externo
|
||||
__settings__ = xbmcaddon.Addon(id="plugin.video." + addon_name)
|
||||
if addon_name.lower() in ['quasar', 'elementum']:
|
||||
addon_path = filetools.join(xbmc.translatePath(__settings__.getAddonInfo('Path')), filetools.join("resources", filetools.join("site-packages", addon_name)))
|
||||
else:
|
||||
addon_path = ''
|
||||
|
||||
#Hay modificaciones en Alfa? Las copiamos al addon
|
||||
if filetools.exists(alfa_addon_updates) and filetools.exists(addon_path):
|
||||
for root, folders, files in os.walk(alfa_addon_updates):
|
||||
for file in files:
|
||||
input_file = filetools.join(root, file)
|
||||
output_file = input_file.replace(alfa_addon_updates, addon_path)
|
||||
if filetools.copy(input_file, output_file, silent=True) == False:
|
||||
logger.error('Error en la copia: Input: %s o Output: %s' % (input_file, output_file))
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
logger.error('Alguna carpeta no existe: Alfa: %s o %s: %s' % (alfa_addon_updates, addon_name, addon_path))
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def verify_Kodi_video_DB():
|
||||
logger.info()
|
||||
import random
|
||||
|
||||
platform = {}
|
||||
path = ''
|
||||
db_files = []
|
||||
|
||||
try:
|
||||
path = filetools.join(xbmc.translatePath("special://masterprofile/"), "Database")
|
||||
if filetools.exists(path):
|
||||
platform = config.get_platform(full_version=True)
|
||||
if platform:
|
||||
db_files = filetools.walk(path)
|
||||
if filetools.exists(filetools.join(path, platform['video_db'])):
|
||||
for root, folders, files in db_files:
|
||||
for file in files:
|
||||
if file != platform['video_db']:
|
||||
if file.startswith('MyVideos'):
|
||||
randnum = str(random.randrange(1, 999999))
|
||||
filetools.rename(filetools.join(path, file), 'OLD_' + randnum +'_' + file)
|
||||
logger.error('BD obsoleta: ' + file)
|
||||
|
||||
else:
|
||||
logger.error('Video_DB: ' + str(platform['video_db']) + ' para versión Kodi ' + str(platform['num_version']) + ' NO EXISTE. Analizar carpeta: ' + str(db_files))
|
||||
else:
|
||||
logger.error('Estructura de get_platform(full_version=True) incorrecta')
|
||||
else:
|
||||
logger.error('Path a Userdata/Database (' + path + ') no encontrado')
|
||||
|
||||
except:
|
||||
logger.error('Platform: ' + str(platform) + ' / Path: ' + str(path) + ' / Files: ' + str(db_files))
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
return
|
||||
351
platformcode/download_and_play.py
Normal file
351
platformcode/download_and_play.py
Normal file
@@ -0,0 +1,351 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# Download and play
|
||||
# ------------------------------------------------------------
|
||||
# Based on code from the Mega add-on (xbmchub.com)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
from core import downloadtools
|
||||
from platformcode import config, logger
|
||||
|
||||
|
||||
# Download a file and start playing while downloading
|
||||
def download_and_play(url, file_name, download_path):
|
||||
# Lanza thread
|
||||
logger.info("Active threads " + str(threading.active_count()))
|
||||
logger.info("" + repr(threading.enumerate()))
|
||||
logger.info("Starting download thread...")
|
||||
download_thread = DownloadThread(url, file_name, download_path)
|
||||
download_thread.start()
|
||||
logger.info("Download thread started")
|
||||
logger.info("Active threads " + str(threading.active_count()))
|
||||
logger.info("" + repr(threading.enumerate()))
|
||||
|
||||
# Espera
|
||||
logger.info("Waiting...")
|
||||
|
||||
while True:
|
||||
cancelled = False
|
||||
dialog = xbmcgui.DialogProgress()
|
||||
dialog.create(config.get_localized_string(60200), config.get_localized_string(60312))
|
||||
dialog.update(0)
|
||||
|
||||
while not cancelled and download_thread.isAlive():
|
||||
dialog.update(download_thread.get_progress(), config.get_localized_string(60313),
|
||||
"Velocidad: " + str(int(download_thread.get_speed() / 1024)) + " KB/s " + str(
|
||||
download_thread.get_actual_size()) + "MB de " + str(
|
||||
download_thread.get_total_size()) + "MB",
|
||||
"Tiempo restante: " + str(downloadtools.sec_to_hms(download_thread.get_remaining_time())))
|
||||
xbmc.sleep(1000)
|
||||
|
||||
if dialog.iscanceled():
|
||||
cancelled = True
|
||||
break
|
||||
|
||||
dialog.close()
|
||||
|
||||
logger.info("End of waiting")
|
||||
|
||||
# Lanza el reproductor
|
||||
player = CustomPlayer()
|
||||
player.set_download_thread(download_thread)
|
||||
player.PlayStream(download_thread.get_file_name())
|
||||
|
||||
# Fin de reproducción
|
||||
logger.info("Fin de reproducción")
|
||||
|
||||
if player.is_stopped():
|
||||
logger.info("Terminado por el usuario")
|
||||
break
|
||||
else:
|
||||
if not download_thread.isAlive():
|
||||
logger.info("La descarga ha terminado")
|
||||
break
|
||||
else:
|
||||
logger.info("Continua la descarga")
|
||||
|
||||
# Cuando el reproductor acaba, si continúa descargando lo para ahora
|
||||
logger.info("Download thread alive=" + str(download_thread.isAlive()))
|
||||
if download_thread.isAlive():
|
||||
logger.info("Killing download thread")
|
||||
download_thread.force_stop()
|
||||
|
||||
|
||||
class CustomPlayer(xbmc.Player):
|
||||
def __init__(self, *args, **kwargs):
|
||||
logger.info()
|
||||
self.actualtime = 0
|
||||
self.totaltime = 0
|
||||
self.stopped = False
|
||||
xbmc.Player.__init__(self)
|
||||
|
||||
def PlayStream(self, url):
|
||||
logger.info("url=" + url)
|
||||
self.play(url)
|
||||
self.actualtime = 0
|
||||
self.url = url
|
||||
while self.isPlaying():
|
||||
self.actualtime = self.getTime()
|
||||
self.totaltime = self.getTotalTime()
|
||||
logger.info("actualtime=" + str(self.actualtime) + " totaltime=" + str(self.totaltime))
|
||||
xbmc.sleep(3000)
|
||||
|
||||
def set_download_thread(self, download_thread):
|
||||
logger.info()
|
||||
self.download_thread = download_thread
|
||||
|
||||
def force_stop_download_thread(self):
|
||||
logger.info()
|
||||
|
||||
if self.download_thread.isAlive():
|
||||
logger.info("Killing download thread")
|
||||
self.download_thread.force_stop()
|
||||
|
||||
# while self.download_thread.isAlive():
|
||||
# xbmc.sleep(1000)
|
||||
|
||||
def onPlayBackStarted(self):
|
||||
logger.info("PLAYBACK STARTED")
|
||||
|
||||
def onPlayBackEnded(self):
|
||||
logger.info("PLAYBACK ENDED")
|
||||
|
||||
def onPlayBackStopped(self):
|
||||
logger.info("PLAYBACK STOPPED")
|
||||
self.stopped = True
|
||||
self.force_stop_download_thread()
|
||||
|
||||
def is_stopped(self):
|
||||
return self.stopped
|
||||
|
||||
|
||||
# Download in background
|
||||
class DownloadThread(threading.Thread):
|
||||
def __init__(self, url, file_name, download_path):
|
||||
logger.info(repr(file))
|
||||
self.url = url
|
||||
self.download_path = download_path
|
||||
self.file_name = os.path.join(download_path, file_name)
|
||||
self.progress = 0
|
||||
self.force_stop_file_name = os.path.join(self.download_path, "force_stop.tmp")
|
||||
self.velocidad = 0
|
||||
self.tiempofalta = 0
|
||||
self.actual_size = 0
|
||||
self.total_size = 0
|
||||
|
||||
if os.path.exists(self.force_stop_file_name):
|
||||
os.remove(self.force_stop_file_name)
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
logger.info("Download starts...")
|
||||
|
||||
if "megacrypter.com" in self.url:
|
||||
self.download_file_megacrypter()
|
||||
else:
|
||||
self.download_file()
|
||||
logger.info("Download ends")
|
||||
|
||||
def force_stop(self):
|
||||
logger.info()
|
||||
force_stop_file = open(self.force_stop_file_name, "w")
|
||||
force_stop_file.write("0")
|
||||
force_stop_file.close()
|
||||
|
||||
def get_progress(self):
|
||||
return self.progress
|
||||
|
||||
def get_file_name(self):
|
||||
return self.file_name
|
||||
|
||||
def get_speed(self):
|
||||
return self.velocidad
|
||||
|
||||
def get_remaining_time(self):
|
||||
return self.tiempofalta
|
||||
|
||||
def get_actual_size(self):
|
||||
return self.actual_size
|
||||
|
||||
def get_total_size(self):
|
||||
return self.total_size
|
||||
|
||||
def download_file_megacrypter(self):
|
||||
logger.info()
|
||||
|
||||
comando = "./megacrypter.sh"
|
||||
logger.info("comando=" + comando)
|
||||
|
||||
oldcwd = os.getcwd()
|
||||
logger.info("oldcwd=" + oldcwd)
|
||||
|
||||
cwd = os.path.join(config.get_runtime_path(), "tools")
|
||||
logger.info("cwd=" + cwd)
|
||||
os.chdir(cwd)
|
||||
logger.info("directory changed to=" + os.getcwd())
|
||||
|
||||
logger.info("destino=" + self.download_path)
|
||||
|
||||
os.system(comando + " '" + self.url + "' \"" + self.download_path + "\"")
|
||||
# p = subprocess.Popen([comando , self.url , self.download_path], cwd=cwd, stdout=subprocess.PIPE , stderr=subprocess.PIPE )
|
||||
# out, err = p.communicate()
|
||||
# logger.info("DownloadThread.download_file out="+out)
|
||||
|
||||
os.chdir(oldcwd)
|
||||
|
||||
def download_file(self):
|
||||
logger.info("Direct download")
|
||||
|
||||
headers = []
|
||||
|
||||
# Se asegura de que el fichero se podrá crear
|
||||
logger.info("nombrefichero=" + self.file_name)
|
||||
self.file_name = xbmc.makeLegalFilename(self.file_name)
|
||||
logger.info("nombrefichero=" + self.file_name)
|
||||
logger.info("url=" + self.url)
|
||||
|
||||
# Crea el fichero
|
||||
existSize = 0
|
||||
f = open(self.file_name, 'wb')
|
||||
grabado = 0
|
||||
|
||||
# Interpreta las cabeceras en una URL como en XBMC
|
||||
if "|" in self.url:
|
||||
additional_headers = self.url.split("|")[1]
|
||||
if "&" in additional_headers:
|
||||
additional_headers = additional_headers.split("&")
|
||||
else:
|
||||
additional_headers = [additional_headers]
|
||||
|
||||
for additional_header in additional_headers:
|
||||
logger.info("additional_header: " + additional_header)
|
||||
name = re.findall("(.*?)=.*?", additional_header)[0]
|
||||
value = urllib.unquote_plus(re.findall(".*?=(.*?)$", additional_header)[0])
|
||||
headers.append([name, value])
|
||||
|
||||
self.url = self.url.split("|")[0]
|
||||
logger.info("url=" + self.url)
|
||||
|
||||
# Timeout del socket a 60 segundos
|
||||
socket.setdefaulttimeout(60)
|
||||
|
||||
# Crea la petición y añade las cabeceras
|
||||
h = urllib2.HTTPHandler(debuglevel=0)
|
||||
request = urllib2.Request(self.url)
|
||||
for header in headers:
|
||||
logger.info("Header=" + header[0] + ": " + header[1])
|
||||
request.add_header(header[0], header[1])
|
||||
|
||||
# Lanza la petición
|
||||
opener = urllib2.build_opener(h)
|
||||
urllib2.install_opener(opener)
|
||||
try:
|
||||
connexion = opener.open(request)
|
||||
except urllib2.HTTPError, e:
|
||||
logger.error("error %d (%s) al abrir la url %s" % (e.code, e.msg, self.url))
|
||||
# print e.code
|
||||
# print e.msg
|
||||
# print e.hdrs
|
||||
# print e.fp
|
||||
f.close()
|
||||
|
||||
# El error 416 es que el rango pedido es mayor que el fichero => es que ya está completo
|
||||
if e.code == 416:
|
||||
return 0
|
||||
else:
|
||||
return -2
|
||||
|
||||
try:
|
||||
totalfichero = int(connexion.headers["Content-Length"])
|
||||
except:
|
||||
totalfichero = 1
|
||||
|
||||
self.total_size = int(float(totalfichero) / float(1024 * 1024))
|
||||
|
||||
logger.info("Content-Length=%s" % totalfichero)
|
||||
blocksize = 100 * 1024
|
||||
|
||||
bloqueleido = connexion.read(blocksize)
|
||||
logger.info("Iniciando descarga del fichero, bloqueleido=%s" % len(bloqueleido))
|
||||
|
||||
maxreintentos = 10
|
||||
|
||||
while len(bloqueleido) > 0:
|
||||
try:
|
||||
if os.path.exists(self.force_stop_file_name):
|
||||
logger.info("Detectado fichero force_stop, se interrumpe la descarga")
|
||||
f.close()
|
||||
|
||||
xbmc.executebuiltin((u'XBMC.Notification("Cancelado", "Descarga en segundo plano cancelada", 300)'))
|
||||
|
||||
return
|
||||
|
||||
# Escribe el bloque leido
|
||||
# try:
|
||||
# import xbmcvfs
|
||||
# f.write( bloqueleido )
|
||||
# except:
|
||||
f.write(bloqueleido)
|
||||
grabado = grabado + len(bloqueleido)
|
||||
logger.info("grabado=%d de %d" % (grabado, totalfichero))
|
||||
percent = int(float(grabado) * 100 / float(totalfichero))
|
||||
self.progress = percent;
|
||||
totalmb = float(float(totalfichero) / (1024 * 1024))
|
||||
descargadosmb = float(float(grabado) / (1024 * 1024))
|
||||
self.actual_size = int(descargadosmb)
|
||||
|
||||
# Lee el siguiente bloque, reintentando para no parar todo al primer timeout
|
||||
reintentos = 0
|
||||
while reintentos <= maxreintentos:
|
||||
try:
|
||||
|
||||
before = time.time()
|
||||
bloqueleido = connexion.read(blocksize)
|
||||
after = time.time()
|
||||
if (after - before) > 0:
|
||||
self.velocidad = len(bloqueleido) / ((after - before))
|
||||
falta = totalfichero - grabado
|
||||
if self.velocidad > 0:
|
||||
self.tiempofalta = falta / self.velocidad
|
||||
else:
|
||||
self.tiempofalta = 0
|
||||
break
|
||||
except:
|
||||
import sys
|
||||
reintentos = reintentos + 1
|
||||
logger.info("ERROR en la descarga del bloque, reintento %d" % reintentos)
|
||||
for line in sys.exc_info():
|
||||
logger.error("%s" % line)
|
||||
|
||||
# Ha habido un error en la descarga
|
||||
if reintentos > maxreintentos:
|
||||
logger.error("ERROR en la descarga del fichero")
|
||||
f.close()
|
||||
|
||||
return -2
|
||||
|
||||
except:
|
||||
import traceback, sys
|
||||
from pprint import pprint
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
lines = traceback.format_exception(exc_type, exc_value, exc_tb)
|
||||
for line in lines:
|
||||
line_splits = line.split("\n")
|
||||
for line_split in line_splits:
|
||||
logger.error(line_split)
|
||||
|
||||
f.close()
|
||||
return -2
|
||||
|
||||
return
|
||||
161
platformcode/keymaptools.py
Normal file
161
platformcode/keymaptools.py
Normal file
@@ -0,0 +1,161 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from threading import Timer
|
||||
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
from channelselector import get_thumb
|
||||
from platformcode import config
|
||||
|
||||
|
||||
class KeyListener(xbmcgui.WindowXMLDialog):
|
||||
TIMEOUT = 10
|
||||
|
||||
def __new__(cls):
|
||||
gui_api = tuple(map(int, xbmcaddon.Addon('xbmc.gui').getAddonInfo('version').split('.')))
|
||||
if gui_api >= (5, 11, 0):
|
||||
filenname = "DialogNotification.xml"
|
||||
else:
|
||||
filenname = "DialogKaiToast.xml"
|
||||
return super(KeyListener, cls).__new__(cls, filenname, "")
|
||||
|
||||
def __init__(self):
|
||||
self.key = None
|
||||
|
||||
def onInit(self):
|
||||
try:
|
||||
self.getControl(401).addLabel("Presiona la tecla a usar para abrir la ventana")
|
||||
self.getControl(402).addLabel("Tienes %s segundos" % self.TIMEOUT)
|
||||
except AttributeError:
|
||||
self.getControl(401).setLabel("Presiona la tecla a usar para abrir la ventana")
|
||||
self.getControl(402).setLabel("Tienes %s segundos" % self.TIMEOUT)
|
||||
|
||||
def onAction(self, action):
|
||||
code = action.getButtonCode()
|
||||
if code == 0:
|
||||
self.key = None
|
||||
else:
|
||||
self.key = str(code)
|
||||
self.close()
|
||||
|
||||
@staticmethod
|
||||
def record_key():
|
||||
dialog = KeyListener()
|
||||
timeout = Timer(KeyListener.TIMEOUT, dialog.close)
|
||||
timeout.start()
|
||||
dialog.doModal()
|
||||
timeout.cancel()
|
||||
key = dialog.key
|
||||
del dialog
|
||||
return key
|
||||
|
||||
|
||||
def set_key():
|
||||
saved_key = config.get_setting("shortcut_key")
|
||||
new_key = KeyListener().record_key()
|
||||
|
||||
if new_key and saved_key != new_key:
|
||||
from core import filetools
|
||||
from platformcode import platformtools
|
||||
import xbmc
|
||||
file_xml = "special://profile/keymaps/alfa.xml"
|
||||
data = '<keymap><global><keyboard><key id="%s">' % new_key + 'runplugin(plugin://' \
|
||||
'plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAia2V5bWFwIiwNCiAgICAib3BlbiI6IHRydWUNCn0=)</key></keyboard></global></keymap>'
|
||||
filetools.write(xbmc.translatePath(file_xml), data)
|
||||
platformtools.dialog_notification("Tecla guardada", "Reinicia Kodi para que se apliquen los cambios")
|
||||
|
||||
config.set_setting("shortcut_key", new_key)
|
||||
# file_idioma = filetools.join(config.get_runtime_path(), 'resources', 'language', 'Spanish', 'strings.xml')
|
||||
# data = filetools.read(file_idioma)
|
||||
# value_xml = scrapertools.find_single_match(data, '<string id="31100">([^<]+)<')
|
||||
# if "tecla" in value_xml:
|
||||
# data = data.replace(value_xml, 'Cambiar tecla/botón para abrir la ventana (Guardada: %s)' % new_key)
|
||||
# elif "key" in value_xml:
|
||||
# data = data.replace(value_xml, 'Change key/button to open the window (Saved: %s)' % new_key)
|
||||
# else:
|
||||
# data = data.replace(value_xml,
|
||||
# 'Cambiamento di chiave/pulsante per aprire la finestra (Salvato: %s)' % new_key)
|
||||
# filetools.write(file_idioma, data)
|
||||
|
||||
return
|
||||
|
||||
|
||||
MAIN_MENU = {
|
||||
"news": {"label": "Novedades", "icon": get_thumb("news.png"), "order": 0},
|
||||
"channels": {"label": "Canales", "icon": get_thumb("channels.png"), "order": 1},
|
||||
"search": {"label": "Buscador", "icon": get_thumb("search.png"), "order": 2},
|
||||
"favorites": {"label": "Favoritos", "icon": get_thumb("favorites.png"), "order": 3},
|
||||
"videolibrary": {"label": "Videoteca", "icon": get_thumb("videolibrary.png"), "order": 4},
|
||||
"downloads": {"label": "Descargas", "icon": get_thumb("downloads.png"), "order": 5},
|
||||
"settings": {"label": "Configuración", "icon": get_thumb("setting_0.png"), "order": 6}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
for menuentry in MAIN_MENU.keys():
|
||||
item = xbmcgui.ListItem(MAIN_MENU[menuentry]["label"])
|
||||
item.setProperty("thumb", str(MAIN_MENU[menuentry]["icon"]))
|
||||
item.setProperty("identifier", str(menuentry))
|
||||
item.setProperty("order", str(MAIN_MENU[menuentry]["order"]))
|
||||
self.items.append(item)
|
||||
|
||||
self.items.sort(key=lambda it: it.getProperty("order"))
|
||||
self.getControl(32500).addItems(self.items)
|
||||
self.setFocusId(32500)
|
||||
|
||||
def onClick(self, control_id):
|
||||
if control_id == 32500:
|
||||
identifier = self.getControl(32500).getSelectedItem().getProperty("identifier")
|
||||
if identifier == "news":
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
xbmc.executebuiltin(
|
||||
'ActivateWindow(10025, "plugin://plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAibWFpbmxpc3QiLCANCiAgICAiY2hhbm5lbCI6ICJuZXdzIg0KfQ==")')
|
||||
elif identifier == "channels":
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
xbmc.executebuiltin(
|
||||
'ActivateWindow(10025, "plugin://plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAiZ2V0Y2hhbm5lbHR5cGVzIiwgDQogICAgImNoYW5uZWwiOiAiY2hhbm5lbHNlbGVjdG9yIg0KfQ==")')
|
||||
elif identifier == "search":
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
xbmc.executebuiltin(
|
||||
'ActivateWindow(10025, "plugin://plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAibWFpbmxpc3QiLCANCiAgICAiY2hhbm5lbCI6ICJzZWFyY2giDQp9")')
|
||||
elif identifier == "favorites":
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
xbmc.executebuiltin(
|
||||
'ActivateWindow(10025, "plugin://plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAibWFpbmxpc3QiLCANCiAgICAiY2hhbm5lbCI6ICJmYXZvcml0ZXMiDQp9")')
|
||||
elif identifier == "videolibrary":
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
xbmc.executebuiltin(
|
||||
'ActivateWindow(10025, "plugin://plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAibWFpbmxpc3QiLCANCiAgICAiY2hhbm5lbCI6ICJ2aWRlb2xpYnJhcnkiDQp9")')
|
||||
elif identifier == "downloads":
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
xbmc.executebuiltin(
|
||||
'ActivateWindow(10025, "plugin://plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAibWFpbmxpc3QiLCANCiAgICAiY2hhbm5lbCI6ICJkb3dubG9hZHMiDQp9")')
|
||||
elif identifier == "settings":
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
xbmc.executebuiltin(
|
||||
'ActivateWindow(10025, "plugin://plugin.video.alfa/?ew0KICAgICJhY3Rpb24iOiAibWFpbmxpc3QiLCANCiAgICAiY2hhbm5lbCI6ICJzZXR0aW5nIg0KfQ==")')
|
||||
|
||||
|
||||
def onAction(self, action):
|
||||
# exit
|
||||
if action.getId() in [xbmcgui.ACTION_PREVIOUS_MENU, xbmcgui.ACTION_NAV_BACK]:
|
||||
# main.close()
|
||||
xbmc.executebuiltin('Dialog.Close(all,true)')
|
||||
|
||||
if action.getId() == xbmcgui.ACTION_CONTEXT_MENU:
|
||||
config.open_settings()
|
||||
|
||||
|
||||
def open_shortcut_menu():
|
||||
main = Main('ShortCutMenu.xml', config.get_runtime_path())
|
||||
main.doModal()
|
||||
del main
|
||||
489
platformcode/launcher.py
Normal file
489
platformcode/launcher.py
Normal file
@@ -0,0 +1,489 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# XBMC Launcher (xbmc / kodi)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import urllib2
|
||||
import time
|
||||
|
||||
from core import channeltools
|
||||
from core import scrapertools
|
||||
from core import servertools
|
||||
from core import videolibrarytools
|
||||
from core import trakt_tools
|
||||
from core.item import Item
|
||||
from platformcode import config, logger
|
||||
from platformcode import platformtools
|
||||
from platformcode.logger import WebErrorException
|
||||
|
||||
|
||||
def start():
|
||||
""" Primera funcion que se ejecuta al entrar en el plugin.
|
||||
Dentro de esta funcion deberian ir todas las llamadas a las
|
||||
funciones que deseamos que se ejecuten nada mas abrir el plugin.
|
||||
"""
|
||||
logger.info()
|
||||
#config.set_setting('show_once', True)
|
||||
# Test if all the required directories are created
|
||||
config.verify_directories_created()
|
||||
|
||||
|
||||
def run(item=None):
|
||||
logger.info()
|
||||
|
||||
if not item:
|
||||
# Extract item from sys.argv
|
||||
if sys.argv[2]:
|
||||
item = Item().fromurl(sys.argv[2])
|
||||
|
||||
# If no item, this is mainlist
|
||||
else:
|
||||
if config.get_setting("start_page"):
|
||||
|
||||
if not config.get_setting("custom_start"):
|
||||
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")]
|
||||
item = Item(channel="news", action="novedades", extra=category, mode = 'silent')
|
||||
else:
|
||||
from channels import side_menu
|
||||
item= Item()
|
||||
item = side_menu.check_user_home(item)
|
||||
item.start = True;
|
||||
else:
|
||||
item = Item(channel="channelselector", action="getmainlist", viewmode="movie")
|
||||
if not config.get_setting('show_once'):
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.ask_set_content(1)
|
||||
config.set_setting('show_once', True)
|
||||
|
||||
logger.info(item.tostring())
|
||||
|
||||
try:
|
||||
# If item has no action, stops here
|
||||
if item.action == "":
|
||||
logger.info("Item sin accion")
|
||||
return
|
||||
|
||||
# Action for main menu in channelselector
|
||||
elif item.action == "getmainlist":
|
||||
import channelselector
|
||||
|
||||
itemlist = channelselector.getmainlist()
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# Action for channel types on channelselector: movies, series, etc.
|
||||
elif item.action == "getchanneltypes":
|
||||
import channelselector
|
||||
itemlist = channelselector.getchanneltypes()
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# Action for channel listing on channelselector
|
||||
elif item.action == "filterchannels":
|
||||
import channelselector
|
||||
itemlist = channelselector.filterchannels(item.channel_type)
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# Special action for playing a video from the library
|
||||
elif item.action == "play_from_library":
|
||||
play_from_library(item)
|
||||
return
|
||||
|
||||
elif item.action == "keymap":
|
||||
from platformcode import keymaptools
|
||||
if item.open:
|
||||
return keymaptools.open_shortcut_menu()
|
||||
else:
|
||||
return keymaptools.set_key()
|
||||
|
||||
elif item.action == "script":
|
||||
from core import tmdb
|
||||
if tmdb.drop_bd():
|
||||
platformtools.dialog_notification(config.get_localized_string(20000), config.get_localized_string(60011), time=2000, sound=False)
|
||||
|
||||
# Action in certain channel specified in "action" and "channel" parameters
|
||||
else:
|
||||
|
||||
# Entry point for a channel is the "mainlist" action, so here we check parental control
|
||||
if item.action == "mainlist":
|
||||
|
||||
|
||||
# Parental control
|
||||
# If it is an adult channel, and user has configured pin, asks for it
|
||||
if channeltools.is_adult(item.channel) and config.get_setting("adult_request_password"):
|
||||
tecleado = platformtools.dialog_input("", config.get_localized_string(60334), True)
|
||||
if tecleado is None or tecleado != config.get_setting("adult_password"):
|
||||
return
|
||||
|
||||
# # Actualiza el canal individual
|
||||
# if (item.action == "mainlist" and item.channel != "channelselector" and
|
||||
# config.get_setting("check_for_channel_updates") == True):
|
||||
# from core import updater
|
||||
# updater.update_channel(item.channel)
|
||||
|
||||
# Checks if channel exists
|
||||
channel_file = os.path.join(config.get_runtime_path(),
|
||||
'channels', item.channel + ".py")
|
||||
logger.info("channel_file=%s" % channel_file)
|
||||
|
||||
channel = None
|
||||
|
||||
if os.path.exists(channel_file):
|
||||
try:
|
||||
channel = __import__('channels.%s' % item.channel, None,
|
||||
None, ["channels.%s" % item.channel])
|
||||
except ImportError:
|
||||
exec "import channels." + item.channel + " as channel"
|
||||
|
||||
logger.info("Running channel %s | %s" % (channel.__name__, channel.__file__))
|
||||
|
||||
# Special play action
|
||||
if item.action == "play":
|
||||
#define la info para trakt
|
||||
try:
|
||||
trakt_tools.set_trakt_info(item)
|
||||
except:
|
||||
pass
|
||||
logger.info("item.action=%s" % item.action.upper())
|
||||
# logger.debug("item_toPlay: " + "\n" + item.tostring('\n'))
|
||||
|
||||
# First checks if channel has a "play" function
|
||||
if hasattr(channel, 'play'):
|
||||
logger.info("Executing channel 'play' method")
|
||||
itemlist = channel.play(item)
|
||||
b_favourite = item.isFavourite
|
||||
# Play should return a list of playable URLS
|
||||
if len(itemlist) > 0 and isinstance(itemlist[0], Item):
|
||||
item = itemlist[0]
|
||||
if b_favourite:
|
||||
item.isFavourite = True
|
||||
platformtools.play_video(item)
|
||||
|
||||
# Permitir varias calidades desde play en el canal
|
||||
elif len(itemlist) > 0 and isinstance(itemlist[0], list):
|
||||
item.video_urls = itemlist
|
||||
platformtools.play_video(item)
|
||||
|
||||
# If not, shows user an error message
|
||||
else:
|
||||
platformtools.dialog_ok(config.get_localized_string(20000), config.get_localized_string(60339))
|
||||
|
||||
# If player don't have a "play" function, not uses the standard play from platformtools
|
||||
else:
|
||||
logger.info("Executing core 'play' method")
|
||||
platformtools.play_video(item)
|
||||
|
||||
# Special action for findvideos, where the plugin looks for known urls
|
||||
elif item.action == "findvideos":
|
||||
|
||||
# First checks if channel has a "findvideos" function
|
||||
if hasattr(channel, 'findvideos'):
|
||||
itemlist = getattr(channel, item.action)(item)
|
||||
itemlist = servertools.filter_servers(itemlist)
|
||||
|
||||
# If not, uses the generic findvideos function
|
||||
else:
|
||||
logger.info("No channel 'findvideos' method, "
|
||||
"executing core method")
|
||||
itemlist = servertools.find_video_items(item)
|
||||
|
||||
if config.get_setting("max_links", "videolibrary") != 0:
|
||||
itemlist = limit_itemlist(itemlist)
|
||||
|
||||
from platformcode import subtitletools
|
||||
subtitletools.saveSubtitleName(item)
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# Special action for adding a movie to the library
|
||||
elif item.action == "add_pelicula_to_library":
|
||||
videolibrarytools.add_movie(item)
|
||||
|
||||
# Special action for adding a serie to the library
|
||||
elif item.action == "add_serie_to_library":
|
||||
videolibrarytools.add_tvshow(item, channel)
|
||||
|
||||
# Special action for downloading all episodes from a serie
|
||||
elif item.action == "download_all_episodes":
|
||||
from channels import downloads
|
||||
item.action = item.extra
|
||||
del item.extra
|
||||
downloads.save_download(item)
|
||||
|
||||
# Special action for searching, first asks for the words then call the "search" function
|
||||
elif item.action == "search":
|
||||
logger.info("item.action=%s" % item.action.upper())
|
||||
|
||||
last_search = ""
|
||||
last_search_active = config.get_setting("last_search", "search")
|
||||
if last_search_active:
|
||||
try:
|
||||
current_saved_searches_list = list(config.get_setting("saved_searches_list", "search"))
|
||||
last_search = current_saved_searches_list[0]
|
||||
except:
|
||||
pass
|
||||
|
||||
tecleado = platformtools.dialog_input(last_search)
|
||||
if tecleado is not None:
|
||||
if last_search_active and not tecleado.startswith("http"):
|
||||
from channels import search
|
||||
search.save_search(tecleado)
|
||||
|
||||
itemlist = channel.search(item, tecleado)
|
||||
else:
|
||||
return
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# For all other actions
|
||||
else:
|
||||
logger.info("Executing channel '%s' method" % item.action)
|
||||
itemlist = getattr(channel, item.action)(item)
|
||||
if config.get_setting('trakt_sync'):
|
||||
token_auth = config.get_setting("token_trakt", "trakt")
|
||||
if not token_auth:
|
||||
trakt_tools.auth_trakt()
|
||||
else:
|
||||
import xbmc
|
||||
if not xbmc.getCondVisibility('System.HasAddon(script.trakt)') and config.get_setting(
|
||||
'install_trakt'):
|
||||
trakt_tools.ask_install_script()
|
||||
itemlist = trakt_tools.trakt_check(itemlist)
|
||||
else:
|
||||
config.set_setting('install_trakt', True)
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
except urllib2.URLError, e:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Grab inner and third party errors
|
||||
if hasattr(e, 'reason'):
|
||||
logger.error("Razon del error, codigo: %s | Razon: %s" % (str(e.reason[0]), str(e.reason[1])))
|
||||
texto = config.get_localized_string(30050) # "No se puede conectar con el sitio web"
|
||||
platformtools.dialog_ok("alfa", texto)
|
||||
|
||||
# Grab server response errors
|
||||
elif hasattr(e, 'code'):
|
||||
logger.error("Codigo de error HTTP : %d" % e.code)
|
||||
# "El sitio web no funciona correctamente (error http %d)"
|
||||
platformtools.dialog_ok("alfa", config.get_localized_string(30051) % e.code)
|
||||
except WebErrorException, e:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
patron = 'File "' + os.path.join(config.get_runtime_path(), "channels", "").replace("\\",
|
||||
"\\\\") + '([^.]+)\.py"'
|
||||
canal = scrapertools.find_single_match(traceback.format_exc(), patron)
|
||||
|
||||
platformtools.dialog_ok(
|
||||
config.get_localized_string(59985) + canal,
|
||||
config.get_localized_string(60013) %(e))
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
patron = 'File "' + os.path.join(config.get_runtime_path(), "channels", "").replace("\\",
|
||||
"\\\\") + '([^.]+)\.py"'
|
||||
canal = scrapertools.find_single_match(traceback.format_exc(), patron)
|
||||
|
||||
try:
|
||||
import xbmc
|
||||
if config.get_platform(True)['num_version'] < 14:
|
||||
log_name = "xbmc.log"
|
||||
else:
|
||||
log_name = "kodi.log"
|
||||
log_message = config.get_localized_string(50004) + xbmc.translatePath("special://logpath") + log_name
|
||||
except:
|
||||
log_message = ""
|
||||
|
||||
if canal:
|
||||
platformtools.dialog_ok(
|
||||
config.get_localized_string(60087) %canal,
|
||||
config.get_localized_string(60014),
|
||||
log_message)
|
||||
else:
|
||||
platformtools.dialog_ok(
|
||||
config.get_localized_string(60038),
|
||||
config.get_localized_string(60015),
|
||||
log_message)
|
||||
|
||||
|
||||
|
||||
def reorder_itemlist(itemlist):
|
||||
logger.info()
|
||||
# logger.debug("Inlet itemlist size: %i" % len(itemlist))
|
||||
|
||||
new_list = []
|
||||
mod_list = []
|
||||
not_mod_list = []
|
||||
|
||||
modified = 0
|
||||
not_modified = 0
|
||||
|
||||
to_change = [[config.get_localized_string(60335), '[V]'],
|
||||
[config.get_localized_string(60336), '[D]']]
|
||||
|
||||
for item in itemlist:
|
||||
old_title = unicode(item.title, "utf8").lower().encode("utf8")
|
||||
for before, after in to_change:
|
||||
if before in item.title:
|
||||
item.title = item.title.replace(before, after)
|
||||
break
|
||||
|
||||
new_title = unicode(item.title, "utf8").lower().encode("utf8")
|
||||
if old_title != new_title:
|
||||
mod_list.append(item)
|
||||
modified += 1
|
||||
else:
|
||||
not_mod_list.append(item)
|
||||
not_modified += 1
|
||||
|
||||
# logger.debug("OLD: %s | NEW: %s" % (old_title, new_title))
|
||||
|
||||
new_list.extend(mod_list)
|
||||
new_list.extend(not_mod_list)
|
||||
|
||||
logger.info("Titulos modificados:%i | No modificados:%i" % (modified, not_modified))
|
||||
|
||||
if len(new_list) == 0:
|
||||
new_list = itemlist
|
||||
|
||||
# logger.debug("Outlet itemlist size: %i" % len(new_list))
|
||||
return new_list
|
||||
|
||||
|
||||
def limit_itemlist(itemlist):
|
||||
logger.info()
|
||||
# logger.debug("Inlet itemlist size: %i" % len(itemlist))
|
||||
|
||||
try:
|
||||
opt = config.get_setting("max_links", "videolibrary")
|
||||
if opt == 0:
|
||||
new_list = itemlist
|
||||
else:
|
||||
i_max = 30 * opt
|
||||
new_list = itemlist[:i_max]
|
||||
|
||||
# logger.debug("Outlet itemlist size: %i" % len(new_list))
|
||||
return new_list
|
||||
except:
|
||||
return itemlist
|
||||
|
||||
|
||||
def play_from_library(item):
|
||||
"""
|
||||
Los .strm al reproducirlos desde kodi, este espera que sea un archivo "reproducible" asi que no puede contener
|
||||
más items, como mucho se puede colocar un dialogo de seleccion.
|
||||
Esto lo solucionamos "engañando a kodi" y haciendole creer que se ha reproducido algo, asi despues mediante
|
||||
"Container.Update()" cargamos el strm como si un item desde dentro del addon se tratara, quitando todas
|
||||
las limitaciones y permitiendo reproducir mediante la funcion general sin tener que crear nuevos métodos para
|
||||
la videoteca.
|
||||
@type item: item
|
||||
@param item: elemento con información
|
||||
"""
|
||||
logger.info()
|
||||
#logger.debug("item: \n" + item.tostring('\n'))
|
||||
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
import xbmc
|
||||
from time import sleep
|
||||
|
||||
# Intentamos reproducir una imagen (esto no hace nada y ademas no da error)
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True,
|
||||
xbmcgui.ListItem(
|
||||
path=os.path.join(config.get_runtime_path(), "resources", "subtitle.mp4")))
|
||||
|
||||
# Por si acaso la imagen hiciera (en futuras versiones) le damos a stop para detener la reproduccion
|
||||
sleep(0.5) ### Si no se pone esto se bloquea Kodi
|
||||
xbmc.Player().stop()
|
||||
|
||||
# modificamos el action (actualmente la videoteca necesita "findvideos" ya que es donde se buscan las fuentes
|
||||
item.action = "findvideos"
|
||||
|
||||
window_type = config.get_setting("window_type", "videolibrary")
|
||||
|
||||
# y volvemos a lanzar kodi
|
||||
if xbmc.getCondVisibility('Window.IsMedia') and not window_type == 1:
|
||||
# Ventana convencional
|
||||
xbmc.executebuiltin("Container.Update(" + sys.argv[0] + "?" + item.tourl() + ")")
|
||||
|
||||
else:
|
||||
# Ventana emergente
|
||||
from channels import videolibrary
|
||||
p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(20000), config.get_localized_string(70004))
|
||||
p_dialog.update(0, '')
|
||||
|
||||
itemlist = videolibrary.findvideos(item)
|
||||
|
||||
|
||||
while platformtools.is_playing():
|
||||
# Ventana convencional
|
||||
sleep(5)
|
||||
p_dialog.update(50, '')
|
||||
|
||||
'''# Se filtran los enlaces segun la lista negra
|
||||
if config.get_setting('filter_servers', "servers"):
|
||||
itemlist = servertools.filter_servers(itemlist)'''
|
||||
|
||||
# Se limita la cantidad de enlaces a mostrar
|
||||
if config.get_setting("max_links", "videolibrary") != 0:
|
||||
itemlist = limit_itemlist(itemlist)
|
||||
|
||||
# Se "limpia" ligeramente la lista de enlaces
|
||||
if config.get_setting("replace_VD", "videolibrary") == 1:
|
||||
itemlist = reorder_itemlist(itemlist)
|
||||
|
||||
|
||||
import time
|
||||
p_dialog.update(100, '')
|
||||
time.sleep(0.5)
|
||||
p_dialog.close()
|
||||
|
||||
|
||||
if len(itemlist) > 0:
|
||||
while not xbmc.Monitor().abortRequested():
|
||||
# El usuario elige el mirror
|
||||
opciones = []
|
||||
for item in itemlist:
|
||||
opciones.append(item.title)
|
||||
|
||||
# Se abre la ventana de seleccion
|
||||
if (item.contentSerieName != "" and
|
||||
item.contentSeason != "" and
|
||||
item.contentEpisodeNumber != ""):
|
||||
cabecera = ("%s - %sx%s -- %s" %
|
||||
(item.contentSerieName,
|
||||
item.contentSeason,
|
||||
item.contentEpisodeNumber,
|
||||
config.get_localized_string(30163)))
|
||||
else:
|
||||
cabecera = config.get_localized_string(30163)
|
||||
|
||||
seleccion = platformtools.dialog_select(cabecera, opciones)
|
||||
|
||||
if seleccion == -1:
|
||||
return
|
||||
else:
|
||||
item = videolibrary.play(itemlist[seleccion])[0]
|
||||
platformtools.play_video(item)
|
||||
|
||||
from channels import autoplay
|
||||
if (platformtools.is_playing() and item.action) or item.server == 'torrent' or autoplay.is_active(item.contentChannel):
|
||||
break
|
||||
86
platformcode/logger.py
Normal file
86
platformcode/logger.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# --------------------------------------------------------------------------------
|
||||
# Logger (kodi)
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
import inspect
|
||||
|
||||
import xbmc
|
||||
from platformcode import config
|
||||
|
||||
loggeractive = (config.get_setting("debug") == True)
|
||||
|
||||
|
||||
def log_enable(active):
|
||||
global loggeractive
|
||||
loggeractive = active
|
||||
|
||||
|
||||
def encode_log(message=""):
|
||||
# Unicode to utf8
|
||||
if type(message) == unicode:
|
||||
message = message.encode("utf8")
|
||||
|
||||
# All encodings to utf8
|
||||
elif type(message) == str:
|
||||
message = unicode(message, "utf8", errors="replace").encode("utf8")
|
||||
|
||||
# Objects to string
|
||||
else:
|
||||
message = str(message)
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def get_caller(message=None):
|
||||
module = inspect.getmodule(inspect.currentframe().f_back.f_back)
|
||||
|
||||
if module == None:
|
||||
module = "None"
|
||||
else:
|
||||
module = module.__name__
|
||||
|
||||
function = inspect.currentframe().f_back.f_back.f_code.co_name
|
||||
|
||||
if module == "__main__":
|
||||
module = "alfa"
|
||||
else:
|
||||
module = "alfa." + module
|
||||
if message:
|
||||
if module not in message:
|
||||
if function == "<module>":
|
||||
return module + " " + message
|
||||
else:
|
||||
return module + " [" + function + "] " + message
|
||||
else:
|
||||
return message
|
||||
else:
|
||||
if function == "<module>":
|
||||
return module
|
||||
else:
|
||||
return module + "." + function
|
||||
|
||||
|
||||
def info(texto=""):
|
||||
if loggeractive:
|
||||
xbmc.log(get_caller(encode_log(texto)), xbmc.LOGNOTICE)
|
||||
|
||||
|
||||
def debug(texto=""):
|
||||
if loggeractive:
|
||||
texto = " [" + get_caller() + "] " + encode_log(texto)
|
||||
|
||||
xbmc.log("######## DEBUG #########", xbmc.LOGNOTICE)
|
||||
xbmc.log(texto, xbmc.LOGNOTICE)
|
||||
|
||||
|
||||
def error(texto=""):
|
||||
texto = " [" + get_caller() + "] " + encode_log(texto)
|
||||
|
||||
xbmc.log("######## ERROR #########", xbmc.LOGERROR)
|
||||
xbmc.log(texto, xbmc.LOGERROR)
|
||||
|
||||
|
||||
class WebErrorException(Exception):
|
||||
def __init__(self, *args, **kwargs):
|
||||
Exception.__init__(self, *args, **kwargs)
|
||||
738
platformcode/mct.py
Normal file
738
platformcode/mct.py
Normal file
@@ -0,0 +1,738 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# MCT - Mini Cliente Torrent
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
try:
|
||||
from python_libtorrent import get_libtorrent, get_platform
|
||||
|
||||
lt = get_libtorrent()
|
||||
except Exception, e:
|
||||
import libtorrent as lt
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
from platformcode import config
|
||||
from core import httptools
|
||||
from core import scrapertools
|
||||
from core import filetools
|
||||
|
||||
|
||||
def play(url, xlistitem={}, is_view=None, subtitle="", item=None):
|
||||
allocate = True
|
||||
try:
|
||||
import platform
|
||||
xbmc.log("XXX KODI XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
xbmc.log("OS platform: %s %s" % (platform.system(), platform.release()))
|
||||
xbmc.log("xbmc/kodi version: %s" % xbmc.getInfoLabel("System.BuildVersion"))
|
||||
xbmc_version = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
||||
xbmc.log("xbmc/kodi version number: %s" % xbmc_version)
|
||||
xbmc.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX KODI XXXX")
|
||||
|
||||
_platform = get_platform()
|
||||
if str(_platform['system']) in ["android_armv7", "linux_armv6", "linux_armv7"]:
|
||||
allocate = False
|
||||
# -- log ------------------------------------------------
|
||||
xbmc.log("XXX platform XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
xbmc.log("_platform['system']: %s" % _platform['system'])
|
||||
xbmc.log("allocate: %s" % allocate)
|
||||
xbmc.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX platform XXXX")
|
||||
# -- ----------------------------------------------------
|
||||
except:
|
||||
pass
|
||||
|
||||
DOWNLOAD_PATH = config.get_setting("downloadpath")
|
||||
|
||||
# -- adfly: ------------------------------------
|
||||
if url.startswith("http://adf.ly/"):
|
||||
try:
|
||||
data = httptools.downloadpage(url).data
|
||||
url = decode_adfly(data)
|
||||
except:
|
||||
ddd = xbmcgui.Dialog()
|
||||
ddd.ok("alfa-MCT: Sin soporte adf.ly",
|
||||
"El script no tiene soporte para el acortador de urls adf.ly.", "", "url: " + url)
|
||||
return
|
||||
|
||||
# -- Necesario para algunas webs ----------------------------
|
||||
if not url.endswith(".torrent") and not url.startswith("magnet"):
|
||||
t_file = scrapertools.get_header_from_response(url, header_to_get="location")
|
||||
if len(t_file) > 0:
|
||||
url = t_file
|
||||
t_file = scrapertools.get_header_from_response(url, header_to_get="location")
|
||||
if len(t_file) > 0:
|
||||
url = t_file
|
||||
|
||||
# -- Crear dos carpetas en descargas para los archivos ------
|
||||
save_path_videos = os.path.join(DOWNLOAD_PATH, "torrent-videos")
|
||||
save_path_torrents = os.path.join(DOWNLOAD_PATH, "torrent-torrents")
|
||||
if not os.path.exists(save_path_torrents): os.mkdir(save_path_torrents)
|
||||
|
||||
# -- Usar - archivo torrent desde web, magnet o HD ---------
|
||||
if not os.path.isfile(url) and not url.startswith("magnet"):
|
||||
# -- http - crear archivo torrent -----------------------
|
||||
data = url_get(url)
|
||||
# -- El nombre del torrent será el que contiene en los --
|
||||
# -- datos. -
|
||||
re_name = urllib.unquote(scrapertools.scrapertools.find_single_match(data, ':name\d+:(.*?)\d+:'))
|
||||
torrent_file = filetools.join(save_path_torrents, filetools.encode(re_name + '.torrent'))
|
||||
|
||||
f = open(torrent_file, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
elif os.path.isfile(url):
|
||||
# -- file - para usar torrens desde el HD ---------------
|
||||
torrent_file = url
|
||||
else:
|
||||
# -- magnet ---------------------------------------------
|
||||
torrent_file = url
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# -- MCT - MiniClienteTorrent -------------------------------
|
||||
ses = lt.session()
|
||||
|
||||
# -- log ----------------------------------------------------
|
||||
xbmc.log("### Init session ########")
|
||||
xbmc.log(lt.version)
|
||||
xbmc.log("#########################")
|
||||
# -- --------------------------------------------------------
|
||||
|
||||
ses.add_dht_router("router.bittorrent.com", 6881)
|
||||
ses.add_dht_router("router.utorrent.com", 6881)
|
||||
ses.add_dht_router("dht.transmissionbt.com", 6881)
|
||||
|
||||
trackers = [
|
||||
"udp://tracker.openbittorrent.com:80/announce",
|
||||
"http://tracker.torrentbay.to:6969/announce",
|
||||
"http://tracker.pow7.com/announce",
|
||||
"udp://tracker.ccc.de:80/announce",
|
||||
"udp://open.demonii.com:1337",
|
||||
|
||||
"http://9.rarbg.com:2710/announce",
|
||||
"http://bt.careland.com.cn:6969/announce",
|
||||
"http://explodie.org:6969/announce",
|
||||
"http://mgtracker.org:2710/announce",
|
||||
"http://tracker.best-torrents.net:6969/announce",
|
||||
"http://tracker.tfile.me/announce",
|
||||
"http://tracker1.wasabii.com.tw:6969/announce",
|
||||
"udp://9.rarbg.com:2710/announce",
|
||||
"udp://9.rarbg.me:2710/announce",
|
||||
"udp://coppersurfer.tk:6969/announce",
|
||||
|
||||
"http://www.spanishtracker.com:2710/announce",
|
||||
"http://www.todotorrents.com:2710/announce",
|
||||
]
|
||||
|
||||
video_file = ""
|
||||
# -- magnet2torrent -----------------------------------------
|
||||
if torrent_file.startswith("magnet"):
|
||||
try:
|
||||
import zlib
|
||||
btih = hex(zlib.crc32(
|
||||
scrapertools.scrapertools.find_single_match(torrent_file, 'magnet:\?xt=urn:(?:[A-z0-9:]+|)([A-z0-9]{32})')) & 0xffffffff)
|
||||
files = [f for f in os.listdir(save_path_torrents) if os.path.isfile(os.path.join(save_path_torrents, f))]
|
||||
for file in files:
|
||||
if btih in os.path.basename(file):
|
||||
torrent_file = os.path.join(save_path_torrents, file)
|
||||
except:
|
||||
pass
|
||||
|
||||
if torrent_file.startswith("magnet"):
|
||||
try:
|
||||
tempdir = tempfile.mkdtemp()
|
||||
except IOError:
|
||||
tempdir = os.path.join(save_path_torrents, "temp")
|
||||
if not os.path.exists(tempdir):
|
||||
os.mkdir(tempdir)
|
||||
params = {
|
||||
'save_path': tempdir,
|
||||
'trackers': trackers,
|
||||
'storage_mode': lt.storage_mode_t.storage_mode_allocate,
|
||||
'paused': False,
|
||||
'auto_managed': True,
|
||||
'duplicate_is_error': True
|
||||
}
|
||||
h = lt.add_magnet_uri(ses, torrent_file, params)
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
while not h.has_metadata():
|
||||
message, porcent, msg_file, s, download = getProgress(h, "Creando torrent desde magnet")
|
||||
dp.update(porcent, message, msg_file)
|
||||
if s.state == 1: download = 1
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
h.force_dht_announce()
|
||||
xbmc.sleep(1000)
|
||||
|
||||
dp.close()
|
||||
info = h.get_torrent_info()
|
||||
data = lt.bencode(lt.create_torrent(info).generate())
|
||||
|
||||
torrent_file = os.path.join(save_path_torrents,
|
||||
unicode(info.name() + "-" + btih, "'utf-8'", errors="replace") + ".torrent")
|
||||
f = open(torrent_file, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
ses.remove_torrent(h)
|
||||
shutil.rmtree(tempdir)
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# -- Archivos torrent ---------------------------------------
|
||||
e = lt.bdecode(open(torrent_file, 'rb').read())
|
||||
info = lt.torrent_info(e)
|
||||
|
||||
# -- El más gordo o uno de los más gordo se entiende que es -
|
||||
# -- el vídeo o es el vídeo que se usará como referencia -
|
||||
# -- para el tipo de archivo -
|
||||
xbmc.log("##### Archivos ## %s ##" % len(info.files()))
|
||||
_index_file, _video_file, _size_file = get_video_file(info)
|
||||
|
||||
# -- Prioritarizar/Seleccionar archivo-----------------------
|
||||
_index, video_file, video_size, len_files = get_video_files_sizes(info)
|
||||
if len_files == 0:
|
||||
dp = xbmcgui.Dialog().ok("No se puede reproducir", "El torrent no contiene ningún archivo de vídeo")
|
||||
|
||||
if _index == -1:
|
||||
_index = _index_file
|
||||
video_file = _video_file
|
||||
video_size = _size_file
|
||||
|
||||
_video_file_ext = os.path.splitext(_video_file)[1]
|
||||
xbmc.log("##### _video_file_ext ## %s ##" % _video_file_ext)
|
||||
if (_video_file_ext == ".avi" or _video_file_ext == ".mp4") and allocate:
|
||||
xbmc.log("##### storage_mode_t.storage_mode_allocate (" + _video_file_ext + ") #####")
|
||||
h = ses.add_torrent({'ti': info, 'save_path': save_path_videos, 'trackers': trackers,
|
||||
'storage_mode': lt.storage_mode_t.storage_mode_allocate})
|
||||
else:
|
||||
xbmc.log("##### storage_mode_t.storage_mode_sparse (" + _video_file_ext + ") #####")
|
||||
h = ses.add_torrent({'ti': info, 'save_path': save_path_videos, 'trackers': trackers,
|
||||
'storage_mode': lt.storage_mode_t.storage_mode_sparse})
|
||||
allocate = True
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# -- Descarga secuencial - trozo 1, trozo 2, ... ------------
|
||||
h.set_sequential_download(True)
|
||||
|
||||
h.force_reannounce()
|
||||
h.force_dht_announce()
|
||||
|
||||
# -- Inicio de variables para 'pause' automático cuando el -
|
||||
# -- el vídeo se acerca a una pieza sin completar -
|
||||
is_greater_num_pieces = False
|
||||
is_greater_num_pieces_plus = False
|
||||
is_greater_num_pieces_pause = False
|
||||
|
||||
porcent4first_pieces = int(video_size * 0.000000005)
|
||||
if porcent4first_pieces < 10: porcent4first_pieces = 10
|
||||
if porcent4first_pieces > 100: porcent4first_pieces = 100
|
||||
porcent4last_pieces = int(porcent4first_pieces / 2)
|
||||
|
||||
num_pieces_to_resume = int(video_size * 0.0000000025)
|
||||
if num_pieces_to_resume < 5: num_pieces_to_resume = 5
|
||||
if num_pieces_to_resume > 25: num_pieces_to_resume = 25
|
||||
|
||||
xbmc.log("##### porcent4first_pieces ## %s ##" % porcent4first_pieces)
|
||||
xbmc.log("##### porcent4last_pieces ## %s ##" % porcent4last_pieces)
|
||||
xbmc.log("##### num_pieces_to_resume ## %s ##" % num_pieces_to_resume)
|
||||
|
||||
# -- Prioritarizar o seleccionar las piezas del archivo que -
|
||||
# -- se desea reproducir con 'file_priorities' -
|
||||
piece_set = set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate)
|
||||
|
||||
# -- Crear diálogo de progreso para el primer bucle ---------
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
|
||||
_pieces_info = {}
|
||||
|
||||
# -- Doble bucle anidado ------------------------------------
|
||||
# -- Descarga - Primer bucle -
|
||||
while not h.is_seed():
|
||||
s = h.status()
|
||||
|
||||
xbmc.sleep(100)
|
||||
|
||||
# -- Recuperar los datos del progreso -------------------
|
||||
message, porcent, msg_file, s, download = getProgress(h, video_file, _pf=_pieces_info)
|
||||
|
||||
# -- Si hace 'checking' existe descarga -----------------
|
||||
# -- 'download' Se usará para saber si hay datos -
|
||||
# -- descargados para el diálogo de 'remove_files' -
|
||||
if s.state == 1: download = 1
|
||||
|
||||
# -- Player - play --------------------------------------
|
||||
# -- Comprobar si se han completado las piezas para el -
|
||||
# -- inicio del vídeo -
|
||||
first_pieces = True
|
||||
|
||||
_c = 0
|
||||
for i in range(piece_set[0], piece_set[porcent4first_pieces]):
|
||||
first_pieces &= h.have_piece(i)
|
||||
if h.have_piece(i): _c += 1
|
||||
_pieces_info = {'current': 0, 'continuous': "%s/%s" % (_c, porcent4first_pieces), 'continuous2': "",
|
||||
'have': h.status().num_pieces, 'len': len(piece_set)}
|
||||
|
||||
last_pieces = True
|
||||
if not allocate:
|
||||
_c = len(piece_set) - 1;
|
||||
_cc = 0
|
||||
for i in range(len(piece_set) - porcent4last_pieces, len(piece_set)):
|
||||
last_pieces &= h.have_piece(i)
|
||||
if h.have_piece(i): _c -= 1; _cc += 1
|
||||
_pieces_info['continuous2'] = "[%s/%s] " % (_cc, porcent4last_pieces)
|
||||
|
||||
if is_view != "Ok" and first_pieces and last_pieces:
|
||||
_pieces_info['continuous2'] = ""
|
||||
xbmc.log("##### porcent [%.2f%%]" % (s.progress * 100))
|
||||
is_view = "Ok"
|
||||
dp.close()
|
||||
|
||||
# -- Player - Ver el vídeo --------------------------
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
|
||||
ren_video_file = os.path.join(save_path_videos, video_file)
|
||||
try:
|
||||
playlist.add(ren_video_file, xlistitem)
|
||||
except:
|
||||
playlist.add(ren_video_file)
|
||||
|
||||
if xbmc_version < 17:
|
||||
player = play_video(xbmc.PLAYER_CORE_AUTO)
|
||||
else:
|
||||
player = play_video()
|
||||
player.play(playlist)
|
||||
|
||||
# -- Contador de cancelaciones para la ventana de -
|
||||
# -- 'pause' automático -
|
||||
is_greater_num_pieces_canceled = 0
|
||||
continuous_pieces = 0
|
||||
porcent_time = 0.00
|
||||
current_piece = 0
|
||||
set_next_continuous_pieces = porcent4first_pieces
|
||||
|
||||
# -- Impedir que kodi haga 'resume' a un archivo ----
|
||||
# -- que se reprodujo con anterioridad y que se -
|
||||
# -- eliminó para impedir que intente la reprucción -
|
||||
# -- en una pieza que aún no se ha completado y se -
|
||||
# -- active 'pause' automático -
|
||||
not_resume = True
|
||||
|
||||
# -- Bandera subTítulos
|
||||
_sub = False
|
||||
|
||||
# -- Segundo bucle - Player - Control de eventos ----
|
||||
while player.isPlaying():
|
||||
xbmc.sleep(100)
|
||||
|
||||
# -- Añadir subTítulos
|
||||
if subtitle != "" and not _sub:
|
||||
_sub = True
|
||||
player.setSubtitles(subtitle)
|
||||
|
||||
# -- Impedir que kodi haga 'resume' al inicio ---
|
||||
# -- de la descarga de un archivo conocido -
|
||||
if not_resume:
|
||||
player.seekTime(0)
|
||||
not_resume = False
|
||||
|
||||
# -- Control 'pause' automático -
|
||||
continuous_pieces = count_completed_continuous_pieces(h, piece_set)
|
||||
|
||||
if xbmc.Player().isPlaying():
|
||||
|
||||
# -- Porcentage del progreso del vídeo ------
|
||||
player_getTime = player.getTime()
|
||||
player_getTotalTime = player.getTotalTime()
|
||||
porcent_time = player_getTime / player_getTotalTime * 100
|
||||
|
||||
# -- Pieza que se está reproduciendo --------
|
||||
current_piece = int(porcent_time / 100 * len(piece_set))
|
||||
|
||||
# -- Banderas de control --------------------
|
||||
is_greater_num_pieces = (current_piece > continuous_pieces - num_pieces_to_resume)
|
||||
is_greater_num_pieces_plus = (current_piece + porcent4first_pieces > continuous_pieces)
|
||||
is_greater_num_pieces_finished = (current_piece + porcent4first_pieces >= len(piece_set))
|
||||
|
||||
# -- Activa 'pause' automático --------------
|
||||
if is_greater_num_pieces and not player.paused and not is_greater_num_pieces_finished:
|
||||
is_greater_num_pieces_pause = True
|
||||
player.pause()
|
||||
|
||||
if continuous_pieces >= set_next_continuous_pieces:
|
||||
set_next_continuous_pieces = continuous_pieces + num_pieces_to_resume
|
||||
next_continuous_pieces = str(continuous_pieces - current_piece) + "/" + str(
|
||||
set_next_continuous_pieces - current_piece)
|
||||
_pieces_info = {'current': current_piece, 'continuous': next_continuous_pieces,
|
||||
'continuous2': _pieces_info['continuous2'], 'have': h.status().num_pieces,
|
||||
'len': len(piece_set)}
|
||||
|
||||
# si es un archivo de la videoteca enviar a marcar como visto
|
||||
if item.strm_path:
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.mark_auto_as_watched(item)
|
||||
|
||||
# -- Cerrar el diálogo de progreso --------------
|
||||
if player.resumed:
|
||||
dp.close()
|
||||
|
||||
# -- Mostrar el diálogo de progreso -------------
|
||||
if player.paused:
|
||||
# -- Crear diálogo si no existe -------------
|
||||
if not player.statusDialogoProgress:
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
player.setDialogoProgress()
|
||||
|
||||
# -- Diálogos de estado en el visionado -----
|
||||
if not h.is_seed():
|
||||
# -- Recuperar los datos del progreso ---
|
||||
message, porcent, msg_file, s, download = getProgress(h, video_file, _pf=_pieces_info)
|
||||
dp.update(porcent, message, msg_file)
|
||||
else:
|
||||
dp.update(100, "Descarga completa: " + video_file)
|
||||
|
||||
# -- Se canceló el progreso en el visionado -
|
||||
# -- Continuar -
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
player.pause()
|
||||
|
||||
# -- Se canceló el progreso en el visionado -
|
||||
# -- en la ventana de 'pause' automático. -
|
||||
# -- Parar si el contador llega a 3 -
|
||||
if dp.iscanceled() and is_greater_num_pieces_pause:
|
||||
is_greater_num_pieces_canceled += 1
|
||||
if is_greater_num_pieces_canceled == 3:
|
||||
player.stop()
|
||||
|
||||
# -- Desactiva 'pause' automático y ---------
|
||||
# -- reinicia el contador de cancelaciones -
|
||||
if not dp.iscanceled() and not is_greater_num_pieces_plus and is_greater_num_pieces_pause:
|
||||
dp.close()
|
||||
player.pause()
|
||||
is_greater_num_pieces_pause = False
|
||||
is_greater_num_pieces_canceled = 0
|
||||
|
||||
# -- El usuario cancelo el visionado --------
|
||||
# -- Terminar -
|
||||
if player.ended:
|
||||
# -- Diálogo eliminar archivos ----------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
|
||||
# -- Kodi - Se cerró el visionado -----------------------
|
||||
# -- Continuar | Terminar -
|
||||
if is_view == "Ok" and not xbmc.Player().isPlaying():
|
||||
|
||||
if info.num_files() == 1:
|
||||
# -- Diálogo continuar o terminar ---------------
|
||||
d = xbmcgui.Dialog()
|
||||
ok = d.yesno('alfa-MCT', 'XBMC-Kodi Cerró el vídeo.', '¿Continuar con la sesión?')
|
||||
else:
|
||||
ok = False
|
||||
# -- SI ---------------------------------------------
|
||||
if ok:
|
||||
# -- Continuar: ---------------------------------
|
||||
is_view = None
|
||||
else:
|
||||
# -- Terminar: ----------------------------------
|
||||
# -- Comprobar si el vídeo pertenece a una ------
|
||||
# -- lista de archivos -
|
||||
_index, video_file, video_size, len_files = get_video_files_sizes(info)
|
||||
if _index == -1 or len_files == 1:
|
||||
# -- Diálogo eliminar archivos --------------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
else:
|
||||
# -- Lista de archivos. Diálogo de opciones -
|
||||
piece_set = set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate)
|
||||
is_view = None
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
|
||||
# -- Mostar progeso antes del visionado -----------------
|
||||
if is_view != "Ok":
|
||||
dp.update(porcent, message, msg_file)
|
||||
|
||||
# -- Se canceló el progreso antes del visionado ---------
|
||||
# -- Terminar -
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
# -- Comprobar si el vídeo pertenece a una lista de -
|
||||
# -- archivos -
|
||||
_index, video_file, video_size, len_files = get_video_files_sizes(info)
|
||||
if _index == -1 or len_files == 1:
|
||||
# -- Diálogo eliminar archivos ------------------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
else:
|
||||
# -- Lista de archivos. Diálogo de opciones -----
|
||||
piece_set = set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate)
|
||||
is_view = None
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
|
||||
# -- Kodi - Error? - No debería llegar aquí -----------------
|
||||
if is_view == "Ok" and not xbmc.Player().isPlaying():
|
||||
dp.close()
|
||||
# -- Diálogo eliminar archivos --------------------------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
|
||||
return
|
||||
|
||||
|
||||
# -- Progreso de la descarga ------------------------------------
|
||||
def getProgress(h, video_file, _pf={}):
|
||||
if len(_pf) > 0:
|
||||
_pf_msg = "[%s] [%s] %s[%s] [%s]" % (
|
||||
_pf['current'], _pf['continuous'], _pf['continuous2'], _pf['have'], _pf['len'])
|
||||
else:
|
||||
_pf_msg = ""
|
||||
|
||||
s = h.status()
|
||||
|
||||
state_str = ['queued', 'checking', 'downloading metadata', \
|
||||
'downloading', 'finished', 'seeding', 'allocating', 'checking fastresume']
|
||||
|
||||
message = '%.2f%% d:%.1f kb/s u:%.1f kb/s p:%d s:%d %s' % \
|
||||
(s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \
|
||||
s.num_peers, s.num_seeds, state_str[s.state])
|
||||
porcent = int(s.progress * 100)
|
||||
|
||||
download = (s.progress * 100)
|
||||
|
||||
if "/" in video_file: video_file = video_file.split("/")[1]
|
||||
msg_file = video_file
|
||||
|
||||
if len(msg_file) > 50:
|
||||
msg_file = msg_file.replace(video_file,
|
||||
os.path.splitext(video_file)[0][:40] + "... " + os.path.splitext(video_file)[1])
|
||||
msg_file = msg_file + "[CR]" + "%.2f MB" % (s.total_wanted / 1048576.0) + " - " + _pf_msg
|
||||
|
||||
return (message, porcent, msg_file, s, download)
|
||||
|
||||
|
||||
# -- Clase play_video - Controlar eventos -----------------------
|
||||
class play_video(xbmc.Player):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.paused = False
|
||||
self.resumed = True
|
||||
self.statusDialogoProgress = False
|
||||
self.ended = False
|
||||
|
||||
def onPlayBackPaused(self):
|
||||
self.paused = True
|
||||
self.resumed = False
|
||||
|
||||
def onPlayBackResumed(self):
|
||||
self.paused = False
|
||||
self.resumed = True
|
||||
self.statusDialogoProgress = False
|
||||
|
||||
def is_paused(self):
|
||||
return self.paused
|
||||
|
||||
def setDialogoProgress(self):
|
||||
self.statusDialogoProgress = True
|
||||
|
||||
def is_started(self):
|
||||
self.ended = False
|
||||
|
||||
def is_ended(self):
|
||||
self.ended = True
|
||||
|
||||
|
||||
# -- Conseguir el nombre un alchivo de vídeo del metadata -------
|
||||
# -- El más gordo o uno de los más gordo se entiende que es el -
|
||||
# -- vídeo o es vídeo que se usará como referencia para el tipo -
|
||||
# -- de archivo -
|
||||
def get_video_file(info):
|
||||
size_file = 0
|
||||
for i, f in enumerate(info.files()):
|
||||
if f.size > size_file:
|
||||
video_file = f.path.replace("\\", "/")
|
||||
size_file = f.size
|
||||
index_file = i
|
||||
return index_file, video_file, size_file
|
||||
|
||||
|
||||
# -- Listado de selección del vídeo a prioritarizar -------------
|
||||
def get_video_files_sizes(info):
|
||||
opciones = {}
|
||||
vfile_name = {}
|
||||
vfile_size = {}
|
||||
|
||||
# -- Eliminar errores con tíldes -----------------------------
|
||||
for i, f in enumerate(info.files()):
|
||||
_title = unicode(f.path, "iso-8859-1", errors="replace")
|
||||
_title = unicode(f.path, "'utf-8'", errors="replace")
|
||||
|
||||
extensions_list = ['.aaf', '.3gp', '.asf', '.avi', '.flv', '.mpeg',
|
||||
'.m1v', '.m2v', '.m4v', '.mkv', '.mov', '.mpg',
|
||||
'.mpe', '.mp4', '.ogg', '.rar', '.wmv', '.zip']
|
||||
|
||||
for i, f in enumerate(info.files()):
|
||||
_index = int(i)
|
||||
_title = f.path.replace("\\", "/")
|
||||
_size = f.size
|
||||
|
||||
_file_name = os.path.splitext(_title)[0]
|
||||
if "/" in _file_name: _file_name = _file_name.split('/')[1]
|
||||
|
||||
_file_ext = os.path.splitext(_title)[1]
|
||||
|
||||
if _file_ext in extensions_list:
|
||||
index = len(opciones)
|
||||
_caption = str(index) + \
|
||||
" - " + \
|
||||
_file_name + _file_ext + \
|
||||
" - %.2f MB" % (_size / 1048576.0)
|
||||
|
||||
vfile_name[index] = _title
|
||||
vfile_size[index] = _size
|
||||
|
||||
opciones[i] = _caption
|
||||
|
||||
if len(opciones) > 1:
|
||||
d = xbmcgui.Dialog()
|
||||
seleccion = d.select("alfa-MCT: Lista de vídeos", opciones.values())
|
||||
else:
|
||||
seleccion = 0
|
||||
|
||||
index = opciones.keys()[seleccion]
|
||||
if seleccion == -1:
|
||||
vfile_name[seleccion] = ""
|
||||
vfile_size[seleccion] = 0
|
||||
index = seleccion
|
||||
|
||||
return index, vfile_name[seleccion], vfile_size[seleccion], len(opciones)
|
||||
|
||||
|
||||
# -- Preguntar si se desea borrar lo descargado -----------------
|
||||
def remove_files(download, torrent_file, video_file, ses, h):
|
||||
dialog_view = False
|
||||
torrent = False
|
||||
|
||||
if os.path.isfile(torrent_file):
|
||||
dialog_view = True
|
||||
torrent = True
|
||||
|
||||
if download > 0:
|
||||
dialog_view = True
|
||||
|
||||
if "/" in video_file: video_file = video_file.split("/")[0]
|
||||
|
||||
if dialog_view:
|
||||
d = xbmcgui.Dialog()
|
||||
ok = d.yesno('alfa-MCT', 'Borrar las descargas del video', video_file)
|
||||
|
||||
# -- SI -------------------------------------------------
|
||||
if ok:
|
||||
# -- Borrar archivo - torrent -----------------------
|
||||
if torrent:
|
||||
os.remove(torrent_file)
|
||||
# -- Borrar carpeta/archivos y sesión - vídeo -------
|
||||
ses.remove_torrent(h, 1)
|
||||
xbmc.log("### End session #########")
|
||||
else:
|
||||
# -- Borrar sesión ----------------------------------
|
||||
ses.remove_torrent(h)
|
||||
xbmc.log("### End session #########")
|
||||
else:
|
||||
# -- Borrar sesión --------------------------------------
|
||||
ses.remove_torrent(h)
|
||||
xbmc.log("### End session #########")
|
||||
|
||||
return
|
||||
|
||||
|
||||
# -- Descargar de la web los datos para crear el torrent --------
|
||||
# -- Si queremos aligerar el script mct.py se puede importar la -
|
||||
# -- función del conentor torrent.py -
|
||||
def url_get(url, params={}, headers={}):
|
||||
from contextlib import closing
|
||||
|
||||
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:20.0) Gecko/20100101 Firefox/20.0"
|
||||
|
||||
if params:
|
||||
import urllib
|
||||
url = "%s?%s" % (url, urllib.urlencode(params))
|
||||
|
||||
req = urllib2.Request(url)
|
||||
req.add_header("User-Agent", USER_AGENT)
|
||||
|
||||
for k, v in headers.items():
|
||||
req.add_header(k, v)
|
||||
|
||||
try:
|
||||
with closing(urllib2.urlopen(req)) as response:
|
||||
data = response.read()
|
||||
if response.headers.get("Content-Encoding", "") == "gzip":
|
||||
import zlib
|
||||
return zlib.decompressobj(16 + zlib.MAX_WBITS).decompress(data)
|
||||
return data
|
||||
except urllib2.HTTPError:
|
||||
return None
|
||||
|
||||
|
||||
# -- Contar las piezas contiguas completas del vídeo ------------
|
||||
def count_completed_continuous_pieces(h, piece_set):
|
||||
not_zero = 0
|
||||
for i, _set in enumerate(piece_set):
|
||||
if not h.have_piece(_set):
|
||||
break
|
||||
else:
|
||||
not_zero = 1
|
||||
return i + not_zero
|
||||
|
||||
|
||||
# -- Prioritarizar o seleccionar las piezas del archivo que se -
|
||||
# -- desea reproducir con 'file_priorities' estableciendo a 1 -
|
||||
# -- el archivo deseado y a 0 el resto de archivos almacenando -
|
||||
# -- en una lista los índices de de las piezas del archivo -
|
||||
def set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate):
|
||||
for i, _set in enumerate(h.file_priorities()):
|
||||
if i != _index:
|
||||
h.file_priority(i, 0)
|
||||
else:
|
||||
h.file_priority(i, 1)
|
||||
|
||||
piece_set = []
|
||||
for i, _set in enumerate(h.piece_priorities()):
|
||||
if _set == 1: piece_set.append(i)
|
||||
|
||||
if not allocate:
|
||||
for i in range(0, porcent4first_pieces):
|
||||
h.set_piece_deadline(piece_set[i], 10000)
|
||||
|
||||
for i in range(len(piece_set) - porcent4last_pieces, len(piece_set)):
|
||||
h.set_piece_deadline(piece_set[i], 10000)
|
||||
|
||||
return piece_set
|
||||
|
||||
|
||||
def decode_adfly(data):
|
||||
import base64
|
||||
ysmm = scrapertools.find_single_match(data, "var ysmm = '([^']+)'")
|
||||
left = ''
|
||||
right = ''
|
||||
for c in [ysmm[i:i + 2] for i in range(0, len(ysmm), 2)]:
|
||||
left += c[0]
|
||||
right = c[1] + right
|
||||
|
||||
decoded_url = base64.b64decode(left.encode() + right.encode())[2:].decode()
|
||||
return decoded_url
|
||||
1339
platformcode/platformtools.py
Normal file
1339
platformcode/platformtools.py
Normal file
File diff suppressed because it is too large
Load Diff
73
platformcode/recaptcha.py
Normal file
73
platformcode/recaptcha.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import xbmcgui
|
||||
from core import httptools
|
||||
from core import scrapertools
|
||||
from platformcode import config
|
||||
from platformcode import platformtools
|
||||
|
||||
|
||||
class Recaptcha(xbmcgui.WindowXMLDialog):
|
||||
def Start(self, key, referer):
|
||||
self.referer = referer
|
||||
self.key = key
|
||||
self.headers = {'Referer': self.referer}
|
||||
|
||||
api_js = httptools.downloadpage("http://www.google.com/recaptcha/api.js?hl=es").data
|
||||
version = scrapertools.find_single_match(api_js, 'po.src = \'(.*?)\';').split("/")[5]
|
||||
self.url = "http://www.google.com/recaptcha/api/fallback?k=%s&hl=es&v=%s&t=2&ff=true" % (self.key, version)
|
||||
self.doModal()
|
||||
# Reload
|
||||
if self.result == {}:
|
||||
self.result = Recaptcha("Recaptcha.xml", config.get_runtime_path()).Start(self.key, self.referer)
|
||||
|
||||
return self.result
|
||||
|
||||
def update_window(self):
|
||||
data = httptools.downloadpage(self.url, headers=self.headers).data
|
||||
self.message = scrapertools.find_single_match(data,
|
||||
'<div class="rc-imageselect-desc-no-canonical">(.*?)(?:</label>|</div>)').replace(
|
||||
"<strong>", "[B]").replace("</strong>", "[/B]")
|
||||
self.token = scrapertools.find_single_match(data, 'name="c" value="([^"]+)"')
|
||||
self.image = "http://www.google.com/recaptcha/api2/payload?k=%s&c=%s" % (self.key, self.token)
|
||||
self.result = {}
|
||||
self.getControl(10020).setImage(self.image)
|
||||
self.getControl(10000).setText(self.message)
|
||||
self.setFocusId(10005)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.mensaje = kwargs.get("mensaje")
|
||||
self.imagen = kwargs.get("imagen")
|
||||
|
||||
def onInit(self):
|
||||
#### Compatibilidad con Kodi 18 ####
|
||||
if config.get_platform(True)['num_version'] < 18:
|
||||
self.setCoordinateResolution(2)
|
||||
self.update_window()
|
||||
|
||||
def onClick(self, control):
|
||||
if control == 10003:
|
||||
self.result = None
|
||||
self.close()
|
||||
|
||||
elif control == 10004:
|
||||
self.result = {}
|
||||
self.close()
|
||||
|
||||
elif control == 10002:
|
||||
self.result = [int(k) for k in range(9) if self.result.get(k, False) == True]
|
||||
post = "c=%s" % self.token
|
||||
|
||||
for r in self.result:
|
||||
post += "&response=%s" % r
|
||||
|
||||
data = httptools.downloadpage(self.url, post, headers=self.headers).data
|
||||
self.result = scrapertools.find_single_match(data, '<div class="fbc-verification-token">.*?>([^<]+)<')
|
||||
if self.result:
|
||||
platformtools.dialog_notification("Captcha Correcto", "La verificación ha concluido")
|
||||
self.close()
|
||||
else:
|
||||
self.result = {}
|
||||
self.close()
|
||||
else:
|
||||
self.result[control - 10005] = not self.result.get(control - 10005, False)
|
||||
268
platformcode/subtitletools.py
Normal file
268
platformcode/subtitletools.py
Normal file
@@ -0,0 +1,268 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import urllib
|
||||
from unicodedata import normalize
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
from platformcode import config, logger
|
||||
|
||||
allchars = string.maketrans('', '')
|
||||
deletechars = ',\\/:*"<>|?'
|
||||
|
||||
|
||||
# Extraemos el nombre de la serie, temporada y numero de capitulo ejemplo: 'fringe 1x01'
|
||||
def regex_tvshow(compare, file, sub=""):
|
||||
regex_expressions = ['[Ss]([0-9]+)[][._-]*[Ee]([0-9]+)([^\\\\/]*)$',
|
||||
'[\._ \-]([0-9]+)x([0-9]+)([^\\/]*)', # foo.1x09
|
||||
'[\._ \-]([0-9]+)([0-9][0-9])([\._ \-][^\\/]*)', # foo.109
|
||||
'([0-9]+)([0-9][0-9])([\._ \-][^\\/]*)',
|
||||
'[\\\\/\\._ -]([0-9]+)([0-9][0-9])[^\\/]*',
|
||||
'Season ([0-9]+) - Episode ([0-9]+)[^\\/]*',
|
||||
'Season ([0-9]+) Episode ([0-9]+)[^\\/]*',
|
||||
'[\\\\/\\._ -][0]*([0-9]+)x[0]*([0-9]+)[^\\/]*',
|
||||
'[[Ss]([0-9]+)\]_\[[Ee]([0-9]+)([^\\/]*)', # foo_[s01]_[e01]
|
||||
'[\._ \-][Ss]([0-9]+)[\.\-]?[Ee]([0-9]+)([^\\/]*)', # foo, s01e01, foo.s01.e01, foo.s01-e01
|
||||
's([0-9]+)ep([0-9]+)[^\\/]*', # foo - s01ep03, foo - s1ep03
|
||||
'[Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+)([^\\\\/]*)$',
|
||||
'[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+)([^\\\\/]*)$',
|
||||
'[\\\\/\\._ \\[\\(-]([0-9]+)X([0-9]+)([^\\\\/]*)$'
|
||||
]
|
||||
sub_info = ""
|
||||
tvshow = 0
|
||||
|
||||
for regex in regex_expressions:
|
||||
response_file = re.findall(regex, file)
|
||||
if len(response_file) > 0:
|
||||
print "Regex File Se: %s, Ep: %s," % (str(response_file[0][0]), str(response_file[0][1]),)
|
||||
tvshow = 1
|
||||
if not compare:
|
||||
title = re.split(regex, file)[0]
|
||||
for char in ['[', ']', '_', '(', ')', '.', '-']:
|
||||
title = title.replace(char, ' ')
|
||||
if title.endswith(" "): title = title.strip()
|
||||
print "title: %s" % title
|
||||
return title, response_file[0][0], response_file[0][1]
|
||||
else:
|
||||
break
|
||||
|
||||
if (tvshow == 1):
|
||||
for regex in regex_expressions:
|
||||
response_sub = re.findall(regex, sub)
|
||||
if len(response_sub) > 0:
|
||||
try:
|
||||
sub_info = "Regex Subtitle Ep: %s," % (str(response_sub[0][1]),)
|
||||
if (int(response_sub[0][1]) == int(response_file[0][1])):
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
if compare:
|
||||
return True
|
||||
else:
|
||||
return "", "", ""
|
||||
|
||||
# Obtiene el nombre de la pelicula o capitulo de la serie guardado previamente en configuraciones del plugin
|
||||
# y luego lo busca en el directorio de subtitulos, si los encuentra los activa.
|
||||
|
||||
|
||||
def set_Subtitle():
|
||||
logger.info()
|
||||
|
||||
exts = [".srt", ".sub", ".txt", ".smi", ".ssa", ".ass"]
|
||||
subtitle_folder_path = os.path.join(config.get_data_path(), "subtitles")
|
||||
|
||||
subtitle_type = config.get_setting("subtitle_type")
|
||||
|
||||
if subtitle_type == "2":
|
||||
subtitle_path = config.get_setting("subtitlepath_file")
|
||||
logger.info("Con subtitulo : " + subtitle_path)
|
||||
xbmc.Player().setSubtitles(subtitle_path)
|
||||
else:
|
||||
if subtitle_type == "0":
|
||||
subtitle_path = config.get_setting("subtitlepath_folder")
|
||||
if subtitle_path == "":
|
||||
subtitle_path = subtitle_folder_path
|
||||
config.set_setting("subtitlepath_folder", subtitle_path)
|
||||
else:
|
||||
subtitle_path = config.get_setting("subtitlepath_keyboard")
|
||||
long = len(subtitle_path)
|
||||
if long > 0:
|
||||
if subtitle_path.startswith("http") or subtitle_path[long - 4, long] in exts:
|
||||
logger.info("Con subtitulo : " + subtitle_path)
|
||||
xbmc.Player().setSubtitles(subtitle_path)
|
||||
return
|
||||
else:
|
||||
subtitle_path = subtitle_folder_path
|
||||
config.set_setting("subtitlepath_keyboard", subtitle_path)
|
||||
|
||||
import glob
|
||||
|
||||
subtitle_name = config.get_setting("subtitle_name").replace("amp;", "")
|
||||
tvshow_title, season, episode = regex_tvshow(False, subtitle_name)
|
||||
try:
|
||||
if episode != "":
|
||||
Subnames = glob.glob(os.path.join(subtitle_path, "Tvshows", tvshow_title,
|
||||
"%s %sx%s" % (tvshow_title, season, episode) + "*.??.???"))
|
||||
else:
|
||||
Subnames = glob.glob(os.path.join(subtitle_path, "Movies", subtitle_name + "*.??.???"))
|
||||
for Subname in Subnames:
|
||||
if os.path.splitext(Subname)[1] in exts:
|
||||
logger.info("Con subtitulo : " + os.path.split(Subname)[1])
|
||||
xbmc.Player().setSubtitles((Subname))
|
||||
except:
|
||||
logger.error("error al cargar subtitulos")
|
||||
|
||||
# Limpia los caracteres unicode
|
||||
|
||||
|
||||
def _normalize(title, charset='utf-8'):
|
||||
'''Removes all accents and illegal chars for titles from the String'''
|
||||
if isinstance(title, unicode):
|
||||
title = string.translate(title, allchars, deletechars)
|
||||
try:
|
||||
title = title.encode("utf-8")
|
||||
title = normalize('NFKD', title).encode('ASCII', 'ignore')
|
||||
except UnicodeEncodeError:
|
||||
logger.error("Error de encoding")
|
||||
else:
|
||||
title = string.translate(title, allchars, deletechars)
|
||||
try:
|
||||
# iso-8859-1
|
||||
title = title.decode(charset).encode('utf-8')
|
||||
title = normalize('NFKD', unicode(title, 'utf-8'))
|
||||
title = title.encode('ASCII', 'ignore')
|
||||
except UnicodeEncodeError:
|
||||
logger.error("Error de encoding")
|
||||
return title
|
||||
|
||||
#
|
||||
|
||||
|
||||
def searchSubtitle(item):
|
||||
if config.get_setting("subtitle_type") == 0:
|
||||
subtitlepath = config.get_setting("subtitlepath_folder")
|
||||
if subtitlepath == "":
|
||||
subtitlepath = os.path.join(config.get_data_path(), "subtitles")
|
||||
config.set_setting("subtitlepath_folder", subtitlepath)
|
||||
|
||||
elif config.get_setting("subtitle_type") == 1:
|
||||
subtitlepath = config.get_setting("subtitlepath_keyboard")
|
||||
if subtitlepath == "":
|
||||
subtitlepath = os.path.join(config.get_data_path(), "subtitles")
|
||||
config.set_setting("subtitlepathkeyboard", subtitlepath)
|
||||
elif subtitlepath.startswith("http"):
|
||||
subtitlepath = config.get_setting("subtitlepath_folder")
|
||||
|
||||
else:
|
||||
subtitlepath = config.get_setting("subtitlepath_folder")
|
||||
if subtitlepath == "":
|
||||
subtitlepath = os.path.join(config.get_data_path(), "subtitles")
|
||||
config.set_setting("subtitlepath_folder", subtitlepath)
|
||||
if not os.path.exists(subtitlepath):
|
||||
try:
|
||||
os.mkdir(subtitlepath)
|
||||
except:
|
||||
logger.error("error no se pudo crear path subtitulos")
|
||||
return
|
||||
|
||||
path_movie_subt = xbmc.translatePath(os.path.join(subtitlepath, "Movies"))
|
||||
if not os.path.exists(path_movie_subt):
|
||||
try:
|
||||
os.mkdir(path_movie_subt)
|
||||
except:
|
||||
logger.error("error no se pudo crear el path Movies")
|
||||
return
|
||||
full_path_tvshow = ""
|
||||
path_tvshow_subt = xbmc.translatePath(os.path.join(subtitlepath, "Tvshows"))
|
||||
if not os.path.exists(path_tvshow_subt):
|
||||
try:
|
||||
os.mkdir(path_tvshow_subt)
|
||||
except:
|
||||
logger.error("error no pudo crear el path Tvshows")
|
||||
return
|
||||
if item.show in item.title:
|
||||
title_new = title = urllib.unquote_plus(item.title)
|
||||
else:
|
||||
title_new = title = urllib.unquote_plus(item.show + " - " + item.title)
|
||||
path_video_temp = xbmc.translatePath(os.path.join(config.get_runtime_path(), "resources", "subtitle.mp4"))
|
||||
if not os.path.exists(path_video_temp):
|
||||
logger.error("error : no existe el video temporal de subtitulos")
|
||||
return
|
||||
# path_video_temp = xbmc.translatePath(os.path.join( ,video_temp + ".mp4" ))
|
||||
|
||||
title_new = _normalize(title_new)
|
||||
tvshow_title, season, episode = regex_tvshow(False, title_new)
|
||||
if episode != "":
|
||||
full_path_tvshow = xbmc.translatePath(os.path.join(path_tvshow_subt, tvshow_title))
|
||||
if not os.path.exists(full_path_tvshow):
|
||||
os.mkdir(full_path_tvshow) # title_new + ".mp4"
|
||||
full_path_video_new = xbmc.translatePath(
|
||||
os.path.join(full_path_tvshow, "%s %sx%s.mp4" % (tvshow_title, season, episode)))
|
||||
logger.info(full_path_video_new)
|
||||
listitem = xbmcgui.ListItem(title_new, iconImage="DefaultVideo.png", thumbnailImage="")
|
||||
listitem.setInfo("video",
|
||||
{"Title": title_new, "Genre": "Tv shows", "episode": int(episode), "season": int(season),
|
||||
"tvshowtitle": tvshow_title})
|
||||
|
||||
else:
|
||||
full_path_video_new = xbmc.translatePath(os.path.join(path_movie_subt, title_new + ".mp4"))
|
||||
listitem = xbmcgui.ListItem(title, iconImage="DefaultVideo.png", thumbnailImage="")
|
||||
listitem.setInfo("video", {"Title": title_new, "Genre": "Movies"})
|
||||
|
||||
import shutil, time
|
||||
|
||||
try:
|
||||
shutil.copy(path_video_temp, full_path_video_new)
|
||||
copy = True
|
||||
logger.info("nuevo path =" + full_path_video_new)
|
||||
time.sleep(2)
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
playlist.add(full_path_video_new, listitem)
|
||||
# xbmcPlayer = xbmc.Player( xbmc.PLAYER_CORE_AUTO )
|
||||
xbmcPlayer = xbmc.Player()
|
||||
xbmcPlayer.play(playlist)
|
||||
|
||||
# xbmctools.launchplayer(full_path_video_new,listitem)
|
||||
except:
|
||||
copy = False
|
||||
logger.error("Error : no se pudo copiar")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if copy:
|
||||
if xbmc.Player().isPlayingVideo():
|
||||
xbmc.executebuiltin("RunScript(script.xbmc.subtitles)")
|
||||
while xbmc.Player().isPlayingVideo():
|
||||
continue
|
||||
|
||||
time.sleep(1)
|
||||
os.remove(full_path_video_new)
|
||||
try:
|
||||
if full_path_tvshow != "":
|
||||
os.rmdir(full_path_tvshow)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def saveSubtitleName(item):
|
||||
if item.show in item.title:
|
||||
title = item.title
|
||||
else:
|
||||
title = item.show + " - " + item.title
|
||||
try:
|
||||
title = _normalize(title)
|
||||
except:
|
||||
pass
|
||||
|
||||
tvshow_title, season, episode = regex_tvshow(False, title)
|
||||
if episode != "":
|
||||
# title = "% %sx%s" %(tvshow_title,season,episode)
|
||||
config.set_setting("subtitle_name", title)
|
||||
else:
|
||||
config.set_setting("subtitle_name", title)
|
||||
return
|
||||
607
platformcode/unify.py
Normal file
607
platformcode/unify.py
Normal file
@@ -0,0 +1,607 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# Unify
|
||||
# ------------------------------------------------------------
|
||||
# Herramientas responsables de unificar diferentes tipos de
|
||||
# datos obtenidos de las paginas
|
||||
# ----------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import urllib
|
||||
import unicodedata
|
||||
import re
|
||||
|
||||
import config
|
||||
from core.item import Item
|
||||
from core import scrapertools
|
||||
from platformcode import logger
|
||||
|
||||
thumb_dict = {"movies": "https://s10.postimg.cc/fxtqzdog9/peliculas.png",
|
||||
"tvshows": "https://s10.postimg.cc/kxvslawe1/series.png",
|
||||
"all": "https://s10.postimg.cc/h1igpgw0p/todas.png",
|
||||
"genres": "https://s10.postimg.cc/6c4rx3x1l/generos.png",
|
||||
"search": "https://s10.postimg.cc/v985e2izd/buscar.png",
|
||||
"quality": "https://s10.postimg.cc/9bbojsbjd/calidad.png",
|
||||
"audio": "https://s10.postimg.cc/b34nern7d/audio.png",
|
||||
"newest": "https://s10.postimg.cc/g1s5tf1bt/novedades.png",
|
||||
"last": "https://s10.postimg.cc/i6ciuk0eh/ultimas.png",
|
||||
"hot": "https://s10.postimg.cc/yu40x8q2x/destacadas.png",
|
||||
"year": "https://s10.postimg.cc/atzrqg921/a_o.png",
|
||||
"alphabet": "https://s10.postimg.cc/4dy3ytmgp/a-z.png",
|
||||
"recomended": "https://s10.postimg.cc/7xk1oqccp/recomendadas.png",
|
||||
"more watched": "https://s10.postimg.cc/c6orr5neh/masvistas.png",
|
||||
"more voted": "https://s10.postimg.cc/lwns2d015/masvotadas.png",
|
||||
"favorites": "https://s10.postimg.cc/rtg147gih/favoritas.png",
|
||||
"colections": "https://s10.postimg.cc/ywnwjvytl/colecciones.png",
|
||||
"categories": "https://s10.postimg.cc/v0ako5lmh/categorias.png",
|
||||
"premieres": "https://s10.postimg.cc/sk8r9xdq1/estrenos.png",
|
||||
"documentaries": "https://s10.postimg.cc/68aygmmcp/documentales.png",
|
||||
"language": "https://s10.postimg.cc/6wci189ft/idioma.png",
|
||||
"new episodes": "https://s10.postimg.cc/fu4iwpnqh/nuevoscapitulos.png",
|
||||
"country": "https://s10.postimg.cc/yz0h81j15/pais.png",
|
||||
"adults": "https://s10.postimg.cc/s8raxc51l/adultos.png",
|
||||
"recents": "https://s10.postimg.cc/649u24kp5/recents.png",
|
||||
"updated" : "https://s10.postimg.cc/46m3h6h9l/updated.png",
|
||||
"actors": "https://i.postimg.cc/tC2HMhVV/actors.png",
|
||||
"cast": "https://i.postimg.cc/qvfP5Xvt/cast.png",
|
||||
"lat": "https://i.postimg.cc/Gt8fMH0J/lat.png",
|
||||
"vose": "https://i.postimg.cc/kgmnbd8h/vose.png",
|
||||
"accion": "https://s14.postimg.cc/sqy3q2aht/action.png",
|
||||
"adolescente" : "https://s10.postimg.cc/inq7u4p61/teens.png",
|
||||
"adultos": "https://s10.postimg.cc/s8raxc51l/adultos.png",
|
||||
"animacion": "https://s14.postimg.cc/vl193mupd/animation.png",
|
||||
"anime" : "https://s10.postimg.cc/n9mc2ikzt/anime.png",
|
||||
"artes marciales" : "https://s10.postimg.cc/4u1v51tzt/martial_arts.png",
|
||||
"asiaticas" : "https://i.postimg.cc/Xq0HXD5d/asiaticas.png",
|
||||
"aventura": "https://s14.postimg.cc/ky7fy5he9/adventure.png",
|
||||
"belico": "https://s14.postimg.cc/5e027lru9/war.png",
|
||||
"biografia" : "https://s10.postimg.cc/jq0ecjxnt/biographic.png",
|
||||
"carreras": "https://s14.postimg.cc/yt5qgdr69/races.png",
|
||||
"ciencia ficcion": "https://s14.postimg.cc/8kulr2jy9/scifi.png",
|
||||
"cine negro" : "https://s10.postimg.cc/6ym862qgp/noir.png",
|
||||
"comedia": "https://s14.postimg.cc/9ym8moog1/comedy.png",
|
||||
"cortometraje" : "https://s10.postimg.cc/qggvlxndl/shortfilm.png",
|
||||
"crimen": "https://s14.postimg.cc/duzkipjq9/crime.png",
|
||||
"de la tv": "https://s10.postimg.cc/94gj0iwh5/image.png",
|
||||
"deporte": "https://s14.postimg.cc/x1crlnnap/sports.png",
|
||||
"destacadas": "https://s10.postimg.cc/yu40x8q2x/destacadas.png",
|
||||
"documental": "https://s10.postimg.cc/68aygmmcp/documentales.png",
|
||||
"doramas":"https://s10.postimg.cc/h4dyr4nfd/doramas.png",
|
||||
"drama": "https://s14.postimg.cc/fzjxjtnxt/drama.png",
|
||||
"erotica" : "https://s10.postimg.cc/dcbb9bfx5/erotic.png",
|
||||
"espanolas" : "https://s10.postimg.cc/x1y6zikx5/spanish.png",
|
||||
"estrenos" : "https://s10.postimg.cc/sk8r9xdq1/estrenos.png",
|
||||
"extranjera": "https://s10.postimg.cc/f44a4eerd/foreign.png",
|
||||
"familiar": "https://s14.postimg.cc/jj5v9ndsx/family.png",
|
||||
"fantasia": "https://s14.postimg.cc/p7c60ksg1/fantasy.png",
|
||||
"fantastico" : "https://s10.postimg.cc/tedufx5eh/fantastic.png",
|
||||
"historica": "https://s10.postimg.cc/p1faxj6yh/historic.png",
|
||||
"horror" : "https://s10.postimg.cc/8exqo6yih/horror2.png",
|
||||
"infantil": "https://s14.postimg.cc/4zyq842mp/childish.png",
|
||||
"intriga": "https://s14.postimg.cc/5qrgdimw1/intrigue.png",
|
||||
"latino" : "https://s10.postimg.cc/swip0b86h/latin.png",
|
||||
"mexicanas" : "https://s10.postimg.cc/swip0b86h/latin.png",
|
||||
"misterio": "https://s14.postimg.cc/3m73cg8ep/mistery.png",
|
||||
"musical": "https://s10.postimg.cc/hy7fhtecp/musical.png",
|
||||
"peleas" : "https://s10.postimg.cc/7a3ojbjwp/Fight.png",
|
||||
"policial" : "https://s10.postimg.cc/wsw0wbgbd/cops.png",
|
||||
"recomendadas": "https://s10.postimg.cc/7xk1oqccp/recomendadas.png",
|
||||
"religion" : "https://s10.postimg.cc/44j2skquh/religion.png",
|
||||
"romance" : "https://s10.postimg.cc/yn8vdll6x/romance.png",
|
||||
"romantica": "https://s14.postimg.cc/8xlzx7cht/romantic.png",
|
||||
"suspenso": "https://s10.postimg.cc/7peybxdfd/suspense.png",
|
||||
"telenovelas": "https://i.postimg.cc/QCXZkyDM/telenovelas.png",
|
||||
"terror": "https://s14.postimg.cc/thqtvl52p/horror.png",
|
||||
"thriller": "https://s14.postimg.cc/uwsekl8td/thriller.png",
|
||||
"western": "https://s10.postimg.cc/5wc1nokjt/western.png"
|
||||
}
|
||||
|
||||
def set_genre(string):
|
||||
#logger.info()
|
||||
|
||||
genres_dict = {'accion':['accion', 'action', 'accion y aventura', 'action & adventure'],
|
||||
'adultos':['adultos', 'adultos +', 'adulto'],
|
||||
'animacion':['animacion', 'animacion e infantil', 'dibujos animados'],
|
||||
'adolescente':['adolescente', 'adolescentes', 'adolescencia', 'adolecentes'],
|
||||
'aventura':['aventura', 'aventuras'],
|
||||
'belico':['belico', 'belica', 'belicas', 'guerra', 'belico guerra'],
|
||||
'biografia':['biografia', 'biografias', 'biografica', 'biograficas', 'biografico'],
|
||||
'ciencia ficcion':['ciencia ficcion', 'cienciaficcion', 'sci fi', 'c ficcion'],
|
||||
'cine negro':['film noir', 'negro'],
|
||||
'comedia':['comedia', 'comedias'],
|
||||
'cortometraje':['cortometraje', 'corto', 'cortos'],
|
||||
'de la tv':['de la tv', 'television', 'tv'],
|
||||
'deporte':['deporte', 'deportes'],
|
||||
'destacadas':['destacada', 'destacadas'],
|
||||
'documental':['documental', 'documentales'],
|
||||
'erotica':['erotica', 'erotica +', 'eroticas', 'eroticas +', 'erotico', 'erotico +'],
|
||||
'estrenos':['estrenos', 'estrenos'],
|
||||
'extranjera':['extrajera', 'extrajeras', 'foreign'],
|
||||
'familiar':['familiar', 'familia'],
|
||||
'fantastico':['fantastico', 'fantastica', 'fantasticas'],
|
||||
'historica':['historica', 'historicas', 'historico', 'historia'],
|
||||
'infantil':['infantil', 'kids'],
|
||||
'musical':['musical', 'musicales', 'musica'],
|
||||
'policial':['policial', 'policiaco', 'policiaca'],
|
||||
'recomendadas':['recomedada', 'recomendadas'],
|
||||
'religion':['religion', 'religiosa', 'religiosas'],
|
||||
'romantica':['romantica', 'romanticas', 'romantico'],
|
||||
'suspenso':['suspenso', 'suspense'],
|
||||
'thriller':['thriller', 'thrillers'],
|
||||
'western':['western', 'westerns', 'oeste western']
|
||||
}
|
||||
string = re.sub(r'peliculas de |pelicula de la |peli |cine ','', string)
|
||||
for genre, variants in genres_dict.items():
|
||||
if string in variants:
|
||||
string = genre
|
||||
|
||||
return string
|
||||
|
||||
def remove_format(string):
|
||||
#logger.info()
|
||||
#logger.debug('entra en remove: %s' % string)
|
||||
string = string.rstrip()
|
||||
string = re.sub(r'(\[|\[\/)(?:color|COLOR|b|B|i|I).*?\]|\[|\]|\(|\)|\:|\.', '', string)
|
||||
#logger.debug('sale de remove: %s' % string)
|
||||
return string
|
||||
|
||||
def normalize(string):
|
||||
string = string.decode('utf-8')
|
||||
normal = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
|
||||
return normal
|
||||
|
||||
|
||||
def simplify(string):
|
||||
|
||||
#logger.info()
|
||||
#logger.debug('entra en simplify: %s'%string)
|
||||
string = remove_format(string)
|
||||
string = string.replace('-',' ').replace('_',' ')
|
||||
string = re.sub(r'\d+','', string)
|
||||
string = string.strip()
|
||||
|
||||
notilde = normalize(string)
|
||||
try:
|
||||
string = notilde.decode()
|
||||
except:
|
||||
pass
|
||||
string = string.lower()
|
||||
#logger.debug('sale de simplify: %s' % string)
|
||||
|
||||
return string
|
||||
|
||||
def add_languages(title, languages):
|
||||
#logger.info()
|
||||
|
||||
if isinstance(languages, list):
|
||||
for language in languages:
|
||||
title = '%s %s' % (title, set_color(language, language))
|
||||
else:
|
||||
title = '%s %s' % (title, set_color(languages, languages))
|
||||
return title
|
||||
|
||||
def set_color(title, category):
|
||||
#logger.info()
|
||||
|
||||
color_scheme = {'otro': 'white', 'dual': 'white'}
|
||||
|
||||
#logger.debug('category antes de remove: %s' % category)
|
||||
category = remove_format(category).lower()
|
||||
#logger.debug('category despues de remove: %s' % category)
|
||||
# Lista de elementos posibles en el titulo
|
||||
color_list = ['movie', 'tvshow', 'year', 'rating_1', 'rating_2', 'rating_3', 'quality', 'cast', 'lat', 'vose',
|
||||
'vos', 'vo', 'server', 'library', 'update', 'no_update']
|
||||
|
||||
# Se verifica el estado de la opcion de colores personalizados
|
||||
custom_colors = config.get_setting('title_color')
|
||||
|
||||
# Se Forma el diccionario de colores para cada elemento, la opcion esta activas utiliza la configuracion del
|
||||
# usuario, si no pone el titulo en blanco.
|
||||
if title not in ['', ' ']:
|
||||
|
||||
for element in color_list:
|
||||
if custom_colors:
|
||||
color_scheme[element] = remove_format(config.get_setting('%s_color' % element))
|
||||
else:
|
||||
color_scheme[element] = 'white'
|
||||
if category in ['update', 'no_update']:
|
||||
#logger.debug('title antes de updates: %s' % title)
|
||||
title= re.sub(r'\[COLOR .*?\]','[COLOR %s]' % color_scheme[category],title)
|
||||
else:
|
||||
if category not in ['movie', 'tvshow', 'library', 'otro']:
|
||||
title = "[COLOR %s][%s][/COLOR]"%(color_scheme[category], title)
|
||||
else:
|
||||
title = "[COLOR %s]%s[/COLOR]" % (color_scheme[category], title)
|
||||
return title
|
||||
|
||||
def set_lang(language):
|
||||
#logger.info()
|
||||
|
||||
cast =['castellano','espanol','cast','esp','espaol', 'es','zc', 'spa', 'spanish', 'vc']
|
||||
ita =['italiano','italian','ita','it']
|
||||
lat=['latino','lat','la', 'espanol latino', 'espaol latino', 'zl', 'mx', 'co', 'vl']
|
||||
vose=['subtitulado','subtitulada','sub','sub espanol','vose','espsub','su','subs castellano',
|
||||
'sub: español', 'vs', 'zs', 'vs', 'english-spanish subs', 'ingles sub espanol']
|
||||
vosi=['sottotitolato','sottotitolata','sub','sub ita','vosi','sub-ita','subs italiano',
|
||||
'sub: italiano', 'inglese sottotitolato']
|
||||
vos=['vos', 'sub ingles', 'engsub', 'vosi','ingles subtitulado', 'sub: ingles']
|
||||
vo=['ingles', 'en','vo', 'ovos', 'eng','v.o', 'english']
|
||||
dual=['dual']
|
||||
|
||||
language = scrapertools.decodeHtmlentities(language)
|
||||
old_lang = language
|
||||
|
||||
language = simplify(language)
|
||||
|
||||
#logger.debug('language before simplify: %s' % language)
|
||||
#logger.debug('old language: %s' % old_lang)
|
||||
if language in cast:
|
||||
language = 'cast'
|
||||
elif language in lat:
|
||||
language = 'lat'
|
||||
elif language in ita:
|
||||
language = 'ita'
|
||||
elif language in vose:
|
||||
language = 'vose'
|
||||
elif language in vos:
|
||||
language = 'vos'
|
||||
elif language in vo:
|
||||
language = 'vo'
|
||||
elif language in dual:
|
||||
language = 'dual'
|
||||
elif language in dual:
|
||||
language = 'vosi'
|
||||
else:
|
||||
language = 'otro'
|
||||
|
||||
#logger.debug('language after simplify: %s' % language)
|
||||
|
||||
return language
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def title_format(item):
|
||||
#logger.info()
|
||||
|
||||
lang = False
|
||||
valid = True
|
||||
language_color = 'otro'
|
||||
|
||||
#logger.debug('item.title antes de formatear: %s' % item.title.lower())
|
||||
|
||||
# TODO se deberia quitar cualquier elemento que no sea un enlace de la lista de findvideos para quitar esto
|
||||
|
||||
#Palabras "prohibidas" en los titulos (cualquier titulo que contengas estas no se procesara en unify)
|
||||
excluded_words = ['online', 'descarga', 'downloads', 'trailer', 'videoteca', 'gb', 'autoplay']
|
||||
|
||||
# Actions excluidos, (se define canal y action) los titulos que contengan ambos valores no se procesaran en unify
|
||||
excluded_actions = [('videolibrary','get_episodes')]
|
||||
|
||||
# Verifica si hay marca de visto de trakt
|
||||
|
||||
visto = False
|
||||
#logger.debug('titlo con visto? %s' % item.title)
|
||||
|
||||
if '[[I]v[/I]]' in item.title or '[COLOR limegreen][v][/COLOR]' in item.title:
|
||||
visto = True
|
||||
|
||||
# Se elimina cualquier formato previo en el titulo
|
||||
if item.action != '' and item.action !='mainlist':
|
||||
item.title = remove_format(item.title)
|
||||
|
||||
#logger.debug('visto? %s' % visto)
|
||||
|
||||
# Evita que aparezcan los idiomas en los mainlist de cada canal
|
||||
if item.action == 'mainlist':
|
||||
item.language =''
|
||||
|
||||
info = item.infoLabels
|
||||
#logger.debug('item antes de formatear: %s'%item)
|
||||
|
||||
if hasattr(item,'text_color'):
|
||||
item.text_color=''
|
||||
|
||||
#Verifica el item sea valido para ser formateado por unify
|
||||
|
||||
if item.channel == 'trailertools' or (item.channel.lower(), item.action.lower()) in excluded_actions or \
|
||||
item.action=='':
|
||||
valid = False
|
||||
else:
|
||||
for word in excluded_words:
|
||||
if word in item.title.lower():
|
||||
valid = False
|
||||
break
|
||||
|
||||
if valid and item.unify!=False:
|
||||
|
||||
# Formamos el titulo para serie, se debe definir contentSerieName
|
||||
# o show en el item para que esto funcione.
|
||||
if item.contentSerieName:
|
||||
|
||||
# Si se tiene la informacion en infolabels se utiliza
|
||||
if item.contentType == 'episode' and info['episode'] != '':
|
||||
if info['title'] == '':
|
||||
info['title'] = '%s - Episodio %s'% (info['tvshowtitle'], info['episode'])
|
||||
elif 'Episode' in info['title']:
|
||||
episode = info['title'].lower().replace('episode', 'episodio')
|
||||
info['title'] = '%s - %s' % (info['tvshowtitle'], episode.capitalize())
|
||||
elif info['episodio_titulo']!='':
|
||||
#logger.debug('info[episode_titulo]: %s' % info['episodio_titulo'])
|
||||
if 'episode' in info['episodio_titulo'].lower():
|
||||
episode = info['episodio_titulo'].lower().replace('episode', 'episodio')
|
||||
item.title = '%sx%s - %s' % (info['season'],info['episode'], episode.capitalize())
|
||||
else:
|
||||
item.title = '%sx%s - %s' % (info['season'], info['episode'], info['episodio_titulo'].capitalize())
|
||||
else:
|
||||
item.title = '%sx%s - %s' % (info['season'],info['episode'], info['title'])
|
||||
item.title = set_color(item.title, 'tvshow')
|
||||
|
||||
else:
|
||||
|
||||
# En caso contrario se utiliza el titulo proporcionado por el canal
|
||||
#logger.debug ('color_scheme[tvshow]: %s' % color_scheme['tvshow'])
|
||||
item.title = '%s' % set_color(item.title, 'tvshow')
|
||||
|
||||
elif item.contentTitle:
|
||||
# Si el titulo no tiene contentSerieName entonces se formatea como pelicula
|
||||
saga = False
|
||||
if 'saga' in item.title.lower():
|
||||
item.title = '%s [Saga]' % set_color(item.contentTitle, 'movie')
|
||||
elif 'miniserie' in item.title.lower():
|
||||
item.title = '%s [Miniserie]' % set_color(item.contentTitle, 'movie')
|
||||
elif 'extend' in item.title.lower():
|
||||
item.title = '%s [V.Extend.]' % set_color(item.contentTitle, 'movie')
|
||||
else:
|
||||
item.title = '%s' % set_color(item.contentTitle, 'movie')
|
||||
if item.contentType=='movie':
|
||||
if item.context:
|
||||
if isinstance(item.context, list):
|
||||
item.context.append('Buscar esta pelicula en otros canales')
|
||||
|
||||
if 'Novedades' in item.category and item.from_channel=='news':
|
||||
#logger.debug('novedades')
|
||||
item.title = '%s [%s]'%(item.title, item.channel)
|
||||
|
||||
# Verificamos si item.language es una lista, si lo es se toma
|
||||
# cada valor y se normaliza formado una nueva lista
|
||||
|
||||
if hasattr(item,'language') and item.language !='':
|
||||
#logger.debug('tiene language: %s'%item.language)
|
||||
if isinstance(item.language, list):
|
||||
language_list =[]
|
||||
for language in item.language:
|
||||
if language != '':
|
||||
lang = True
|
||||
language_list.append(set_lang(remove_format(language)).upper())
|
||||
#logger.debug('language_list: %s' % language_list)
|
||||
simple_language = language_list
|
||||
else:
|
||||
# Si item.language es un string se normaliza
|
||||
if item.language != '':
|
||||
lang = True
|
||||
simple_language = set_lang(item.language).upper()
|
||||
else:
|
||||
simple_language = ''
|
||||
|
||||
#item.language = simple_language
|
||||
|
||||
# Damos formato al año si existiera y lo agregamos
|
||||
# al titulo excepto que sea un episodio
|
||||
if info and info.get("year", "") not in [""," "] and item.contentType != 'episode' and not info['season']:
|
||||
try:
|
||||
year = '%s' % set_color(info['year'], 'year')
|
||||
item.title = item.title = '%s %s' % (item.title, year)
|
||||
except:
|
||||
logger.debug('infoLabels: %s'%info)
|
||||
|
||||
# Damos formato al puntaje si existiera y lo agregamos al titulo
|
||||
if info and info['rating'] and info['rating']!='0.0' and not info['season']:
|
||||
|
||||
# Se normaliza el puntaje del rating
|
||||
|
||||
rating_value = check_rating(info['rating'])
|
||||
|
||||
# Asignamos el color dependiendo el puntaje, malo, bueno, muy bueno, en caso de que exista
|
||||
|
||||
if rating_value:
|
||||
value = float(rating_value)
|
||||
if value <= 3:
|
||||
color_rating = 'rating_1'
|
||||
elif value > 3 and value <= 7:
|
||||
color_rating = 'rating_2'
|
||||
else:
|
||||
color_rating = 'rating_3'
|
||||
|
||||
rating = '%s' % rating_value
|
||||
else:
|
||||
rating = ''
|
||||
color_rating = 'otro'
|
||||
item.title = '%s %s' % (item.title, set_color(rating, color_rating))
|
||||
|
||||
# Damos formato a la calidad si existiera y lo agregamos al titulo
|
||||
if item.quality and isinstance(item.quality, str):
|
||||
quality = item.quality.strip()
|
||||
item.title = '%s %s' % (item.title, set_color(quality, 'quality'))
|
||||
else:
|
||||
quality = ''
|
||||
|
||||
# Damos formato al idioma si existiera y lo agregamos al titulo
|
||||
if lang:
|
||||
item.title = add_languages(item.title, simple_language)
|
||||
|
||||
# Para las busquedas por canal
|
||||
if item.from_channel != '':
|
||||
from core import channeltools
|
||||
channel_parameters = channeltools.get_channel_parameters(item.from_channel)
|
||||
logger.debug(channel_parameters)
|
||||
item.title = '%s [%s]' % (item.title, channel_parameters['title'])
|
||||
|
||||
|
||||
# Formato para actualizaciones de series en la videoteca sobreescribe los colores anteriores
|
||||
|
||||
if item.channel=='videolibrary' and item.context!='':
|
||||
if item.action=='get_seasons':
|
||||
if 'Desactivar' in item.context[1]['title']:
|
||||
item.title= '%s' % (set_color(item.title, 'update'))
|
||||
if 'Activar' in item.context[1]['title']:
|
||||
item.title= '%s' % (set_color(item.title, 'no_update'))
|
||||
|
||||
#logger.debug('Despues del formato: %s' % item)
|
||||
# Damos formato al servidor si existiera
|
||||
if item.server:
|
||||
server = '%s' % set_color(item.server.strip().capitalize(), 'server')
|
||||
|
||||
# Compureba si estamos en findvideos, y si hay server, si es asi no se muestra el
|
||||
# titulo sino el server, en caso contrario se muestra el titulo normalmente.
|
||||
|
||||
#logger.debug('item.title antes de server: %s'%item.title)
|
||||
if item.action != 'play' and item.server:
|
||||
item.title ='%s %s'%(item.title, server.strip())
|
||||
elif item.action == 'play' and item.server:
|
||||
|
||||
if item.quality == 'default':
|
||||
quality = ''
|
||||
#logger.debug('language_color: %s'%language_color)
|
||||
item.title = '%s %s' % (server, set_color(quality,'quality'))
|
||||
if lang:
|
||||
item.title = add_languages(item.title, simple_language)
|
||||
#logger.debug('item.title: %s' % item.title)
|
||||
# si hay verificacion de enlaces
|
||||
if item.alive != '':
|
||||
if item.alive.lower() == 'no':
|
||||
item.title = '[[COLOR red][B]X[/B][/COLOR]] %s' % item.title
|
||||
elif item.alive == '??':
|
||||
item.title = '[[COLOR yellow][B]?[/B][/COLOR]] %s' % item.title
|
||||
else:
|
||||
item.title = '%s' % item.title
|
||||
#logger.debug('item.title despues de server: %s' % item.title)
|
||||
elif 'library' in item.action:
|
||||
item.title = '%s' % set_color(item.title, 'library')
|
||||
elif item.action == '' and item.title !='':
|
||||
item.title='**- %s -**'%item.title
|
||||
else:
|
||||
item.title = '%s' % set_color(item.title, 'otro')
|
||||
#logger.debug('antes de salir %s' % item.title)
|
||||
if visto:
|
||||
try:
|
||||
check = u'\u221a'
|
||||
|
||||
title = '[B][COLOR limegreen][%s][/COLOR][/B] %s' % (check, item.title.decode('utf-8'))
|
||||
item.title = title.encode('utf-8')
|
||||
except:
|
||||
check = 'v'
|
||||
title = '[B][COLOR limegreen][%s][/COLOR][/B] %s' % (check, item.title.decode('utf-8'))
|
||||
item.title = title.encode('utf-8')
|
||||
|
||||
return item
|
||||
|
||||
def thumbnail_type(item):
|
||||
#logger.info()
|
||||
# Se comprueba que tipo de thumbnail se utilizara en findvideos,
|
||||
# Poster o Logo del servidor
|
||||
|
||||
thumb_type = config.get_setting('video_thumbnail_type')
|
||||
info = item.infoLabels
|
||||
if not item.contentThumbnail:
|
||||
item.contentThumbnail = item.thumbnail
|
||||
|
||||
if info:
|
||||
if info['thumbnail'] !='':
|
||||
item.contentThumbnail = info['thumbnail']
|
||||
|
||||
if item.action == 'play':
|
||||
if thumb_type == 0:
|
||||
if info['thumbnail'] != '':
|
||||
item.thumbnail = info['thumbnail']
|
||||
elif thumb_type == 1:
|
||||
from core.servertools import get_server_parameters
|
||||
#logger.debug('item.server: %s'%item.server)
|
||||
server_parameters = get_server_parameters(item.server.lower())
|
||||
item.thumbnail = server_parameters.get("thumbnail", item.contentThumbnail)
|
||||
|
||||
return item.thumbnail
|
||||
|
||||
|
||||
from decimal import *
|
||||
|
||||
|
||||
def check_rating(rating):
|
||||
# logger.debug("\n\nrating %s" % rating)
|
||||
|
||||
def check_decimal_length(_rating):
|
||||
"""
|
||||
Dejamos que el float solo tenga un elemento en su parte decimal, "7.10" --> "7.1"
|
||||
@param _rating: valor del rating
|
||||
@type _rating: float
|
||||
@return: devuelve el valor modificado si es correcto, si no devuelve None
|
||||
@rtype: float|None
|
||||
"""
|
||||
# logger.debug("rating %s" % _rating)
|
||||
|
||||
try:
|
||||
# convertimos los deciamles p.e. 7.1
|
||||
return "%.1f" % round(_rating, 1)
|
||||
except Exception, ex_dl:
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex_dl).__name__, ex_dl.args)
|
||||
logger.error(message)
|
||||
return None
|
||||
|
||||
def check_range(_rating):
|
||||
"""
|
||||
Comprobamos que el rango de rating sea entre 0.0 y 10.0
|
||||
@param _rating: valor del rating
|
||||
@type _rating: float
|
||||
@return: devuelve el valor si está dentro del rango, si no devuelve None
|
||||
@rtype: float|None
|
||||
"""
|
||||
# logger.debug("rating %s" % _rating)
|
||||
# fix para comparacion float
|
||||
dec = Decimal(_rating)
|
||||
if 0.0 <= dec <= 10.0:
|
||||
# logger.debug("estoy en el rango!")
|
||||
return _rating
|
||||
else:
|
||||
# logger.debug("NOOO estoy en el rango!")
|
||||
return None
|
||||
|
||||
def convert_float(_rating):
|
||||
try:
|
||||
return float(_rating)
|
||||
except ValueError, ex_ve:
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex_ve).__name__, ex_ve.args)
|
||||
logger.error(message)
|
||||
return None
|
||||
|
||||
if type(rating) != float:
|
||||
# logger.debug("no soy float")
|
||||
if type(rating) == int:
|
||||
# logger.debug("soy int")
|
||||
rating = convert_float(rating)
|
||||
elif type(rating) == str:
|
||||
# logger.debug("soy str")
|
||||
|
||||
rating = rating.replace("<", "")
|
||||
rating = convert_float(rating)
|
||||
|
||||
if rating is None:
|
||||
# logger.debug("error al convertir str, rating no es un float")
|
||||
# obtenemos los valores de numericos
|
||||
new_rating = scrapertools.find_single_match(rating, "(\d+)[,|:](\d+)")
|
||||
if len(new_rating) > 0:
|
||||
rating = convert_float("%s.%s" % (new_rating[0], new_rating[1]))
|
||||
|
||||
else:
|
||||
logger.error("no se que soy!!")
|
||||
# obtenemos un valor desconocido no devolvemos nada
|
||||
return None
|
||||
|
||||
if rating:
|
||||
rating = check_decimal_length(rating)
|
||||
rating = check_range(rating)
|
||||
|
||||
return rating
|
||||
160
platformcode/updater.py
Normal file
160
platformcode/updater.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# --------------------------------------------------------------------------------
|
||||
# Updater (kodi)
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
from platformcode import config, logger, platformtools
|
||||
|
||||
from core import httptools
|
||||
from core import jsontools
|
||||
from core import downloadtools
|
||||
from core import ziptools
|
||||
from core import filetools
|
||||
|
||||
|
||||
def check_addon_init():
|
||||
logger.info()
|
||||
|
||||
# Subtarea de monitor. Se activa cada X horas para comprobar si hay FIXES al addon
|
||||
def check_addon_monitor():
|
||||
logger.info()
|
||||
|
||||
# Obtiene el íntervalo entre actualizaciones y si se quieren mensajes
|
||||
try:
|
||||
timer = int(config.get_setting('addon_update_timer')) # Intervalo entre actualizaciones, en Ajustes de Alfa
|
||||
if timer <= 0:
|
||||
return # 0. No se quieren actualizaciones
|
||||
verbose = config.get_setting('addon_update_message')
|
||||
except:
|
||||
timer = 12 # Por defecto cada 12 horas
|
||||
verbose = False # Por defecto, sin mensajes
|
||||
timer = timer * 3600 # Lo pasamos a segundos
|
||||
|
||||
if config.get_platform(True)['num_version'] >= 14: # Si es Kodi, lanzamos el monitor
|
||||
import xbmc
|
||||
monitor = xbmc.Monitor()
|
||||
else: # Lanzamos solo una actualización y salimos
|
||||
check_addon_updates(verbose) # Lanza la actualización
|
||||
return
|
||||
|
||||
while not monitor.abortRequested(): # Loop infinito hasta cancelar Kodi
|
||||
|
||||
check_addon_updates(verbose) # Lanza la actualización
|
||||
|
||||
if monitor.waitForAbort(timer): # Espera el tiempo programado o hasta que cancele Kodi
|
||||
break # Cancelación de Kodi, salimos
|
||||
|
||||
return
|
||||
|
||||
# Lanzamos en Servicio de actualización de FIXES
|
||||
try:
|
||||
threading.Thread(target=check_addon_monitor).start() # Creamos un Thread independiente, hasta el fin de Kodi
|
||||
time.sleep(5) # Dejamos terminar la primera verificación...
|
||||
except: # Si hay problemas de threading, se llama una sola vez
|
||||
try:
|
||||
timer = int(config.get_setting('addon_update_timer')) # Intervalo entre actualizaciones, en Ajustes de Alfa
|
||||
if timer <= 0:
|
||||
return # 0. No se quieren actualizaciones
|
||||
verbose = config.get_setting('addon_update_message')
|
||||
except:
|
||||
verbose = False # Por defecto, sin mensajes
|
||||
pass
|
||||
check_addon_updates(verbose) # Lanza la actualización, en Ajustes de Alfa
|
||||
time.sleep(5) # Dejamos terminar la primera verificación...
|
||||
|
||||
return
|
||||
|
||||
|
||||
def check_addon_updates(verbose=False):
|
||||
logger.info()
|
||||
|
||||
ADDON_UPDATES_JSON = 'https://extra.alfa-addon.com/addon_updates/updates.json'
|
||||
ADDON_UPDATES_ZIP = 'https://extra.alfa-addon.com/addon_updates/updates.zip'
|
||||
|
||||
try:
|
||||
last_fix_json = os.path.join(config.get_runtime_path(), 'last_fix.json') # información de la versión fixeada del usuario
|
||||
# Se guarda en get_runtime_path en lugar de get_data_path para que se elimine al cambiar de versión
|
||||
|
||||
# Descargar json con las posibles actualizaciones
|
||||
# -----------------------------------------------
|
||||
data = httptools.downloadpage(ADDON_UPDATES_JSON, timeout=2).data
|
||||
if data == '':
|
||||
logger.info('No se encuentran actualizaciones del addon')
|
||||
if verbose:
|
||||
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668))
|
||||
return False
|
||||
|
||||
data = jsontools.load(data)
|
||||
if 'addon_version' not in data or 'fix_version' not in data:
|
||||
logger.info('No hay actualizaciones del addon')
|
||||
if verbose:
|
||||
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668))
|
||||
return False
|
||||
|
||||
# Comprobar versión que tiene instalada el usuario con versión de la actualización
|
||||
# --------------------------------------------------------------------------------
|
||||
current_version = config.get_addon_version(with_fix=False)
|
||||
if current_version != data['addon_version']:
|
||||
logger.info('No hay actualizaciones para la versión %s del addon' % current_version)
|
||||
if verbose:
|
||||
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668))
|
||||
return False
|
||||
|
||||
if os.path.exists(last_fix_json):
|
||||
try:
|
||||
lastfix = {}
|
||||
lastfix = jsontools.load(filetools.read(last_fix_json))
|
||||
if lastfix['addon_version'] == data['addon_version'] and lastfix['fix_version'] == data['fix_version']:
|
||||
logger.info(config.get_localized_string(70670) % (data['addon_version'], data['fix_version']))
|
||||
if verbose:
|
||||
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70671) % (data['addon_version'], data['fix_version']))
|
||||
return False
|
||||
except:
|
||||
if lastfix:
|
||||
logger.error('last_fix.json: ERROR en: ' + str(lastfix))
|
||||
else:
|
||||
logger.error('last_fix.json: ERROR desconocido')
|
||||
lastfix = {}
|
||||
|
||||
# Descargar zip con las actualizaciones
|
||||
# -------------------------------------
|
||||
localfilename = os.path.join(config.get_data_path(), 'temp_updates.zip')
|
||||
if os.path.exists(localfilename): os.remove(localfilename)
|
||||
|
||||
downloadtools.downloadfile(ADDON_UPDATES_ZIP, localfilename, silent=True)
|
||||
|
||||
# Descomprimir zip dentro del addon
|
||||
# ---------------------------------
|
||||
try:
|
||||
unzipper = ziptools.ziptools()
|
||||
unzipper.extract(localfilename, config.get_runtime_path())
|
||||
except:
|
||||
import xbmc
|
||||
xbmc.executebuiltin('XBMC.Extract("%s", "%s")' % (localfilename, config.get_runtime_path()))
|
||||
time.sleep(1)
|
||||
|
||||
# Borrar el zip descargado
|
||||
# ------------------------
|
||||
os.remove(localfilename)
|
||||
|
||||
# Guardar información de la versión fixeada
|
||||
# -----------------------------------------
|
||||
if 'files' in data: data.pop('files', None)
|
||||
filetools.write(last_fix_json, jsontools.dump(data))
|
||||
|
||||
logger.info(config.get_localized_string(70672) % (data['addon_version'], data['fix_version']))
|
||||
if verbose:
|
||||
platformtools.dialog_notification(config.get_localized_string(70673), config.get_localized_string(70671) % (data['addon_version'], data['fix_version']))
|
||||
return True
|
||||
|
||||
except:
|
||||
logger.error('Error al comprobar actualizaciones del addon!')
|
||||
logger.error(traceback.format_exc())
|
||||
if verbose:
|
||||
platformtools.dialog_notification(config.get_localized_string(70674), config.get_localized_string(70675))
|
||||
return False
|
||||
1008
platformcode/xbmc_config_menu.py
Normal file
1008
platformcode/xbmc_config_menu.py
Normal file
File diff suppressed because it is too large
Load Diff
331
platformcode/xbmc_info_window.py
Normal file
331
platformcode/xbmc_info_window.py
Normal file
@@ -0,0 +1,331 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import xbmcgui
|
||||
from core.tmdb import Tmdb
|
||||
from platformcode import config, logger
|
||||
|
||||
ID_BUTTON_CLOSE = 10003
|
||||
ID_BUTTON_PREVIOUS = 10025
|
||||
ID_BUTTON_NEXT = 10026
|
||||
ID_BUTTON_CANCEL = 10027
|
||||
ID_BUTTON_OK = 10028
|
||||
|
||||
|
||||
class InfoWindow(xbmcgui.WindowXMLDialog):
|
||||
otmdb = None
|
||||
|
||||
item_title = ""
|
||||
item_serie = ""
|
||||
item_temporada = 0
|
||||
item_episodio = 0
|
||||
result = {}
|
||||
|
||||
# PARA TMDB
|
||||
@staticmethod
|
||||
def get_language(lng):
|
||||
# Cambiamos el formato del Idioma
|
||||
languages = {
|
||||
'aa': 'Afar', 'ab': 'Abkhazian', 'af': 'Afrikaans', 'ak': 'Akan', 'sq': 'Albanian', 'am': 'Amharic',
|
||||
'ar': 'Arabic', 'an': 'Aragonese', 'as': 'Assamese', 'av': 'Avaric', 'ae': 'Avestan', 'ay': 'Aymara',
|
||||
'az': 'Azerbaijani', 'ba': 'Bashkir', 'bm': 'Bambara', 'eu': 'Basque', 'be': 'Belarusian', 'bn': 'Bengali',
|
||||
'bh': 'Bihari languages', 'bi': 'Bislama', 'bo': 'Tibetan', 'bs': 'Bosnian', 'br': 'Breton',
|
||||
'bg': 'Bulgarian', 'my': 'Burmese', 'ca': 'Catalan; Valencian', 'cs': 'Czech', 'ch': 'Chamorro',
|
||||
'ce': 'Chechen', 'zh': 'Chinese',
|
||||
'cu': 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic', 'cv': 'Chuvash',
|
||||
'kw': 'Cornish', 'co': 'Corsican', 'cr': 'Cree', 'cy': 'Welsh', 'da': 'Danish', 'de': 'German',
|
||||
'dv': 'Divehi; Dhivehi; Maldivian', 'nl': 'Dutch; Flemish', 'dz': 'Dzongkha', 'en': 'English',
|
||||
'eo': 'Esperanto', 'et': 'Estonian', 'ee': 'Ewe', 'fo': 'Faroese', 'fa': 'Persian', 'fj': 'Fijian',
|
||||
'fi': 'Finnish', 'fr': 'French', 'fy': 'Western Frisian', 'ff': 'Fulah', 'Ga': 'Georgian',
|
||||
'gd': 'Gaelic; Scottish Gaelic', 'ga': 'Irish', 'gl': 'Galician', 'gv': 'Manx',
|
||||
'el': 'Greek, Modern (1453-)', 'gn': 'Guarani', 'gu': 'Gujarati', 'ht': 'Haitian; Haitian Creole',
|
||||
'ha': 'Hausa', 'he': 'Hebrew', 'hz': 'Herero', 'hi': 'Hindi', 'ho': 'Hiri Motu', 'hr': 'Croatian',
|
||||
'hu': 'Hungarian', 'hy': 'Armenian', 'ig': 'Igbo', 'is': 'Icelandic', 'io': 'Ido',
|
||||
'ii': 'Sichuan Yi; Nuosu', 'iu': 'Inuktitut', 'ie': 'Interlingue; Occidental',
|
||||
'ia': 'Interlingua (International Auxiliary Language Association)', 'id': 'Indonesian', 'ik': 'Inupiaq',
|
||||
'it': 'Italian', 'jv': 'Javanese', 'ja': 'Japanese', 'kl': 'Kalaallisut; Greenlandic', 'kn': 'Kannada',
|
||||
'ks': 'Kashmiri', 'ka': 'Georgian', 'kr': 'Kanuri', 'kk': 'Kazakh', 'km': 'Central Khmer',
|
||||
'ki': 'Kikuyu; Gikuyu', 'rw': 'Kinyarwanda', 'ky': 'Kirghiz; Kyrgyz', 'kv': 'Komi', 'kg': 'Kongo',
|
||||
'ko': 'Korean', 'kj': 'Kuanyama; Kwanyama', 'ku': 'Kurdish', 'lo': 'Lao', 'la': 'Latin', 'lv': 'Latvian',
|
||||
'li': 'Limburgan; Limburger; Limburgish', 'ln': 'Lingala', 'lt': 'Lithuanian',
|
||||
'lb': 'Luxembourgish; Letzeburgesch', 'lu': 'Luba-Katanga', 'lg': 'Ganda', 'mk': 'Macedonian',
|
||||
'mh': 'Marshallese', 'ml': 'Malayalam', 'mi': 'Maori', 'mr': 'Marathi', 'ms': 'Malay', 'Mi': 'Micmac',
|
||||
'mg': 'Malagasy', 'mt': 'Maltese', 'mn': 'Mongolian', 'na': 'Nauru', 'nv': 'Navajo; Navaho',
|
||||
'nr': 'Ndebele, South; South Ndebele', 'nd': 'Ndebele, North; North Ndebele', 'ng': 'Ndonga',
|
||||
'ne': 'Nepali', 'nn': 'Norwegian Nynorsk; Nynorsk, Norwegian', 'nb': 'Bokmål, Norwegian; Norwegian Bokmål',
|
||||
'no': 'Norwegian', 'oc': 'Occitan (post 1500)', 'oj': 'Ojibwa', 'or': 'Oriya', 'om': 'Oromo',
|
||||
'os': 'Ossetian; Ossetic', 'pa': 'Panjabi; Punjabi', 'pi': 'Pali', 'pl': 'Polish', 'pt': 'Portuguese',
|
||||
'ps': 'Pushto; Pashto', 'qu': 'Quechua', 'ro': 'Romanian; Moldavian; Moldovan', 'rn': 'Rundi',
|
||||
'ru': 'Russian', 'sg': 'Sango', 'rm': 'Romansh', 'sa': 'Sanskrit', 'si': 'Sinhala; Sinhalese',
|
||||
'sk': 'Slovak', 'sl': 'Slovenian', 'se': 'Northern Sami', 'sm': 'Samoan', 'sn': 'Shona', 'sd': 'Sindhi',
|
||||
'so': 'Somali', 'st': 'Sotho, Southern', 'es': 'Spanish', 'sc': 'Sardinian', 'sr': 'Serbian', 'ss': 'Swati',
|
||||
'su': 'Sundanese', 'sw': 'Swahili', 'sv': 'Swedish', 'ty': 'Tahitian', 'ta': 'Tamil', 'tt': 'Tatar',
|
||||
'te': 'Telugu', 'tg': 'Tajik', 'tl': 'Tagalog', 'th': 'Thai', 'ti': 'Tigrinya',
|
||||
'to': 'Tonga (Tonga Islands)', 'tn': 'Tswana', 'ts': 'Tsonga', 'tk': 'Turkmen', 'tr': 'Turkish',
|
||||
'tw': 'Twi', 'ug': 'Uighur; Uyghur', 'uk': 'Ukrainian', 'ur': 'Urdu', 'uz': 'Uzbek', 've': 'Venda',
|
||||
'vi': 'Vietnamese', 'vo': 'Volapük', 'wa': 'Walloon', 'wo': 'Wolof', 'xh': 'Xhosa', 'yi': 'Yiddish',
|
||||
'yo': 'Yoruba', 'za': 'Zhuang; Chuang', 'zu': 'Zulu'}
|
||||
|
||||
return languages.get(lng, lng)
|
||||
|
||||
def get_scraper_data(self, data_in):
|
||||
self.otmdb = None
|
||||
# logger.debug(str(data_in))
|
||||
|
||||
if self.listData:
|
||||
# Datos comunes a todos los listados
|
||||
infoLabels = self.scraper().get_infoLabels(origen=data_in)
|
||||
|
||||
if "original_language" in infoLabels:
|
||||
infoLabels["language"] = self.get_language(infoLabels["original_language"])
|
||||
infoLabels["puntuacion"] = "%s/10 (%s)" % (infoLabels.get("rating", "?"), infoLabels.get("votes", "N/A"))
|
||||
|
||||
self.result = infoLabels
|
||||
|
||||
def start(self, data, caption="Información del vídeo", item=None, scraper=Tmdb):
|
||||
"""
|
||||
Muestra una ventana con la info del vídeo. Opcionalmente se puede indicar el titulo de la ventana mendiante
|
||||
el argumento 'caption'.
|
||||
|
||||
Si se pasa un item como argumento 'data' usa el scrapper Tmdb para buscar la info del vídeo
|
||||
En caso de peliculas:
|
||||
Coge el titulo de los siguientes campos (en este orden)
|
||||
1. contentTitle (este tiene prioridad 1)
|
||||
2. fulltitle (este tiene prioridad 2)
|
||||
3. title (este tiene prioridad 3)
|
||||
El primero que contenga "algo" lo interpreta como el titulo (es importante asegurarse que el titulo este en
|
||||
su sitio)
|
||||
|
||||
En caso de series:
|
||||
1. Busca la temporada y episodio en los campos contentSeason y contentEpisodeNumber
|
||||
2. Intenta Sacarlo del titulo del video (formato: 1x01)
|
||||
|
||||
Aqui hay dos opciones posibles:
|
||||
1. Tenemos Temporada y episodio
|
||||
Muestra la información del capitulo concreto
|
||||
2. NO Tenemos Temporada y episodio
|
||||
En este caso muestra la informacion generica de la serie
|
||||
|
||||
Si se pasa como argumento 'data' un objeto InfoLabels(ver item.py) muestra en la ventana directamente
|
||||
la información pasada (sin usar el scrapper)
|
||||
Formato:
|
||||
En caso de peliculas:
|
||||
infoLabels({
|
||||
"type" : "movie",
|
||||
"title" : "Titulo de la pelicula",
|
||||
"original_title" : "Titulo original de la pelicula",
|
||||
"date" : "Fecha de lanzamiento",
|
||||
"language" : "Idioma original de la pelicula",
|
||||
"rating" : "Puntuacion de la pelicula",
|
||||
"votes" : "Numero de votos",
|
||||
"genres" : "Generos de la pelicula",
|
||||
"thumbnail" : "Ruta para el thumbnail",
|
||||
"fanart" : "Ruta para el fanart",
|
||||
"plot" : "Sinopsis de la pelicula"
|
||||
}
|
||||
En caso de series:
|
||||
infoLabels({
|
||||
"type" : "tv",
|
||||
"title" : "Titulo de la serie",
|
||||
"episode_title" : "Titulo del episodio",
|
||||
"date" : "Fecha de emision",
|
||||
"language" : "Idioma original de la serie",
|
||||
"rating" : "Puntuacion de la serie",
|
||||
"votes" : "Numero de votos",
|
||||
"genres" : "Generos de la serie",
|
||||
"thumbnail" : "Ruta para el thumbnail",
|
||||
"fanart" : "Ruta para el fanart",
|
||||
"plot" : "Sinopsis de la del episodio o de la serie",
|
||||
"seasons" : "Numero de Temporadas",
|
||||
"season" : "Temporada",
|
||||
"episodes" : "Numero de episodios de la temporada",
|
||||
"episode" : "Episodio"
|
||||
}
|
||||
Si se pasa como argumento 'data' un listado de InfoLabels() con la estructura anterior, muestra los botones
|
||||
'Anterior' y 'Siguiente' para ir recorriendo la lista. Ademas muestra los botones 'Aceptar' y 'Cancelar' que
|
||||
llamaran a la funcion 'callback' del canal desde donde se realiza la llamada pasandole como parametros el elemento
|
||||
actual (InfoLabels()) o None respectivamente.
|
||||
|
||||
@param data: información para obtener datos del scraper.
|
||||
@type data: item, InfoLabels, list(InfoLabels)
|
||||
@param caption: titulo de la ventana.
|
||||
@type caption: str
|
||||
@param item: elemento del que se va a mostrar la ventana de información
|
||||
@type item: Item
|
||||
@param scraper: scraper que tiene los datos de las peliculas o series a mostrar en la ventana.
|
||||
@type scraper: Scraper
|
||||
"""
|
||||
|
||||
# Capturamos los parametros
|
||||
self.caption = caption
|
||||
self.item = item
|
||||
self.indexList = -1
|
||||
self.listData = None
|
||||
self.return_value = None
|
||||
self.scraper = scraper
|
||||
|
||||
logger.debug(data)
|
||||
if type(data) == list:
|
||||
self.listData = data
|
||||
self.indexList = 0
|
||||
data = self.listData[self.indexList]
|
||||
|
||||
self.get_scraper_data(data)
|
||||
|
||||
# Muestra la ventana
|
||||
self.doModal()
|
||||
return self.return_value
|
||||
|
||||
def __init__(self, *args):
|
||||
self.caption = ""
|
||||
self.item = None
|
||||
self.listData = None
|
||||
self.indexList = 0
|
||||
self.return_value = None
|
||||
self.scraper = Tmdb
|
||||
|
||||
def onInit(self):
|
||||
#### Compatibilidad con Kodi 18 ####
|
||||
if config.get_platform(True)['num_version'] < 18:
|
||||
if xbmcgui.__version__ == "1.2":
|
||||
self.setCoordinateResolution(1)
|
||||
else:
|
||||
self.setCoordinateResolution(5)
|
||||
|
||||
# Ponemos el título y las imagenes
|
||||
self.getControl(10002).setLabel(self.caption)
|
||||
self.getControl(10004).setImage(self.result.get("fanart", ""))
|
||||
self.getControl(10005).setImage(self.result.get("thumbnail", "images/img_no_disponible.png"))
|
||||
|
||||
# Cargamos los datos para el formato pelicula
|
||||
if self.result.get("mediatype", "movie") == "movie":
|
||||
self.getControl(10006).setLabel(config.get_localized_string(60377))
|
||||
self.getControl(10007).setLabel(self.result.get("title", "N/A"))
|
||||
self.getControl(10008).setLabel(config.get_localized_string(60378))
|
||||
self.getControl(10009).setLabel(self.result.get("originaltitle", "N/A"))
|
||||
self.getControl(100010).setLabel(config.get_localized_string(60379))
|
||||
self.getControl(100011).setLabel(self.result.get("language", "N/A"))
|
||||
self.getControl(100012).setLabel(config.get_localized_string(60380))
|
||||
self.getControl(100013).setLabel(self.result.get("puntuacion", "N/A"))
|
||||
self.getControl(100014).setLabel(config.get_localized_string(60381))
|
||||
self.getControl(100015).setLabel(self.result.get("release_date", "N/A"))
|
||||
self.getControl(100016).setLabel(config.get_localized_string(60382))
|
||||
self.getControl(100017).setLabel(self.result.get("genre", "N/A"))
|
||||
|
||||
# Cargamos los datos para el formato serie
|
||||
else:
|
||||
self.getControl(10006).setLabel(config.get_localized_string(60383))
|
||||
self.getControl(10007).setLabel(self.result.get("title", "N/A"))
|
||||
self.getControl(10008).setLabel(config.get_localized_string(60379))
|
||||
self.getControl(10009).setLabel(self.result.get("language", "N/A"))
|
||||
self.getControl(100010).setLabel(config.get_localized_string(60380))
|
||||
self.getControl(100011).setLabel(self.result.get("puntuacion", "N/A"))
|
||||
self.getControl(100012).setLabel(config.get_localized_string(60382))
|
||||
self.getControl(100013).setLabel(self.result.get("genre", "N/A"))
|
||||
|
||||
if self.result.get("season"):
|
||||
self.getControl(100014).setLabel(config.get_localized_string(60384))
|
||||
self.getControl(100015).setLabel(self.result.get("temporada_nombre", "N/A"))
|
||||
self.getControl(100016).setLabel(config.get_localized_string(60385))
|
||||
self.getControl(100017).setLabel(self.result.get("season", "N/A") + " de " +
|
||||
self.result.get("seasons", "N/A"))
|
||||
if self.result.get("episode"):
|
||||
self.getControl(100014).setLabel(config.get_localized_string(60377))
|
||||
self.getControl(100015).setLabel(self.result.get("episode_title", "N/A"))
|
||||
self.getControl(100018).setLabel(config.get_localized_string(60386))
|
||||
self.getControl(100019).setLabel(self.result.get("episode", "N/A") + " de " +
|
||||
self.result.get("episodes", "N/A"))
|
||||
self.getControl(100020).setLabel(config.get_localized_string(60387))
|
||||
self.getControl(100021).setLabel(self.result.get("date", "N/A"))
|
||||
|
||||
# Sinopsis
|
||||
if self.result['plot']:
|
||||
self.getControl(100022).setLabel(config.get_localized_string(60388))
|
||||
self.getControl(100023).setText(self.result.get("plot", "N/A"))
|
||||
else:
|
||||
self.getControl(100022).setLabel("")
|
||||
self.getControl(100023).setText("")
|
||||
|
||||
# Cargamos los botones si es necesario
|
||||
self.getControl(10024).setVisible(self.indexList > -1) # Grupo de botones
|
||||
self.getControl(ID_BUTTON_PREVIOUS).setEnabled(self.indexList > 0) # Anterior
|
||||
|
||||
if self.listData:
|
||||
m = len(self.listData)
|
||||
else:
|
||||
m = 1
|
||||
|
||||
self.getControl(ID_BUTTON_NEXT).setEnabled(self.indexList + 1 != m) # Siguiente
|
||||
self.getControl(100029).setLabel("(%s/%s)" % (self.indexList + 1, m)) # x/m
|
||||
|
||||
# Ponemos el foco en el Grupo de botones, si estuviera desactivado "Anterior" iria el foco al boton "Siguiente"
|
||||
# si "Siguiente" tb estuviera desactivado pasara el foco al botón "Cancelar"
|
||||
self.setFocus(self.getControl(10024))
|
||||
|
||||
return self.return_value
|
||||
|
||||
def onClick(self, _id):
|
||||
logger.info("onClick id=" + repr(_id))
|
||||
if _id == ID_BUTTON_PREVIOUS and self.indexList > 0:
|
||||
self.indexList -= 1
|
||||
self.get_scraper_data(self.listData[self.indexList])
|
||||
self.onInit()
|
||||
|
||||
elif _id == ID_BUTTON_NEXT and self.indexList < len(self.listData) - 1:
|
||||
self.indexList += 1
|
||||
self.get_scraper_data(self.listData[self.indexList])
|
||||
self.onInit()
|
||||
|
||||
elif _id == ID_BUTTON_OK or _id == ID_BUTTON_CLOSE or _id == ID_BUTTON_CANCEL:
|
||||
self.close()
|
||||
|
||||
if _id == ID_BUTTON_OK:
|
||||
self.return_value = self.listData[self.indexList]
|
||||
else:
|
||||
self.return_value = None
|
||||
|
||||
def onAction(self, action):
|
||||
logger.info("action=" + repr(action.getId()))
|
||||
action = action.getId()
|
||||
|
||||
# Obtenemos el foco
|
||||
focus = self.getFocusId()
|
||||
|
||||
# Accion 1: Flecha izquierda
|
||||
if action == 1:
|
||||
|
||||
if focus == ID_BUTTON_OK:
|
||||
self.setFocus(self.getControl(ID_BUTTON_CANCEL))
|
||||
|
||||
elif focus == ID_BUTTON_CANCEL:
|
||||
if self.indexList + 1 != len(self.listData):
|
||||
# vamos al botón Siguiente
|
||||
self.setFocus(self.getControl(ID_BUTTON_NEXT))
|
||||
elif self.indexList > 0:
|
||||
# vamos al botón Anterior ya que Siguiente no está activo (estamos al final de la lista)
|
||||
self.setFocus(self.getControl(ID_BUTTON_PREVIOUS))
|
||||
|
||||
elif focus == ID_BUTTON_NEXT:
|
||||
if self.indexList > 0:
|
||||
# vamos al botón Anterior
|
||||
self.setFocus(self.getControl(ID_BUTTON_PREVIOUS))
|
||||
|
||||
# Accion 2: Flecha derecha
|
||||
elif action == 2:
|
||||
|
||||
if focus == ID_BUTTON_PREVIOUS:
|
||||
if self.indexList + 1 != len(self.listData):
|
||||
# vamos al botón Siguiente
|
||||
self.setFocus(self.getControl(ID_BUTTON_NEXT))
|
||||
else:
|
||||
# vamos al botón Cancelar ya que Siguiente no está activo (estamos al final de la lista)
|
||||
self.setFocus(self.getControl(ID_BUTTON_CANCEL))
|
||||
|
||||
elif focus == ID_BUTTON_NEXT:
|
||||
self.setFocus(self.getControl(ID_BUTTON_CANCEL))
|
||||
|
||||
elif focus == ID_BUTTON_CANCEL:
|
||||
self.setFocus(self.getControl(ID_BUTTON_OK))
|
||||
|
||||
# Pulsa ESC o Atrás, simula click en boton cancelar
|
||||
if action in [10, 92]:
|
||||
self.onClick(ID_BUTTON_CANCEL)
|
||||
927
platformcode/xbmc_videolibrary.py
Normal file
927
platformcode/xbmc_videolibrary.py
Normal file
@@ -0,0 +1,927 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# XBMC Library Tools
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import urllib2
|
||||
|
||||
import xbmc
|
||||
from core import filetools
|
||||
from core import jsontools
|
||||
from platformcode import config, logger
|
||||
from platformcode import platformtools
|
||||
|
||||
|
||||
def mark_auto_as_watched(item):
|
||||
def mark_as_watched_subThread(item):
|
||||
logger.info()
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
|
||||
condicion = config.get_setting("watched_setting", "videolibrary")
|
||||
|
||||
time_limit = time.time() + 30
|
||||
while not platformtools.is_playing() and time.time() < time_limit:
|
||||
time.sleep(1)
|
||||
|
||||
sync_with_trakt = False
|
||||
|
||||
while platformtools.is_playing():
|
||||
tiempo_actual = xbmc.Player().getTime()
|
||||
totaltime = xbmc.Player().getTotalTime()
|
||||
|
||||
mark_time = 0
|
||||
if condicion == 0: # '5 minutos'
|
||||
mark_time = 300
|
||||
elif condicion == 1: # '30%'
|
||||
mark_time = totaltime * 0.3
|
||||
elif condicion == 2: # '50%'
|
||||
mark_time = totaltime * 0.5
|
||||
elif condicion == 3: # '80%'
|
||||
mark_time = totaltime * 0.8
|
||||
elif condicion == 4: # '0 seg'
|
||||
mark_time = -1
|
||||
|
||||
# logger.debug(str(tiempo_actual))
|
||||
# logger.debug(str(mark_time))
|
||||
|
||||
if tiempo_actual > mark_time:
|
||||
logger.debug("marcado")
|
||||
item.playcount = 1
|
||||
sync_with_trakt = True
|
||||
from channels import videolibrary
|
||||
videolibrary.mark_content_as_watched2(item)
|
||||
break
|
||||
|
||||
time.sleep(30)
|
||||
|
||||
# Sincronizacion silenciosa con Trakt
|
||||
if sync_with_trakt:
|
||||
if config.get_setting("sync_trakt_watched", "videolibrary"):
|
||||
sync_trakt_kodi()
|
||||
|
||||
# logger.debug("Fin del hilo")
|
||||
|
||||
# Si esta configurado para marcar como visto
|
||||
if config.get_setting("mark_as_watched", "videolibrary"):
|
||||
threading.Thread(target=mark_as_watched_subThread, args=[item]).start()
|
||||
|
||||
|
||||
def sync_trakt_addon(path_folder):
|
||||
"""
|
||||
Actualiza los valores de episodios vistos si
|
||||
"""
|
||||
logger.info()
|
||||
# si existe el addon hacemos la busqueda
|
||||
if xbmc.getCondVisibility('System.HasAddon("script.trakt")'):
|
||||
# importamos dependencias
|
||||
paths = ["special://home/addons/script.module.dateutil/lib/", "special://home/addons/script.module.six/lib/",
|
||||
"special://home/addons/script.module.arrow/lib/", "special://home/addons/script.module.trakt/lib/",
|
||||
"special://home/addons/script.trakt/"]
|
||||
|
||||
for path in paths:
|
||||
import sys
|
||||
sys.path.append(xbmc.translatePath(path))
|
||||
|
||||
# se obtiene las series vistas
|
||||
try:
|
||||
from resources.lib.traktapi import traktAPI
|
||||
traktapi = traktAPI()
|
||||
except:
|
||||
return
|
||||
|
||||
shows = traktapi.getShowsWatched({})
|
||||
shows = shows.items()
|
||||
|
||||
# obtenemos el id de la serie para comparar
|
||||
import re
|
||||
_id = re.findall("\[(.*?)\]", path_folder, flags=re.DOTALL)[0]
|
||||
logger.debug("el id es %s" % _id)
|
||||
|
||||
if "tt" in _id:
|
||||
type_id = "imdb"
|
||||
elif "tvdb_" in _id:
|
||||
_id = _id.strip("tvdb_")
|
||||
type_id = "tvdb"
|
||||
elif "tmdb_" in _id:
|
||||
type_id = "tmdb"
|
||||
_id = _id.strip("tmdb_")
|
||||
else:
|
||||
logger.error("No hay _id de la serie")
|
||||
return
|
||||
|
||||
# obtenemos los valores de la serie
|
||||
from core import videolibrarytools
|
||||
tvshow_file = filetools.join(path_folder, "tvshow.nfo")
|
||||
head_nfo, serie = videolibrarytools.read_nfo(tvshow_file)
|
||||
|
||||
# buscamos en las series de trakt
|
||||
for show in shows:
|
||||
show_aux = show[1].to_dict()
|
||||
|
||||
try:
|
||||
_id_trakt = show_aux['ids'].get(type_id, None)
|
||||
# logger.debug("ID ES %s" % _id_trakt)
|
||||
if _id_trakt:
|
||||
if _id == _id_trakt:
|
||||
logger.debug("ENCONTRADO!! %s" % show_aux)
|
||||
|
||||
# creamos el diccionario de trakt para la serie encontrada con el valor que tiene "visto"
|
||||
dict_trakt_show = {}
|
||||
|
||||
for idx_season, season in enumerate(show_aux['seasons']):
|
||||
for idx_episode, episode in enumerate(show_aux['seasons'][idx_season]['episodes']):
|
||||
sea_epi = "%sx%s" % (show_aux['seasons'][idx_season]['number'],
|
||||
str(show_aux['seasons'][idx_season]['episodes'][idx_episode][
|
||||
'number']).zfill(2))
|
||||
|
||||
dict_trakt_show[sea_epi] = show_aux['seasons'][idx_season]['episodes'][idx_episode][
|
||||
'watched']
|
||||
logger.debug("dict_trakt_show %s " % dict_trakt_show)
|
||||
|
||||
# obtenemos las keys que son episodios
|
||||
regex_epi = re.compile('\d+x\d+')
|
||||
keys_episodes = [key for key in serie.library_playcounts if regex_epi.match(key)]
|
||||
# obtenemos las keys que son temporadas
|
||||
keys_seasons = [key for key in serie.library_playcounts if 'season ' in key]
|
||||
# obtenemos los numeros de las keys temporadas
|
||||
seasons = [key.strip('season ') for key in keys_seasons]
|
||||
|
||||
# marcamos los episodios vistos
|
||||
for k in keys_episodes:
|
||||
serie.library_playcounts[k] = dict_trakt_show.get(k, 0)
|
||||
|
||||
for season in seasons:
|
||||
episodios_temporada = 0
|
||||
episodios_vistos_temporada = 0
|
||||
|
||||
# obtenemos las keys de los episodios de una determinada temporada
|
||||
keys_season_episodes = [key for key in keys_episodes if key.startswith("%sx" % season)]
|
||||
|
||||
for k in keys_season_episodes:
|
||||
episodios_temporada += 1
|
||||
if serie.library_playcounts[k] > 0:
|
||||
episodios_vistos_temporada += 1
|
||||
|
||||
# se comprueba que si todos los episodios están vistos, se marque la temporada como vista
|
||||
if episodios_temporada == episodios_vistos_temporada:
|
||||
serie.library_playcounts.update({"season %s" % season: 1})
|
||||
|
||||
temporada = 0
|
||||
temporada_vista = 0
|
||||
|
||||
for k in keys_seasons:
|
||||
temporada += 1
|
||||
if serie.library_playcounts[k] > 0:
|
||||
temporada_vista += 1
|
||||
|
||||
# se comprueba que si todas las temporadas están vistas, se marque la serie como vista
|
||||
if temporada == temporada_vista:
|
||||
serie.library_playcounts.update({serie.title: 1})
|
||||
|
||||
logger.debug("los valores nuevos %s " % serie.library_playcounts)
|
||||
filetools.write(tvshow_file, head_nfo + serie.tojson())
|
||||
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
else:
|
||||
logger.error("no se ha podido obtener el id, trakt tiene: %s" % show_aux['ids'])
|
||||
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def sync_trakt_kodi(silent=True):
|
||||
# Para que la sincronizacion no sea silenciosa vale con silent=False
|
||||
if xbmc.getCondVisibility('System.HasAddon("script.trakt")'):
|
||||
notificacion = True
|
||||
if (not config.get_setting("sync_trakt_notification", "videolibrary") and
|
||||
platformtools.is_playing()):
|
||||
notificacion = False
|
||||
|
||||
xbmc.executebuiltin('RunScript(script.trakt,action=sync,silent=%s)' % silent)
|
||||
logger.info("Sincronizacion con Trakt iniciada")
|
||||
|
||||
if notificacion:
|
||||
platformtools.dialog_notification(config.get_localized_string(20000),
|
||||
config.get_localized_string(60045),
|
||||
icon=0,
|
||||
time=2000)
|
||||
|
||||
|
||||
def mark_content_as_watched_on_kodi(item, value=1):
|
||||
"""
|
||||
marca el contenido como visto o no visto en la libreria de Kodi
|
||||
@type item: item
|
||||
@param item: elemento a marcar
|
||||
@type value: int
|
||||
@param value: >0 para visto, 0 para no visto
|
||||
"""
|
||||
logger.info()
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
payload_f = ''
|
||||
|
||||
if item.contentType == "movie":
|
||||
movieid = 0
|
||||
payload = {"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies",
|
||||
"params": {"properties": ["title", "playcount", "originaltitle", "file"]},
|
||||
"id": 1}
|
||||
|
||||
data = get_data(payload)
|
||||
if 'result' in data and "movies" in data['result']:
|
||||
|
||||
if item.strm_path: #Si Item es de un episodio
|
||||
filename = filetools.basename(item.strm_path)
|
||||
head, tail = filetools.split(filetools.split(item.strm_path)[0])
|
||||
else: #Si Item es de la Serie
|
||||
filename = filetools.basename(item.path)
|
||||
head, tail = filetools.split(filetools.split(item.path)[0])
|
||||
path = filetools.join(tail, filename)
|
||||
|
||||
for d in data['result']['movies']:
|
||||
if d['file'].replace("/", "\\").endswith(path.replace("/", "\\")):
|
||||
# logger.debug("marco la pelicula como vista")
|
||||
movieid = d['movieid']
|
||||
break
|
||||
|
||||
if movieid != 0:
|
||||
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": {
|
||||
"movieid": movieid, "playcount": value}, "id": 1}
|
||||
|
||||
else: # item.contentType != 'movie'
|
||||
episodeid = 0
|
||||
payload = {"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes",
|
||||
"params": {"properties": ["title", "playcount", "showtitle", "file", "tvshowid"]},
|
||||
"id": 1}
|
||||
|
||||
data = get_data(payload)
|
||||
if 'result' in data and "episodes" in data['result']:
|
||||
|
||||
if item.strm_path: #Si Item es de un episodio
|
||||
filename = filetools.basename(item.strm_path)
|
||||
head, tail = filetools.split(filetools.split(item.strm_path)[0])
|
||||
else: #Si Item es de la Serie
|
||||
filename = filetools.basename(item.path)
|
||||
head, tail = filetools.split(filetools.split(item.path)[0])
|
||||
path = filetools.join(tail, filename)
|
||||
|
||||
for d in data['result']['episodes']:
|
||||
|
||||
if d['file'].replace("/", "\\").endswith(path.replace("/", "\\")):
|
||||
# logger.debug("marco el episodio como visto")
|
||||
episodeid = d['episodeid']
|
||||
break
|
||||
|
||||
if episodeid != 0:
|
||||
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetEpisodeDetails", "params": {
|
||||
"episodeid": episodeid, "playcount": value}, "id": 1}
|
||||
|
||||
if payload_f:
|
||||
# Marcar como visto
|
||||
data = get_data(payload_f)
|
||||
# logger.debug(str(data))
|
||||
if data['result'] != 'OK':
|
||||
logger.error("ERROR al poner el contenido como visto")
|
||||
|
||||
|
||||
def mark_season_as_watched_on_kodi(item, value=1):
|
||||
"""
|
||||
marca toda la temporada como vista o no vista en la libreria de Kodi
|
||||
@type item: item
|
||||
@param item: elemento a marcar
|
||||
@type value: int
|
||||
@param value: >0 para visto, 0 para no visto
|
||||
"""
|
||||
logger.info()
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
|
||||
# Solo podemos marcar la temporada como vista en la BBDD de Kodi si la BBDD es local,
|
||||
# en caso de compartir BBDD esta funcionalidad no funcionara
|
||||
if config.get_setting("db_mode", "videolibrary"):
|
||||
return
|
||||
|
||||
if value == 0:
|
||||
value = 'Null'
|
||||
|
||||
request_season = ''
|
||||
if item.contentSeason > -1:
|
||||
request_season = ' and c12= %s' % item.contentSeason
|
||||
|
||||
tvshows_path = filetools.join(config.get_videolibrary_path(), config.get_setting("folder_tvshows"))
|
||||
item_path1 = "%" + item.path.replace("\\\\", "\\").replace(tvshows_path, "")
|
||||
if item_path1[:-1] != "\\":
|
||||
item_path1 += "\\"
|
||||
item_path2 = item_path1.replace("\\", "/")
|
||||
|
||||
sql = 'update files set playCount= %s where idFile in ' \
|
||||
'(select idfile from episode_view where (strPath like "%s" or strPath like "%s")%s)' % \
|
||||
(value, item_path1, item_path2, request_season)
|
||||
|
||||
execute_sql_kodi(sql)
|
||||
|
||||
|
||||
def mark_content_as_watched_on_alfa(path):
|
||||
from channels import videolibrary
|
||||
from core import videolibrarytools
|
||||
from core import scrapertools
|
||||
from core import filetools
|
||||
import re
|
||||
"""
|
||||
marca toda la serie o película como vista o no vista en la Videoteca de Alfa basado en su estado en la Videoteca de Kodi
|
||||
@type str: path
|
||||
@param path: carpeta de contenido a marcar
|
||||
"""
|
||||
logger.info()
|
||||
#logger.debug("path: " + path)
|
||||
|
||||
FOLDER_MOVIES = config.get_setting("folder_movies")
|
||||
FOLDER_TVSHOWS = config.get_setting("folder_tvshows")
|
||||
VIDEOLIBRARY_PATH = config.get_videolibrary_config_path()
|
||||
if not VIDEOLIBRARY_PATH:
|
||||
return
|
||||
|
||||
# Solo podemos marcar el contenido como vista en la BBDD de Kodi si la BBDD es local,
|
||||
# en caso de compartir BBDD esta funcionalidad no funcionara
|
||||
#if config.get_setting("db_mode", "videolibrary"):
|
||||
# return
|
||||
|
||||
path2 = ''
|
||||
if "special://" in VIDEOLIBRARY_PATH:
|
||||
if FOLDER_TVSHOWS in path:
|
||||
path2 = re. sub(r'.*?%s' % FOLDER_TVSHOWS, VIDEOLIBRARY_PATH + "/" + FOLDER_TVSHOWS, path).replace("\\", "/")
|
||||
if FOLDER_MOVIES in path:
|
||||
path2 = re. sub(r'.*?%s' % FOLDER_MOVIES, VIDEOLIBRARY_PATH + "/" + FOLDER_MOVIES, path).replace("\\", "/")
|
||||
|
||||
if "\\" in path:
|
||||
path = path.replace("/", "\\")
|
||||
head_nfo, item = videolibrarytools.read_nfo(path) #Leo el .nfo del contenido
|
||||
|
||||
if FOLDER_TVSHOWS in path: #Compruebo si es CINE o SERIE
|
||||
contentType = "episode_view" #Marco la tabla de BBDD de Kodi Video
|
||||
nfo_name = "tvshow.nfo" #Construyo el nombre del .nfo
|
||||
path1 = path.replace("\\\\", "\\").replace(nfo_name, '') #para la SQL solo necesito la carpeta
|
||||
if not path2:
|
||||
path2 = path1.replace("\\", "/") #Formato no Windows
|
||||
else:
|
||||
path2 = path2.replace(nfo_name, '')
|
||||
|
||||
else:
|
||||
contentType = "movie_view" #Marco la tabla de BBDD de Kodi Video
|
||||
path1 = path.replace("\\\\", "\\") #Formato Windows
|
||||
if not path2:
|
||||
path2 = path1.replace("\\", "/") #Formato no Windows
|
||||
nfo_name = scrapertools.find_single_match(path2, '\]\/(.*?)$') #Construyo el nombre del .nfo
|
||||
path1 = path1.replace(nfo_name, '') #para la SQL solo necesito la carpeta
|
||||
path2 = path2.replace(nfo_name, '') #para la SQL solo necesito la carpeta
|
||||
path2 = filetools.remove_smb_credential(path2) #Si el archivo está en un servidor SMB, quiamos las credenciales
|
||||
|
||||
#Ejecutmos la sentencia SQL
|
||||
sql = 'select strFileName, playCount from %s where (strPath like "%s" or strPath like "%s")' % (contentType, path1, path2)
|
||||
nun_records = 0
|
||||
records = None
|
||||
nun_records, records = execute_sql_kodi(sql) #ejecución de la SQL
|
||||
if nun_records == 0: #hay error?
|
||||
logger.error("Error en la SQL: " + sql + ": 0 registros")
|
||||
return #salimos: o no está catalogado en Kodi, o hay un error en la SQL
|
||||
|
||||
for title, playCount in records: #Ahora recorremos todos los registros obtenidos
|
||||
if contentType == "episode_view":
|
||||
title_plain = title.replace('.strm', '') #Si es Serie, quitamos el sufijo .strm
|
||||
else:
|
||||
title_plain = scrapertools.find_single_match(item.strm_path, '.(.*?\s\[.*?\])') #si es peli, quitamos el título
|
||||
if playCount is None or playCount == 0: #todavía no se ha visto, lo ponemos a 0
|
||||
playCount_final = 0
|
||||
elif playCount >= 1:
|
||||
playCount_final = 1
|
||||
title_plain = title_plain.decode("utf-8").encode("utf-8") #Hacemos esto porque si no genera esto: u'title_plain'
|
||||
item.library_playcounts.update({title_plain: playCount_final}) #actualizamos el playCount del .nfo
|
||||
|
||||
if item.infoLabels['mediatype'] == "tvshow": #Actualizamos los playCounts de temporadas y Serie
|
||||
for season in item.library_playcounts:
|
||||
if "season" in season: #buscamos las etiquetas "season" dentro de playCounts
|
||||
season_num = int(scrapertools.find_single_match(season, 'season (\d+)')) #salvamos el núm, de Temporada
|
||||
item = videolibrary.check_season_playcount(item, season_num) #llamamos al método que actualiza Temps. y Series
|
||||
|
||||
filetools.write(path, head_nfo + item.tojson())
|
||||
|
||||
#logger.debug(item)
|
||||
|
||||
|
||||
def get_data(payload):
|
||||
"""
|
||||
obtiene la información de la llamada JSON-RPC con la información pasada en payload
|
||||
@type payload: dict
|
||||
@param payload: data
|
||||
:return:
|
||||
"""
|
||||
logger.info("payload: %s" % payload)
|
||||
# Required header for XBMC JSON-RPC calls, otherwise you'll get a 415 HTTP response code - Unsupported media type
|
||||
headers = {'content-type': 'application/json'}
|
||||
|
||||
if config.get_setting("db_mode", "videolibrary"):
|
||||
try:
|
||||
try:
|
||||
xbmc_port = config.get_setting("xbmc_puerto", "videolibrary")
|
||||
except:
|
||||
xbmc_port = 0
|
||||
|
||||
xbmc_json_rpc_url = "http://" + config.get_setting("xbmc_host", "videolibrary") + ":" + str(
|
||||
xbmc_port) + "/jsonrpc"
|
||||
req = urllib2.Request(xbmc_json_rpc_url, data=jsontools.dump(payload), headers=headers)
|
||||
f = urllib2.urlopen(req)
|
||||
response = f.read()
|
||||
f.close()
|
||||
|
||||
logger.info("get_data: response %s" % response)
|
||||
data = jsontools.load(response)
|
||||
except Exception, ex:
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex).__name__, ex.args)
|
||||
logger.error("error en xbmc_json_rpc_url: %s" % message)
|
||||
data = ["error"]
|
||||
else:
|
||||
try:
|
||||
data = jsontools.load(xbmc.executeJSONRPC(jsontools.dump(payload)))
|
||||
except Exception, ex:
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex).__name__, ex.args)
|
||||
logger.error("error en xbmc.executeJSONRPC: %s" % message)
|
||||
data = ["error"]
|
||||
|
||||
logger.info("data: %s" % data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def update(folder_content=config.get_setting("folder_tvshows"), folder=""):
|
||||
"""
|
||||
Actualiza la libreria dependiendo del tipo de contenido y la ruta que se le pase.
|
||||
|
||||
@type folder_content: str
|
||||
@param folder_content: tipo de contenido para actualizar, series o peliculas
|
||||
@type folder: str
|
||||
@param folder: nombre de la carpeta a escanear.
|
||||
"""
|
||||
logger.info(folder)
|
||||
|
||||
payload = {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "VideoLibrary.Scan",
|
||||
"id": 1
|
||||
}
|
||||
|
||||
if folder:
|
||||
videolibrarypath = config.get_videolibrary_config_path()
|
||||
|
||||
if folder.endswith('/') or folder.endswith('\\'):
|
||||
folder = folder[:-1]
|
||||
|
||||
update_path = None
|
||||
|
||||
if videolibrarypath.startswith("special:"):
|
||||
if videolibrarypath.endswith('/'):
|
||||
videolibrarypath = videolibrarypath[:-1]
|
||||
update_path = videolibrarypath + "/" + folder_content + "/" + folder + "/"
|
||||
else:
|
||||
update_path = filetools.join(videolibrarypath, folder_content, folder) + "/"
|
||||
|
||||
if not update_path.startswith("smb://"):
|
||||
payload["params"] = {"directory": update_path}
|
||||
|
||||
while xbmc.getCondVisibility('Library.IsScanningVideo()'):
|
||||
xbmc.sleep(500)
|
||||
|
||||
data = get_data(payload)
|
||||
|
||||
|
||||
def clean(mostrar_dialogo=False):
|
||||
"""
|
||||
limpia la libreria de elementos que no existen
|
||||
@param mostrar_dialogo: muestra el cuadro de progreso mientras se limpia la videoteca
|
||||
@type mostrar_dialogo: bool
|
||||
"""
|
||||
logger.info()
|
||||
payload = {"jsonrpc": "2.0", "method": "VideoLibrary.Clean", "id": 1,
|
||||
"params": {"showdialogs": mostrar_dialogo}}
|
||||
data = get_data(payload)
|
||||
|
||||
if data.get('result', False) == 'OK':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def search_library_path():
|
||||
sql = 'SELECT strPath FROM path WHERE strPath LIKE "special://%/plugin.video.alfa/library/" AND idParentPath ISNULL'
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records >= 1:
|
||||
logger.debug(records[0][0])
|
||||
return records[0][0]
|
||||
return None
|
||||
|
||||
|
||||
def set_content(content_type, silent=False):
|
||||
"""
|
||||
Procedimiento para auto-configurar la videoteca de kodi con los valores por defecto
|
||||
@type content_type: str ('movie' o 'tvshow')
|
||||
@param content_type: tipo de contenido para configurar, series o peliculas
|
||||
"""
|
||||
continuar = True
|
||||
msg_text = ""
|
||||
videolibrarypath = config.get_setting("videolibrarypath")
|
||||
|
||||
if content_type == 'movie':
|
||||
scraper = [config.get_localized_string(70093), config.get_localized_string(70096)]
|
||||
seleccion = platformtools.dialog_select(config.get_localized_string(70094), scraper)
|
||||
|
||||
# Instalar The Movie Database
|
||||
if seleccion == -1 or seleccion == 0:
|
||||
if not xbmc.getCondVisibility('System.HasAddon(metadata.themoviedb.org)'):
|
||||
if not silent:
|
||||
# Preguntar si queremos instalar metadata.themoviedb.org
|
||||
install = platformtools.dialog_yesno(config.get_localized_string(60046))
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Instalar metadata.themoviedb.org
|
||||
xbmc.executebuiltin('xbmc.installaddon(metadata.themoviedb.org)', True)
|
||||
logger.info("Instalado el Scraper de películas de TheMovieDB")
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and xbmc.getCondVisibility('System.HasAddon(metadata.themoviedb.org)'))
|
||||
if not continuar:
|
||||
msg_text = config.get_localized_string(60047)
|
||||
if continuar:
|
||||
xbmc.executebuiltin('xbmc.addon.opensettings(metadata.themoviedb.org)', True)
|
||||
|
||||
# Instalar Universal Movie Scraper
|
||||
elif seleccion == 1:
|
||||
if continuar and not xbmc.getCondVisibility('System.HasAddon(metadata.universal)'):
|
||||
continuar = False
|
||||
if not silent:
|
||||
# Preguntar si queremos instalar metadata.universal
|
||||
install = platformtools.dialog_yesno(config.get_localized_string(70095))
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
xbmc.executebuiltin('xbmc.installaddon(metadata.universal)', True)
|
||||
if xbmc.getCondVisibility('System.HasAddon(metadata.universal)'):
|
||||
continuar = True
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and continuar)
|
||||
if not continuar:
|
||||
msg_text = config.get_localized_string(70097)
|
||||
if continuar:
|
||||
xbmc.executebuiltin('xbmc.addon.opensettings(metadata.universal)', True)
|
||||
|
||||
else: # SERIES
|
||||
scraper = [config.get_localized_string(70098), config.get_localized_string(70093)]
|
||||
seleccion = platformtools.dialog_select(config.get_localized_string(70107), scraper)
|
||||
|
||||
# Instalar The TVDB
|
||||
if seleccion == -1 or seleccion == 0:
|
||||
if not xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'):
|
||||
if not silent:
|
||||
# Preguntar si queremos instalar metadata.tvdb.com
|
||||
install = platformtools.dialog_yesno(config.get_localized_string(60048))
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Instalar metadata.tvdb.com
|
||||
xbmc.executebuiltin('xbmc.installaddon(metadata.tvdb.com)', True)
|
||||
logger.info("Instalado el Scraper de series de The TVDB")
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'))
|
||||
if not continuar:
|
||||
msg_text = config.get_localized_string(70099)
|
||||
if continuar:
|
||||
xbmc.executebuiltin('xbmc.addon.opensettings(metadata.tvdb.com)', True)
|
||||
|
||||
# Instalar The Movie Database
|
||||
elif seleccion == 1:
|
||||
if continuar and not xbmc.getCondVisibility('System.HasAddon(metadata.tvshows.themoviedb.org)'):
|
||||
continuar = False
|
||||
if not silent:
|
||||
# Preguntar si queremos instalar metadata.tvshows.themoviedb.org
|
||||
install = platformtools.dialog_yesno(config.get_localized_string(70100))
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Instalar metadata.tvshows.themoviedb.org
|
||||
xbmc.executebuiltin('xbmc.installaddon(metadata.tvshows.themoviedb.org)', True)
|
||||
if xbmc.getCondVisibility('System.HasAddon(metadata.tvshows.themoviedb.org)'):
|
||||
continuar = True
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and continuar)
|
||||
if not continuar:
|
||||
msg_text = config.get_localized_string(60047)
|
||||
if continuar:
|
||||
xbmc.executebuiltin('xbmc.addon.opensettings(metadata.tvshows.themoviedb.org)', True)
|
||||
|
||||
idPath = 0
|
||||
idParentPath = 0
|
||||
if continuar:
|
||||
continuar = False
|
||||
|
||||
# Buscamos el idPath
|
||||
sql = 'SELECT MAX(idPath) FROM path'
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
idPath = records[0][0] + 1
|
||||
|
||||
sql_videolibrarypath = videolibrarypath
|
||||
if sql_videolibrarypath.startswith("special://"):
|
||||
sql_videolibrarypath = sql_videolibrarypath.replace('/profile/', '/%/').replace('/home/userdata/', '/%/')
|
||||
sep = '/'
|
||||
elif sql_videolibrarypath.startswith("smb://"):
|
||||
sep = '/'
|
||||
else:
|
||||
sep = os.sep
|
||||
|
||||
if not sql_videolibrarypath.endswith(sep):
|
||||
sql_videolibrarypath += sep
|
||||
|
||||
# Buscamos el idParentPath
|
||||
sql = 'SELECT idPath, strPath FROM path where strPath LIKE "%s"' % sql_videolibrarypath
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
idParentPath = records[0][0]
|
||||
videolibrarypath = records[0][1][:-1]
|
||||
continuar = True
|
||||
else:
|
||||
# No existe videolibrarypath en la BD: la insertamos
|
||||
sql_videolibrarypath = videolibrarypath
|
||||
if not sql_videolibrarypath.endswith(sep):
|
||||
sql_videolibrarypath += sep
|
||||
|
||||
sql = 'INSERT INTO path (idPath, strPath, scanRecursive, useFolderNames, noUpdate, exclude) VALUES ' \
|
||||
'(%s, "%s", 0, 0, 0, 0)' % (idPath, sql_videolibrarypath)
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
continuar = True
|
||||
idParentPath = idPath
|
||||
idPath += 1
|
||||
else:
|
||||
msg_text = config.get_localized_string(70101)
|
||||
|
||||
if continuar:
|
||||
continuar = False
|
||||
|
||||
# Fijamos strContent, strScraper, scanRecursive y strSettings
|
||||
if content_type == 'movie':
|
||||
strContent = 'movies'
|
||||
scanRecursive = 2147483647
|
||||
if seleccion == -1 or seleccion == 0:
|
||||
strScraper = 'metadata.themoviedb.org'
|
||||
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.themoviedb.org/settings.xml")
|
||||
elif seleccion == 1:
|
||||
strScraper = 'metadata.universal'
|
||||
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.universal/settings.xml")
|
||||
settings_data = filetools.read(path_settings)
|
||||
strSettings = ' '.join(settings_data.split()).replace("> <", "><")
|
||||
strSettings = strSettings.replace("\"","\'")
|
||||
strActualizar = "¿Desea configurar este Scraper en español como opción por defecto para películas?"
|
||||
if not videolibrarypath.endswith(sep):
|
||||
videolibrarypath += sep
|
||||
strPath = videolibrarypath + config.get_setting("folder_movies") + sep
|
||||
else:
|
||||
strContent = 'tvshows'
|
||||
scanRecursive = 0
|
||||
if seleccion == -1 or seleccion == 0:
|
||||
strScraper = 'metadata.tvdb.com'
|
||||
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.tvdb.com/settings.xml")
|
||||
elif seleccion == 1:
|
||||
strScraper = 'metadata.tvshows.themoviedb.org'
|
||||
path_settings = xbmc.translatePath("special://profile/addon_data/metadata.tvshows.themoviedb.org/settings.xml")
|
||||
settings_data = filetools.read(path_settings)
|
||||
strSettings = ' '.join(settings_data.split()).replace("> <", "><")
|
||||
strSettings = strSettings.replace("\"","\'")
|
||||
strActualizar = "¿Desea configurar este Scraper en español como opción por defecto para series?"
|
||||
if not videolibrarypath.endswith(sep):
|
||||
videolibrarypath += sep
|
||||
strPath = videolibrarypath + config.get_setting("folder_tvshows") + sep
|
||||
|
||||
logger.info("%s: %s" % (content_type, strPath))
|
||||
# Comprobamos si ya existe strPath en la BD para evitar duplicados
|
||||
sql = 'SELECT idPath FROM path where strPath="%s"' % strPath
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
sql = ""
|
||||
if nun_records == 0:
|
||||
# Insertamos el scraper
|
||||
sql = 'INSERT INTO path (idPath, strPath, strContent, strScraper, scanRecursive, useFolderNames, ' \
|
||||
'strSettings, noUpdate, exclude, idParentPath) VALUES (%s, "%s", "%s", "%s", %s, 0, ' \
|
||||
'"%s", 0, 0, %s)' % (
|
||||
idPath, strPath, strContent, strScraper, scanRecursive, strSettings, idParentPath)
|
||||
else:
|
||||
if not silent:
|
||||
# Preguntar si queremos configurar themoviedb.org como opcion por defecto
|
||||
actualizar = platformtools.dialog_yesno(config.get_localized_string(70098), strActualizar)
|
||||
else:
|
||||
actualizar = True
|
||||
|
||||
if actualizar:
|
||||
# Actualizamos el scraper
|
||||
idPath = records[0][0]
|
||||
sql = 'UPDATE path SET strContent="%s", strScraper="%s", scanRecursive=%s, strSettings="%s" ' \
|
||||
'WHERE idPath=%s' % (strContent, strScraper, scanRecursive, strSettings, idPath)
|
||||
|
||||
if sql:
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
continuar = True
|
||||
|
||||
if not continuar:
|
||||
msg_text = config.get_localized_string(60055)
|
||||
|
||||
if not continuar:
|
||||
heading = config.get_localized_string(70102) % content_type
|
||||
elif content_type == 'SERIES' and not xbmc.getCondVisibility(
|
||||
'System.HasAddon(metadata.tvshows.themoviedb.org)'):
|
||||
heading = config.get_localized_string(70103) % content_type
|
||||
msg_text = config.get_localized_string(60058)
|
||||
else:
|
||||
heading = config.get_localized_string(70103) % content_type
|
||||
msg_text = config.get_localized_string(70104)
|
||||
platformtools.dialog_notification(heading, msg_text, icon=1, time=3000)
|
||||
|
||||
logger.info("%s: %s" % (heading, msg_text))
|
||||
|
||||
|
||||
def execute_sql_kodi(sql):
|
||||
"""
|
||||
Ejecuta la consulta sql contra la base de datos de kodi
|
||||
@param sql: Consulta sql valida
|
||||
@type sql: str
|
||||
@return: Numero de registros modificados o devueltos por la consulta
|
||||
@rtype nun_records: int
|
||||
@return: lista con el resultado de la consulta
|
||||
@rtype records: list of tuples
|
||||
"""
|
||||
logger.info()
|
||||
file_db = ""
|
||||
nun_records = 0
|
||||
records = None
|
||||
|
||||
# Buscamos el archivo de la BBDD de videos segun la version de kodi
|
||||
video_db = config.get_platform(True)['video_db']
|
||||
if video_db:
|
||||
file_db = filetools.join(xbmc.translatePath("special://userdata/Database"), video_db)
|
||||
|
||||
# metodo alternativo para localizar la BBDD
|
||||
if not file_db or not filetools.exists(file_db):
|
||||
file_db = ""
|
||||
for f in filetools.listdir(xbmc.translatePath("special://userdata/Database")):
|
||||
path_f = filetools.join(xbmc.translatePath("special://userdata/Database"), f)
|
||||
|
||||
if filetools.isfile(path_f) and f.lower().startswith('myvideos') and f.lower().endswith('.db'):
|
||||
file_db = path_f
|
||||
break
|
||||
|
||||
if file_db:
|
||||
logger.info("Archivo de BD: %s" % file_db)
|
||||
conn = None
|
||||
try:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect(file_db)
|
||||
cursor = conn.cursor()
|
||||
|
||||
logger.info("Ejecutando sql: %s" % sql)
|
||||
cursor.execute(sql)
|
||||
conn.commit()
|
||||
|
||||
records = cursor.fetchall()
|
||||
if sql.lower().startswith("select"):
|
||||
nun_records = len(records)
|
||||
if nun_records == 1 and records[0][0] is None:
|
||||
nun_records = 0
|
||||
records = []
|
||||
else:
|
||||
nun_records = conn.total_changes
|
||||
|
||||
conn.close()
|
||||
logger.info("Consulta ejecutada. Registros: %s" % nun_records)
|
||||
|
||||
except:
|
||||
logger.error("Error al ejecutar la consulta sql")
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
else:
|
||||
logger.debug("Base de datos no encontrada")
|
||||
|
||||
return nun_records, records
|
||||
|
||||
|
||||
def add_sources(path):
|
||||
logger.info()
|
||||
from xml.dom import minidom
|
||||
|
||||
SOURCES_PATH = xbmc.translatePath("special://userdata/sources.xml")
|
||||
|
||||
if os.path.exists(SOURCES_PATH):
|
||||
xmldoc = minidom.parse(SOURCES_PATH)
|
||||
else:
|
||||
# Crear documento
|
||||
xmldoc = minidom.Document()
|
||||
nodo_sources = xmldoc.createElement("sources")
|
||||
|
||||
for type in ['programs', 'video', 'music', 'picture', 'files']:
|
||||
nodo_type = xmldoc.createElement(type)
|
||||
element_default = xmldoc.createElement("default")
|
||||
element_default.setAttribute("pathversion", "1")
|
||||
nodo_type.appendChild(element_default)
|
||||
nodo_sources.appendChild(nodo_type)
|
||||
xmldoc.appendChild(nodo_sources)
|
||||
|
||||
# Buscamos el nodo video
|
||||
nodo_video = xmldoc.childNodes[0].getElementsByTagName("video")[0]
|
||||
|
||||
# Buscamos el path dentro de los nodos_path incluidos en el nodo_video
|
||||
nodos_paths = nodo_video.getElementsByTagName("path")
|
||||
list_path = [p.firstChild.data for p in nodos_paths]
|
||||
logger.debug(list_path)
|
||||
if path in list_path:
|
||||
logger.debug("La ruta %s ya esta en sources.xml" % path)
|
||||
return
|
||||
logger.debug("La ruta %s NO esta en sources.xml" % path)
|
||||
|
||||
# Si llegamos aqui es por q el path no esta en sources.xml, asi q lo incluimos
|
||||
nodo_source = xmldoc.createElement("source")
|
||||
|
||||
# Nodo <name>
|
||||
nodo_name = xmldoc.createElement("name")
|
||||
sep = os.sep
|
||||
if path.startswith("special://") or path.startswith("smb://"):
|
||||
sep = "/"
|
||||
name = path
|
||||
if path.endswith(sep):
|
||||
name = path[:-1]
|
||||
nodo_name.appendChild(xmldoc.createTextNode(name.rsplit(sep)[-1]))
|
||||
nodo_source.appendChild(nodo_name)
|
||||
|
||||
# Nodo <path>
|
||||
nodo_path = xmldoc.createElement("path")
|
||||
nodo_path.setAttribute("pathversion", "1")
|
||||
nodo_path.appendChild(xmldoc.createTextNode(path))
|
||||
nodo_source.appendChild(nodo_path)
|
||||
|
||||
# Nodo <allowsharing>
|
||||
nodo_allowsharing = xmldoc.createElement("allowsharing")
|
||||
nodo_allowsharing.appendChild(xmldoc.createTextNode('true'))
|
||||
nodo_source.appendChild(nodo_allowsharing)
|
||||
|
||||
# Añadimos <source> a <video>
|
||||
nodo_video.appendChild(nodo_source)
|
||||
|
||||
# Guardamos los cambios
|
||||
filetools.write(SOURCES_PATH,
|
||||
'\n'.join([x for x in xmldoc.toprettyxml().encode("utf-8").splitlines() if x.strip()]))
|
||||
|
||||
|
||||
def ask_set_content(flag, silent=False):
|
||||
logger.info()
|
||||
logger.debug("videolibrary_kodi_flag %s" % config.get_setting("videolibrary_kodi_flag"))
|
||||
logger.debug("videolibrary_kodi %s" % config.get_setting("videolibrary_kodi"))
|
||||
|
||||
def do_config():
|
||||
logger.debug("hemos aceptado")
|
||||
config.set_setting("videolibrary_kodi", True)
|
||||
set_content("movie", silent=True)
|
||||
set_content("tvshow", silent=True)
|
||||
add_sources(config.get_setting("videolibrarypath"))
|
||||
add_sources(config.get_setting("downloadpath"))
|
||||
|
||||
if not silent:
|
||||
heading = config.get_localized_string(59971)
|
||||
linea1 = config.get_localized_string(70105)
|
||||
linea2 = config.get_localized_string(70106)
|
||||
if platformtools.dialog_yesno(heading, linea1, linea2):
|
||||
do_config()
|
||||
else:
|
||||
# no hemos aceptado
|
||||
config.set_setting("videolibrary_kodi", False)
|
||||
|
||||
else:
|
||||
do_config()
|
||||
|
||||
config.set_setting("videolibrary_kodi_flag", flag)
|
||||
Reference in New Issue
Block a user