- Ottimizzata e migliorata la ricerca globale
- fix Mega
- altri cambiamenti minori
This commit is contained in:
marco
2020-05-31 11:46:47 +02:00
parent d26a2def96
commit d62113f9d2
143 changed files with 2775 additions and 9879 deletions

View File

@@ -1,19 +1,13 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# Parámetros de configuración (kodi)
# Configuration parameters (kodi)
# ------------------------------------------------------------
#from builtins import str
import sys
# from builtins import str
import sys, os, re, xbmc, xbmcaddon
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import os
import re
import xbmc
import xbmcaddon
PLUGIN_NAME = "kod"
__settings__ = xbmcaddon.Addon(id="plugin.video." + PLUGIN_NAME)
@@ -29,7 +23,7 @@ def get_addon_core():
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
Returns the version number of the addon, and optionally fix number if there is one
'''
if with_fix:
return __settings__.getAddonInfo('version') + " " + get_addon_version_fix()
@@ -61,17 +55,17 @@ def dev_mode():
def get_platform(full_version=False):
"""
Devuelve la información la version de xbmc o kodi sobre el que se ejecuta el plugin
Returns the information the version of xbmc or kodi on which the plugin is run
@param full_version: indica si queremos toda la informacion o no
@param full_version: indicates if we want all the information or not
@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.
@return: If the full_version parameter is True, a dictionary with the following keys is returned:
'num_version': (float) version number in XX.X format
'name_version': (str) key name of each version
'video_db': (str) name of the file that contains the video database
'plaform': (str) is made up of "kodi-" or "xbmc-" plus the version name as appropriate.
If the full_version parameter is False (default) the value of the 'plaform' key from the previous dictionary is returned.
"""
ret = {}
@@ -130,7 +124,7 @@ def get_channel_url(findhostMethod=None, name=None):
return channels_data[name]
def get_system_platform():
""" fonction: pour recuperer la platform que xbmc tourne """
""" function: to recover the platform that xbmc is running """
platform = "unknown"
if xbmc.getCondVisibility("system.platform.linux"):
platform = "linux"
@@ -172,7 +166,7 @@ def enable_disable_autorun(is_enabled):
return True
def get_all_settings_addon():
# Lee el archivo settings.xml y retorna un diccionario con {id: value}
# Read the settings.xml file and return a dictionary with {id: value}
from core import scrapertools
infile = open(os.path.join(get_data_path(), "settings.xml"), "r")
@@ -194,27 +188,26 @@ def open_settings():
def get_setting(name, channel="", server="", default=None):
"""
Retorna el valor de configuracion del parametro solicitado.
Returns the configuration value of the requested parameter.
Devuelve el valor del parametro 'name' en la configuracion global, en la configuracion propia del canal 'channel'
o en la del servidor 'server'.
Returns the value of the parameter 'name' in the global configuration, in the own configuration of the channel 'channel' or in that of the server '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.
The channel and server parameters should not be used simultaneously. If the channel name is specified it will be returned
the result of calling channeltools.get_channel_setting (name, channel, default). If the name of the
server will return the result of calling servertools.get_channel_setting (name, server, default). If I dont know
Specify none of the above will return the value of the parameter in the global configuration if it exists or
the default value otherwise.
@param name: nombre del parametro
@param name: parameter name
@type name: str
@param channel: nombre del canal
@param channel: channel name
@type channel: str
@param server: nombre del servidor
@param server: server name
@type server: str
@param default: valor devuelto en caso de que no exista el parametro name
@param default: return value in case the name parameter does not exist
@type default: any
@return: El valor del parametro 'name'
@return: The value of the parameter 'name'
@rtype: any
"""
@@ -261,26 +254,24 @@ def get_setting(name, channel="", server="", default=None):
def set_setting(name, value, channel="", server=""):
"""
Fija el valor de configuracion del parametro indicado.
Sets the configuration value of the indicated parameter.
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.
Set 'value' as the value of the parameter 'name' in the global configuration or in the own configuration of the channel 'channel'.
Returns the changed value or None if the assignment could not be completed.
Si se especifica el nombre del canal busca en la ruta \addon_data\plugin.video.kod\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.
If the name of the channel is specified, search in the path \ addon_data \ plugin.video.kod \ settings_channels the
channel_data.json file and set the parameter 'name' to the value indicated by 'value'. If the file
channel_data.json does not exist look in the channels folder for the channel.json file and create a channel_data.json file before modifying the 'name' parameter.
If the parameter 'name' does not exist, it adds it, with its value, to the corresponding file.
Parametros:
name -- nombre del parametro
value -- valor del parametro
channel [opcional] -- nombre del canal
Parameters:
name - name of the parameter
value - value of the parameter
channel [optional] - channel name
Retorna:
'value' en caso de que se haya podido fijar el valor y None en caso contrario
Returns:
'value' if the value could be set and None otherwise
"""
if channel:
@@ -304,7 +295,7 @@ def set_setting(name, value, channel="", server=""):
except Exception as ex:
from platformcode import logger
logger.error("Error al convertir '%s' no se guarda el valor \n%s" % (name, ex))
logger.error("Error converting '%s' value is not saved \n%s" % (name, ex))
return None
return value
@@ -322,7 +313,7 @@ def get_localized_string(code):
# All encodings to utf8
elif not PY3 and isinstance(dev, str):
dev = unicode(dev, "utf8", errors="replace").encode("utf8")
# Bytes encodings to utf8
elif PY3 and isinstance(dev, bytes):
dev = dev.decode("utf8")
@@ -365,7 +356,7 @@ def get_runtime_path():
def get_data_path():
dev = xbmc.translatePath(__settings__.getAddonInfo('Profile'))
# Crea el directorio si no existe
# Create the directory if it doesn't exist
if not os.path.exists(dev):
os.makedirs(dev)
@@ -405,7 +396,7 @@ def verify_directories_created():
for path, default in config_paths:
saved_path = get_setting(path)
# videoteca
# video store
if path == "videolibrarypath":
if not saved_path:
saved_path = xbmc_videolibrary.search_library_path()
@@ -435,7 +426,7 @@ def verify_directories_created():
if not filetools.exists(content_path):
logger.debug("Creating %s: %s" % (path, content_path))
# si se crea el directorio
# if the directory is created
filetools.mkdir(content_path)
from platformcode import xbmc_videolibrary
@@ -444,11 +435,10 @@ def verify_directories_created():
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
# We look for the addon.xml file of the active skin
skindir = filetools.join(xbmc.translatePath("special://home"), 'addons', xbmc.getSkinDir(), 'addon.xml')
if not os.path.isdir(skindir): return # No need to show error in log if folder doesn't exist
# We extract the name of the default resolution folder
folder = ""
data = filetools.read(skindir)
res = scrapertools.find_multiple_matches(data, '(<res .*?>)')
@@ -457,22 +447,18 @@ def verify_directories_created():
folder = scrapertools.find_single_match(r, 'folder="([^"]+)"')
break
# Comprobamos si existe en el addon y sino es así, la creamos
# We check if it exists in the addon and if not, we create it
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
# We copy the file to said folder from the 720p folder if it does not exist or if the size is different
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)
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("When checking or creating the resolution folder")
logger.error(traceback.format_exc())

View File

@@ -1,380 +0,0 @@
# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------------
# Updater (kodi)
# --------------------------------------------------------------------------------
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import traceback
import xbmc
import xbmcaddon
import threading
import subprocess
import time
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.kod/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 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.kod/".
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:
#Borra el .zip de instalación de Alfa de la carpeta Packages, por si está corrupto, y que así se pueda descargar de nuevo
version = 'plugin.video.kod-%s.zip' % config.get_addon_version(with_fix=False)
filetools.remove(filetools.join(xbmc.translatePath('special://home'), 'addons', 'packages', version), True)
#Verifica si Kodi tiene algún achivo de Base de Datos de Vídeo de versiones anteriores, entonces los borra
verify_Kodi_video_DB()
#LIBTORRENT: se descarga el binario de Libtorrent cada vez que se actualiza Alfa
# try:
# threading.Thread(target=update_libtorrent).start() # Creamos un Thread independiente, hasta el fin de Kodi
# time.sleep(2) # Dejamos terminar la inicialización...
# except: # Si hay problemas de threading, nos vamos
# logger.error(traceback.format_exc())
# #QUASAR: Preguntamos si se hacen modificaciones a Quasar
# if not filetools.exists(filetools.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) or \
# (filetools.exists(filetools.join(config.get_data_path(), \
# "quasar.json")) and not xbmc.getCondVisibility('System.HasAddon("plugin.video.quasar")')):
# 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 = filetools.join(config.get_data_path(), 'custom_code')
if not filetools.exists(custom_code_dir):
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 = filetools.join(custom_code_json_path, 'custom_code.json')
if not filetools.exists(custom_code_json):
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...
filetools.mkdir(custom_code_dir)
filetools.mkdir(filetools.join(custom_code_dir, 'channels'))
filetools.mkdir(filetools.join(custom_code_dir, 'core'))
filetools.mkdir(filetools.join(custom_code_dir, 'lib'))
filetools.mkdir(filetools.join(custom_code_dir, 'platformcode'))
filetools.mkdir(filetools.join(custom_code_dir, 'resources'))
filetools.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)
if filetools.exists(json_data_file):
filetools.remove(json_data_file)
result = filetools.write(json_data_file, jsontools.dump({"addon_version": ""}))
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 not json_data or not 'addon_version' in json_data:
create_json(custom_code_json_path)
json_data = jsontools.load(filetools.read(json_data_file))
try:
if current_version == json_data['addon_version']:
return
except:
logger.error(traceback.format_exc(1))
#Ahora copiamos los archivos desde el área de Userdata, Custom_code, sobre las carpetas del add-on
for root, folders, files in filetools.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 not filetools.copy(input_file, output_file, silent=True):
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)
try:
#Verificamos que el addon está instalado
if xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % addon_name):
#Path de actualizaciones de Alfa
alfa_addon_updates_mig = filetools.join(config.get_runtime_path(), "lib")
alfa_addon_updates = filetools.join(alfa_addon_updates_mig, 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_mig = filetools.join(xbmc.translatePath(__settings__.getAddonInfo('Path')), \
filetools.join("resources", "site-packages"))
addon_path = filetools.join(addon_path_mig, addon_name)
else:
addon_path_mig = ''
addon_path = ''
#Hay modificaciones en Alfa? Las copiamos al addon, incuidas las carpetas de migración a PY3
if filetools.exists(alfa_addon_updates) and filetools.exists(addon_path):
for root, folders, files in filetools.walk(alfa_addon_updates_mig):
if ('future' in root or 'past' in root) and not 'concurrent' in root:
for file in files:
alfa_addon_updates_mig_folder = root.replace(alfa_addon_updates_mig, addon_path_mig)
if not filetools.exists(alfa_addon_updates_mig_folder):
filetools.mkdir(alfa_addon_updates_mig_folder)
if file.endswith('.pyo') or file.endswith('.pyd'):
continue
input_file = filetools.join(root, file)
output_file = input_file.replace(alfa_addon_updates_mig, addon_path_mig)
if not filetools.copy(input_file, output_file, silent=True):
logger.error('Error en la copia de MIGRACIÓN: Input: %s o Output: %s' % (input_file, output_file))
return False
for root, folders, files in filetools.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 not filetools.copy(input_file, output_file, silent=True):
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))
# Se ha desinstalado Quasar, reseteamos la opción
else:
config.set_setting('addon_quasar_update', False)
if filetools.exists(filetools.join(config.get_data_path(), "%s.json" % addon_name)):
filetools.remove(filetools.join(config.get_data_path(), "%s.json" % addon_name))
return True
except:
logger.error(traceback.format_exc())
return False
# def update_libtorrent():
# logger.info()
# if not config.get_setting("mct_buffer", server="torrent", default=""):
# default = config.get_setting("torrent_client", server="torrent", default=0)
# config.set_setting("torrent_client", default, server="torrent")
# config.set_setting("mct_buffer", "50", server="torrent")
# if config.get_setting("mct_download_path", server="torrent", default=config.get_setting("downloadpath")):
# config.set_setting("mct_download_path", config.get_setting("downloadpath"), server="torrent")
# config.set_setting("mct_background_download", True, server="torrent")
# config.set_setting("mct_rar_unpack", True, server="torrent")
# config.set_setting("bt_buffer", "50", server="torrent")
# if config.get_setting("bt_download_path", server="torrent", default=config.get_setting("downloadpath")):
# config.set_setting("bt_download_path", config.get_setting("downloadpath"), server="torrent")
# config.set_setting("mct_download_limit", "", server="torrent")
# config.set_setting("magnet2torrent", False, server="torrent")
# if not filetools.exists(filetools.join(config.get_runtime_path(), "custom_code.json")) or not \
# config.get_setting("unrar_path", server="torrent", default=""):
# path = filetools.join(config.get_runtime_path(), 'lib', 'rarfiles')
# creationflags = ''
# sufix = ''
# unrar = ''
# for device in filetools.listdir(path):
# if xbmc.getCondVisibility("system.platform.android") and 'android' not in device: continue
# if xbmc.getCondVisibility("system.platform.windows") and 'windows' not in device: continue
# if not xbmc.getCondVisibility("system.platform.windows") and not xbmc.getCondVisibility("system.platform.android") \
# and ('android' in device or 'windows' in device): continue
# if 'windows' in device:
# creationflags = 0x08000000
# sufix = '.exe'
# else:
# creationflags = ''
# sufix = ''
# unrar = filetools.join(path, device, 'unrar%s') % sufix
# if not filetools.exists(unrar): unrar = ''
# if unrar:
# if not xbmc.getCondVisibility("system.platform.windows"):
# try:
# if xbmc.getCondVisibility("system.platform.android"):
# # Para Android copiamos el binario a la partición del sistema
# unrar_org = unrar
# unrar = filetools.join(xbmc.translatePath('special://xbmc/'), 'files').replace('/cache/apk/assets', '')
# if not filetools.exists(unrar):
# filetools.mkdir(unrar)
# unrar = filetools.join(unrar, 'unrar')
# filetools.copy(unrar_org, unrar, silent=True)
# command = ['chmod', '777', '%s' % unrar]
# p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# output_cmd, error_cmd = p.communicate()
# command = ['ls', '-l', unrar]
# p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# output_cmd, error_cmd = p.communicate()
# xbmc.log('######## UnRAR file: %s' % str(output_cmd), xbmc.LOGNOTICE)
# except:
# xbmc.log('######## UnRAR ERROR in path: %s' % str(unrar), xbmc.LOGNOTICE)
# logger.error(traceback.format_exc(1))
# try:
# if xbmc.getCondVisibility("system.platform.windows"):
# p = subprocess.Popen(unrar, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=creationflags)
# else:
# p = subprocess.Popen(unrar, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# output_cmd, error_cmd = p.communicate()
# if p.returncode != 0 or error_cmd:
# xbmc.log('######## UnRAR returncode in module %s: %s, %s in %s' % \
# (device, str(p.returncode), str(error_cmd), unrar), xbmc.LOGNOTICE)
# unrar = ''
# else:
# xbmc.log('######## UnRAR OK in %s: %s' % (device, unrar), xbmc.LOGNOTICE)
# break
# except:
# xbmc.log('######## UnRAR ERROR in module %s: %s' % (device, unrar), xbmc.LOGNOTICE)
# logger.error(traceback.format_exc(1))
# unrar = ''
# if unrar: config.set_setting("unrar_path", unrar, server="torrent")
# if filetools.exists(filetools.join(config.get_runtime_path(), "custom_code.json")) and \
# config.get_setting("libtorrent_path", server="torrent", default="") :
# return
# try:
# from lib.python_libtorrent.python_libtorrent import get_libtorrent
# except Exception as e:
# logger.error(traceback.format_exc(1))
# if not PY3:
# e = unicode(str(e), "utf8", errors="replace").encode("utf8")
# config.set_setting("libtorrent_path", "", server="torrent")
# if not config.get_setting("libtorrent_error", server="torrent", default=''):
# config.set_setting("libtorrent_error", str(e), server="torrent")
# return
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 and platform['num_version'] <= 19:
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 platform['video_db'] not in file:
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

View File

@@ -14,23 +14,14 @@ import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import urllib.request, urllib.parse, urllib.error
import os
import re
import socket
import threading
import time
import xbmc
import xbmcgui
import urllib.request, urllib.parse, urllib.error, os, re, socket, threading, time, xbmc, 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
# Start thread
logger.info("Active threads " + str(threading.active_count()))
logger.info("" + repr(threading.enumerate()))
logger.info("Starting download thread...")
@@ -40,7 +31,7 @@ def download_and_play(url, file_name, download_path):
logger.info("Active threads " + str(threading.active_count()))
logger.info("" + repr(threading.enumerate()))
# Espera
# Wait
logger.info("Waiting...")
while True:
@@ -52,8 +43,7 @@ def download_and_play(url, file_name, download_path):
while not cancelled and download_thread.isAlive():
dialog.update(download_thread.get_progress(), config.get_localized_string(60313),
config.get_localized_string(60314) + str(int(old_div(download_thread.get_speed(), 1024))) + " KB/s " + str(
download_thread.get_actual_size()) + config.get_localized_string(60316) + str(
download_thread.get_total_size()) + "MB",
download_thread.get_actual_size()) + config.get_localized_string(60316) + str( download_thread.get_total_size()) + "MB",
config.get_localized_string(60202) % (str(downloadtools.sec_to_hms(download_thread.get_remaining_time()))))
xbmc.sleep(1000)
@@ -65,25 +55,25 @@ def download_and_play(url, file_name, download_path):
logger.info("End of waiting")
# Lanza el reproductor
# Launch the player
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")
# End of playback
logger.info("End of playback")
if player.is_stopped():
logger.info("Terminado por el usuario")
logger.info("Terminated by user")
break
else:
if not download_thread.isAlive():
logger.info("La descarga ha terminado")
logger.info("Download has finished")
break
else:
logger.info("Continua la descarga")
# Cuando el reproductor acaba, si continúa descargando lo para ahora
# When the player finishes, if you continue downloading it for now
logger.info("Download thread alive=" + str(download_thread.isAlive()))
if download_thread.isAlive():
logger.info("Killing download thread")
@@ -141,7 +131,7 @@ class CustomPlayer(xbmc.Player):
# Download in background
class DownloadThread(threading.Thread):
def __init__(self, url, file_name, download_path):
logger.info(repr(file))
# logger.info(repr(file))
self.url = url
self.download_path = download_path
self.file_name = os.path.join(download_path, file_name)
@@ -194,17 +184,17 @@ class DownloadThread(threading.Thread):
logger.info()
comando = "./megacrypter.sh"
logger.info("comando=" + comando)
logger.info("command= " + comando)
oldcwd = os.getcwd()
logger.info("oldcwd=" + oldcwd)
logger.info("oldcwd= " + oldcwd)
cwd = os.path.join(config.get_runtime_path(), "tools")
logger.info("cwd=" + cwd)
logger.info("cwd= " + cwd)
os.chdir(cwd)
logger.info("directory changed to=" + os.getcwd())
logger.info("directory changed to= " + os.getcwd())
logger.info("destino=" + self.download_path)
logger.info("destination= " + 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 )
@@ -218,18 +208,18 @@ class DownloadThread(threading.Thread):
headers = []
# Se asegura de que el fichero se podrá crear
logger.info("nombrefichero=" + self.file_name)
# Ensures that the file can be created
logger.info("filename= " + self.file_name)
self.file_name = xbmc.makeLegalFilename(self.file_name)
logger.info("nombrefichero=" + self.file_name)
logger.info("url=" + self.url)
logger.info("filename= " + self.file_name)
logger.info("url= " + self.url)
# Crea el fichero
# Create the file
existSize = 0
f = open(self.file_name, 'wb')
grabado = 0
# Interpreta las cabeceras en una URL como en XBMC
# Interpret headers in a URL like in XBMC
if "|" in self.url:
additional_headers = self.url.split("|")[1]
if "&" in additional_headers:
@@ -244,7 +234,7 @@ class DownloadThread(threading.Thread):
headers.append([name, value])
self.url = self.url.split("|")[0]
logger.info("url=" + self.url)
logger.info("url= " + self.url)
# Timeout del socket a 60 segundos
socket.setdefaulttimeout(60)
@@ -253,7 +243,7 @@ class DownloadThread(threading.Thread):
h = urllib.request.HTTPHandler(debuglevel=0)
request = urllib.request.Request(self.url)
for header in headers:
logger.info("Header=" + header[0] + ": " + header[1])
logger.info("Header= " + header[0] + ": " + header[1])
request.add_header(header[0], header[1])
# Lanza la petición
@@ -262,14 +252,14 @@ class DownloadThread(threading.Thread):
try:
connexion = opener.open(request)
except urllib.error.HTTPError as e:
logger.error("error %d (%s) al abrir la url %s" % (e.code, e.msg, self.url))
logger.error("error %d (%s) opening 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
# Error 416 is that the requested range is greater than the file => is that it is already complete
if e.code == 416:
return 0
else:
@@ -286,21 +276,21 @@ class DownloadThread(threading.Thread):
blocksize = 100 * 1024
bloqueleido = connexion.read(blocksize)
logger.info("Iniciando descarga del fichero, bloqueleido=%s" % len(bloqueleido))
logger.info("Starting file download, blocked= %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")
logger.info("Force_stop file detected, download is interrupted")
f.close()
xbmc.executebuiltin("XBMC.Notification(%s,%s,300)" % (config.get_localized_string(60319),config.get_localized_string(60320)))
return
# Escribe el bloque leido
# Write the block read
# try:
# import xbmcvfs
# f.write( bloqueleido )
@@ -309,12 +299,12 @@ class DownloadThread(threading.Thread):
grabado = grabado + len(bloqueleido)
logger.info("grabado=%d de %d" % (grabado, totalfichero))
percent = int(float(grabado) * 100 / float(totalfichero))
self.progress = percent;
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
#Read the next block, retrying not to stop everything at the first timeout
reintentos = 0
while reintentos <= maxreintentos:
try:
@@ -333,13 +323,13 @@ class DownloadThread(threading.Thread):
except:
import sys
reintentos = reintentos + 1
logger.info("ERROR en la descarga del bloque, reintento %d" % reintentos)
logger.info("ERROR in block download, retry %d" % reintentos)
for line in sys.exc_info():
logger.error("%s" % line)
# Ha habido un error en la descarga
# There was an error in the download
if reintentos > maxreintentos:
logger.error("ERROR en la descarga del fichero")
logger.error("ERROR in the file download")
f.close()
return -2

View File

@@ -10,13 +10,7 @@ import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import xbmc
import xbmcaddon
import os
import subprocess
import re
import platform
import xbmc, xbmcaddon, os, subprocess, re, platform
try:
import ctypes
@@ -123,18 +117,14 @@ def get_environment():
environment['kodi_bmode'] = '0'
environment['kodi_rfactor'] = '4.0'
if filetools.exists(filetools.join(xbmc.translatePath("special://userdata"), "advancedsettings.xml")):
advancedsettings = filetools.read(filetools.join(xbmc.translatePath("special://userdata"),
"advancedsettings.xml")).split('\n')
advancedsettings = filetools.read(filetools.join(xbmc.translatePath("special://userdata"), "advancedsettings.xml")).split('\n')
for label_a in advancedsettings:
if 'memorysize' in label_a:
environment['kodi_buffer'] = str(old_div(int(scrapertools.find_single_match
(label_a, '>(\d+)<\/')), 1024 ** 2))
environment['kodi_buffer'] = str(old_div(int(scrapertools.find_single_match(label_a, r'>(\d+)<\/')), 1024 ** 2))
if 'buffermode' in label_a:
environment['kodi_bmode'] = str(scrapertools.find_single_match
(label_a, '>(\d+)<\/'))
environment['kodi_bmode'] = str(scrapertools.find_single_match(label_a, r'>(\d+)<\/'))
if 'readfactor' in label_a:
environment['kodi_rfactor'] = str(scrapertools.find_single_match
(label_a, '>(.*?)<\/'))
environment['kodi_rfactor'] = str(scrapertools.find_single_match(label_a, r'>(.*?)<\/'))
except:
pass
@@ -142,14 +132,12 @@ def get_environment():
try:
if environment['os_name'].lower() == 'windows':
free_bytes = ctypes.c_ulonglong(0)
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(environment['userdata_path']),
None, None, ctypes.pointer(free_bytes))
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(environment['userdata_path']), None, None, ctypes.pointer(free_bytes))
environment['userdata_free'] = str(round(float(free_bytes.value) / (1024 ** 3), 3))
else:
disk_space = os.statvfs(environment['userdata_path'])
if not disk_space.f_frsize: disk_space.f_frsize = disk_space.f_frsize.f_bsize
environment['userdata_free'] = str(round((float(disk_space.f_bavail) / \
(1024 ** 3)) * float(disk_space.f_frsize), 3))
environment['userdata_free'] = str(round((float(disk_space.f_bavail) / (1024 ** 3)) * float(disk_space.f_frsize), 3))
except:
environment['userdata_free'] = '?'
@@ -158,22 +146,15 @@ def get_environment():
environment['videolab_episodios'] = '?'
environment['videolab_pelis'] = '?'
environment['videolab_path'] = str(xbmc.translatePath(config.get_videolibrary_path()))
if filetools.exists(filetools.join(environment['videolab_path'], \
config.get_setting("folder_tvshows"))):
environment['videolab_series'] = str(len(filetools.listdir(filetools.join(environment['videolab_path'], \
config.get_setting(
"folder_tvshows")))))
if filetools.exists(filetools.join(environment['videolab_path'], config.get_setting("folder_tvshows"))):
environment['videolab_series'] = str(len(filetools.listdir(filetools.join(environment['videolab_path'], config.get_setting("folder_tvshows")))))
counter = 0
for root, folders, files in filetools.walk(filetools.join(environment['videolab_path'], \
config.get_setting("folder_tvshows"))):
for root, folders, files in filetools.walk(filetools.join(environment['videolab_path'], config.get_setting("folder_tvshows"))):
for file in files:
if file.endswith('.strm'): counter += 1
environment['videolab_episodios'] = str(counter)
if filetools.exists(filetools.join(environment['videolab_path'], \
config.get_setting("folder_movies"))):
environment['videolab_pelis'] = str(len(filetools.listdir(filetools.join(environment['videolab_path'], \
config.get_setting(
"folder_movies")))))
if filetools.exists(filetools.join(environment['videolab_path'], config.get_setting("folder_movies"))):
environment['videolab_pelis'] = str(len(filetools.listdir(filetools.join(environment['videolab_path'], config.get_setting("folder_movies")))))
except:
pass
try:
@@ -184,14 +165,12 @@ def get_environment():
try:
if environment['os_name'].lower() == 'windows':
free_bytes = ctypes.c_ulonglong(0)
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(environment['videolab_path']),
None, None, ctypes.pointer(free_bytes))
ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(environment['videolab_path']), None, None, ctypes.pointer(free_bytes))
environment['videolab_free'] = str(round(float(free_bytes.value) / (1024 ** 3), 3))
else:
disk_space = os.statvfs(environment['videolab_path'])
if not disk_space.f_frsize: disk_space.f_frsize = disk_space.f_frsize.f_bsize
environment['videolab_free'] = str(round((float(disk_space.f_bavail) / \
(1024 ** 3)) * float(disk_space.f_frsize), 3))
environment['videolab_free'] = str(round((float(disk_space.f_bavail) / (1024 ** 3)) * float(disk_space.f_frsize), 3))
except:
environment['videolab_free'] = '?'
@@ -439,131 +418,116 @@ def paint_env(item, environment={}):
thumb = get_thumb("setting_0.png")
cabecera = """\
Muestra las [COLOR yellow]variables[/COLOR] del ecosistema de Kodi que puden ser relevantes para el diagnóstico de problema en Alfa:
- Versión de Alfa con Fix
It shows the [COLOR yellow] variables [/ COLOR] of the Kodi ecosystem that may be relevant to the problem diagnosis in Alpha:
- Alpha version with Fix
- Debug Alfa: True/False
"""
plataform = """\
Muestra los datos especificos de la [COLOR yellow]plataforma[/COLOR] en la que está alojado Kodi:
- Sistema Operativo
- Modelo (opt)
- Versión SO
- Procesador
- Aquitectura
- Idioma de Kodi
It shows the specific data of the [COLOR yellow] platform [/ COLOR] where Kodi is hosted:
- Operating system
- Model (opt)
- SO version
- Processor
- Architecture
- Kodi language
"""
kodi = """\
Muestra los datos especificos de la instalación de [COLOR yellow]Kodi[/COLOR]:
- Versión de Kodi
- Base de Datos de Vídeo
- Versión de Python
It shows the specific data of the installation of [COLOR yellow] Kodi [/ COLOR]:
- Kodi version
- Video Database
- Python version
"""
cpu = """\
Muestra los datos consumo actual de [COLOR yellow]CPU(s)[/COLOR]
Displays the current consumption data of [COLOR yellow] CPU (s) [/ COLOR]
"""
memoria = """\
Muestra los datos del uso de [COLOR yellow]Memoria[/COLOR] del sistema:
- Memoria total
- Memoria disponible
Displays the usage data of [COLOR yellow] System [/ COLOR] memory:
- Total memory
- Available memory
- en [COLOR yellow]Advancedsettings.xml[/COLOR]
- Buffer de memoria
configurado:
para Kodi: 3 x valor de
- Memory buffer
configured:
for Kodi: 3 x value of
<memorysize>
- Buffermode: cachea:
- Buffermode: cache:
* Internet (0, 2)
* También local (1)
* Also local (1)
* No Buffer (3)
- Readfactor: readfactor *
avg bitrate vídeo
- Readfactor: readfactor *
avg bitrate video
"""
userdata = """\
Muestra los datos del "path" de [COLOR yellow]Userdata[/COLOR]:
It shows the data of the "path" of [COLOR yellow] Userdata [/ COLOR]:
- Path
- Espacio disponible
- Available space
"""
videoteca = """\
Muestra los datos de la [COLOR yellow]Videoteca[/COLOR]:
- Nº de Series y Episodios
- Nº de Películas
- Tipo de actulización
It shows the data of the [COLOR yellow] Video Library [/ COLOR]:
- Number of Series and Episodes
- No. of Movies
- Update type
- Path
- Espacio disponible
- Available space
"""
torrent = """\
Muestra los datos generales del estado de [COLOR yellow]Torrent[/COLOR]:
- ID del cliente seleccionado
- Descompresión automática de archivos RAR?
- Está activo Libtorrent?
- Se descomprimen los RARs en background?
- Está operativo el módulo UnRAR? Qué plataforma?
It shows the general data of the status of [COLOR yellow] Torrent [/ COLOR]:
- ID of the selected customer
- Automatic decompression of RAR files?
- Is Libtorrent active?
- Are RARs decompressed in the background?
- Is the UnRAR module operational? Which platform?
"""
torrent_error = """\
Muestra los datos del error de importación de [COLOR yellow]Libtorrent[/COLOR]
Displays the import error data for [COLOR yellow] Libtorrent [/ COLOR]
"""
torrent_cliente = """\
Muestra los datos de los [COLOR yellow]Clientes Torrent[/COLOR]:
- Nombre del Cliente
- Tamaño de buffer inicial
- Path de descargas
- Tamaño de buffer en Memoria
It shows the data of the [COLOR yellow] Torrent Clients [/ COLOR]:
- Customer name
- Initial buffer size
- Download path
- Memory buffer size
(opt, si no disco)
- Espacio disponible
- Available space
"""
proxy = """\
Muestra las direcciones de canales o servidores que necesitan [COLOR yellow]Proxy[/COLOR]
Shows the addresses of channels or servers that need [COLOR yellow] Proxy [/ COLOR]
"""
log = """\
Muestra el tamaño actual del [COLOR yellow]Log[/COLOR]
Displays the current size of the [COLOR yellow] Log [/ COLOR]
"""
reporte = """\
Enlaza con la utilidad que permite el [COLOR yellow]envío del Log[/COLOR] de Kodi a través de un servicio Pastebin
Links with the utility that allows the [COLOR yellow] to send the Kodi Log [/ COLOR] through a Pastebin service
"""
itemlist.append(Item(channel=item.channel, title="[COLOR orange][B]Variables " +
"de entorno Alfa: %s Debug: %s[/B][/COLOR]" %
(environment['addon_version'], environment['debug']),
itemlist.append(Item(channel=item.channel, title="KoD environment variables: %s Debug: %s" % (environment['addon_version'], environment['debug']),
action="", plot=cabecera, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]%s[/COLOR]' %
environment['os_name'] + ' ' + environment['prod_model'] + ' ' +
environment['os_release'] + ' ' + environment['machine'] + ' ' +
environment['architecture'] + ' ' + environment['language'],
itemlist.append(Item(channel=item.channel, title=environment['os_name'] + ' ' + environment['prod_model'] + ' ' + environment['os_release'] + ' ' + environment['machine'] + ' ' + environment['architecture'] + ' ' + environment['language'],
action="", plot=plataform, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Kodi [/COLOR]' +
environment['num_version'] + ', Vídeo: ' + environment[
'video_db'] +
', Python ' + environment['python_version'], action="",
itemlist.append(Item(channel=item.channel, title='Kodi ' + environment['num_version'] + ', Vídeo: ' + environment[ 'video_db'] + ', Python ' + environment['python_version'], action="",
plot=kodi, thumbnail=thumb, folder=False))
if environment['cpu_usage']:
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]CPU: [/COLOR]' +
environment['cpu_usage'], action="", plot=cpu, thumbnail=thumb,
folder=False))
itemlist.append(Item(channel=item.channel, title='CPU: ' + environment['cpu_usage'], action="", plot=cpu, thumbnail=thumb, folder=False))
if environment['mem_total'] or environment['mem_free']:
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Memoria: [/COLOR]Total: ' +
itemlist.append(Item(channel=item.channel, title='Memory: Total: ' +
environment['mem_total'] + ' MB / Disp.: ' +
environment['mem_free'] + ' MB / Buffers: ' +
str(int(
environment['kodi_buffer']) * 3) + ' MB / Buffermode: ' +
str(int(environment['kodi_buffer']) * 3) + ' MB / Buffermode: ' +
environment['kodi_bmode'] + ' / Readfactor: ' +
environment['kodi_rfactor'],
action="", plot=memoria, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Userdata: [/COLOR]' +
environment['userdata_path'] + ' - Free: ' + environment[
'userdata_free'].replace('.', ',') +
itemlist.append(Item(channel=item.channel, title='Userdata:' +
environment['userdata_path'] + ' - Free: ' + environment[ 'userdata_free'].replace('.', ',') +
' GB', action="", plot=userdata, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Videoteca: [/COLOR]Series/Epis: ' +
environment['videolab_series'] + '/' + environment[
'videolab_episodios'] +
' - Pelis: ' + environment['videolab_pelis'] + ' - Upd: ' +
environment['videolab_update'] + ' - Path: ' +
environment['videolab_path'] + ' - Free: ' + environment[
'videolab_free'].replace('.', ',') +
itemlist.append(Item(channel=item.channel, title='Video store: Series/Epis: ' +
environment['videolab_series'] + '/' + environment['videolab_episodios'] +
' - Movie: ' + environment['videolab_pelis'] + ' - Upd: ' + environment['videolab_update'] + ' - Path: ' +
environment['videolab_path'] + ' - Free: ' + environment[ 'videolab_free'].replace('.', ',') +
' GB', action="", plot=videoteca, thumbnail=thumb, folder=False))
if environment['torrent_list']:
@@ -571,41 +535,27 @@ def paint_env(item, environment={}):
if x == 0:
cliente_alt = cliente.copy()
del cliente_alt['Torrent_opt']
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Torrent: [/COLOR]Opt: %s, %s' \
% (str(cliente['Torrent_opt']),
str(cliente_alt).replace('{', '').replace('}', '') \
.replace("'", '').replace('_', ' ')), action="",
plot=torrent, thumbnail=thumb,
folder=False))
itemlist.append(Item(channel=item.channel, title='Torrent: Opt: %s, %s' % (str(cliente['Torrent_opt']), str(cliente_alt).replace('{', '').replace('}', '').replace("'", '').replace('_', ' ')), action="",
plot=torrent, thumbnail=thumb, folder=False))
elif x == 1 and environment['torrent_error']:
itemlist.append(Item(channel=item.channel,
title='[COLOR magenta]- %s[/COLOR]' % str(cliente).replace('{', '').replace('}',
'') \
.replace("'", '').replace('_', ' '), action="", plot=torrent_error,
thumbnail=thumb,
folder=False))
title=str(cliente).replace('{', '').replace('}','').replace("'", '').replace('_', ' '), action="", plot=torrent_error,
thumbnail=thumb, folder=False))
else:
cliente_alt = cliente.copy()
del cliente_alt['Plug_in']
cliente_alt['Libre'] = cliente_alt['Libre'].replace('.', ',') + ' GB'
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]- %s: [/COLOR]: %s' %
(str(cliente['Plug_in']),
str(cliente_alt).replace('{', '').replace('}', '') \
.replace("'", '').replace('\\\\', '\\')), action="",
plot=torrent_cliente,
thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='- %s: %s' % (str(cliente['Plug_in']), str(cliente_alt).replace('{', '').replace('}', '').replace("'", '').replace('\\\\', '\\')), action="",
plot=torrent_cliente, thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]Proxy: [/COLOR]' +
environment['proxy_active'], action="", plot=proxy,
thumbnail=thumb,
folder=False))
itemlist.append(Item(channel=item.channel, title='Proxy: ' + environment['proxy_active'], action="", plot=proxy,
thumbnail=thumb, folder=False))
itemlist.append(Item(channel=item.channel, title='[COLOR yellow]TAMAÑO del LOG: [/COLOR]' +
environment['log_size'].replace('.', ',') + ' MB', action="",
itemlist.append(Item(channel=item.channel, title='LOG SIZE: ' + environment['log_size'].replace('.', ',') + ' MB', action="",
plot=log, thumbnail=thumb,
folder=False))
itemlist.append(Item(title="[COLOR hotpink][B]==> Reportar un fallo[/B][/COLOR]",
itemlist.append(Item(title="==> Report a bug",
channel="setting", action="report_menu", category='Configuración',
unify=False, plot=reporte, thumbnail=get_thumb("error.png")))

View File

@@ -11,31 +11,30 @@ PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
# if PY3:
# import urllib.error as urllib2 # Es muy lento en PY2. En PY3 es nativo
# import urllib.error as urllib2 # It is very slow in PY2. In PY3 it is native
# else:
# import urllib2 # Usamos el nativo de PY2 que es más rápido
# import urllib2 # We use the native of PY2 which is faster
import os
from core.item import Item
from platformcode import config, logger
from platformcode import platformtools
from platformcode import config, logger, 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.
""" First function that is executed when entering the plugin.
Within this function all calls should go to
functions that we want to execute as soon as we open the plugin.
"""
logger.info()
#config.set_setting('show_once', True)
# config.set_setting('show_once', True)
# Test if all the required directories are created
config.verify_directories_created()
# controlla se l'utente ha qualche problema di connessione
# se lo ha: non lo fa entrare nell'addon
# se ha problemi di DNS avvia ma lascia entrare
# se tutto ok: entra nell'addon
# check if the user has any connection problems
# if it has: it does not enter the addon
# if it has DNS problems start but let in
# if everything is ok: enter the addon
from specials.checkhost import test_conn
import threading
@@ -191,7 +190,7 @@ def run(item=None):
# Special play action
if item.action == "play":
#define la info para trakt
# define la info para trakt
try:
from core import trakt_tools
trakt_tools.set_trakt_info(item)
@@ -444,14 +443,14 @@ def limit_itemlist(itemlist):
def play_from_library(item):
itemlist=[]
"""
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.
The .strm files when played from kodi, this expects it to be a "playable" file so it cannot contain
more items, at most a selection dialog can be placed.
We solve this by "cheating kodi" and making him believe that something has been reproduced, so later by
"Container.Update ()" we load the strm as if an item from inside the addon were treated, removing all
the limitations and allowing to reproduce through the general function without having to create new methods to
the video library.
@type item: item
@param item: elemento con información
@param item: item with information
"""
item.fromLibrary = True
logger.info()
@@ -463,30 +462,28 @@ def play_from_library(item):
from time import sleep, time
from specials import nextep
# 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", "kod.mp4")))
# We try to reproduce an image (this does nothing and also does not give an error)
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, xbmcgui.ListItem(path=os.path.join(config.get_runtime_path(), "resources", "kod.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
# Just in case the image did (in future versions) we give stop to stop the reproduction
# sleep(0.5) ### If you don't put this on you crash Kodi
xbmc.Player().stop()
# modificamos el action (actualmente la videoteca necesita "findvideos" ya que es donde se buscan las fuentes
# we modify the action (currently the video library needs "findvideos" since this is where the sources are searched
item.action = "findvideos"
check_next_ep = nextep.check(item)
window_type = config.get_setting("window_type", "videolibrary")
# y volvemos a lanzar kodi
# and we launch kodi again
if xbmc.getCondVisibility('Window.IsMedia') and not window_type == 1:
# Ventana convencional
# Conventional window
xbmc.executebuiltin("Container.Update(" + sys.argv[0] + "?" + item.tourl() + ")")
else:
# Ventana emergente
# Pop-up window
item.show_server = True
from specials import videolibrary, autoplay
@@ -505,22 +502,22 @@ def play_from_library(item):
else:
while platformtools.is_playing():
# Ventana convencional
# Conventional window
sleep(5)
p_dialog.update(50, '')
it = item
if item.show_server or not check_next_ep:
'''# Se filtran los enlaces segun la lista negra
if config.get_setting('filter_servers', "servers"):
itemlist = servertools.filter_servers(itemlist)'''
# The links are filtered according to the blacklist
# if config.get_setting('filter_servers', "servers"):
# itemlist = servertools.filter_servers(itemlist)
# Se limita la cantidad de enlaces a mostrar
# The number of links to show is limited
if config.get_setting("max_links", "videolibrary") != 0:
itemlist = limit_itemlist(itemlist)
# Se "limpia" ligeramente la lista de enlaces
# The list of links is slightly "cleaned"
if config.get_setting("replace_VD", "videolibrary") == 1:
itemlist = reorder_itemlist(itemlist)
@@ -532,12 +529,12 @@ def play_from_library(item):
if len(itemlist) > 0:
while not xbmc.Monitor().abortRequested():
# El usuario elige el mirror
# The user chooses the mirror
opciones = []
for item in itemlist:
opciones.append(item.title)
# Se abre la ventana de seleccion
# The selection window opens
if (item.contentSerieName != "" and
item.contentSeason != "" and
item.contentEpisodeNumber != ""):

View File

@@ -21,7 +21,7 @@ def log_enable(active):
def encode_log(message=""):
# Unicode to utf8
if isinstance(message, unicode):
message = message.encode("utf8")
@@ -30,7 +30,7 @@ def encode_log(message=""):
# All encodings to utf8
elif not PY3 and isinstance(message, str):
message = unicode(message, "utf8", errors="replace").encode("utf8")
# Bytes encodings to utf8
elif PY3 and isinstance(message, bytes):
message = message.decode("utf8")
@@ -43,7 +43,7 @@ def encode_log(message=""):
def get_caller(message=None):
if message and isinstance(message, unicode):
message = message.encode("utf8")
if PY3: message = message.decode("utf8")
@@ -52,8 +52,8 @@ def get_caller(message=None):
elif message and not PY3:
message = unicode(message, "utf8", errors="replace").encode("utf8")
elif message:
message = str(message)
message = str(message)
module = inspect.getmodule(inspect.currentframe().f_back.f_back)
if module == None:

View File

@@ -1,973 +0,0 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# MCT - Mini Cliente Torrent
# ------------------------------------------------------------
from __future__ import division
from future import standard_library
standard_library.install_aliases()
from builtins import hex
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
from builtins import range
from past.utils import old_div
import os
import re
import tempfile
import urllib.request, urllib.parse, urllib.error
import platform
import traceback
try:
import xbmc
import xbmcgui
except:
pass
from platformcode import config
LIBTORRENT_PATH = config.get_setting("libtorrent_path", server="torrent", default='')
from servers import torrent as torr
lt, e, e1, e2 = torr.import_libtorrent(LIBTORRENT_PATH)
from core import scrapertools
from core import filetools
from core import httptools
try:
BUFFER = int(config.get_setting("mct_buffer", server="torrent", default="50"))
except:
BUFFER = 50
config.set_setting("mct_buffer", "50", server="torrent")
try:
DOWNLOAD_PATH = ''
DOWNLOAD_PATH = xbmc.translatePath(config.get_setting("mct_download_path", server="torrent", default=config.get_setting("torrent_downloadpath")))
except:
DOWNLOAD_PATH = config.get_setting("mct_download_path", server="torrent", default=config.get_setting("downloadpath"))
if not config.get_setting("mct_download_path", server="torrent") and DOWNLOAD_PATH:
config.set_setting("mct_download_path", DOWNLOAD_PATH, server="torrent")
if not DOWNLOAD_PATH:
try:
DOWNLOAD_PATH = str(xbmc.translatePath(os.path.join(config.get_data_path(), 'downloads')))
config.set_setting("mct_download_path", os.path.join(config.get_data_path(), 'downloads'), server="torrent")
except:
DOWNLOAD_PATH = os.path.join(config.get_data_path(), 'downloads')
config.set_setting("mct_download_path", DOWNLOAD_PATH, server="torrent")
BACKGROUND = config.get_setting("mct_background_download", server="torrent", default=True)
RAR = config.get_setting("mct_rar_unpack", server="torrent", default=True)
DOWNLOAD_LIMIT = config.get_setting("mct_download_limit", server="torrent", default="")
if DOWNLOAD_LIMIT:
try:
DOWNLOAD_LIMIT = int(DOWNLOAD_LIMIT) * 1024
except:
DOWNLOAD_LIMIT = 0
else:
DOWNLOAD_LIMIT = 0
UPLOAD_LIMIT = 100 * 1024
msg_header = 'MCT Client Torrent'
def play(url, xlistitem={}, is_view=None, subtitle="", password="", item=None):
allocate = True
try:
log("XXX KODI XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
log("OS platform: %s %s" % (platform.system(),platform.release()))
log("xbmc/kodi version: %s" % xbmc.getInfoLabel( "System.BuildVersion" ))
xbmc_version = int(xbmc.getInfoLabel( "System.BuildVersion" )[:2])
log("Architecture: %s %s" % (str(platform.machine()), \
str(sys.maxsize > 2 ** 32 and "64-bit" or "32-bit")))
log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX KODI & platform XXXX")
except:
log(traceback.format_exc())
# -- adfly: ------------------------------------
if url.startswith("http://adf.ly/"):
try:
data = httptools.downloadpage(url).data
url = decode_adfly(data)
except:
ddd = xbmcgui.Dialog()
ddd.ok( msg_header + ": No adf.ly support "," The script has no support for the adf.ly url shortener.", "", "url: " + url )
return
"""
# -- Necesario para algunas webs ----------------------------
if not url.endswith(".torrent") and not url.startswith("magnet"):
#t_file = httptools.downloadpage(url, follow_redirects=False).headers["location"]
t_file = scrapertools.get_header_from_response(url, header_to_get="location")
if t_file:
if len(t_file) > 0:
url = t_file
t_file = httptools.downloadpage(url, follow_redirects=False).headers["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 , "MCT-torrent-videos" )
save_path_torrents = os.path.join( DOWNLOAD_PATH , "MCT-torrents" )
if not os.path.exists( save_path_torrents ): os.mkdir(save_path_torrents)
video_path = ''
global bkg_user
bkg_user = False
ses_lt = False
if item:
if item.contentType == 'movie':
video_path = '%s-%s' % (item.contentTitle, item.infoLabels['tmdb_id'])
else:
video_path = '%s-%sx%s-%s' % (item.contentSerieName, item.contentSeason, \
item.contentEpisodeNumber, item.infoLabels['tmdb_id'])
item.rar_path = video_path
# -- 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.parse.unquote( scrapertools.find_single_match(data,':name\d+:(.*?)\d+:') )
torrent_file = os.path.join(save_path_torrents, 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 -------------------------------
try:
log("XXX libtorrent pathname: %s" % str(LIBTORRENT_PATH))
ses = lt.session()
except Exception as e:
do = xbmcgui.Dialog()
e = e1 or e2
do.ok(config.get_localized_string(30035) + 'MCT Libtorrent', config.get_localized_string(30036), config.get_localized_string(60015), str(e))
return
log("XXX libtorrent version: %s" % lt.version)
log("##### Torrent file: %s ##" % torrent_file)
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.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(msg_header)
while not h.has_metadata():
message, porcent, msg_file, s, download = getProgress(h, "Creating torrent from 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")
torrent_file = os.path.join(save_path_torrents, info.name()+"-"+btih+ ".torrent")
f = open(torrent_file,'wb')
f.write(data)
f.close()
ses.remove_torrent(h)
filetools.rmdirtree(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 -
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(config.get_localized_string(20000), config.get_localized_string(60339))
if _index < 0:
log("##### parts = %s #########" % str(video_file))
log("##### video_size = %s #########" % str(video_size))
log("##### _index = %s #########" % str(_index))
#if _index == -1:
# _index = _index_file
# video_size = _size_file
video_file = _video_file
else:
log("##### video_size = %s #########" % str(video_size))
log("##### _index = %s #########" % str(_index))
_video_file_ext = os.path.splitext( _video_file )[1]
log("##### _video_file ## %s ##" % str(_video_file))
log("##### _video_file_ext ## %s ##" % _video_file_ext)
dp_cerrado = True
rar = False
global extracted_rar
extracted_rar = False
global erase_file_path
erase_file_path = ''
if _video_file_ext == ".rar":
rar = True
filename = video_file
if "/" in filename:
filename = filename.split("/")[1]
if RAR and BACKGROUND:
xbmcgui.Dialog().notification(config.get_localized_string(70768) % (video_size / 1048576.0), config.get_localized_string(70769), time=10000)
dialog = True
else:
dialog = xbmcgui.Dialog().yesno(config.get_localized_string(70770), config.get_localized_string(70771) % filename,
config.get_localized_string(70772) % (video_size / 1048576.0), config.get_localized_string(70773))
if dialog:
dp_cerrado = False
dp = xbmcgui.DialogProgressBG()
dp.create(msg_header)
if (_video_file_ext == ".avi" or _video_file_ext == ".mp4" or _video_file_ext == ".mkv") and allocate:
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:
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
global ses_lt
ses_lt = True
# -----------------------------------------------------------
# -- Descarga secuencial - trozo 1, trozo 2, ... ------------
h.set_sequential_download(True)
h.force_reannounce()
h.force_dht_announce()
h.set_upload_limit(UPLOAD_LIMIT)
# -- 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 )
porcent4first_pieces = BUFFER
if porcent4first_pieces < BUFFER: porcent4first_pieces = BUFFER
if porcent4first_pieces > 100: porcent4first_pieces = 100
porcent4last_pieces = int(old_div(porcent4first_pieces,2))
num_pieces_to_resume = int( video_size * 0.0000000025 )
if num_pieces_to_resume < 10: num_pieces_to_resume = 10
if num_pieces_to_resume > 25: num_pieces_to_resume = 25
log("##### porcent4first_pieces ## %s ##" % porcent4first_pieces)
log("##### porcent4last_pieces ## %s ##" % porcent4last_pieces)
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)
global tot_piece_set
tot_piece_set = len(piece_set)
log("##### total piece_set ## %s ##" % len(piece_set))
if dp_cerrado:
# -- Crear diálogo de progreso para el primer bucle ---------
dp = xbmcgui.DialogProgress()
dp.create(msg_header)
_pieces_info = {}
ren_video_file = os.path.join( save_path_videos, video_file )
# -- Doble bucle anidado ------------------------------------
# -- Descarga - Primer bucle
while not h.is_seed():
s = h.status()
xbmc.sleep(1000)
if not dp_cerrado and not BACKGROUND:
dp.close()
dp_cerrado = True
dp = xbmcgui.DialogProgress()
dp.create(msg_header)
# -- 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
if (s.state == 5 or s.state == 4) and rar:
# -- Borrar sesión para que libere los archivos y se pueda renombrar la carpeta -------
ses.pause()
#video_file, rar, play_file = extract_files(video_file, save_path_videos, password, dp, item=item)
video_file, rar, play_file, erase_path = torr.extract_files(video_file, \
save_path_videos, password, dp, item=item, torr_client='MCT') # ... extraemos el vídeo del RAR
dp.close()
erase_file_path = erase_path
ren_video_file = erase_file_path
extracted_rar = rar
if not play_file:
remove_files( download, torrent_file, erase_file_path, ses, h, ren_video_file, erase_file_path )
return
is_view = "Ok"
save_path_videos = play_file
xbmc.sleep(3000)
# -- Player - play --------------------------------------
# -- Comprobar si se han completado las piezas para el -
# -- inicio del vídeo -
first_pieces = True
#if not extracted_rar:
_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 h.status().num_pieces >= BUFFER and not rar and not bkg_user \
or ((s.state == 5 or s.state == 4) and bkg_user):
_pieces_info['continuous2'] = ""
log("##### porcent [%.2f%%]" % (s.progress * 100))
dp.close()
dp_cerrado = True
if not bkg_user:
is_view = "Ok"
else:
remove_files( download, torrent_file, video_file, ses, h, ren_video_file, erase_file_path )
return
if is_view == "Ok":
# -- Esperando a que termine otra reproducción --------------------------
while xbmc.Player().isPlaying():
xbmc.sleep(3000)
# -- 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 ----
bkg_auto = True
log("##### PLAY %s" % (h.status().num_pieces))
if item: torr.mark_auto_as_watched(item)
if ses_lt: h.set_download_limit(DOWNLOAD_LIMIT)
while player.isPlaying():
# -- 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() and not rar:
# -- Porcentage del progreso del vídeo ------
# -- En kodi 18.x se debe controlar -
# -- ZeroDivisionError: float division by -
# -- zero -
player_getTime = player.getTime()
player_getTotalTime = player.getTotalTime()
try: porcent_time = old_div(player_getTime, player_getTotalTime) * 100
except: porcent_time = 0
# -- Pieza que se está reproduciendo --------
# -- En kodi 18.x se debe controlar -
# -- ZeroDivisionError: float division by -
# -- zero -
try: current_piece = int( old_div(porcent_time, 100) * len(piece_set) )
except: current_piece = 0
# -- 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_plus = (current_piece + BUFFER > continuous_pieces)
#is_greater_num_pieces_finished = (current_piece + porcent4first_pieces >= len(piece_set))
is_greater_num_pieces_finished = (current_piece + BUFFER >= 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)}
# -- Cerrar el diálogo de progreso --------------
if player.resumed:
dp.close()
# -- Mostrar el diálogo de progreso -------------
if player.paused and dp_cerrado and not rar:
# -- Crear diálogo si no existe -------------
log("##### PAUSED %s" % (h.status().num_pieces))
if not player.statusDialogoProgress:
dp = xbmcgui.DialogProgressBG()
dp.create(msg_header)
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, '[CR]' + message + '[CR]' + msg_file)
else:
dp.update(100, "Download complete: " + video_file)
# -- Se canceló el progreso en el visionado -
# -- Continuar -
if not bkg_auto and 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 not bkg_auto and 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 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, ren_video_file, erase_file_path )
return
xbmc.sleep(1000)
# -- Kodi - Se cerró el visionado -----------------------
# -- Continuar | Terminar -
if is_view == "Ok" and not xbmc.Player().isPlaying():
dp.close()
if h.status().num_pieces < tot_piece_set:
# -- Diálogo continuar o terminar ---------------
# Preguntamos si el usuario quiere pasar a backgroung
ok = xbmcgui.Dialog().yesno(msg_header, config.get_localized_string(30031), config.get_localized_string(30032))
else: ok = True
# -- NO ---------------------------------------------
if ok:
is_view=None
bkg_user = True
dp_cerrado = False
dp = xbmcgui.DialogProgressBG()
dp.create(msg_header)
else:
# -- Terminar: ----------------------------------
# -- Comprobar si el vídeo pertenece a una ------
# -- lista de archivos -
remove_files( download, torrent_file, video_file, ses, h, ren_video_file, erase_file_path )
dp.close()
return
"""
#_index, video_file, video_size, len_files = get_video_files_sizes( info )
if _index < 0 or len_files == 1:
# -- Diálogo eliminar archivos --------------
#video_file = _video_file
remove_files( download, torrent_file, video_file, ses, h, ren_video_file, erase_file_path )
dp.close()
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(msg_header)
"""
# -- Mostar progeso antes del visionado -----------------
if is_view != "Ok" :
dp.update(porcent, message, msg_file)
# -- Se canceló el progreso antes del visionado ---------
# -- Dar otra oportunidad en background o Terminar -
if not bkg_user and dp_cerrado and dp.iscanceled():
dp.close()
# Preguntamos si el usuario quiere pasar a backgroung
dialog = xbmcgui.Dialog().yesno(msg_header, config.get_localized_string(30031), config.get_localized_string(30032))
if dialog:
bkg_user = True
dp_cerrado = False
dp = xbmcgui.DialogProgressBG()
dp.create(msg_header)
if ses_lt: h.set_download_limit(DOWNLOAD_LIMIT)
else:
remove_files( download, torrent_file, video_file, ses, h, ren_video_file, erase_file_path )
return
# -- 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 < 0 or len_files == 1:
# -- Diálogo eliminar archivos ------------------
#video_file = _video_file
remove_files( download, torrent_file, video_file, ses, h, ren_video_file, erase_file_path )
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(msg_header)
# -- 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, ren_video_file, erase_file_path)
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, old_div(s.download_rate, 1000), old_div(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 ):
extensions_list = ['.aaf', '.3gp', '.asf', '.avi', '.flv', '.mpeg',
'.m1v', '.m2v', '.m4v', '.mkv', '.mov', '.mpg',
'.mpe', '.mp4', '.ogg', '.rar', '.wmv', '.zip']
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
if os.path.splitext( video_file )[1] in extensions_list:
break
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 = {}
rar_parts = 0
rar_size = 0
vid_parts = 0
vid_size = 0
# -- 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 '.rar' in _file_ext or '.zip' in _file_ext:
rar_parts += 1
rar_size += _size
else:
vid_parts += 1
vid_size += _size
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:
if rar_parts > 1:
seleccion = -1
index = -9
return index, rar_parts, rar_size, len(opciones)
else:
d = xbmcgui.Dialog()
seleccion = d.select(msg_header + config.get_localized_string(30034), list(opciones.values()))
else: seleccion = 0
index = list(opciones.keys())[seleccion]
if seleccion == -1:
vfile_name[seleccion] = vid_parts
vfile_size[seleccion] = vid_size
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, ren_video_file="", erase_file_path='' ):
dialog_view = False
torrent = False
if os.path.isfile( torrent_file ):
dialog_view = True
torrent = True
if download > 0:
dialog_view = True
if bkg_user and not extracted_rar:
dialog_view = False
if erase_file_path and erase_file_path != os.path.join( DOWNLOAD_PATH , "MCT-torrent-videos" ):
ren_video_file = erase_file_path
if filetools.isfile(ren_video_file) and filetools.split(ren_video_file)[0] != os.path.join( DOWNLOAD_PATH , "MCT-torrent-videos" ):
ren_video_file = filetools.split(ren_video_file)[0]
elif filetools.isdir(ren_video_file) and ren_video_file == os.path.join( DOWNLOAD_PATH , "MCT-torrent-videos" ):
ren_video_file = ''
if dialog_view and ren_video_file:
if h.status().num_pieces >= tot_piece_set:
d = xbmcgui.Dialog()
ok = d.yesno(msg_header, config.get_localized_string(30031), video_file)
else:
ok = True
# -- SI -------------------------------------------------
if ok:
# -- Borrar archivo - torrent -----------------------
if torrent:
try:
os.remove( torrent_file )
except:
pass
# -- Borrar carpeta/archivos y sesión - vídeo -------
try:
ses.remove_torrent( h, 1 )
ses_lt = False
except:
ses_lt = False
try:
if os.path.isdir(ren_video_file):
filetools.rmdirtree(ren_video_file, silent=True)
elif os.path.exists(ren_video_file) and os.path.isfile(ren_video_file):
os.remove(ren_video_file)
log("##### erase_file_path: %s" % ren_video_file)
except:
log("##### erase_file_path: %s" % ren_video_file)
log("### End session #########")
else:
# -- Borrar sesión ----------------------------------
try:
ses.remove_torrent( h )
ses_lt = False
except:
ses_lt = False
log("### End session #########")
else:
# -- Borrar sesión --------------------------------------
try:
ses.remove_torrent( h )
ses_lt = False
except:
ses_lt = False
# -- Borrar archivo - torrent -----------------------
if torrent:
try:
os.remove( torrent_file )
except:
pass
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:
url = "%s?%s" % (url, urllib.parse.urlencode(params))
req = urllib.request.Request(url)
req.add_header("User-Agent", USER_AGENT)
for k, v in list(headers.items()):
req.add_header(k, v)
try:
with closing(urllib.request.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 urllib.error.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 and _index >= 0:
#h.file_priority(i,0)
xbmc.sleep(1000)
h.file_priority(i,0)
else:
#h.file_priority(i,0)
xbmc.sleep(1000)
h.file_priority(i,1)
piece_set = []
x = 0
for i, _set in enumerate(h.piece_priorities()):
#log("***** Nº Pieza: %s: %s" % (i, str(_set)))
if _set > 0:
piece_set.append(i)
x += 1
log("***** Piezas %s : Activas: %s" % (str(i+1), str(x)))
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
def encode(s):
import unicodedata
#log("### log ######")
#for c in s:
# log("%s : %s" % (c, str(unicodedata.category(c))))
#log("##############")
#return s
return str(''.join((c for c in unicodedata.normalize('NFD', unicode(s, 'utf-8')) if unicodedata.category(c) != 'Mn')))
def log(texto):
xbmc.log(texto, xbmc.LOGNOTICE)

View File

@@ -21,8 +21,7 @@ else:
import os, xbmc, xbmcgui, xbmcplugin
from channelselector import get_thumb
from core import channeltools
from core import trakt_tools, scrapertools
from core import channeltools, trakt_tools, scrapertools
from core.item import Item
from platformcode import logger, config, unify

View File

@@ -42,7 +42,7 @@ class Recaptcha(xbmcgui.WindowXMLDialog):
self.imagen = kwargs.get("imagen")
def onInit(self):
#### Compatibilidad con Kodi 18 ####
#### Kodi 18 compatibility ####
if config.get_platform(True)['num_version'] < 18:
self.setCoordinateResolution(2)
self.update_window()

View File

@@ -9,19 +9,16 @@ if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
if PY3:
#from future import standard_library
#standard_library.install_aliases()
import urllib.parse as urllib # Es muy lento en PY2. En PY3 es nativo
import urllib.parse as urllib # It is very slow in PY2. In PY3 it is native
else:
import urllib # Usamos el nativo de PY2 que es más rápido
import urllib # We use the native of PY2 which is faster
import os
import re
import string
from unicodedata import normalize
from core import filetools
from core import httptools
from core import jsontools
from core import scrapertools
from core import filetools, httptools, jsontools, scrapertools
import xbmc
import xbmcgui
@@ -32,22 +29,22 @@ if not PY3: allchars = string.maketrans('', '')
deletechars = ',\\/:*"<>|?'
# Extraemos el nombre de la serie, temporada y numero de capitulo ejemplo: 'fringe 1x01'
# We extract the name of the series, season and chapter number example: '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]+)([^\\\\/]*)$'
regex_expressions = [r'[Ss]([0-9]+)[][._-]*[Ee]([0-9]+)([^\\\\/]*)$',
r'[\._ \-]([0-9]+)x([0-9]+)([^\\/]*)', # foo.1x09
r'[\._ \-]([0-9]+)([0-9][0-9])([\._ \-][^\\/]*)', # foo.109
r'([0-9]+)([0-9][0-9])([\._ \-][^\\/]*)',
r'[\\\\/\\._ -]([0-9]+)([0-9][0-9])[^\\/]*',
r'Season ([0-9]+) - Episode ([0-9]+)[^\\/]*',
r'Season ([0-9]+) Episode ([0-9]+)[^\\/]*',
r'[\\\\/\\._ -][0]*([0-9]+)x[0]*([0-9]+)[^\\/]*',
r'[[Ss]([0-9]+)\]_\[[Ee]([0-9]+)([^\\/]*)', # foo_[s01]_[e01]
r'[\._ \-][Ss]([0-9]+)[\.\-]?[Ee]([0-9]+)([^\\/]*)', # foo, s01e01, foo.s01.e01, foo.s01-e01
r's([0-9]+)ep([0-9]+)[^\\/]*', # foo - s01ep03, foo - s1ep03
r'[Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+)([^\\\\/]*)$',
r'[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+)([^\\\\/]*)$',
r'[\\\\/\\._ \\[\\(-]([0-9]+)X([0-9]+)([^\\\\/]*)$'
]
sub_info = ""
tvshow = 0
@@ -83,8 +80,7 @@ def regex_tvshow(compare, file, sub=""):
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.
# Gets the name of the movie or episode of the series previously saved in plugin settings and then searches for it in the subtitles directory, if it finds them, activates them.
def set_Subtitle():
@@ -134,7 +130,7 @@ def set_Subtitle():
except:
logger.error("error al cargar subtitulos")
# Limpia los caracteres unicode
# Clean unicode characters
def _normalize(title, charset='utf-8'):
@@ -222,9 +218,7 @@ def searchSubtitle(item):
filetools.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})
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(filetools.join(path_movie_subt, title_new + ".mp4"))
@@ -248,7 +242,7 @@ def searchSubtitle(item):
# xbmctools.launchplayer(full_path_video_new,listitem)
except:
copy = False
logger.error("Error : no se pudo copiar")
logger.error("Error : could not copy")
time.sleep(1)
@@ -289,10 +283,9 @@ def saveSubtitleName(item):
def get_from_subdivx(sub_url):
"""
:param sub_url: Url de descarga del subtitulo alojado en suvdivx.com
Por Ejemplo: http://www.subdivx.com/bajar.php?id=573942&u=8
:param sub_url: Download url of the subtitle hosted on suvdivx.com For Example: http://www.subdivx.com/bajar.php?id=573942&u=8
:return: La ruta al subtitulo descomprimido
:return: The path to the unzipped subtitle
"""
logger.info()
@@ -319,20 +312,20 @@ def get_from_subdivx(sub_url):
filetools.write(filename, data_dl)
sub = extract_file_online(sub_dir, filename)
except:
logger.info('sub no valido')
logger.info('sub invalid')
else:
logger.info('sub no valido')
logger.info('sub invalid')
return sub
def extract_file_online(path, filename):
"""
:param path: Ruta donde se encuentra el archivo comprimido
:param path: Path where the compressed file is located
:param filename: Nombre del archivo comprimido
:param filename:
:return: Devuelve la ruta al subtitulo descomprimido
:return:
"""
logger.info()

View File

@@ -2,24 +2,18 @@
# ------------------------------------------------------------
# Unify
# ------------------------------------------------------------
# Herramientas responsables de unificar diferentes tipos de
# datos obtenidos de las paginas
# Tools responsible for unifying different types of data obtained from the pages
# ----------------------------------------------------------
# from builtins import str
import sys
import sys, os, unicodedata, re
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import os
import unicodedata
import re
from platformcode import config
from platformcode import config, logger
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",
@@ -147,10 +141,10 @@ def set_genre(string):
def remove_format(string):
# logger.info()
# logger.debug('entra en remove: %s' % string)
# logger.debug('enter remove: %s' % string)
string = string.rstrip()
string = re.sub(r'(\[|\[\/)(?:color|COLOR|b|B|i|I).*?\]|\[|\]|\(|\)|\:|\.', '', string)
# logger.debug('sale de remove: %s' % string)
# logger.debug('leaves remove: %s' % string)
return string
@@ -163,7 +157,7 @@ def normalize(string):
def simplify(string):
# logger.info()
# logger.debug('entra en simplify: %s'%string)
# logger.debug('enter simplify: %s'%string)
string = remove_format(string)
string = string.replace('-', ' ').replace('_', ' ')
string = re.sub(r'\d+', '', string)
@@ -196,7 +190,7 @@ def add_info_plot(plot, languages, quality):
last = '[/I][/B]\n'
if languages:
l_part = '[COLOR yellowgreen][B][I]Idiomas:[/COLOR] '
l_part = 'Languages: '
mid = ''
if isinstance(languages, list):
@@ -208,7 +202,7 @@ def add_info_plot(plot, languages, quality):
p_lang = '%s%s%s' % (l_part, mid, last)
if quality:
q_part = '[COLOR yellowgreen][B][I]Calidad:[/COLOR] '
q_part = 'Quality: '
p_quality = '%s%s%s' % (q_part, quality, last)
if languages and quality:
@@ -236,18 +230,17 @@ def set_color(title, category):
color_scheme = {'otro': 'white', 'dual': 'white'}
# logger.debug('category antes de remove: %s' % category)
# logger.debug('category before remove: %s' % category)
category = remove_format(category).lower()
# logger.debug('category despues de remove: %s' % category)
# Lista de elementos posibles en el titulo
# logger.debug('category after remove: %s' % category)
# List of possible elements in the title
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
# Check the status of the custom colors options
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.
# The color dictionary is formed for each element, the option is active uses the user's configuration, if it does not leave the title blank.
if title not in ['', ' ']:
for element in color_list:
@@ -258,13 +251,13 @@ def set_color(title, category):
# color_scheme[element] = 'white'
if category in ['update', 'no_update']:
# logger.debug('title antes de updates: %s' % title)
# logger.debug('title before 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)
title = title
else:
title = "[COLOR %s]%s[/COLOR]" % (color_scheme[category], title)
title = title
return title
@@ -317,17 +310,17 @@ def title_format(item):
language_color = 'otro'
simple_language = ''
# logger.debug('item.title antes de formatear: %s' % item.title.lower())
# logger.debug('item.title before formatting: %s' % item.title.lower())
# TODO se deberia quitar cualquier elemento que no sea un enlace de la lista de findvideos para quitar esto
# TODO any item other than a link should be removed from the findvideos list to remove this
# Palabras "prohibidas" en los titulos (cualquier titulo que contengas estas no se procesara en unify)
# Words "prohibited" in the titles (any title that contains these will not be processed in 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, (channel and action are defined) the titles that contain both values will not be processed in unify
excluded_actions = [('videolibrary', 'get_episodes')]
# Verifica el item sea valido para ser formateado por unify
# Verify the item is valid to be formatted by unify
if item.channel == 'trailertools' or (item.channel.lower(), item.action.lower()) in excluded_actions or \
item.action == '':
@@ -340,37 +333,36 @@ def title_format(item):
if not valid:
return item
# Verifica si hay marca de visto de trakt
# Check for trakt tick marks
visto = False
# logger.debug('titlo con visto? %s' % item.title)
# logger.debug('I titlo with visa? %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
# Any previous format in the title is eliminated
if item.action != '' and item.action != 'mainlist' and item.unify:
item.title = remove_format(item.title)
# logger.debug('visto? %s' % visto)
# logger.debug('seen? %s' % visto)
# Evita que aparezcan los idiomas en los mainlist de cada canal
# Prevents languages from appearing in the main lists of each channel
if item.action == 'mainlist':
item.language = ''
info = item.infoLabels
# logger.debug('item antes de formatear: %s'%item)
# logger.debug('item before formatr: %s'%item)
if hasattr(item, 'text_color'):
item.text_color = ''
if valid and item.unify != False:
# Formamos el titulo para serie, se debe definir contentSerieName
# o show en el item para que esto funcione.
# We form the title for series, contentSerieName or show must be defined in the item for this to work.
if item.contentSerieName:
# Si se tiene la informacion en infolabels se utiliza
# If you have the information in infolabels it is used
if item.contentType == 'episode' and info['episode'] != '':
if info['title'] == '':
info['title'] = '%s - Episodio %s' % (info['tvshowtitle'], info['episode'])
@@ -391,12 +383,12 @@ def title_format(item):
else:
# En caso contrario se utiliza el titulo proporcionado por el canal
# Otherwise the title provided by the channel is used
# 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
# If the title does not have contentSerieName then it is formatted as a movie
saga = False
if 'saga' in item.title.lower():
item.title = '%s [Saga]' % set_color(item.contentTitle, 'movie')
@@ -415,11 +407,10 @@ def title_format(item):
# 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
# We check if item.language is a list, if it is, each value is taken and normalized, forming a new list
if hasattr(item, 'language') and item.language != '':
# logger.debug('tiene language: %s'%item.language)
# logger.debug('has language: %s'%item.language)
if isinstance(item.language, list):
language_list = []
for language in item.language:
@@ -429,7 +420,7 @@ def title_format(item):
# logger.debug('language_list: %s' % language_list)
simple_language = language_list
else:
# Si item.language es un string se normaliza
# If item.language is a string it is normalized
if item.language != '':
lang = True
simple_language = set_lang(item.language).upper()
@@ -438,8 +429,7 @@ def title_format(item):
# item.language = simple_language
# Damos formato al año si existiera y lo agregamos
# al titulo excepto que sea un episodio
# We format the year if it exists and add it to the title except that it is an episode
if info and info.get("year", "") not in ["", " "] and item.contentType != 'episode' and not info['season']:
try:
year = '%s' % set_color(info['year'], 'year')
@@ -447,14 +437,14 @@ def title_format(item):
except:
logger.debug('infoLabels: %s' % info)
# Damos formato al puntaje si existiera y lo agregamos al titulo
# We format the score if it exists and add it to the title
if info and info['rating'] and info['rating'] != '0.0' and not info['season']:
# Se normaliza el puntaje del rating
# The rating score is normalized
rating_value = check_rating(info['rating'])
# Asignamos el color dependiendo el puntaje, malo, bueno, muy bueno, en caso de que exista
# We assign the color depending on the score, bad, good, very good, in case it exists
if rating_value:
value = float(rating_value)
@@ -471,13 +461,13 @@ def title_format(item):
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
# We format the quality if it exists and add it to the title
if item.quality and isinstance(item.quality, str):
quality = item.quality.strip()
else:
quality = ''
# Damos formato al idioma-calidad si existieran y los agregamos al plot
# We format the language-quality if they exist and add them to the plot
quality_ = set_color(quality, 'quality')
if (lang or quality) and item.action == "play":
@@ -498,14 +488,14 @@ def title_format(item):
plot_ = add_info_plot('', simple_language, quality_)
item.contentPlot = plot_
# Para las busquedas por canal
# For channel searches
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
# Format for series updates in the video library overwrites the previous colors
if item.channel == 'videolibrary' and item.context != '':
if item.action == 'get_seasons':
@@ -514,15 +504,14 @@ def title_format(item):
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
# logger.debug('After the format: %s' % item)
# We format the server if it exists
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.
# Check if we are in findvideos, and if there is a server, if so, the title is not shown but the server, otherwise the title is normally shown.
# logger.debug('item.title antes de server: %s'%item.title)
# logger.debug('item.title before server: %s'%item.title)
if item.action != 'play' and item.server:
item.title = '%s %s' % (item.title, server.strip())
@@ -544,7 +533,7 @@ def title_format(item):
if item.channel == 'videolibrary':
item.title += ' [%s]' % item.contentChannel
# si hay verificacion de enlaces
# if there is verification of links
if item.alive != '':
if item.alive.lower() == 'no':
item.title = '[[COLOR red][B]X[/B][/COLOR]] %s' % item.title
@@ -553,14 +542,14 @@ def title_format(item):
else:
item.title = '%s' % item.title
# logger.debug('item.title despues de server: %s' % item.title)
# logger.debug('item.title after 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
elif item.unify:
item.title = '%s' % set_color(item.title, 'otro')
# logger.debug('antes de salir %s' % item.title)
# logger.debug('before leaving %s' % item.title)
if visto:
try:
check = u'\u221a'
@@ -579,8 +568,7 @@ def title_format(item):
def thumbnail_type(item):
# logger.info()
# Se comprueba que tipo de thumbnail se utilizara en findvideos,
# Poster o Logo del servidor
# Check what type of thumbnail will be used in findvideos, Poster or Logo of the server
thumb_type = config.get_setting('video_thumbnail_type')
info = item.infoLabels
@@ -612,16 +600,16 @@ def check_rating(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
We let the float only have one element in its decimal part, "7.10" --> "7.1"
@param _rating: rating value
@type _rating: float
@return: devuelve el valor modificado si es correcto, si no devuelve None
@return: returns the modified value if it is correct, if it does not return None
@rtype: float|None
"""
# logger.debug("rating %s" % _rating)
try:
# convertimos los deciamles p.e. 7.1
# we convert the deciamles ex. 7.1
return "%.1f" % round(_rating, 1)
except Exception as ex_dl:
template = "An exception of type %s occured. Arguments:\n%r"
@@ -631,20 +619,20 @@ def check_rating(rating):
def check_range(_rating):
"""
Comprobamos que el rango de rating sea entre 0.0 y 10.0
@param _rating: valor del rating
We check that the rating range is between 0.0 and 10.0
@param _rating: rating value
@type _rating: float
@return: devuelve el valor si está dentro del rango, si no devuelve None
@return: returns the value if it is within the range, if it does not return None
@rtype: float|None
"""
# logger.debug("rating %s" % _rating)
# fix para comparacion float
# fix for float comparison
dec = Decimal(_rating)
if 0.0 <= dec <= 10.0:
# logger.debug("estoy en el rango!")
# logger.debug("i'm in range!")
return _rating
else:
# logger.debug("NOOO estoy en el rango!")
# logger.debug("NOOO I'm in range!")
return None
def convert_float(_rating):
@@ -657,26 +645,26 @@ def check_rating(rating):
return None
if not isinstance(rating, float):
# logger.debug("no soy float")
# logger.debug("I'm not float")
if isinstance(rating, int):
# logger.debug("soy int")
# logger.debug("I am int")
rating = convert_float(rating)
elif isinstance(rating, str):
# logger.debug("soy str")
# logger.debug("I'm 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
# logger.debug("error converting str, rating is not a float")
# we get the numerical values
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
# we get an unknown value we don't return anything
return None
if rating:

View File

@@ -25,12 +25,12 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
Construction method:
SettingWindow(list_controls, dict_values, title, callback, item)
Parametros:
list_controls: (list) Lista de controles a incluir en la ventana, segun el siguiente esquema:
Parameters:
list_controls: (list) List of controls to include in the window, according to the following scheme:
(opcional)list_controls= [
{'id': "nameControl1",
'type': "bool", # bool, text, list, label
'label': "Control 1: tipo RadioButton",
'label': "Control 1: type RadioButton",
'color': '0xFFee66CC', # text color in hexadecimal ARGB format
'default': True,
'enabled': True,
@@ -38,7 +38,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
},
{'id': "nameControl2",
'type': "text", # bool, text, list, label
'label': "Control 2: tipo Cuadro de texto",
'label': "Control 2: type text box",
'color': '0xFFee66CC',
'default': "Valor por defecto",
'hidden': False, # only for type = text Indicates whether to hide the text (for passwords)
@@ -47,7 +47,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
},
{'id': "nameControl3",
'type': "list", # bool, text, list, label
'label': "Control 3: tipo Lista",
'label': "Control 3: type List",
'color': '0xFFee66CC',
'default': 0, # Default value index in lvalues
'enabled': True,
@@ -71,7 +71,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
{
"id": "name_control_1",
"type": "bool",
"label": "Control 1: tipo RadioButton",
"label": "Control 1: type RadioButton",
"default": false,
"enabled": true,
"visible": true,
@@ -80,8 +80,8 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
{
"id": "name_control_2",
"type": "text",
"label": "Control 2: tipo Cuadro de texto",
"default": "Valor por defecto",
"label": "Control 2: type text box",
"default": "Default value",
"hidden": true,
"enabled": true,
"visible": true,
@@ -90,7 +90,7 @@ class SettingsWindow(xbmcgui.WindowXMLDialog):
{
"id": "name_control_3",
"type": "list",
"label": "Control 3: tipo Lista",
"label": "Control 3: type List",
"default": 0,
"enabled": true,
"visible": true,

View File

@@ -73,7 +73,7 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
# logger.debug(str(data_in))
if self.listData:
# Datos comunes a todos los listados
# Data common to all listings
infoLabels = self.scraper().get_infoLabels(origen=data_in)
if "original_language" in infoLabels:
@@ -84,78 +84,77 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
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'.
It shows a window with the info of the video. Optionally, the title of the window can be indicated by means of the argument '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. title (este tiene prioridad 2)
El primero que contenga "algo" lo interpreta como el titulo (es importante asegurarse que el titulo este en
su sitio)
If an item is passed as the 'data' argument use the Tmdb scrapper to find the video info
In case of movies:
Take the title from the following fields (in this order)
1. contentTitle (this has priority 1)
2. title (this has priority 2)
The first one containing "something" interprets it as the title (it is important to make sure that the title is in
your site)
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)
In case of series:
1. Find the season and episode in the contentSeason and contentEpisodeNumber fields
2. Try to remove it from the video title (format: 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
Here are two possible options:
1. We have Season and episode
Shows the information of the specific chapter
2. We DO NOT have Season and episode
In this case it shows the generic information of the series
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({
If an InfoLabels object (see item.py) is passed as an argument 'data' it shows in the window directly
the past information (without using the scrapper)
Format:
In case of movies:
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"
"title": "Title of the movie",
"original_title": "Original movie title",
"date": "Release date",
"language": "Original language of the movie",
"rating": "Rating of the movie",
"votes": "Number of votes",
"genres": "Genres of the movie",
"thumbnail": "Path for the thumbnail",
"fanart": "Route for the fanart",
"plot": "Synopsis of the movie"
}
En caso de series:
infoLabels({
In case of 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"
"title": "Title of the series",
"episode_title": "Episode title",
"date": "Date of issue",
"language": "Original language of the series",
"rating": "Punctuation of the series",
"votes": "Number of votes",
"genres": "Genres of the series",
"thumbnail": "Path for the thumbnail",
"fanart": "Route for the fanart",
"plot": "Synopsis of the episode or series",
"seasons": "Number of Seasons",
"season": "Season",
"episodes": "Number of episodes of the season",
"episode": "Episode"
}
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.
If a list of InfoLabels () with the previous structure is passed as the 'data' argument, it shows the buttons
'Previous' and 'Next' to scroll through the list. It also shows the 'Accept' and 'Cancel' buttons that
call the function 'callback' of the channel from where the call is made, passing the element as parameters
current (InfoLabels ()) or None respectively.
@param data: información para obtener datos del scraper.
@param data: information to get scraper data.
@type data: item, InfoLabels, list(InfoLabels)
@param caption: titulo de la ventana.
@param caption: window title.
@type caption: str
@param item: elemento del que se va a mostrar la ventana de información
@param item: item for which the information window is to be displayed
@type item: Item
@param scraper: scraper que tiene los datos de las peliculas o series a mostrar en la ventana.
@param scraper: scraper that has the data of the movies or series to show in the window.
@type scraper: Scraper
"""
# Capturamos los parametros
# We capture the parameters
self.caption = caption
self.item = item
self.indexList = -1
@@ -171,7 +170,7 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
self.get_scraper_data(data)
# Muestra la ventana
# Show window
self.doModal()
return self.return_value
@@ -184,19 +183,19 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
self.scraper = Tmdb
def onInit(self):
#### Compatibilidad con Kodi 18 ####
#### Kodi 18 compatibility ####
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
# We put the title and the images
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
# We load the data for the movie format
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"))
@@ -211,7 +210,7 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
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
# We load the data for the serial format
else:
self.getControl(10006).setLabel(config.get_localized_string(60383))
self.getControl(10007).setLabel(self.result.get("title", "N/A"))
@@ -235,7 +234,7 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
self.getControl(100020).setLabel(config.get_localized_string(60387))
self.getControl(100021).setLabel(self.result.get("date", "N/A"))
# Sinopsis
# Synopsis
if self.result['plot']:
self.getControl(100022).setLabel(config.get_localized_string(60388))
self.getControl(100023).setText(self.result.get("plot", "N/A"))
@@ -243,20 +242,20 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
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
# We load the buttons if necessary
self.getControl(10024).setVisible(self.indexList > -1) # Button group
self.getControl(ID_BUTTON_PREVIOUS).setEnabled(self.indexList > 0) # Previous
if self.listData:
m = len(self.listData)
else:
m = 1
self.getControl(ID_BUTTON_NEXT).setEnabled(self.indexList + 1 != m) # Siguiente
self.getControl(ID_BUTTON_NEXT).setEnabled(self.indexList + 1 != m) # Following
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"
# We put the focus in the Group of buttons, if "Previous" was deactivated the focus would go to the "Next" button
# if "Next" tb is deactivated it will pass the focus to the "Cancel" button
self.setFocus(self.getControl(10024))
return self.return_value
@@ -331,6 +330,6 @@ class InfoWindow(xbmcgui.WindowXMLDialog):
# Down
elif action == 4:
self.setFocus(self.getControl(ID_BUTTON_OK))
# Pulsa ESC o Atrás, simula click en boton cancelar
# Press ESC or Back, simulate click on cancel button
if action in [10, 92]:
self.onClick(ID_BUTTON_CANCEL)

View File

@@ -5,21 +5,12 @@
from future import standard_library
standard_library.install_aliases()
#from builtins import str
import sys
import sys, os, threading, time, re, math, xbmc
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
import os
import threading
import time
import re
import math
import xbmc
from core import filetools
from core import jsontools
from platformcode import config, logger
from platformcode import platformtools
from core import filetools, jsontools
from platformcode import config, logger, platformtools
from core import scrapertools
from xml.dom import minidom
@@ -82,25 +73,25 @@ def mark_auto_as_watched(item):
time.sleep(5)
# Sincronizacion silenciosa con Trakt
# Silent sync with Trakt
if sync_with_trakt and config.get_setting("trakt_sync"):
sync_trakt_kodi()
# logger.debug("Fin del hilo")
# Si esta configurado para marcar como visto
# If it is configured to mark as seen
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
Updates the values of episodes seen if
"""
logger.info()
# si existe el addon hacemos la busqueda
# if the addon exists we do the search
if xbmc.getCondVisibility('System.HasAddon("script.trakt")'):
# importamos dependencias
# we import dependencies
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/"]
@@ -108,7 +99,7 @@ def sync_trakt_addon(path_folder):
for path in paths:
sys.path.append(xbmc.translatePath(path))
# se obtiene las series vistas
# the series seen is obtained
try:
from resources.lib.traktapi import traktAPI
traktapi = traktAPI()
@@ -118,9 +109,9 @@ def sync_trakt_addon(path_folder):
shows = traktapi.getShowsWatched({})
shows = list(shows.items())
# obtenemos el id de la serie para comparar
# we get the series id to compare
_id = re.findall("\[(.*?)\]", path_folder, flags=re.DOTALL)[0]
logger.debug("el id es %s" % _id)
logger.debug("the id is %s" % _id)
if "tt" in _id:
type_id = "imdb"
@@ -131,15 +122,15 @@ def sync_trakt_addon(path_folder):
type_id = "tmdb"
_id = _id.strip("tmdb_")
else:
logger.error("No hay _id de la serie")
logger.error("There is no _id of the series")
return
# obtenemos los valores de la serie
# we obtain the values of the series
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
# we look in the trakt series
for show in shows:
show_aux = show[1].to_dict()
@@ -148,30 +139,27 @@ def sync_trakt_addon(path_folder):
# logger.debug("ID ES %s" % _id_trakt)
if _id_trakt:
if _id == _id_trakt:
logger.debug("ENCONTRADO!! %s" % show_aux)
logger.debug("FOUND! %s" % show_aux)
# creamos el diccionario de trakt para la serie encontrada con el valor que tiene "visto"
# we create the trakt dictionary for the found series with the value that has "seen"
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))
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']
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
# we get the keys that are episodes
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
# we get the keys that are seasons
keys_seasons = [key for key in serie.library_playcounts if 'season ' in key]
# obtenemos los numeros de las keys temporadas
# we get the numbers of the seasons keys
seasons = [key.strip('season ') for key in keys_seasons]
# marcamos los episodios vistos
# we mark the episodes watched
for k in keys_episodes:
serie.library_playcounts[k] = dict_trakt_show.get(k, 0)
@@ -179,7 +167,7 @@ def sync_trakt_addon(path_folder):
episodios_temporada = 0
episodios_vistos_temporada = 0
# obtenemos las keys de los episodios de una determinada temporada
# we obtain the keys of the episodes of a certain season
keys_season_episodes = [key for key in keys_episodes if key.startswith("%sx" % season)]
for k in keys_season_episodes:
@@ -187,7 +175,7 @@ def sync_trakt_addon(path_folder):
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
# it is verified that if all the episodes are watched, the season is marked as watched
if episodios_temporada == episodios_vistos_temporada:
serie.library_playcounts.update({"season %s" % season: 1})
@@ -199,11 +187,11 @@ def sync_trakt_addon(path_folder):
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
# sCheck that if all seasons are viewed, the series is marked as view
if temporada == temporada_vista:
serie.library_playcounts.update({serie.title: 1})
logger.debug("los valores nuevos %s " % serie.library_playcounts)
logger.debug("the new values %s " % serie.library_playcounts)
filetools.write(tvshow_file, head_nfo + serie.tojson())
break
@@ -211,7 +199,7 @@ def sync_trakt_addon(path_folder):
continue
else:
logger.error("no se ha podido obtener el id, trakt tiene: %s" % show_aux['ids'])
logger.error("could not get id, trakt has: %s" % show_aux['ids'])
except:
import traceback
@@ -219,7 +207,7 @@ def sync_trakt_addon(path_folder):
def sync_trakt_kodi(silent=True):
# Para que la sincronizacion no sea silenciosa vale con silent=False
# So that the synchronization is not silent it is worth with silent = False
if xbmc.getCondVisibility('System.HasAddon("script.trakt")'):
notificacion = True
if (not config.get_setting("sync_trakt_notification", "videolibrary") and platformtools.is_playing()):
@@ -234,11 +222,11 @@ def sync_trakt_kodi(silent=True):
def mark_content_as_watched_on_kodi(item, value=1):
"""
marca el contenido como visto o no visto en la libreria de Kodi
mark the content as seen or not seen in the Kodi library
@type item: item
@param item: elemento a marcar
@param item: element to mark
@type value: int
@param value: >0 para visto, 0 para no visto
@param value: > 0 for seen, 0 for not seen
"""
logger.info()
# logger.debug("item:\n" + item.tostring('\n'))
@@ -253,23 +241,22 @@ def mark_content_as_watched_on_kodi(item, value=1):
data = get_data(payload)
if 'result' in data and "movies" in data['result']:
if item.strm_path: #Si Item es de un episodio
if item.strm_path: # If Item is from an episode
filename = filetools.basename(item.strm_path)
head, tail = filetools.split(filetools.split(item.strm_path)[0])
else: #Si Item es de la Serie
else: # If Item is from the Series
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")
# logger.debug("I mark the movie as a view")
movieid = d['movieid']
break
if movieid != 0:
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": {
"movieid": movieid, "playcount": value}, "id": 1}
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": {"movieid": movieid, "playcount": value}, "id": 1}
else: # item.contentType != 'movie'
episodeid = 0
@@ -280,10 +267,10 @@ def mark_content_as_watched_on_kodi(item, value=1):
data = get_data(payload)
if 'result' in data and "episodes" in data['result']:
if item.strm_path: #Si Item es de un episodio
if item.strm_path: # If Item is from an episode
filename = filetools.basename(item.strm_path)
head, tail = filetools.split(filetools.split(item.strm_path)[0])
else: #Si Item es de la Serie
else: # If Item is from the Series
filename = filetools.basename(item.path)
head, tail = filetools.split(filetools.split(item.path)[0])
path = filetools.join(tail, filename)
@@ -291,35 +278,33 @@ def mark_content_as_watched_on_kodi(item, value=1):
for d in data['result']['episodes']:
if d['file'].replace("/", "\\").endswith(path.replace("/", "\\")):
# logger.debug("marco el episodio como visto")
# logger.debug("I mark the episode as seen")
episodeid = d['episodeid']
break
if episodeid != 0:
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetEpisodeDetails", "params": {
"episodeid": episodeid, "playcount": value}, "id": 1}
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetEpisodeDetails", "params": {"episodeid": episodeid, "playcount": value}, "id": 1}
if payload_f:
# Marcar como visto
# Mark as seen
data = get_data(payload_f)
# logger.debug(str(data))
if data['result'] != 'OK':
logger.error("ERROR al poner el contenido como visto")
logger.error("ERROR putting content as viewed")
def mark_season_as_watched_on_kodi(item, value=1):
"""
marca toda la temporada como vista o no vista en la libreria de Kodi
mark the entire season as seen or unseen in the Kodi library
@type item: item
@param item: elemento a marcar
@param item: element to mark
@type value: int
@param value: >0 para visto, 0 para no visto
@param value: > 0 for seen, 0 for not seen
"""
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
# We can only mark the season as seen in the Kodi database if the database is local, in case of sharing database this functionality will not work
if config.get_setting("db_mode", "videolibrary"):
return
@@ -336,9 +321,7 @@ def mark_season_as_watched_on_kodi(item, value=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)
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)
@@ -348,9 +331,9 @@ def mark_content_as_watched_on_kod(path):
from core import videolibrarytools
"""
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
mark the entire series or movie as viewed or unseen in the Alpha Video Library based on their status in the Kodi Video Library
@type str: path
@param path: carpeta de contenido a marcar
@param path: content folder to mark
"""
logger.info()
#logger.debug("path: " + path)
@@ -361,9 +344,8 @@ def mark_content_as_watched_on_kod(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"):
# We can only mark the content as a view in the Kodi database if the database is local, in case of sharing database this functionality will not work
# if config.get_setting("db_mode", "videolibrary"):
# return
path2 = ''
@@ -375,60 +357,60 @@ def mark_content_as_watched_on_kod(path):
if "\\" in path:
path = path.replace("/", "\\")
head_nfo, item = videolibrarytools.read_nfo(path) #Leo el .nfo del contenido
head_nfo, item = videolibrarytools.read_nfo(path) # I read the content .nfo
if not item:
logger.error('.NFO no encontrado: ' + path)
logger.error('.NFO not found: ' + path)
return
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 FOLDER_TVSHOWS in path: # I check if it is CINEMA or SERIES
contentType = "episode_view" # I mark the Kodi Video BBDD table
nfo_name = "tvshow.nfo" # I build the name of the .nfo
path1 = path.replace("\\\\", "\\").replace(nfo_name, '') # for SQL I just need the folder
if not path2:
path2 = path1.replace("\\", "/") #Formato no Windows
path2 = path1.replace("\\", "/") # Format 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
contentType = "movie_view" # I mark the Kodi Video BBDD table
path1 = path.replace("\\\\", "\\") # Windows format
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, quitamos las credenciales
path2 = path1.replace("\\", "/") # Format no Windows
nfo_name = scrapertools.find_single_match(path2, '\]\/(.*?)$') # I build the name of the .nfo
path1 = path1.replace(nfo_name, '') # for SQL I just need the folder
path2 = path2.replace(nfo_name, '') # for SQL I just need the folder
path2 = filetools.remove_smb_credential(path2) # If the file is on an SMB server, we remove the credentials
#Ejecutmos la sentencia SQL
# Let's execute the SQL statement
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
nun_records, records = execute_sql_kodi(sql) # SQL execution
if nun_records == 0: # is there an error?
logger.error("SQL error: " + sql + ": 0 registros")
return # we quit: either it is not cataloged in Kodi, or there is an error in the SQL
for title, playCount in records: #Ahora recorremos todos los registros obtenidos
for title, playCount in records: # Now we go through all the records obtained
if contentType == "episode_view":
title_plain = title.replace('.strm', '') #Si es Serie, quitamos el sufijo .strm
title_plain = title.replace('.strm', '') # If it is Serial, we remove the suffix .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
title_plain = scrapertools.find_single_match(item.strm_path, '.(.*?\s\[.*?\])') # if it's a movie, we remove the title
if playCount is None or playCount == 0: # not yet seen, we set it to 0
playCount_final = 0
elif playCount >= 1:
playCount_final = 1
elif not PY3 and isinstance(title_plain, (str, unicode)):
title_plain = title_plain.decode("utf-8").encode("utf-8") #Hacemos esto porque si no genera esto: u'title_plain'
title_plain = title_plain.decode("utf-8").encode("utf-8") # We do this because if it doesn't generate this: u'title_plain '
elif PY3 and isinstance(title_plain, bytes):
title_plain = title_plain.decode('utf-8')
item.library_playcounts.update({title_plain: playCount_final}) #actualizamos el playCount del .nfo
item.library_playcounts.update({title_plain: playCount_final}) # update the .nfo playCount
if item.infoLabels['mediatype'] == "tvshow": #Actualizamos los playCounts de temporadas y Serie
if item.infoLabels['mediatype'] == "tvshow": # We update the Season and Series playCounts
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
if "season" in season: # we look for the tags "season" inside playCounts
season_num = int(scrapertools.find_single_match(season, 'season (\d+)')) # we save the season number
item = videolibrary.check_season_playcount(item, season_num) # We call the method that updates Temps. and series
filetools.write(path, head_nfo + item.tojson())
@@ -437,7 +419,7 @@ def mark_content_as_watched_on_kod(path):
def get_data(payload):
"""
obtiene la información de la llamada JSON-RPC con la información pasada en payload
get the information of the JSON-RPC call with the information passed in payload
@type payload: dict
@param payload: data
:return:
@@ -483,12 +465,12 @@ def get_data(payload):
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.
Update the library depending on the type of content and the path passed to it.
@type folder_content: str
@param folder_content: tipo de contenido para actualizar, series o peliculas
@param folder_content: type of content to update, series or movies
@type folder: str
@param folder: nombre de la carpeta a escanear.
@param folder: name of the folder to scan.
"""
logger.info(folder)
@@ -512,7 +494,7 @@ def update(folder_content=config.get_setting("folder_tvshows"), folder=""):
videolibrarypath = videolibrarypath[:-1]
update_path = videolibrarypath + "/" + folder_content + "/" + folder + "/"
else:
#update_path = filetools.join(videolibrarypath, folder_content, folder) + "/" # Problemas de encode en "folder"
# update_path = filetools.join(videolibrarypath, folder_content, folder) + "/" # Encoder problems in "folder"
update_path = filetools.join(videolibrarypath, folder_content, ' ').rstrip()
if videolibrarypath.startswith("special:") or not scrapertools.find_single_match(update_path, '(^\w+:\/\/)'):
@@ -535,9 +517,9 @@ def search_library_path():
def set_content(content_type, silent=False, custom=False):
"""
Procedimiento para auto-configurar la videoteca de kodi con los valores por defecto
Procedure to auto-configure the kodi video library with the default values
@type content_type: str ('movie' o 'tvshow')
@param content_type: tipo de contenido para configurar, series o peliculas
@param content_type: content type to configure, series or movies
"""
logger.info()
continuar = True
@@ -556,14 +538,14 @@ def set_content(content_type, silent=False, custom=False):
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
# Ask if we want to install metadata.themoviedb.org
install = platformtools.dialog_yesno(config.get_localized_string(60046))
else:
install = True
if install:
try:
# Instalar metadata.themoviedb.org
# Install metadata.themoviedb.org
xbmc.executebuiltin('xbmc.installaddon(metadata.themoviedb.org)', True)
logger.info("Instalado el Scraper de películas de TheMovieDB")
except:
@@ -580,7 +562,7 @@ def set_content(content_type, silent=False, custom=False):
if continuar and not xbmc.getCondVisibility('System.HasAddon(metadata.universal)'):
continuar = False
if not silent:
# Preguntar si queremos instalar metadata.universal
# Ask if we want to install metadata.universal
install = platformtools.dialog_yesno(config.get_localized_string(70095))
else:
install = True
@@ -610,16 +592,16 @@ def set_content(content_type, silent=False, custom=False):
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
#Ask if we want to install metadata.tvdb.com
install = platformtools.dialog_yesno(config.get_localized_string(60048))
else:
install = True
if install:
try:
# Instalar metadata.tvdb.com
# Install metadata.tvdb.com
xbmc.executebuiltin('xbmc.installaddon(metadata.tvdb.com)', True)
logger.info("Instalado el Scraper de series de The TVDB")
logger.info("The TVDB series Scraper installed ")
except:
pass
@@ -634,14 +616,14 @@ def set_content(content_type, silent=False, custom=False):
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
# Ask if we want to install metadata.tvshows.themoviedb.org
install = platformtools.dialog_yesno(config.get_localized_string(60050))
else:
install = True
if install:
try:
# Instalar metadata.tvshows.themoviedb.org
# Install metadata.tvshows.themoviedb.org
xbmc.executebuiltin('xbmc.installaddon(metadata.tvshows.themoviedb.org)', True)
if xbmc.getCondVisibility('System.HasAddon(metadata.tvshows.themoviedb.org)'):
continuar = True
@@ -659,7 +641,7 @@ def set_content(content_type, silent=False, custom=False):
if continuar:
continuar = False
# Buscamos el idPath
# We look for the idPath
sql = 'SELECT MAX(idPath) FROM path'
nun_records, records = execute_sql_kodi(sql)
if nun_records == 1:
@@ -677,7 +659,7 @@ def set_content(content_type, silent=False, custom=False):
if not sql_videolibrarypath.endswith(sep):
sql_videolibrarypath += sep
# Buscamos el idParentPath
# We are looking for the idParentPath
sql = 'SELECT idPath, strPath FROM path where strPath LIKE "%s"' % sql_videolibrarypath
nun_records, records = execute_sql_kodi(sql)
if nun_records == 1:
@@ -685,7 +667,7 @@ def set_content(content_type, silent=False, custom=False):
videolibrarypath = records[0][1][:-1]
continuar = True
else:
# No existe videolibrarypath en la BD: la insertamos
# There is no videolibrarypath in the DB: we insert it
sql_videolibrarypath = videolibrarypath
if not sql_videolibrarypath.endswith(sep):
sql_videolibrarypath += sep
@@ -703,7 +685,7 @@ def set_content(content_type, silent=False, custom=False):
if continuar:
continuar = False
# Fijamos strContent, strScraper, scanRecursive y strSettings
# We set strContent, strScraper, scanRecursive and strSettings
if content_type == 'movie':
strContent = 'movies'
scanRecursive = 2147483647
@@ -719,7 +701,7 @@ def set_content(content_type, silent=False, custom=False):
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?"
strActualizar = "Do you want to set this Scraper in Spanish as the default option for movies?"
if not videolibrarypath.endswith(sep):
videolibrarypath += sep
strPath = videolibrarypath + config.get_setting("folder_movies") + sep
@@ -738,13 +720,13 @@ def set_content(content_type, silent=False, custom=False):
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?"
strActualizar = "Do you want to configure this Scraper in Spanish as a default option for 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
# We check if strPath already exists in the DB to avoid duplicates
sql = 'SELECT idPath FROM path where strPath="%s"' % strPath
nun_records, records = execute_sql_kodi(sql)
sql = ""
@@ -1011,12 +993,12 @@ def clean(path_list=[]):
def execute_sql_kodi(sql):
"""
Ejecuta la consulta sql contra la base de datos de kodi
@param sql: Consulta sql valida
Run sql query against kodi database
@param sql: Valid sql query
@type sql: str
@return: Numero de registros modificados o devueltos por la consulta
@return: Number of records modified or returned by the query
@rtype nun_records: int
@return: lista con el resultado de la consulta
@return: list with the query result
@rtype records: list of tuples
"""
logger.info()
@@ -1024,12 +1006,12 @@ def execute_sql_kodi(sql):
nun_records = 0
records = None
# Buscamos el archivo de la BBDD de videos segun la version de kodi
# We look for the archive of the video database according to the version of 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
# alternative method to locate the database
if not file_db or not filetools.exists(file_db):
file_db = ""
for f in filetools.listdir(xbmc.translatePath("special://userdata/Database")):
@@ -1040,14 +1022,14 @@ def execute_sql_kodi(sql):
break
if file_db:
logger.info("Archivo de BD: %s" % file_db)
logger.info("DB file: %s" % file_db)
conn = None
try:
import sqlite3
conn = sqlite3.connect(file_db)
cursor = conn.cursor()
logger.info("Ejecutando sql: %s" % sql)
logger.info("Running sql: %s" % sql)
cursor.execute(sql)
conn.commit()
@@ -1061,15 +1043,15 @@ def execute_sql_kodi(sql):
nun_records = conn.total_changes
conn.close()
logger.info("Consulta ejecutada. Registros: %s" % nun_records)
logger.info("Query executed. Records: %s" % nun_records)
except:
logger.error("Error al ejecutar la consulta sql")
logger.error("Error executing sql query")
if conn:
conn.close()
else:
logger.debug("Base de datos no encontrada")
logger.debug("Database not found")
return nun_records, records