KoD 1.1
- Ottimizzata e migliorata la ricerca globale - fix Mega - altri cambiamenti minori
This commit is contained in:
+46
-53
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# channeltools - Herramientas para trabajar con canales
|
||||
# channeltools - Tools for working with channels
|
||||
# ------------------------------------------------------------
|
||||
|
||||
from __future__ import absolute_import
|
||||
@@ -29,11 +29,11 @@ def get_channel_parameters(channel_name):
|
||||
channel_parameters = get_channel_json(channel_name)
|
||||
# logger.debug(channel_parameters)
|
||||
if channel_parameters:
|
||||
# cambios de nombres y valores por defecto
|
||||
# name and default changes
|
||||
channel_parameters["title"] = channel_parameters.pop("name") + (' [DEPRECATED]' if 'deprecated' in channel_parameters and channel_parameters['deprecated'] else '')
|
||||
channel_parameters["channel"] = channel_parameters.pop("id")
|
||||
|
||||
# si no existe el key se declaran valor por defecto para que no de fallos en las funciones que lo llaman
|
||||
# if the key does not exist, they are declared a default value so that there are no failures in the functions that call it
|
||||
channel_parameters["update_url"] = channel_parameters.get("update_url", DEFAULT_UPDATE_URL)
|
||||
channel_parameters["language"] = channel_parameters.get("language", ["all"])
|
||||
channel_parameters["active"] = channel_parameters.get("active", False)
|
||||
@@ -45,7 +45,7 @@ def get_channel_parameters(channel_name):
|
||||
channel_parameters["banner"] = channel_parameters.get("banner", "")
|
||||
channel_parameters["fanart"] = channel_parameters.get("fanart", "")
|
||||
|
||||
# Imagenes: se admiten url y archivos locales dentro de "resources/images"
|
||||
# Images: url and local files are allowed inside "resources / images"
|
||||
if channel_parameters.get("thumbnail") and "://" not in channel_parameters["thumbnail"]:
|
||||
channel_parameters["thumbnail"] = filetools.join(remote_path, "resources", "thumb", channel_parameters["thumbnail"])
|
||||
if channel_parameters.get("banner") and "://" not in channel_parameters["banner"]:
|
||||
@@ -53,7 +53,7 @@ def get_channel_parameters(channel_name):
|
||||
if channel_parameters.get("fanart") and "://" not in channel_parameters["fanart"]:
|
||||
channel_parameters["fanart"] = filetools.join(remote_path, "resources", channel_parameters["fanart"])
|
||||
|
||||
# Obtenemos si el canal tiene opciones de configuración
|
||||
# We obtain if the channel has configuration options
|
||||
channel_parameters["has_settings"] = False
|
||||
if 'settings' in channel_parameters:
|
||||
channel_parameters['settings'] = get_default_settings(channel_name)
|
||||
@@ -71,8 +71,7 @@ def get_channel_parameters(channel_name):
|
||||
dict_channels_parameters[channel_name] = channel_parameters
|
||||
|
||||
else:
|
||||
# para evitar casos donde canales no están definidos como configuración
|
||||
# lanzamos la excepcion y asi tenemos los valores básicos
|
||||
# To avoid cases where channels are not defined as configuration, we throw the exception and thus we have the basic values
|
||||
raise Exception
|
||||
|
||||
except Exception as ex:
|
||||
@@ -123,7 +122,7 @@ def get_channel_controls_settings(channel_name):
|
||||
|
||||
for c in list_controls:
|
||||
if 'id' not in c or 'type' not in c or 'default' not in c:
|
||||
# Si algun control de la lista no tiene id, type o default lo ignoramos
|
||||
# If any control in the list does not have id, type or default, we ignore it
|
||||
continue
|
||||
|
||||
# new dict with key(id) and value(default) from settings
|
||||
@@ -173,7 +172,7 @@ def get_default_settings(channel_name):
|
||||
default_controls_renumber = default_file['renumber']
|
||||
channel_json = get_channel_json(channel_name)
|
||||
|
||||
# Collects configurations
|
||||
# Collects configurations
|
||||
channel_language = channel_json['language']
|
||||
channel_controls = channel_json['settings']
|
||||
categories = channel_json['categories']
|
||||
@@ -189,28 +188,22 @@ def get_default_settings(channel_name):
|
||||
label = label[-1]
|
||||
if label == 'peliculas':
|
||||
if 'movie' in categories:
|
||||
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(
|
||||
30122)
|
||||
control['default'] = False if ('include_in_newest' in default_off) or (
|
||||
'include_in_newest_peliculas' in default_off) else True
|
||||
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string( 30122)
|
||||
control['default'] = False if ('include_in_newest' in default_off) or ( ' include_in_newest_peliculas' in default_off) else True
|
||||
channel_controls.append(control)
|
||||
else:
|
||||
pass
|
||||
elif label == 'series':
|
||||
if 'tvshow' in categories:
|
||||
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(
|
||||
30123)
|
||||
control['default'] = False if ('include_in_newest' in default_off) or (
|
||||
'include_in_newest_series' in default_off) else True
|
||||
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string( 30123)
|
||||
control['default'] = False if ('include_in_newest' in default_off) or ( 'include_in_newest_series' in default_off) else True
|
||||
channel_controls.append(control)
|
||||
else:
|
||||
pass
|
||||
elif label == 'anime':
|
||||
if 'anime' in categories:
|
||||
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string(
|
||||
30124)
|
||||
control['default'] = False if ('include_in_newest' in default_off) or (
|
||||
'include_in_newest_anime' in default_off) else True
|
||||
control['label'] = config.get_localized_string(70727) + ' - ' + config.get_localized_string( 30124)
|
||||
control['default'] = False if ('include_in_newest' in default_off) or ( 'include_in_newest_anime' in default_off) else True
|
||||
channel_controls.append(control)
|
||||
else:
|
||||
pass
|
||||
@@ -239,24 +232,24 @@ def get_default_settings(channel_name):
|
||||
def get_channel_setting(name, channel, default=None):
|
||||
from core import filetools
|
||||
"""
|
||||
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 propia del canal 'channel'.
|
||||
Returns the value of the parameter 'name' in the own configuration of the channel 'channel'.
|
||||
|
||||
Busca en la ruta \addon_data\plugin.video.kod\settings_channels el archivo channel_data.json y lee
|
||||
el valor del parametro 'name'. 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 retornar el valor solicitado. Si el parametro 'name'
|
||||
tampoco existe en el el archivo channel.json se devuelve el parametro default.
|
||||
Look in the path \addon_data\plugin.video.kod\settings_channels for the file channel_data.json and read
|
||||
the value of the parameter 'name'. If the file channel_data.json does not exist look in the channels folder for the file
|
||||
channel.json and create a channel_data.json file before returning the requested value. If the parameter 'name'
|
||||
also does not exist in the channel.json file the default parameter is returned.
|
||||
|
||||
|
||||
@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 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
|
||||
|
||||
"""
|
||||
@@ -266,58 +259,58 @@ def get_channel_setting(name, channel, default=None):
|
||||
if channel not in ['trakt']: def_settings = get_default_settings(channel)
|
||||
|
||||
if filetools.exists(file_settings):
|
||||
# Obtenemos configuracion guardada de ../settings/channel_data.json
|
||||
# We get saved configuration from ../settings/channel_data.json
|
||||
try:
|
||||
dict_file = jsontools.load(filetools.read(file_settings))
|
||||
if isinstance(dict_file, dict) and 'settings' in dict_file:
|
||||
dict_settings = dict_file['settings']
|
||||
except EnvironmentError:
|
||||
logger.error("ERROR al leer el archivo: %s" % file_settings)
|
||||
logger.error("ERROR when reading the file: %s" % file_settings)
|
||||
|
||||
if not dict_settings or name not in dict_settings:
|
||||
# Obtenemos controles del archivo ../channels/channel.json
|
||||
# We get controls from the file ../channels/channel.json
|
||||
try:
|
||||
list_controls, default_settings = get_channel_controls_settings(channel)
|
||||
except:
|
||||
default_settings = {}
|
||||
|
||||
if name in default_settings: # Si el parametro existe en el channel.json creamos el channel_data.json
|
||||
if name in default_settings: #If the parameter exists in the channel.json we create the channel_data.json
|
||||
default_settings.update(dict_settings)
|
||||
dict_settings = default_settings
|
||||
dict_file['settings'] = dict_settings
|
||||
# Creamos el archivo ../settings/channel_data.json
|
||||
# We create the file ../settings/channel_data.json
|
||||
json_data = jsontools.dump(dict_file)
|
||||
if not filetools.write(file_settings, json_data, silent=True):
|
||||
logger.error("ERROR al salvar el archivo: %s" % file_settings)
|
||||
logger.error("ERROR saving file: %s" % file_settings)
|
||||
|
||||
# Devolvemos el valor del parametro local 'name' si existe, si no se devuelve default
|
||||
# We return the value of the local parameter 'name' if it exists, if default is not returned
|
||||
return dict_settings.get(name, default)
|
||||
|
||||
|
||||
def set_channel_setting(name, value, channel):
|
||||
from core import filetools
|
||||
"""
|
||||
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 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 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 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 parameter 'name' does not exist, it adds it, with its value, to the corresponding file.
|
||||
|
||||
@param name: nombre del parametro
|
||||
@param name: parameter name
|
||||
@type name: str
|
||||
@param value: valor del parametro
|
||||
@param value: parameter value
|
||||
@type value: str
|
||||
@param channel: nombre del canal
|
||||
@param channel: channel name
|
||||
@type channel: str
|
||||
|
||||
@return: 'value' en caso de que se haya podido fijar el valor y None en caso contrario
|
||||
@return: 'value' if the value could be set and None otherwise
|
||||
@rtype: str, None
|
||||
|
||||
"""
|
||||
# Creamos la carpeta si no existe
|
||||
# We create the folder if it does not exist
|
||||
if not filetools.exists(filetools.join(config.get_data_path(), "settings_channels")):
|
||||
filetools.mkdir(filetools.join(config.get_data_path(), "settings_channels"))
|
||||
|
||||
@@ -327,16 +320,16 @@ def set_channel_setting(name, value, channel):
|
||||
dict_file = None
|
||||
|
||||
if filetools.exists(file_settings):
|
||||
# Obtenemos configuracion guardada de ../settings/channel_data.json
|
||||
# We get saved settings from ../settings/channel_data.json
|
||||
try:
|
||||
dict_file = jsontools.load(filetools.read(file_settings))
|
||||
dict_settings = dict_file.get('settings', {})
|
||||
except EnvironmentError:
|
||||
logger.error("ERROR al leer el archivo: %s" % file_settings)
|
||||
logger.error("ERROR when reading the file: %s" % file_settings)
|
||||
|
||||
dict_settings[name] = value
|
||||
|
||||
# comprobamos si existe dict_file y es un diccionario, sino lo creamos
|
||||
# we check if dict_file exists and it is a dictionary, if not we create it
|
||||
if dict_file is None or not dict_file:
|
||||
dict_file = {}
|
||||
|
||||
@@ -345,7 +338,7 @@ def set_channel_setting(name, value, channel):
|
||||
# Creamos el archivo ../settings/channel_data.json
|
||||
json_data = jsontools.dump(dict_file)
|
||||
if not filetools.write(file_settings, json_data, silent=True):
|
||||
logger.error("ERROR al salvar el archivo: %s" % file_settings)
|
||||
logger.error("ERROR saving file: %s" % file_settings)
|
||||
return None
|
||||
|
||||
return value
|
||||
|
||||
+49
-79
@@ -1,20 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Clase Downloader
|
||||
Downloader class
|
||||
Downloader(url, path [, filename, headers, resume])
|
||||
|
||||
url : string - url para descargar
|
||||
path : string - Directorio donde se guarda la descarga
|
||||
filename : [opt] string - Nombre de archivo para guardar
|
||||
headers : [opt] dict - Headers para usar en la descarga
|
||||
resume : [opt] bool - continuar una descarga previa en caso de existir, por defecto True
|
||||
url : string - url to download
|
||||
path : string - Directory where the download is saved
|
||||
filename : [opt] string - File name to save
|
||||
headers : [opt] dict - Headers to use for download
|
||||
resume : [opt] bool - continue a previous download if it exists, by default True
|
||||
|
||||
|
||||
metodos:
|
||||
start_dialog() Inicia la descarga mostrando el progreso
|
||||
start() Inicia la descarga en segundo plano
|
||||
stop(erase = False) Detiene la descarga, con erase = True elimina los datos descargados
|
||||
start_dialog() Start the download showing the progress
|
||||
start() Download starts in the background
|
||||
stop(erase = False) Stop the download, with erase = True it deletes the downloaded data
|
||||
|
||||
"""
|
||||
from __future__ import division
|
||||
@@ -26,7 +26,7 @@ standard_library.install_aliases()
|
||||
from builtins import range
|
||||
from builtins import object
|
||||
from past.utils import old_div
|
||||
#from builtins import str
|
||||
# from builtins import str
|
||||
import sys
|
||||
PY3 = False
|
||||
VFS = True
|
||||
@@ -53,8 +53,7 @@ class Downloader(object):
|
||||
|
||||
@property
|
||||
def connections(self):
|
||||
return len([c for c in self._download_info["parts"] if
|
||||
c["status"] in [self.states.downloading, self.states.connecting]]), self._max_connections
|
||||
return len([c for c in self._download_info["parts"] if c["status"] in [self.states.downloading, self.states.connecting]]), self._max_connections
|
||||
|
||||
@property
|
||||
def downloaded(self):
|
||||
@@ -102,7 +101,7 @@ class Downloader(object):
|
||||
def fullpath(self):
|
||||
return os.path.abspath(filetools.join(self._path, self._filename))
|
||||
|
||||
# Funciones
|
||||
# Features
|
||||
def start_dialog(self, title=config.get_localized_string(60200)):
|
||||
from platformcode import platformtools
|
||||
progreso = platformtools.dialog_progress_bg(title, config.get_localized_string(60201))
|
||||
@@ -111,9 +110,7 @@ class Downloader(object):
|
||||
while self.state == self.states.downloading:
|
||||
time.sleep(0.2)
|
||||
line1 = "%s" % (self.filename)
|
||||
line2 = config.get_localized_string(59983) % (
|
||||
self.downloaded[1], self.downloaded[2], self.size[1], self.size[2],
|
||||
self.speed[1], self.speed[2], self.connections[0], self.connections[1])
|
||||
line2 = config.get_localized_string(59983) % ( self.downloaded[1], self.downloaded[2], self.size[1], self.size[2], self.speed[1], self.speed[2], self.connections[0], self.connections[1])
|
||||
line3 = config.get_localized_string(60202) % (self.remaining_time)
|
||||
|
||||
progreso.update(int(self.progress), line1, line2 + " " + line3)
|
||||
@@ -130,9 +127,7 @@ class Downloader(object):
|
||||
conns.append(self.__open_connection__("0", ""))
|
||||
except:
|
||||
self._max_connections = x
|
||||
self._threads = [
|
||||
Thread(target=self.__start_part__, name="Downloader %s/%s" % (x + 1, self._max_connections)) for x
|
||||
in range(self._max_connections)]
|
||||
self._threads = [ Thread(target=self.__start_part__, name="Downloader %s/%s" % (x + 1, self._max_connections)) for x in range(self._max_connections)]
|
||||
break
|
||||
del conns
|
||||
self._start_time = time.time() - 1
|
||||
@@ -144,7 +139,7 @@ class Downloader(object):
|
||||
|
||||
def stop(self, erase=False):
|
||||
if self._state == self.states.downloading:
|
||||
# Detenemos la descarga
|
||||
# We stop downloading
|
||||
self._state = self.states.stopped
|
||||
for t in self._threads:
|
||||
if t.isAlive(): t.join()
|
||||
@@ -193,10 +188,10 @@ class Downloader(object):
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
# Funciones internas
|
||||
# Internal functions
|
||||
def __init__(self, url, path, filename=None, headers=[], resume=True, max_connections=10, block_size=2 ** 17,
|
||||
part_size=2 ** 24, max_buffer=10, json_path=None):
|
||||
# Parametros
|
||||
# Parameters
|
||||
self._resume = resume
|
||||
self._path = path
|
||||
self._filename = filename
|
||||
@@ -214,29 +209,26 @@ class Downloader(object):
|
||||
except:
|
||||
self.tmp_path = os.getenv("TEMP") or os.getenv("TMP") or os.getenv("TMPDIR")
|
||||
|
||||
self.states = type('states', (),
|
||||
{"stopped": 0, "connecting": 1, "downloading": 2, "completed": 3, "error": 4, "saving": 5})
|
||||
self.states = type('states', (), {"stopped": 0, "connecting": 1, "downloading": 2, "completed": 3, "error": 4, "saving": 5})
|
||||
|
||||
self._state = self.states.stopped
|
||||
self._download_lock = Lock()
|
||||
self._headers = {
|
||||
"User-Agent": "Kodi/15.2 (Windows NT 10.0; WOW64) App_Bitness/32 Version/15.2-Git:20151019-02e7013"}
|
||||
self._headers = {"User-Agent": "Kodi/15.2 (Windows NT 10.0; WOW64) App_Bitness/32 Version/15.2-Git:20151019-02e7013"}
|
||||
self._speed = 0
|
||||
self._buffer = {}
|
||||
self._seekable = True
|
||||
|
||||
self._threads = [Thread(target=self.__start_part__, name="Downloader %s/%s" % (x + 1, self._max_connections))
|
||||
for x in range(self._max_connections)]
|
||||
self._threads = [Thread(target=self.__start_part__, name="Downloader %s/%s" % (x + 1, self._max_connections)) for x in range(self._max_connections)]
|
||||
self._speed_thread = Thread(target=self.__speed_metter__, name="Speed Meter")
|
||||
self._save_thread = Thread(target=self.__save_file__, name="File Writer")
|
||||
|
||||
# Actualizamos los headers
|
||||
# We update the headers
|
||||
self._headers.update(dict(headers))
|
||||
|
||||
# Separamos los headers de la url
|
||||
# We separate the headers from the url
|
||||
self.__url_to_headers__(url)
|
||||
|
||||
# Obtenemos la info del servidor
|
||||
# We get the server info
|
||||
self.__get_download_headers__()
|
||||
|
||||
self._file_size = int(self.response_headers.get("content-length", "0"))
|
||||
@@ -246,10 +238,10 @@ class Downloader(object):
|
||||
self._part_size = 0
|
||||
self._resume = False
|
||||
|
||||
# Obtenemos el nombre del archivo
|
||||
# We get the file name
|
||||
self.__get_download_filename__()
|
||||
|
||||
# Abrimos en modo "a+" para que cree el archivo si no existe, luego en modo "r+b" para poder hacer seek()
|
||||
# We open in "a+" mode to create the file if it does not exist, then in "r + b" mode to be able to do seek ()
|
||||
self.file = filetools.file_open(filetools.join(self._path, self._filename), "a+", vfs=VFS)
|
||||
if self.file: self.file.close()
|
||||
self.file = filetools.file_open(filetools.join(self._path, self._filename), "r+b", vfs=VFS)
|
||||
@@ -266,20 +258,17 @@ class Downloader(object):
|
||||
self.__get_download_info__()
|
||||
|
||||
try:
|
||||
logger.info("Download started: Parts: %s | Path: %s | File: %s | Size: %s" % \
|
||||
(str(len(self._download_info["parts"])), self._pathencode('utf-8'), \
|
||||
self._filenameencode('utf-8'), str(self._download_info["size"])))
|
||||
logger.info("Download started: Parts: %s | Path: %s | File: %s | Size: %s" % (str(len(self._download_info["parts"])), self._pathencode('utf-8'), self._filenameencode('utf-8'), str(self._download_info["size"])))
|
||||
except:
|
||||
pass
|
||||
|
||||
def __url_to_headers__(self, url):
|
||||
# Separamos la url de los headers adicionales
|
||||
# We separate the url from the additional headers
|
||||
self.url = url.split("|")[0]
|
||||
|
||||
# headers adicionales
|
||||
# additional headers
|
||||
if "|" in url:
|
||||
self._headers.update(dict([[header.split("=")[0], urllib.parse.unquote_plus(header.split("=")[1])] for header in
|
||||
url.split("|")[1].split("&")]))
|
||||
self._headers.update(dict([[header.split("=")[0], urllib.parse.unquote_plus(header.split("=")[1])] for header in url.split("|")[1].split("&")]))
|
||||
|
||||
def __get_download_headers__(self):
|
||||
if self.url.startswith("https"):
|
||||
@@ -307,29 +296,21 @@ class Downloader(object):
|
||||
break
|
||||
|
||||
def __get_download_filename__(self):
|
||||
# Obtenemos nombre de archivo y extension
|
||||
if "filename" in self.response_headers.get("content-disposition",
|
||||
"") and "attachment" in self.response_headers.get(
|
||||
"content-disposition", ""):
|
||||
cd_filename, cd_ext = os.path.splitext(urllib.parse.unquote_plus(
|
||||
re.compile("attachment; filename ?= ?[\"|']?([^\"']+)[\"|']?").match(
|
||||
self.response_headers.get("content-disposition")).group(1)))
|
||||
elif "filename" in self.response_headers.get("content-disposition", "") and "inline" in self.response_headers.get(
|
||||
"content-disposition", ""):
|
||||
cd_filename, cd_ext = os.path.splitext(urllib.parse.unquote_plus(
|
||||
re.compile("inline; filename ?= ?[\"|']?([^\"']+)[\"|']?").match(
|
||||
self.response_headers.get("content-disposition")).group(1)))
|
||||
# We get file name and extension
|
||||
if "filename" in self.response_headers.get("content-disposition", "") and "attachment" in self.response_headers.get("content-disposition", ""):
|
||||
cd_filename, cd_ext = os.path.splitext(urllib.parse.unquote_plus( re.compile("attachment; filename ?= ?[\"|']?([^\"']+)[\"|']?").match(self.response_headers.get("content-disposition")).group(1)))
|
||||
elif "filename" in self.response_headers.get("content-disposition", "") and "inline" in self.response_headers.get("content-disposition", ""):
|
||||
cd_filename, cd_ext = os.path.splitext(urllib.parse.unquote_plus(re.compile("inline; filename ?= ?[\"|']?([^\"']+)[\"|']?").match(self.response_headers.get("content-disposition")).group(1)))
|
||||
else:
|
||||
cd_filename, cd_ext = "", ""
|
||||
|
||||
url_filename, url_ext = os.path.splitext(
|
||||
urllib.parse.unquote_plus(filetools.basename(urllib.parse.urlparse(self.url)[2])))
|
||||
url_filename, url_ext = os.path.splitext(urllib.parse.unquote_plus(filetools.basename(urllib.parse.urlparse(self.url)[2])))
|
||||
if self.response_headers.get("content-type", "application/octet-stream") != "application/octet-stream":
|
||||
mime_ext = mimetypes.guess_extension(self.response_headers.get("content-type"))
|
||||
else:
|
||||
mime_ext = ""
|
||||
|
||||
# Seleccionamos el nombre mas adecuado
|
||||
# We select the most suitable name
|
||||
if cd_filename:
|
||||
self.remote_filename = cd_filename
|
||||
if not self._filename:
|
||||
@@ -340,7 +321,7 @@ class Downloader(object):
|
||||
if not self._filename:
|
||||
self._filename = url_filename
|
||||
|
||||
# Seleccionamos la extension mas adecuada
|
||||
# We select the most suitable extension
|
||||
if cd_ext:
|
||||
if not cd_ext in self._filename: self._filename += cd_ext
|
||||
if self.remote_filename: self.remote_filename += cd_ext
|
||||
@@ -360,7 +341,7 @@ class Downloader(object):
|
||||
return value, old_div(value, 1024.0 ** int(math.log(value, 1024))), units[int(math.log(value, 1024))]
|
||||
|
||||
def __get_download_info__(self):
|
||||
# Continuamos con una descarga que contiene la info al final del archivo
|
||||
# We continue with a download that contains the info at the end of the file
|
||||
self._download_info = {}
|
||||
|
||||
try:
|
||||
@@ -386,25 +367,21 @@ class Downloader(object):
|
||||
part["current"] == part["start"]
|
||||
|
||||
self._start_downloaded = sum([c["current"] - c["start"] for c in self._download_info["parts"]])
|
||||
self.pending_parts = set(
|
||||
[x for x, a in enumerate(self._download_info["parts"]) if not a["status"] == self.states.completed])
|
||||
self.completed_parts = set(
|
||||
[x for x, a in enumerate(self._download_info["parts"]) if a["status"] == self.states.completed])
|
||||
self.pending_parts = set([x for x, a in enumerate(self._download_info["parts"]) if not a["status"] == self.states.completed])
|
||||
self.completed_parts = set([x for x, a in enumerate(self._download_info["parts"]) if a["status"] == self.states.completed])
|
||||
self.save_parts = set()
|
||||
self.download_parts = set()
|
||||
|
||||
# La info no existe o no es correcta, comenzamos de 0
|
||||
# The info does not exist or is not correct, we start from 0
|
||||
except:
|
||||
self._download_info["parts"] = []
|
||||
if self._file_size and self._part_size:
|
||||
for x in range(0, self._file_size, self._part_size):
|
||||
end = x + self._part_size - 1
|
||||
if end >= self._file_size: end = self._file_size - 1
|
||||
self._download_info["parts"].append(
|
||||
{"start": x, "end": end, "current": x, "status": self.states.stopped})
|
||||
self._download_info["parts"].append({"start": x, "end": end, "current": x, "status": self.states.stopped})
|
||||
else:
|
||||
self._download_info["parts"].append(
|
||||
{"start": 0, "end": self._file_size - 1, "current": 0, "status": self.states.stopped})
|
||||
self._download_info["parts"].append({"start": 0, "end": self._file_size - 1, "current": 0, "status": self.states.stopped})
|
||||
|
||||
self._download_info["size"] = self._file_size
|
||||
self._start_downloaded = 0
|
||||
@@ -436,7 +413,7 @@ class Downloader(object):
|
||||
logger.info("Thread started: %s" % threading.current_thread().name)
|
||||
|
||||
while self._state == self.states.downloading:
|
||||
if not self.pending_parts and not self.download_parts and not self.save_parts: # Descarga finalizada
|
||||
if not self.pending_parts and not self.download_parts and not self.save_parts: # Download finished
|
||||
self._state = self.states.completed
|
||||
self.file.close()
|
||||
continue
|
||||
@@ -446,8 +423,7 @@ class Downloader(object):
|
||||
|
||||
save_id = min(self.save_parts)
|
||||
|
||||
if not self._seekable and self._download_info["parts"][save_id][
|
||||
"start"] >= 2 ** 31 and not self.__check_consecutive__(save_id):
|
||||
if not self._seekable and self._download_info["parts"][save_id]["start"] >= 2 ** 31 and not self.__check_consecutive__(save_id):
|
||||
continue
|
||||
|
||||
if self._seekable or self._download_info["parts"][save_id]["start"] < 2 ** 31:
|
||||
@@ -533,8 +509,7 @@ class Downloader(object):
|
||||
self.__set_part_connecting__(id)
|
||||
|
||||
try:
|
||||
connection = self.__open_connection__(self._download_info["parts"][id]["current"],
|
||||
self._download_info["parts"][id]["end"])
|
||||
connection = self.__open_connection__(self._download_info["parts"][id]["current"], self._download_info["parts"][id]["end"])
|
||||
except:
|
||||
self.__set_part__error__(id)
|
||||
time.sleep(5)
|
||||
@@ -559,8 +534,7 @@ class Downloader(object):
|
||||
self.download_parts.remove(id)
|
||||
break
|
||||
else:
|
||||
if len(buffer) and self._download_info["parts"][id]["current"] < self._download_info["parts"][id][
|
||||
"end"]:
|
||||
if len(buffer) and self._download_info["parts"][id]["current"] < self._download_info["parts"][id]["end"]:
|
||||
# file.write(buffer)
|
||||
self._buffer[id].append(buffer)
|
||||
self._download_info["parts"][id]["current"] += len(buffer)
|
||||
@@ -570,13 +544,9 @@ class Downloader(object):
|
||||
vm = self.__change_units__(velocidad_minima)
|
||||
v = self.__change_units__(velocidad)
|
||||
|
||||
if velocidad_minima > speed[-1] and velocidad_minima > speed[-2] and \
|
||||
self._download_info["parts"][id]["current"] < \
|
||||
self._download_info["parts"][id]["end"]:
|
||||
if velocidad_minima > speed[-1] and velocidad_minima > speed[-2] and self._download_info["parts"][id]["current"] < self._download_info["parts"][id]["end"]:
|
||||
if connection.fp: connection.fp._sock.close()
|
||||
logger.info(
|
||||
"ID: %s Restarting connection! | Minimum Speed: %.2f %s/s | Speed: %.2f %s/s" % \
|
||||
(id, vm[1], vm[2], v[1], v[2]))
|
||||
logger.info("ID: %s Restarting connection! | Minimum Speed: %.2f %s/s | Speed: %.2f %s/s" % (id, vm[1], vm[2], v[1], v[2]))
|
||||
# file.close()
|
||||
break
|
||||
else:
|
||||
|
||||
+92
-101
@@ -92,8 +92,8 @@ def limpia_nombre_sin_acentos(s):
|
||||
def limpia_nombre_excepto_1(s):
|
||||
if not s:
|
||||
return ''
|
||||
# Titulo de entrada
|
||||
# Convierte a unicode
|
||||
# Entrance title
|
||||
# Convert to unicode
|
||||
try:
|
||||
s = unicode(s, "utf-8")
|
||||
except UnicodeError:
|
||||
@@ -103,12 +103,12 @@ def limpia_nombre_excepto_1(s):
|
||||
except UnicodeError:
|
||||
# logger.info("no es iso-8859-1")
|
||||
pass
|
||||
# Elimina acentos
|
||||
# Remove accents
|
||||
s = limpia_nombre_sin_acentos(s)
|
||||
# Elimina caracteres prohibidos
|
||||
# Remove prohibited characters
|
||||
validchars = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!#$%&'()-@[]^_`{}~."
|
||||
stripped = ''.join(c for c in s if c in validchars)
|
||||
# Convierte a iso
|
||||
# Convert to iso
|
||||
s = stripped.encode("iso-8859-1")
|
||||
if PY3:
|
||||
s = s.decode('utf-8')
|
||||
@@ -124,30 +124,30 @@ def limpia_nombre_excepto_2(s):
|
||||
|
||||
|
||||
def getfilefromtitle(url, title):
|
||||
# Imprime en el log lo que va a descartar
|
||||
# Print in the log what you will discard
|
||||
logger.info("title=" + title)
|
||||
logger.info("url=" + url)
|
||||
plataforma = config.get_system_platform()
|
||||
logger.info("platform=" + plataforma)
|
||||
|
||||
# nombrefichero = xbmc.makeLegalFilename(title + url[-4:])
|
||||
# filename = xbmc.makeLegalFilename(title + url[-4:])
|
||||
from core import scrapertools
|
||||
|
||||
nombrefichero = title + scrapertools.get_filename_from_url(url)[-4:]
|
||||
logger.info("filename=%s" % nombrefichero)
|
||||
logger.info("filename= %s" % nombrefichero)
|
||||
if "videobb" in url or "videozer" in url or "putlocker" in url:
|
||||
nombrefichero = title + ".flv"
|
||||
if "videobam" in url:
|
||||
nombrefichero = title + "." + url.rsplit(".", 1)[1][0:3]
|
||||
|
||||
logger.info("filename=%s" % nombrefichero)
|
||||
logger.info("filename= %s" % nombrefichero)
|
||||
|
||||
nombrefichero = limpia_nombre_caracteres_especiales(nombrefichero)
|
||||
|
||||
logger.info("filename=%s" % nombrefichero)
|
||||
logger.info("filename= %s" % nombrefichero)
|
||||
|
||||
fullpath = filetools.join(config.get_setting("downloadpath"), nombrefichero)
|
||||
logger.info("fullpath=%s" % fullpath)
|
||||
logger.info("fullpath= %s" % fullpath)
|
||||
|
||||
if config.is_xbmc() and fullpath.startswith("special://"):
|
||||
import xbmc
|
||||
@@ -164,7 +164,7 @@ def downloadtitle(url, title):
|
||||
def downloadbest(video_urls, title, continuar=False):
|
||||
logger.info()
|
||||
|
||||
# Le da la vuelta, para poner el de más calidad primero ( list() es para que haga una copia )
|
||||
# Flip it over, to put the highest quality one first (list () is for you to make a copy of)
|
||||
invertida = list(video_urls)
|
||||
invertida.reverse()
|
||||
|
||||
@@ -176,10 +176,10 @@ def downloadbest(video_urls, title, continuar=False):
|
||||
else:
|
||||
logger.info("Downloading option " + title + " " + url.encode('ascii', 'ignore').decode('utf-8'))
|
||||
|
||||
# Calcula el fichero donde debe grabar
|
||||
# Calculate the file where you should record
|
||||
try:
|
||||
fullpath = getfilefromtitle(url, title.strip())
|
||||
# Si falla, es porque la URL no vale para nada
|
||||
# If it fails, it is because the URL is useless
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
@@ -188,24 +188,24 @@ def downloadbest(video_urls, title, continuar=False):
|
||||
# Descarga
|
||||
try:
|
||||
ret = downloadfile(url, fullpath, continuar=continuar)
|
||||
# Llegados a este punto, normalmente es un timeout
|
||||
# At this point, it is usually a timeout.
|
||||
except urllib.error.URLError as e:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
ret = -2
|
||||
|
||||
# El usuario ha cancelado la descarga
|
||||
# The user has canceled the download
|
||||
if ret == -1:
|
||||
return -1
|
||||
else:
|
||||
# El fichero ni siquiera existe
|
||||
# EThe file doesn't even exist
|
||||
if not filetools.exists(fullpath):
|
||||
logger.info("-> You have not downloaded anything, testing with the following option if there is")
|
||||
# El fichero existe
|
||||
# The file exists
|
||||
else:
|
||||
tamanyo = filetools.getsize(fullpath)
|
||||
|
||||
# Tiene tamaño 0
|
||||
# It has size 0
|
||||
if tamanyo == 0:
|
||||
logger.info("-> Download a file with size 0, testing with the following option if it exists")
|
||||
os.remove(fullpath)
|
||||
@@ -217,8 +217,8 @@ def downloadbest(video_urls, title, continuar=False):
|
||||
|
||||
|
||||
def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False, resumir=True):
|
||||
logger.info("url=" + url)
|
||||
logger.info("filename=" + nombrefichero)
|
||||
logger.info("url= " + url)
|
||||
logger.info("filename= " + nombrefichero)
|
||||
|
||||
if headers is None:
|
||||
headers = []
|
||||
@@ -230,36 +230,36 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
nombrefichero = xbmc.translatePath(nombrefichero)
|
||||
|
||||
try:
|
||||
# Si no es XBMC, siempre a "Silent"
|
||||
# If it is not XBMC, always "Silent"
|
||||
from platformcode import platformtools
|
||||
|
||||
# antes
|
||||
# before
|
||||
# f=open(nombrefichero,"wb")
|
||||
try:
|
||||
import xbmc
|
||||
nombrefichero = xbmc.makeLegalFilename(nombrefichero)
|
||||
except:
|
||||
pass
|
||||
logger.info("filename=" + nombrefichero)
|
||||
logger.info("filename= " + nombrefichero)
|
||||
|
||||
# El fichero existe y se quiere continuar
|
||||
# The file exists and you want to continue
|
||||
if filetools.exists(nombrefichero) and continuar:
|
||||
f = filetools.file_open(nombrefichero, 'r+b', vfs=VFS)
|
||||
if resumir:
|
||||
exist_size = filetools.getsize(nombrefichero)
|
||||
logger.info("the file exists, size=%d" % exist_size)
|
||||
logger.info("the file exists, size= %d" % exist_size)
|
||||
grabado = exist_size
|
||||
f.seek(exist_size)
|
||||
else:
|
||||
exist_size = 0
|
||||
grabado = 0
|
||||
|
||||
# el fichero ya existe y no se quiere continuar, se aborta
|
||||
# the file already exists and you don't want to continue, it aborts
|
||||
elif filetools.exists(nombrefichero) and not continuar:
|
||||
logger.info("the file exists, it does not download again")
|
||||
return -3
|
||||
|
||||
# el fichero no existe
|
||||
# the file does not exist
|
||||
else:
|
||||
exist_size = 0
|
||||
logger.info("the file does not exist")
|
||||
@@ -267,11 +267,11 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
f = filetools.file_open(nombrefichero, 'wb', vfs=VFS)
|
||||
grabado = 0
|
||||
|
||||
# Crea el diálogo de progreso
|
||||
# Create the progress dialog
|
||||
if not silent:
|
||||
progreso = platformtools.dialog_progress("plugin", "Downloading...", url, nombrefichero)
|
||||
|
||||
# Si la plataforma no devuelve un cuadro de diálogo válido, asume modo silencio
|
||||
# If the platform does not return a valid dialog box, it assumes silent mode
|
||||
if progreso is None:
|
||||
silent = True
|
||||
|
||||
@@ -291,29 +291,28 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
url = url.split("|")[0]
|
||||
logger.info("url=" + url)
|
||||
|
||||
# Timeout del socket a 60 segundos
|
||||
# Socket timeout at 60 seconds
|
||||
socket.setdefaulttimeout(60)
|
||||
|
||||
h = urllib.request.HTTPHandler(debuglevel=0)
|
||||
request = urllib.request.Request(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])
|
||||
|
||||
if exist_size > 0:
|
||||
request.add_header('Range', 'bytes=%d-' % (exist_size,))
|
||||
request.add_header('Range', 'bytes= %d-' % (exist_size,))
|
||||
|
||||
opener = urllib.request.build_opener(h)
|
||||
urllib.request.install_opener(opener)
|
||||
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, url))
|
||||
logger.error("error %d (%s) opening url %s" % (e.code, e.msg, url))
|
||||
f.close()
|
||||
if not silent:
|
||||
progreso.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:
|
||||
@@ -327,25 +326,25 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
if exist_size > 0:
|
||||
totalfichero = totalfichero + exist_size
|
||||
|
||||
logger.info("Content-Length=%s" % totalfichero)
|
||||
logger.info("Content-Length= %s" % totalfichero)
|
||||
|
||||
blocksize = 100 * 1024
|
||||
|
||||
bloqueleido = connexion.read(blocksize)
|
||||
logger.info("Starting downloading the file, blocked=%s" % len(bloqueleido))
|
||||
logger.info("Starting downloading the file, blocked= %s" % len(bloqueleido))
|
||||
|
||||
maxreintentos = 10
|
||||
|
||||
while len(bloqueleido) > 0:
|
||||
try:
|
||||
# Escribe el bloque leido
|
||||
# Write the block read
|
||||
f.write(bloqueleido)
|
||||
grabado += len(bloqueleido)
|
||||
percent = int(float(grabado) * 100 / float(totalfichero))
|
||||
totalmb = float(float(totalfichero) / (1024 * 1024))
|
||||
descargadosmb = float(float(grabado) / (1024 * 1024))
|
||||
|
||||
# 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:
|
||||
@@ -371,7 +370,7 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
import traceback
|
||||
logger.error(traceback.print_exc())
|
||||
|
||||
# El usuario cancelo la descarga
|
||||
# The user cancels the download
|
||||
try:
|
||||
if progreso.iscanceled():
|
||||
logger.info("Download of file canceled")
|
||||
@@ -381,7 +380,7 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
except:
|
||||
pass
|
||||
|
||||
# Ha habido un error en la descarga
|
||||
# There was an error in the download
|
||||
if reintentos > maxreintentos:
|
||||
logger.info("ERROR in the file download")
|
||||
f.close()
|
||||
@@ -407,7 +406,7 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
error = downloadfileRTMP(url, nombrefichero, silent)
|
||||
if error and not silent:
|
||||
from platformcode import platformtools
|
||||
platformtools.dialog_ok("No puedes descargar ese vídeo", "Las descargas en RTMP aún no", "están soportadas")
|
||||
platformtools.dialog_ok("You cannot download that video "," RTMP downloads not yet "," are supported")
|
||||
else:
|
||||
import traceback
|
||||
from pprint import pprint
|
||||
@@ -433,21 +432,21 @@ def downloadfile(url, nombrefichero, headers=None, silent=False, continuar=False
|
||||
|
||||
|
||||
def downloadfileRTMP(url, nombrefichero, silent):
|
||||
''' No usa librtmp ya que no siempre está disponible.
|
||||
Lanza un subproceso con rtmpdump. En Windows es necesario instalarlo.
|
||||
No usa threads así que no muestra ninguna barra de progreso ni tampoco
|
||||
se marca el final real de la descarga en el log info.
|
||||
'''
|
||||
Do not use librtmp as it is not always available.
|
||||
Launch a thread with rtmpdump. In Windows it is necessary to install it.
|
||||
It doesn't use threads so it doesn't show any progress bar nor the actual end of the download is marked in the log info.
|
||||
'''
|
||||
Programfiles = os.getenv('Programfiles')
|
||||
if Programfiles: # Windows
|
||||
rtmpdump_cmd = Programfiles + "/rtmpdump/rtmpdump.exe"
|
||||
nombrefichero = '"' + nombrefichero + '"' # Windows necesita las comillas en el nombre
|
||||
nombrefichero = '"' + nombrefichero + '"' # Windows needs the quotes in the name
|
||||
else:
|
||||
rtmpdump_cmd = "/usr/bin/rtmpdump"
|
||||
|
||||
if not filetools.isfile(rtmpdump_cmd) and not silent:
|
||||
from platformcode import platformtools
|
||||
advertencia = platformtools.dialog_ok("Falta " + rtmpdump_cmd, "Comprueba que rtmpdump está instalado")
|
||||
advertencia = platformtools.dialog_ok("Lack " + rtmpdump_cmd, "Check that rtmpdump is installed")
|
||||
return True
|
||||
|
||||
valid_rtmpdump_options = ["help", "url", "rtmp", "host", "port", "socks", "protocol", "playpath", "playlist",
|
||||
@@ -475,13 +474,11 @@ def downloadfileRTMP(url, nombrefichero, silent):
|
||||
try:
|
||||
rtmpdump_args = [rtmpdump_cmd] + rtmpdump_args + ["-o", nombrefichero]
|
||||
from os import spawnv, P_NOWAIT
|
||||
logger.info("Iniciando descarga del fichero: %s" % " ".join(rtmpdump_args))
|
||||
logger.info("Initiating file download: %s" % " ".join(rtmpdump_args))
|
||||
rtmpdump_exit = spawnv(P_NOWAIT, rtmpdump_cmd, rtmpdump_args)
|
||||
if not silent:
|
||||
from platformcode import platformtools
|
||||
advertencia = platformtools.dialog_ok("La opción de descarga RTMP es experimental",
|
||||
"y el vídeo se descargará en segundo plano.",
|
||||
"No se mostrará ninguna barra de progreso.")
|
||||
advertencia = platformtools.dialog_ok("RTMP download option is experimental", "and the video will download in the background.", "No progress bar will be displayed.")
|
||||
except:
|
||||
return True
|
||||
|
||||
@@ -489,13 +486,13 @@ def downloadfileRTMP(url, nombrefichero, silent):
|
||||
|
||||
|
||||
def downloadfileGzipped(url, pathfichero):
|
||||
logger.info("url=" + url)
|
||||
logger.info("url= " + url)
|
||||
nombrefichero = pathfichero
|
||||
logger.info("filename=" + nombrefichero)
|
||||
logger.info("filename= " + nombrefichero)
|
||||
|
||||
import xbmc
|
||||
nombrefichero = xbmc.makeLegalFilename(nombrefichero)
|
||||
logger.info("filename=" + nombrefichero)
|
||||
logger.info("filename= " + nombrefichero)
|
||||
patron = "(http://[^/]+)/.+"
|
||||
matches = re.compile(patron, re.DOTALL).findall(url)
|
||||
|
||||
@@ -519,11 +516,11 @@ def downloadfileGzipped(url, pathfichero):
|
||||
|
||||
txdata = ""
|
||||
|
||||
# Crea el diálogo de progreso
|
||||
# Create the progress dialog
|
||||
from platformcode import platformtools
|
||||
progreso = platformtools.dialog_progress("addon", config.get_localized_string(60200), url.split("|")[0], nombrefichero)
|
||||
|
||||
# Timeout del socket a 60 segundos
|
||||
# Socket timeout at 60 seconds
|
||||
socket.setdefaulttimeout(10)
|
||||
|
||||
h = urllib.request.HTTPHandler(debuglevel=0)
|
||||
@@ -536,10 +533,10 @@ def downloadfileGzipped(url, pathfichero):
|
||||
try:
|
||||
connexion = opener.open(request)
|
||||
except urllib.error.HTTPError as e:
|
||||
logger.error("error %d (%s) al abrir la url %s" %
|
||||
logger.error("error %d (%s) when opening the url %s" %
|
||||
(e.code, e.msg, url))
|
||||
progreso.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:
|
||||
@@ -562,13 +559,13 @@ def downloadfileGzipped(url, pathfichero):
|
||||
nombrefichero = filetools.join(pathfichero, titulo)
|
||||
totalfichero = int(connexion.headers["Content-Length"])
|
||||
|
||||
# despues
|
||||
# then
|
||||
f = filetools.file_open(nombrefichero, 'w', vfs=VFS)
|
||||
|
||||
logger.info("new file open")
|
||||
|
||||
grabado = 0
|
||||
logger.info("Content-Length=%s" % totalfichero)
|
||||
logger.info("Content-Length= %s" % totalfichero)
|
||||
|
||||
blocksize = 100 * 1024
|
||||
|
||||
@@ -581,7 +578,7 @@ def downloadfileGzipped(url, pathfichero):
|
||||
gzipper = gzip.GzipFile(fileobj=compressedstream)
|
||||
bloquedata = gzipper.read()
|
||||
gzipper.close()
|
||||
logger.info("Starting downloading the file, blocked=%s" % len(bloqueleido))
|
||||
logger.info("Starting downloading the file, blocked= %s" % len(bloqueleido))
|
||||
except:
|
||||
logger.error("ERROR: The file to be downloaded is not compressed with Gzip")
|
||||
f.close()
|
||||
@@ -592,14 +589,14 @@ def downloadfileGzipped(url, pathfichero):
|
||||
|
||||
while len(bloqueleido) > 0:
|
||||
try:
|
||||
# Escribe el bloque leido
|
||||
# Write the block read
|
||||
f.write(bloquedata)
|
||||
grabado += len(bloqueleido)
|
||||
percent = int(float(grabado) * 100 / float(totalfichero))
|
||||
totalmb = float(float(totalfichero) / (1024 * 1024))
|
||||
descargadosmb = float(float(grabado) / (1024 * 1024))
|
||||
|
||||
# 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:
|
||||
@@ -621,8 +618,7 @@ def downloadfileGzipped(url, pathfichero):
|
||||
else:
|
||||
tiempofalta = 0
|
||||
logger.info(sec_to_hms(tiempofalta))
|
||||
progreso.update(percent, "%.2fMB/%.2fMB (%d%%) %.2f Kb/s %s mancanti " %
|
||||
(descargadosmb, totalmb, percent, old_div(velocidad, 1024), sec_to_hms(tiempofalta)))
|
||||
progreso.update(percent, "%.2fMB/%.2fMB (%d%%) %.2f Kb/s %s left " % (descargadosmb, totalmb, percent, old_div(velocidad, 1024), sec_to_hms(tiempofalta)))
|
||||
break
|
||||
except:
|
||||
reintentos += 1
|
||||
@@ -630,14 +626,14 @@ def downloadfileGzipped(url, pathfichero):
|
||||
for line in sys.exc_info():
|
||||
logger.error("%s" % line)
|
||||
|
||||
# El usuario cancelo la descarga
|
||||
# The user cancels the download
|
||||
if progreso.iscanceled():
|
||||
logger.info("Download of file canceled")
|
||||
f.close()
|
||||
progreso.close()
|
||||
return -1
|
||||
|
||||
# Ha habido un error en la descarga
|
||||
# There was an error in the download
|
||||
if reintentos > maxreintentos:
|
||||
logger.info("ERROR in the file download")
|
||||
f.close()
|
||||
@@ -662,10 +658,10 @@ def downloadfileGzipped(url, pathfichero):
|
||||
|
||||
|
||||
def GetTitleFromFile(title):
|
||||
# Imprime en el log lo que va a descartar
|
||||
logger.info("title=" + title)
|
||||
# Print in the log what you will discard
|
||||
logger.info("title= " + title)
|
||||
plataforma = config.get_system_platform()
|
||||
logger.info("plataform=" + plataforma)
|
||||
logger.info("plataform= " + plataforma)
|
||||
|
||||
# nombrefichero = xbmc.makeLegalFilename(title + url[-4:])
|
||||
nombrefichero = title
|
||||
@@ -681,16 +677,15 @@ def sec_to_hms(seconds):
|
||||
def downloadIfNotModifiedSince(url, timestamp):
|
||||
logger.info("(" + url + "," + time.ctime(timestamp) + ")")
|
||||
|
||||
# Convierte la fecha a GMT
|
||||
# Convert date to GMT
|
||||
fecha_formateada = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(timestamp))
|
||||
logger.info("fechaFormateada=%s" % fecha_formateada)
|
||||
logger.info("Formatted date= %s" % fecha_formateada)
|
||||
|
||||
# Comprueba si ha cambiado
|
||||
# Check if it has changed
|
||||
inicio = time.clock()
|
||||
req = urllib.request.Request(url)
|
||||
req.add_header('If-Modified-Since', fecha_formateada)
|
||||
req.add_header('User-Agent',
|
||||
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; es-ES; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12')
|
||||
req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; es-ES; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12')
|
||||
|
||||
updated = False
|
||||
|
||||
@@ -698,18 +693,18 @@ def downloadIfNotModifiedSince(url, timestamp):
|
||||
response = urllib.request.urlopen(req)
|
||||
data = response.read()
|
||||
|
||||
# Si llega hasta aquí, es que ha cambiado
|
||||
# If it gets this far, it has changed
|
||||
updated = True
|
||||
response.close()
|
||||
|
||||
except urllib.error.URLError as e:
|
||||
# Si devuelve 304 es que no ha cambiado
|
||||
# If it returns 304 it is that it has not changed
|
||||
if hasattr(e, 'code'):
|
||||
logger.info("HTTP response code : %d" % e.code)
|
||||
if e.code == 304:
|
||||
logger.info("It has not changed")
|
||||
updated = False
|
||||
# Agarra los errores con codigo de respuesta del servidor externo solicitado
|
||||
# Grab errors with response code from requested external server
|
||||
else:
|
||||
for line in sys.exc_info():
|
||||
logger.error("%s" % line)
|
||||
@@ -722,20 +717,20 @@ def downloadIfNotModifiedSince(url, timestamp):
|
||||
|
||||
|
||||
def download_all_episodes(item, channel, first_episode="", preferred_server="vidspot", filter_language=""):
|
||||
logger.info("show=" + item.show)
|
||||
logger.info("show= " + item.show)
|
||||
show_title = item.show
|
||||
|
||||
# Obtiene el listado desde el que se llamó
|
||||
# Gets the listing from which it was called
|
||||
action = item.extra
|
||||
|
||||
# Esta marca es porque el item tiene algo más aparte en el atributo "extra"
|
||||
# This mark is because the item has something else apart in the "extra" attribute
|
||||
if "###" in item.extra:
|
||||
action = item.extra.split("###")[0]
|
||||
item.extra = item.extra.split("###")[1]
|
||||
|
||||
episode_itemlist = getattr(channel, action)(item)
|
||||
|
||||
# Ordena los episodios para que funcione el filtro de first_episode
|
||||
# Sort episodes for the first_episode filter to work
|
||||
episode_itemlist = sorted(episode_itemlist, key=lambda it: it.title)
|
||||
|
||||
from core import servertools
|
||||
@@ -744,7 +739,7 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
|
||||
best_server = preferred_server
|
||||
# worst_server = "moevideos"
|
||||
|
||||
# Para cada episodio
|
||||
# For each episode
|
||||
if first_episode == "":
|
||||
empezar = True
|
||||
else:
|
||||
@@ -752,9 +747,9 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
|
||||
|
||||
for episode_item in episode_itemlist:
|
||||
try:
|
||||
logger.info("episode=" + episode_item.title)
|
||||
episode_title = scrapertools.find_single_match(episode_item.title, "(\d+x\d+)")
|
||||
logger.info("episode=" + episode_title)
|
||||
logger.info("episode= " + episode_item.title)
|
||||
episode_title = scrapertools.find_single_match(episode_item.title, r"(\d+x\d+)")
|
||||
logger.info("episode= " + episode_title)
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
@@ -769,7 +764,7 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
|
||||
if not empezar:
|
||||
continue
|
||||
|
||||
# Extrae los mirrors
|
||||
# Extract the mirrors
|
||||
try:
|
||||
mirrors_itemlist = channel.findvideos(episode_item)
|
||||
except:
|
||||
@@ -787,7 +782,7 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
|
||||
|
||||
for mirror_item in mirrors_itemlist:
|
||||
|
||||
# Si está en español va al principio, si no va al final
|
||||
# If it is in Spanish it goes to the beginning, if it does not go to the end
|
||||
if "(Italiano)" in mirror_item.title:
|
||||
if best_server in mirror_item.title.lower():
|
||||
new_mirror_itemlist_1.append(mirror_item)
|
||||
@@ -818,7 +813,7 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
|
||||
new_mirror_itemlist_4 + new_mirror_itemlist_5 + new_mirror_itemlist_6)
|
||||
|
||||
for mirror_item in mirrors_itemlist:
|
||||
logger.info("mirror=" + mirror_item.title)
|
||||
logger.info("mirror= " + mirror_item.title)
|
||||
|
||||
if "(Italiano)" in mirror_item.title:
|
||||
idioma = "(Italiano)"
|
||||
@@ -854,16 +849,13 @@ def download_all_episodes(item, channel, first_episode="", preferred_server="vid
|
||||
if len(video_items) > 0:
|
||||
video_item = video_items[0]
|
||||
|
||||
# Comprueba que está disponible
|
||||
video_urls, puedes, motivo = servertools.resolve_video_urls_for_playing(video_item.server,
|
||||
video_item.url,
|
||||
video_password="",
|
||||
muestra_dialogo=False)
|
||||
# Check that it is available
|
||||
video_urls, puedes, motivo = servertools.resolve_video_urls_for_playing(video_item.server, video_item.url, video_password="", muestra_dialogo=False)
|
||||
|
||||
# Lo añade a la lista de descargas
|
||||
# Adds it to the download list
|
||||
if puedes:
|
||||
logger.info("downloading mirror started...")
|
||||
# El vídeo de más calidad es el último
|
||||
# The highest quality video is the latest
|
||||
# mediaurl = video_urls[len(video_urls) - 1][1]
|
||||
devuelve = downloadbest(video_urls, show_title + " " + episode_title + " " + idioma +
|
||||
" [" + video_item.server + "]", continuar=False)
|
||||
@@ -896,9 +888,8 @@ def episodio_ya_descargado(show_title, episode_title):
|
||||
|
||||
for fichero in ficheros:
|
||||
# logger.info("fichero="+fichero)
|
||||
if fichero.lower().startswith(show_title.lower()) and \
|
||||
scrapertools.find_single_match(fichero, "(\d+x\d+)") == episode_title:
|
||||
logger.info("encontrado!")
|
||||
if fichero.lower().startswith(show_title.lower()) and scrapertools.find_single_match(fichero, "(\d+x\d+)") == episode_title:
|
||||
logger.info("found!")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
+122
-123
@@ -1,11 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# filetools
|
||||
# Gestion de archivos con discriminación xbmcvfs/samba/local
|
||||
# File management with xbmcvfs / samba / local discrimination
|
||||
# ------------------------------------------------------------
|
||||
|
||||
from __future__ import division
|
||||
#from builtins import str
|
||||
# from builtins import str
|
||||
from future.builtins import range
|
||||
from past.utils import old_div
|
||||
import sys
|
||||
@@ -18,13 +18,13 @@ import traceback
|
||||
from core import scrapertools
|
||||
from platformcode import platformtools, logger
|
||||
|
||||
xbmc_vfs = True # False para desactivar XbmcVFS, True para activar
|
||||
xbmc_vfs = True # False to disable XbmcVFS, True to enable
|
||||
if xbmc_vfs:
|
||||
try:
|
||||
import xbmcvfs
|
||||
if not PY3:
|
||||
reload(sys) ### Workoround. Revisar en la migración a Python 3
|
||||
sys.setdefaultencoding('utf-8') # xbmcvfs degrada el valor de defaultencoding. Se reestablece
|
||||
reload(sys) # Workoround. Review on migration to Python 3
|
||||
sys.setdefaultencoding('utf-8') # xbmcvfs demeans the value of defaultencoding. It is reestablished
|
||||
xbmc_vfs = True
|
||||
except:
|
||||
xbmc_vfs = False
|
||||
@@ -35,9 +35,9 @@ if not xbmc_vfs:
|
||||
from lib.sambatools import libsmb as samba
|
||||
except:
|
||||
samba = None
|
||||
# Python 2.4 No compatible con modulo samba, hay que revisar
|
||||
# Python 2.4 Not compatible with samba module, you have to check
|
||||
|
||||
# Windows es "mbcs" linux, osx, android es "utf8"
|
||||
# Windows is "mbcs" linux, osx, android is "utf8"
|
||||
if os.name == "nt":
|
||||
fs_encoding = ""
|
||||
else:
|
||||
@@ -47,15 +47,15 @@ else:
|
||||
|
||||
def validate_path(path):
|
||||
"""
|
||||
Elimina cáracteres no permitidos
|
||||
@param path: cadena a validar
|
||||
Eliminate illegal characters
|
||||
@param path: string to validate
|
||||
@type path: str
|
||||
@rtype: str
|
||||
@return: devuelve la cadena sin los caracteres no permitidos
|
||||
@return: returns the string without the characters not allowed
|
||||
"""
|
||||
chars = ":*?<>|"
|
||||
if scrapertools.find_single_match(path, '(^\w+:\/\/)'):
|
||||
protocolo = scrapertools.find_single_match(path, '(^\w+:\/\/)')
|
||||
if scrapertools.find_single_match(path, r'(^\w+:\/\/)'):
|
||||
protocolo = scrapertools.find_single_match(path, r'(^\w+:\/\/)')
|
||||
import re
|
||||
parts = re.split(r'^\w+:\/\/(.+?)/(.+)', path)[1:3]
|
||||
return protocolo + parts[0] + "/" + ''.join([c for c in parts[1] if c not in chars])
|
||||
@@ -72,19 +72,19 @@ def validate_path(path):
|
||||
|
||||
def encode(path, _samba=False):
|
||||
"""
|
||||
Codifica una ruta según el sistema operativo que estemos utilizando.
|
||||
El argumento path tiene que estar codificado en utf-8
|
||||
@type path unicode o str con codificación utf-8
|
||||
@param path parámetro a codificar
|
||||
It encodes a path according to the operating system we are using.
|
||||
The path argument has to be encoded in utf-8
|
||||
@type unicode or str path with utf-8 encoding
|
||||
@param path parameter to encode
|
||||
@type _samba bool
|
||||
@para _samba si la ruta es samba o no
|
||||
@para _samba if the path is samba or not
|
||||
@rtype: str
|
||||
@return ruta codificada en juego de caracteres del sistema o utf-8 si samba
|
||||
@return path encoded in system character set or utf-8 if samba
|
||||
"""
|
||||
if not isinstance(path, unicode):
|
||||
path = unicode(path, "utf-8", "ignore")
|
||||
|
||||
if scrapertools.find_single_match(path, '(^\w+:\/\/)') or _samba:
|
||||
if scrapertools.find_single_match(path, r'(^\w+:\/\/)') or _samba:
|
||||
path = path.encode("utf-8", "ignore")
|
||||
else:
|
||||
if fs_encoding and not PY3:
|
||||
@@ -95,12 +95,12 @@ def encode(path, _samba=False):
|
||||
|
||||
def decode(path):
|
||||
"""
|
||||
Convierte una cadena de texto al juego de caracteres utf-8
|
||||
eliminando los caracteres que no estén permitidos en utf-8
|
||||
@type: str, unicode, list de str o unicode
|
||||
@param path: puede ser una ruta o un list() con varias rutas
|
||||
Converts a text string to the utf-8 character set
|
||||
removing characters that are not allowed in utf-8
|
||||
@type: str, unicode, list of str o unicode
|
||||
@param path:can be a path or a list () with multiple paths
|
||||
@rtype: str
|
||||
@return: ruta codificado en UTF-8
|
||||
@return: ruta encoded in UTF-8
|
||||
"""
|
||||
if isinstance(path, list):
|
||||
for x in range(len(path)):
|
||||
@@ -116,16 +116,15 @@ def decode(path):
|
||||
|
||||
def read(path, linea_inicio=0, total_lineas=None, whence=0, silent=False, vfs=True):
|
||||
"""
|
||||
Lee el contenido de un archivo y devuelve los datos
|
||||
@param path: ruta del fichero
|
||||
Read the contents of a file and return the data
|
||||
@param path: file path
|
||||
@type path: str
|
||||
@param linea_inicio: primera linea a leer del fichero
|
||||
@type linea_inicio: int positivo
|
||||
@param total_lineas: numero maximo de lineas a leer. Si es None o superior al total de lineas se leera el
|
||||
fichero hasta el final.
|
||||
@type total_lineas: int positivo
|
||||
@param linea_inicio: first line to read from the file
|
||||
@type linea_inicio: positive int
|
||||
@param total_lineas: maximum number of lines to read. If it is None or greater than the total lines, the file will be read until the end.
|
||||
@type total_lineas: positive int
|
||||
@rtype: str
|
||||
@return: datos que contiene el fichero
|
||||
@return: data contained in the file
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -182,13 +181,13 @@ def read(path, linea_inicio=0, total_lineas=None, whence=0, silent=False, vfs=Tr
|
||||
|
||||
def write(path, data, mode="wb", silent=False, vfs=True):
|
||||
"""
|
||||
Guarda los datos en un archivo
|
||||
@param path: ruta del archivo a guardar
|
||||
Save the data to a file
|
||||
@param path: file path to save
|
||||
@type path: str
|
||||
@param data: datos a guardar
|
||||
@param data: data to save
|
||||
@type data: str
|
||||
@rtype: bool
|
||||
@return: devuelve True si se ha escrito correctamente o False si ha dado un error
|
||||
@return: returns True if it was written correctly or False if it gave an error
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -205,7 +204,7 @@ def write(path, data, mode="wb", silent=False, vfs=True):
|
||||
f.write(data)
|
||||
f.close()
|
||||
except:
|
||||
logger.error("ERROR al guardar el archivo: %s" % path)
|
||||
logger.error("ERROR saving file: %s" % path)
|
||||
if not silent:
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
@@ -215,11 +214,11 @@ def write(path, data, mode="wb", silent=False, vfs=True):
|
||||
|
||||
def file_open(path, mode="r", silent=False, vfs=True):
|
||||
"""
|
||||
Abre un archivo
|
||||
@param path: ruta
|
||||
Open a file
|
||||
@param path: path
|
||||
@type path: str
|
||||
@rtype: str
|
||||
@return: objeto file
|
||||
@return: file object
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -245,11 +244,11 @@ def file_open(path, mode="r", silent=False, vfs=True):
|
||||
|
||||
def file_stat(path, silent=False, vfs=True):
|
||||
"""
|
||||
Stat de un archivo
|
||||
@param path: ruta
|
||||
Stat of a file
|
||||
@param path: path
|
||||
@type path: str
|
||||
@rtype: str
|
||||
@return: objeto file
|
||||
@return: file object
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -266,13 +265,13 @@ def file_stat(path, silent=False, vfs=True):
|
||||
|
||||
def rename(path, new_name, silent=False, strict=False, vfs=True):
|
||||
"""
|
||||
Renombra un archivo o carpeta
|
||||
@param path: ruta del fichero o carpeta a renombrar
|
||||
Rename a file or folder
|
||||
@param path: path of the file or folder to rename
|
||||
@type path: str
|
||||
@param new_name: nuevo nombre
|
||||
@param new_name: new name
|
||||
@type new_name: str
|
||||
@rtype: bool
|
||||
@return: devuelve False en caso de error
|
||||
@return: returns False on error
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -309,13 +308,13 @@ def rename(path, new_name, silent=False, strict=False, vfs=True):
|
||||
|
||||
def move(path, dest, silent=False, strict=False, vfs=True):
|
||||
"""
|
||||
Mueve un archivo
|
||||
@param path: ruta del fichero a mover
|
||||
Move a file
|
||||
@param path: path of the file to move
|
||||
@type path: str
|
||||
@param dest: ruta donde mover
|
||||
@param dest: path where to move
|
||||
@type dest: str
|
||||
@rtype: bool
|
||||
@return: devuelve False en caso de error
|
||||
@return: returns False on error
|
||||
"""
|
||||
try:
|
||||
if xbmc_vfs and vfs:
|
||||
@@ -343,10 +342,10 @@ def move(path, dest, silent=False, strict=False, vfs=True):
|
||||
dest = encode(dest)
|
||||
path = encode(path)
|
||||
os.rename(path, dest)
|
||||
# mixto En este caso se copia el archivo y luego se elimina el de origen
|
||||
# mixed In this case the file is copied and then the source file is deleted
|
||||
else:
|
||||
if not silent:
|
||||
dialogo = platformtools.dialog_progress("Copiando archivo", "")
|
||||
dialogo = platformtools.dialog_progress("Copying file", "")
|
||||
return copy(path, dest) == True and remove(path) == True
|
||||
except:
|
||||
logger.error("ERROR when moving file: %s to %s" % (path, dest))
|
||||
@@ -359,29 +358,29 @@ def move(path, dest, silent=False, strict=False, vfs=True):
|
||||
|
||||
def copy(path, dest, silent=False, vfs=True):
|
||||
"""
|
||||
Copia un archivo
|
||||
@param path: ruta del fichero a copiar
|
||||
Copy a file
|
||||
@param path: path of the file to copy
|
||||
@type path: str
|
||||
@param dest: ruta donde copiar
|
||||
@param dest: path to copy
|
||||
@type dest: str
|
||||
@param silent: se muestra o no el cuadro de dialogo
|
||||
@param silent: the dialog box is displayed or not
|
||||
@type silent: bool
|
||||
@rtype: bool
|
||||
@return: devuelve False en caso de error
|
||||
@return: returns False on error
|
||||
"""
|
||||
try:
|
||||
if xbmc_vfs and vfs:
|
||||
path = encode(path)
|
||||
dest = encode(dest)
|
||||
if not silent:
|
||||
dialogo = platformtools.dialog_progress("Copiando archivo", "")
|
||||
dialogo = platformtools.dialog_progress("Copying file", "")
|
||||
return bool(xbmcvfs.copy(path, dest))
|
||||
|
||||
fo = file_open(path, "rb")
|
||||
fd = file_open(dest, "wb")
|
||||
if fo and fd:
|
||||
if not silent:
|
||||
dialogo = platformtools.dialog_progress("Copiando archivo", "")
|
||||
dialogo = platformtools.dialog_progress("Copying file", "")
|
||||
size = getsize(path)
|
||||
copiado = 0
|
||||
while True:
|
||||
@@ -408,11 +407,11 @@ def copy(path, dest, silent=False, vfs=True):
|
||||
|
||||
def exists(path, silent=False, vfs=True):
|
||||
"""
|
||||
Comprueba si existe una carpeta o fichero
|
||||
@param path: ruta
|
||||
Check if there is a folder or file
|
||||
@param path: path
|
||||
@type path: str
|
||||
@rtype: bool
|
||||
@return: Retorna True si la ruta existe, tanto si es una carpeta como un archivo
|
||||
@return: Returns True if the path exists, whether it is a folder or a file
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -434,16 +433,16 @@ def exists(path, silent=False, vfs=True):
|
||||
|
||||
def isfile(path, silent=False, vfs=True):
|
||||
"""
|
||||
Comprueba si la ruta es un fichero
|
||||
@param path: ruta
|
||||
Check if the path is a file
|
||||
@param path: path
|
||||
@type path: str
|
||||
@rtype: bool
|
||||
@return: Retorna True si la ruta existe y es un archivo
|
||||
@return: Returns True if the path exists and is a file
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
if xbmc_vfs and vfs:
|
||||
if not scrapertools.find_single_match(path, '(^\w+:\/\/)'):
|
||||
if not scrapertools.find_single_match(path, r'(^\w+:\/\/)'):
|
||||
return os.path.isfile(path)
|
||||
if path.endswith('/') or path.endswith('\\'):
|
||||
path = path[:-1]
|
||||
@@ -466,16 +465,16 @@ def isfile(path, silent=False, vfs=True):
|
||||
|
||||
def isdir(path, silent=False, vfs=True):
|
||||
"""
|
||||
Comprueba si la ruta es un directorio
|
||||
@param path: ruta
|
||||
Check if the path is a directory
|
||||
@param path: path
|
||||
@type path: str
|
||||
@rtype: bool
|
||||
@return: Retorna True si la ruta existe y es un directorio
|
||||
@return: Returns True if the path exists and is a directory
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
if xbmc_vfs and vfs:
|
||||
if not scrapertools.find_single_match(path, '(^\w+:\/\/)'):
|
||||
if not scrapertools.find_single_match(path, r'(^\w+:\/\/)'):
|
||||
return os.path.isdir(path)
|
||||
if path.endswith('/') or path.endswith('\\'):
|
||||
path = path[:-1]
|
||||
@@ -498,11 +497,11 @@ def isdir(path, silent=False, vfs=True):
|
||||
|
||||
def getsize(path, silent=False, vfs=True):
|
||||
"""
|
||||
Obtiene el tamaño de un archivo
|
||||
@param path: ruta del fichero
|
||||
Gets the size of a file
|
||||
@param path: file path
|
||||
@type path: str
|
||||
@rtype: str
|
||||
@return: tamaño del fichero
|
||||
@return: file size
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -525,11 +524,11 @@ def getsize(path, silent=False, vfs=True):
|
||||
|
||||
def remove(path, silent=False, vfs=True):
|
||||
"""
|
||||
Elimina un archivo
|
||||
@param path: ruta del fichero a eliminar
|
||||
Delete a file
|
||||
@param path: path of the file to delete
|
||||
@type path: str
|
||||
@rtype: bool
|
||||
@return: devuelve False en caso de error
|
||||
@return: returns False on error
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -551,11 +550,11 @@ def remove(path, silent=False, vfs=True):
|
||||
|
||||
def rmdirtree(path, silent=False, vfs=True):
|
||||
"""
|
||||
Elimina un directorio y su contenido
|
||||
@param path: ruta a eliminar
|
||||
Delete a directory and its contents
|
||||
@param path: path to remove
|
||||
@type path: str
|
||||
@rtype: bool
|
||||
@return: devuelve False en caso de error
|
||||
@return: returns False on error
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -591,11 +590,11 @@ def rmdirtree(path, silent=False, vfs=True):
|
||||
|
||||
def rmdir(path, silent=False, vfs=True):
|
||||
"""
|
||||
Elimina un directorio
|
||||
@param path: ruta a eliminar
|
||||
Delete a directory
|
||||
@param path: path to remove
|
||||
@type path: str
|
||||
@rtype: bool
|
||||
@return: devuelve False en caso de error
|
||||
@return: returns False on error
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -619,11 +618,11 @@ def rmdir(path, silent=False, vfs=True):
|
||||
|
||||
def mkdir(path, silent=False, vfs=True):
|
||||
"""
|
||||
Crea un directorio
|
||||
@param path: ruta a crear
|
||||
Create a directory
|
||||
@param path: path to create
|
||||
@type path: str
|
||||
@rtype: bool
|
||||
@return: devuelve False en caso de error
|
||||
@return: returns False on error
|
||||
"""
|
||||
path = encode(path)
|
||||
try:
|
||||
@@ -652,37 +651,37 @@ def mkdir(path, silent=False, vfs=True):
|
||||
|
||||
def walk(top, topdown=True, onerror=None, vfs=True):
|
||||
"""
|
||||
Lista un directorio de manera recursiva
|
||||
@param top: Directorio a listar, debe ser un str "UTF-8"
|
||||
List a directory recursively
|
||||
@param top: Directory to list, must be a str "UTF-8"
|
||||
@type top: str
|
||||
@param topdown: se escanea de arriba a abajo
|
||||
@param topdown: scanned from top to bottom
|
||||
@type topdown: bool
|
||||
@param onerror: muestra error para continuar con el listado si tiene algo seteado sino levanta una excepción
|
||||
@param onerror: show error to continue listing if you have something set but raise an exception
|
||||
@type onerror: bool
|
||||
***El parametro followlinks que por defecto es True, no se usa aqui, ya que en samba no discrimina los links
|
||||
***The followlinks parameter, which by default is True, is not used here, since in samba it does not discriminate links
|
||||
"""
|
||||
top = encode(top)
|
||||
if xbmc_vfs and vfs:
|
||||
for a, b, c in walk_vfs(top, topdown, onerror):
|
||||
# list(b) es para que haga una copia del listado de directorios
|
||||
# si no da error cuando tiene que entrar recursivamente en directorios con caracteres especiales
|
||||
# list (b) is for you to make a copy of the directory listing
|
||||
# if it doesn't give error when you have to recursively enter directories with special characters
|
||||
yield a, list(b), c
|
||||
elif top.lower().startswith("smb://"):
|
||||
for a, b, c in samba.walk(top, topdown, onerror):
|
||||
# list(b) es para que haga una copia del listado de directorios
|
||||
# si no da error cuando tiene que entrar recursivamente en directorios con caracteres especiales
|
||||
# list (b) is for you to make a copy of the directory listing
|
||||
# if it doesn't give error when you have to recursively enter directories with special characters
|
||||
yield decode(a), decode(list(b)), decode(c)
|
||||
else:
|
||||
for a, b, c in os.walk(top, topdown, onerror):
|
||||
# list(b) es para que haga una copia del listado de directorios
|
||||
# si no da error cuando tiene que entrar recursivamente en directorios con caracteres especiales
|
||||
# list (b) is for you to make a copy of the directory listing
|
||||
# if it doesn't give error when you have to recursively enter directories with special characters
|
||||
yield decode(a), decode(list(b)), decode(c)
|
||||
|
||||
|
||||
def walk_vfs(top, topdown=True, onerror=None):
|
||||
"""
|
||||
Lista un directorio de manera recursiva
|
||||
Como xmbcvfs no tiene esta función, se copia la lógica de libsmb(samba) para realizar la previa al Walk
|
||||
List a directory recursively
|
||||
Since xmbcvfs does not have this function, the logic of libsmb (samba) is copied to carry out the pre-Walk
|
||||
"""
|
||||
top = encode(top)
|
||||
dirs, nondirs = xbmcvfs.listdir(top)
|
||||
@@ -707,11 +706,11 @@ def walk_vfs(top, topdown=True, onerror=None):
|
||||
|
||||
def listdir(path, silent=False, vfs=True):
|
||||
"""
|
||||
Lista un directorio
|
||||
@param path: Directorio a listar, debe ser un str "UTF-8"
|
||||
List a directory
|
||||
@param path: Directory to list, must be a str "UTF-8"
|
||||
@type path: str
|
||||
@rtype: str
|
||||
@return: contenido de un directorio
|
||||
@return: content of a directory
|
||||
"""
|
||||
|
||||
path = encode(path)
|
||||
@@ -732,10 +731,10 @@ def listdir(path, silent=False, vfs=True):
|
||||
|
||||
def join(*paths):
|
||||
"""
|
||||
Junta varios directorios
|
||||
Corrige las barras "/" o "\" segun el sistema operativo y si es o no smaba
|
||||
Join several directories
|
||||
Correct the bars "/" or "\" according to the operating system and whether or not it is smaba
|
||||
@rytpe: str
|
||||
@return: la ruta concatenada
|
||||
@return: the concatenated path
|
||||
"""
|
||||
list_path = []
|
||||
if paths[0].startswith("/"):
|
||||
@@ -754,14 +753,14 @@ def join(*paths):
|
||||
|
||||
def split(path, vfs=True):
|
||||
"""
|
||||
Devuelve una tupla formada por el directorio y el nombre del fichero de una ruta
|
||||
Returns a tuple consisting of the directory and filename of a path
|
||||
@param path: ruta
|
||||
@type path: str
|
||||
@return: (dirname, basename)
|
||||
@rtype: tuple
|
||||
"""
|
||||
if scrapertools.find_single_match(path, '(^\w+:\/\/)'):
|
||||
protocol = scrapertools.find_single_match(path, '(^\w+:\/\/)')
|
||||
if scrapertools.find_single_match(path, r'(^\w+:\/\/)'):
|
||||
protocol = scrapertools.find_single_match(path, r'(^\w+:\/\/)')
|
||||
if '/' not in path[6:]:
|
||||
path = path.replace(protocol, protocol + "/", 1)
|
||||
return path.rsplit('/', 1)
|
||||
@@ -771,10 +770,10 @@ def split(path, vfs=True):
|
||||
|
||||
def basename(path, vfs=True):
|
||||
"""
|
||||
Devuelve el nombre del fichero de una ruta
|
||||
@param path: ruta
|
||||
Returns the file name of a path
|
||||
@param path: path
|
||||
@type path: str
|
||||
@return: fichero de la ruta
|
||||
@return: path file
|
||||
@rtype: str
|
||||
"""
|
||||
return split(path)[1]
|
||||
@@ -782,10 +781,10 @@ def basename(path, vfs=True):
|
||||
|
||||
def dirname(path, vfs=True):
|
||||
"""
|
||||
Devuelve el directorio de una ruta
|
||||
@param path: ruta
|
||||
Returns the directory of a path
|
||||
@param path: path
|
||||
@type path: str
|
||||
@return: directorio de la ruta
|
||||
@return: path directory
|
||||
@rtype: str
|
||||
"""
|
||||
return split(path)[0]
|
||||
@@ -797,15 +796,15 @@ def is_relative(path):
|
||||
|
||||
def remove_tags(title):
|
||||
"""
|
||||
devuelve el titulo sin tags como color
|
||||
returns the title without tags as color
|
||||
@type title: str
|
||||
@param title: title
|
||||
@rtype: str
|
||||
@return: cadena sin tags
|
||||
@return: string without tags
|
||||
"""
|
||||
logger.info()
|
||||
|
||||
title_without_tags = scrapertools.find_single_match(title, '\[color .+?\](.+)\[\/color\]')
|
||||
title_without_tags = scrapertools.find_single_match(title, r'\[color .+?\](.+)\[\/color\]')
|
||||
|
||||
if title_without_tags:
|
||||
return title_without_tags
|
||||
@@ -815,19 +814,19 @@ def remove_tags(title):
|
||||
|
||||
def remove_smb_credential(path):
|
||||
"""
|
||||
devuelve el path sin contraseña/usuario para paths de SMB
|
||||
@param path: ruta
|
||||
returns the path without password / user for SMB paths
|
||||
@param path: path
|
||||
@type path: str
|
||||
@return: cadena sin credenciales
|
||||
@return: chain without credentials
|
||||
@rtype: str
|
||||
"""
|
||||
logger.info()
|
||||
|
||||
if not scrapertools.find_single_match(path, '(^\w+:\/\/)'):
|
||||
if not scrapertools.find_single_match(path, r'(^\w+:\/\/)'):
|
||||
return path
|
||||
|
||||
protocol = scrapertools.find_single_match(path, '(^\w+:\/\/)')
|
||||
path_without_credentials = scrapertools.find_single_match(path, '^\w+:\/\/(?:[^;\n]+;)?(?:[^:@\n]+[:|@])?(?:[^@\n]+@)?(.*?$)')
|
||||
protocol = scrapertools.find_single_match(path, r'(^\w+:\/\/)')
|
||||
path_without_credentials = scrapertools.find_single_match(path, r'^\w+:\/\/(?:[^;\n]+;)?(?:[^:@\n]+[:|@])?(?:[^@\n]+@)?(.*?$)')
|
||||
|
||||
if path_without_credentials:
|
||||
return (protocol + path_without_credentials)
|
||||
|
||||
+1
-1
@@ -384,7 +384,7 @@ def downloadpage(url, **opt):
|
||||
info_dict.append(('Success', 'False'))
|
||||
response['code'] = str(e)
|
||||
info_dict.append(('Response code', str(e)))
|
||||
info_dict.append(('Finalizado en', time.time() - inicio))
|
||||
info_dict.append(('Finished in', time.time() - inicio))
|
||||
if not opt.get('alfa_s', False):
|
||||
show_infobox(info_dict)
|
||||
return type('HTTPResponse', (), response)
|
||||
|
||||
+54
-56
@@ -12,9 +12,9 @@ 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
|
||||
from core.scrapertools import unescape
|
||||
|
||||
import base64
|
||||
@@ -29,14 +29,14 @@ class InfoLabels(dict):
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
if name in ["season", "episode"]:
|
||||
# forzamos int() en season y episode
|
||||
# we force int () in season and episode
|
||||
try:
|
||||
super(InfoLabels, self).__setitem__(name, int(value))
|
||||
except:
|
||||
pass
|
||||
|
||||
elif name in ['IMDBNumber', 'imdb_id']:
|
||||
# Por compatibilidad hemos de guardar el valor en los tres campos
|
||||
# For compatibility we have to save the value in the three fields
|
||||
super(InfoLabels, self).__setitem__('IMDBNumber', str(value))
|
||||
# super(InfoLabels, self).__setitem__('code', value)
|
||||
super(InfoLabels, self).__setitem__('imdb_id', str(value))
|
||||
@@ -62,22 +62,22 @@ class InfoLabels(dict):
|
||||
El parametro 'default' en la funcion obj_infoLabels.get(key,default) tiene preferencia sobre los aqui definidos.
|
||||
"""
|
||||
if key in ['rating']:
|
||||
# Ejemplo de clave q devuelve un str formateado como float por defecto
|
||||
# Key example q returns a str formatted as float by default
|
||||
return '0.0'
|
||||
|
||||
elif key == 'code':
|
||||
code = []
|
||||
# Añadir imdb_id al listado de codigos
|
||||
# Add imdb_id to the code list
|
||||
if 'imdb_id' in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__('imdb_id'):
|
||||
code.append(super(InfoLabels, self).__getitem__('imdb_id'))
|
||||
|
||||
# Completar con el resto de codigos
|
||||
# Complete with the rest of the codes
|
||||
for scr in ['tmdb_id', 'tvdb_id', 'noscrap_id']:
|
||||
if scr in list(super(InfoLabels, self).keys()) and super(InfoLabels, self).__getitem__(scr):
|
||||
value = "%s%s" % (scr[:-2], super(InfoLabels, self).__getitem__(scr))
|
||||
code.append(value)
|
||||
|
||||
# Opcion añadir un code del tipo aleatorio
|
||||
# Option to add a code of the random type
|
||||
if not code:
|
||||
import time
|
||||
value = time.strftime("%Y%m%d%H%M%S", time.gmtime())
|
||||
@@ -109,7 +109,7 @@ class InfoLabels(dict):
|
||||
return 'list'
|
||||
|
||||
else:
|
||||
# El resto de claves devuelven cadenas vacias por defecto
|
||||
# The rest of the keys return empty strings by default
|
||||
return ""
|
||||
|
||||
def tostring(self, separador=', '):
|
||||
@@ -132,7 +132,7 @@ class InfoLabels(dict):
|
||||
class Item(object):
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Inicializacion del item
|
||||
Item initialization
|
||||
"""
|
||||
|
||||
# Creamos el atributo infoLabels
|
||||
@@ -159,14 +159,13 @@ class Item(object):
|
||||
|
||||
def __contains__(self, m):
|
||||
"""
|
||||
Comprueba si un atributo existe en el item
|
||||
Check if an attribute exists in the item
|
||||
"""
|
||||
return m in self.__dict__
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""
|
||||
Función llamada al modificar cualquier atributo del item, modifica algunos atributos en función de los datos
|
||||
modificados.
|
||||
Function called when modifying any attribute of the item, modifies some attributes based on the modified data.
|
||||
"""
|
||||
if PY3: name = self.toutf8(name)
|
||||
value = self.toutf8(value)
|
||||
@@ -175,14 +174,14 @@ class Item(object):
|
||||
self.__setattr__(key, value[key])
|
||||
return
|
||||
|
||||
# Descodificamos los HTML entities
|
||||
# We decode the HTML entities
|
||||
if name in ["title", "plot", "fulltitle", "contentPlot", "contentTitle"]:
|
||||
value = self.decode_html(value)
|
||||
|
||||
# Al modificar cualquiera de estos atributos content...
|
||||
# By modifying any of these attributes content...
|
||||
if name in ["contentTitle", "contentPlot", "plot", "contentSerieName", "contentType", "contentEpisodeTitle",
|
||||
"contentSeason", "contentEpisodeNumber", "contentThumbnail", "show", "contentQuality", "quality"]:
|
||||
# ...y actualizamos infoLables
|
||||
# ...and update infoLables
|
||||
if name == "contentTitle":
|
||||
self.__dict__["infoLabels"]["title"] = value
|
||||
elif name == "contentPlot" or name == "plot":
|
||||
@@ -203,13 +202,13 @@ class Item(object):
|
||||
self.__dict__["infoLabels"]["quality"] = value
|
||||
|
||||
elif name == "duration":
|
||||
# String q representa la duracion del video en segundos
|
||||
# String q represents the duration of the video in seconds
|
||||
self.__dict__["infoLabels"]["duration"] = str(value)
|
||||
|
||||
elif name == "viewcontent" and value not in ["files", "movies", "tvshows", "seasons", "episodes"]:
|
||||
super(Item, self).__setattr__("viewcontent", "files")
|
||||
|
||||
# Al asignar un valor a infoLables
|
||||
# When assigning a value to infoLables
|
||||
elif name == "infoLabels":
|
||||
if isinstance(value, dict):
|
||||
value_defaultdict = InfoLabels(value)
|
||||
@@ -220,22 +219,22 @@ class Item(object):
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
Devuelve los valores por defecto en caso de que el atributo solicitado no exista en el item
|
||||
Returns the default values in case the requested attribute does not exist in the item
|
||||
"""
|
||||
if name.startswith("__"):
|
||||
return super(Item, self).__getattribute__(name)
|
||||
|
||||
# valor por defecto para folder
|
||||
# default value for folder
|
||||
if name == "folder":
|
||||
return True
|
||||
|
||||
# valor por defecto para contentChannel
|
||||
# default value for contentChannel
|
||||
elif name == "contentChannel":
|
||||
return "list"
|
||||
|
||||
# valor por defecto para viewcontent
|
||||
# default value for viewcontent
|
||||
elif name == "viewcontent":
|
||||
# intentamos fijarlo segun el tipo de contenido...
|
||||
# we try to fix it according to the type of content...
|
||||
if self.__dict__["infoLabels"]["mediatype"] == 'movie':
|
||||
viewcontent = 'movies'
|
||||
elif self.__dict__["infoLabels"]["mediatype"] in ["tvshow", "season", "episode"]:
|
||||
@@ -246,7 +245,7 @@ class Item(object):
|
||||
self.__dict__["viewcontent"] = viewcontent
|
||||
return viewcontent
|
||||
|
||||
# valores guardados en infoLabels
|
||||
# values saved in infoLabels
|
||||
elif name in ["contentTitle", "contentPlot", "contentSerieName", "show", "contentType", "contentEpisodeTitle",
|
||||
"contentSeason", "contentEpisodeNumber", "contentThumbnail", "plot", "duration",
|
||||
"contentQuality", "quality"]:
|
||||
@@ -258,7 +257,7 @@ class Item(object):
|
||||
return self.__dict__["infoLabels"]["tvshowtitle"]
|
||||
elif name == "contentType":
|
||||
ret = self.__dict__["infoLabels"]["mediatype"]
|
||||
if ret == 'list' and self.__dict__.get("fulltitle", None): # retrocompatibilidad
|
||||
if ret == 'list' and self.__dict__.get("fulltitle", None): # backward compatibility
|
||||
ret = 'movie'
|
||||
self.__dict__["infoLabels"]["mediatype"] = ret
|
||||
return ret
|
||||
@@ -275,7 +274,7 @@ class Item(object):
|
||||
else:
|
||||
return self.__dict__["infoLabels"][name]
|
||||
|
||||
# valor por defecto para el resto de atributos
|
||||
# default value for all other attributes
|
||||
else:
|
||||
return ""
|
||||
|
||||
@@ -284,28 +283,28 @@ class Item(object):
|
||||
|
||||
def set_parent_content(self, parentContent):
|
||||
"""
|
||||
Rellena los campos contentDetails con la informacion del item "padre"
|
||||
@param parentContent: item padre
|
||||
Fill the contentDetails fields with the information of the item "parent"
|
||||
@param parentContent: item father
|
||||
@type parentContent: item
|
||||
"""
|
||||
# Comprueba que parentContent sea un Item
|
||||
# Check that parentContent is an Item
|
||||
if not type(parentContent) == type(self):
|
||||
return
|
||||
# Copia todos los atributos que empiecen por "content" y esten declarados y los infoLabels
|
||||
# Copy all the attributes that start with "content" and are declared and the infoLabels
|
||||
for attr in parentContent.__dict__:
|
||||
if attr.startswith("content") or attr == "infoLabels":
|
||||
self.__setattr__(attr, parentContent.__dict__[attr])
|
||||
|
||||
def tostring(self, separator=", "):
|
||||
"""
|
||||
Genera una cadena de texto con los datos del item para el log
|
||||
Uso: logger.info(item.tostring())
|
||||
@param separator: cadena que se usará como separador
|
||||
Generate a text string with the item's data for the log
|
||||
Use: logger.info(item.tostring())
|
||||
@param separator: string to be used as a separator
|
||||
@type separator: str
|
||||
'"""
|
||||
dic = self.__dict__.copy()
|
||||
|
||||
# Añadimos los campos content... si tienen algun valor
|
||||
# We add the content fields... if they have any value
|
||||
for key in ["contentTitle", "contentPlot", "contentSerieName", "contentEpisodeTitle",
|
||||
"contentSeason", "contentEpisodeNumber", "contentThumbnail"]:
|
||||
value = self.__getattr__(key)
|
||||
@@ -337,10 +336,9 @@ class Item(object):
|
||||
|
||||
def tourl(self):
|
||||
"""
|
||||
Genera una cadena de texto con los datos del item para crear una url, para volver generar el Item usar
|
||||
item.fromurl().
|
||||
Generate a text string with the item data to create a url, to re-generate the Item use item.fromurl ().
|
||||
|
||||
Uso: url = item.tourl()
|
||||
Use: url = item.tourl()
|
||||
"""
|
||||
dump = json.dump(self.__dict__).encode("utf8")
|
||||
# if empty dict
|
||||
@@ -351,9 +349,9 @@ class Item(object):
|
||||
|
||||
def fromurl(self, url):
|
||||
"""
|
||||
Genera un item a partir de una cadena de texto. La cadena puede ser creada por la funcion tourl() o tener
|
||||
el formato antiguo: plugin://plugin.video.kod/?channel=... (+ otros parametros)
|
||||
Uso: item.fromurl("cadena")
|
||||
Generate an item from a text string. The string can be created by the tourl () function or have
|
||||
the old format: plugin: //plugin.video.kod/? channel = ... (+ other parameters)
|
||||
Use: item.fromurl("string")
|
||||
|
||||
@param url: url
|
||||
@type url: str
|
||||
@@ -384,12 +382,12 @@ class Item(object):
|
||||
def tojson(self, path=""):
|
||||
from core import filetools
|
||||
"""
|
||||
Crea un JSON a partir del item, para guardar archivos de favoritos, lista de descargas, etc...
|
||||
Si se especifica un path, te lo guarda en la ruta especificada, si no, devuelve la cadena json
|
||||
Usos: item.tojson(path="ruta\archivo\json.json")
|
||||
file.write(item.tojson())
|
||||
Create a JSON from the item, to save favorite files, download list, etc....
|
||||
If a path is specified, it saves it in the specified path, if not, it returns the string json
|
||||
Applications: item.tojson(path="path\archivo\json.json")
|
||||
file.write(item.tojson())
|
||||
|
||||
@param path: ruta
|
||||
@param path: path
|
||||
@type path: str
|
||||
"""
|
||||
if path:
|
||||
@@ -401,14 +399,14 @@ class Item(object):
|
||||
def fromjson(self, json_item=None, path=""):
|
||||
from core import filetools
|
||||
"""
|
||||
Genera un item a partir de un archivo JSON
|
||||
Si se especifica un path, lee directamente el archivo, si no, lee la cadena de texto pasada.
|
||||
Usos: item = Item().fromjson(path="ruta\archivo\json.json")
|
||||
item = Item().fromjson("Cadena de texto json")
|
||||
Generate an item from a JSON file
|
||||
If a path is specified, it directly reads the file, if not, it reads the passed text string.
|
||||
Applications: item = Item().fromjson(path="path\archivo\json.json")
|
||||
item = Item().fromjson("Cadena de texto json")
|
||||
|
||||
@param json_item: item
|
||||
@type json_item: json
|
||||
@param path: ruta
|
||||
@param path: path
|
||||
@type path: str
|
||||
"""
|
||||
if path:
|
||||
@@ -431,9 +429,9 @@ class Item(object):
|
||||
|
||||
def clone(self, **kwargs):
|
||||
"""
|
||||
Genera un nuevo item clonando el item actual
|
||||
Usos: NuevoItem = item.clone()
|
||||
NuevoItem = item.clone(title="Nuevo Titulo", action = "Nueva Accion")
|
||||
Generate a new item by cloning the current item
|
||||
Applications: NewItem = item.clone()
|
||||
NuewItem = item.clone(title="New Title", action = "New Action")
|
||||
"""
|
||||
newitem = copy.deepcopy(self)
|
||||
if "infoLabels" in kwargs:
|
||||
@@ -447,8 +445,8 @@ class Item(object):
|
||||
@staticmethod
|
||||
def decode_html(value):
|
||||
"""
|
||||
Descodifica las HTML entities
|
||||
@param value: valor a decodificar
|
||||
Decode the HTML entities
|
||||
@param value: value to decode
|
||||
@type value: str
|
||||
"""
|
||||
try:
|
||||
@@ -461,7 +459,7 @@ class Item(object):
|
||||
|
||||
def toutf8(self, *args):
|
||||
"""
|
||||
Pasa el item a utf8
|
||||
Pass the item to utf8
|
||||
"""
|
||||
if len(args) > 0:
|
||||
value = args[0]
|
||||
|
||||
+17
-20
@@ -80,15 +80,14 @@ def to_utf8(dct):
|
||||
|
||||
def get_node_from_file(name_file, node, path=None):
|
||||
"""
|
||||
Obtiene el nodo de un fichero JSON
|
||||
Gets the node of a JSON file
|
||||
|
||||
@param name_file: Puede ser el nombre de un canal o server (sin incluir extension)
|
||||
o bien el nombre de un archivo json (con extension)
|
||||
@param name_file: It can be the name of a channel or server (not including extension) or the name of a json file (with extension)
|
||||
@type name_file: str
|
||||
@param node: nombre del nodo a obtener
|
||||
@param node: name of the node to obtain
|
||||
@type node: str
|
||||
@param path: Ruta base del archivo json. Por defecto la ruta de settings_channels.
|
||||
@return: dict con el nodo a devolver
|
||||
@param path: Base path of the json file. By default the path of settings_channels.
|
||||
@return: dict with the node to return
|
||||
@rtype: dict
|
||||
"""
|
||||
logger.info()
|
||||
@@ -121,14 +120,13 @@ def get_node_from_file(name_file, node, path=None):
|
||||
|
||||
def check_to_backup(data, fname, dict_data):
|
||||
"""
|
||||
Comprueba que si dict_data(conversion del fichero JSON a dict) no es un diccionario, se genere un fichero con
|
||||
data de nombre fname.bk.
|
||||
Check that if dict_data (conversion of the JSON file to dict) is not a dictionary, a file with data name fname.bk will be generated.
|
||||
|
||||
@param data: contenido del fichero fname
|
||||
@param data: fname file content
|
||||
@type data: str
|
||||
@param fname: nombre del fichero leido
|
||||
@param fname: name of the read file
|
||||
@type fname: str
|
||||
@param dict_data: nombre del diccionario
|
||||
@param dict_data: dictionary name
|
||||
@type dict_data: dict
|
||||
"""
|
||||
logger.info()
|
||||
@@ -137,7 +135,7 @@ def check_to_backup(data, fname, dict_data):
|
||||
logger.error("Error loading json from file %s" % fname)
|
||||
|
||||
if data != "":
|
||||
# se crea un nuevo fichero
|
||||
# a new file is created
|
||||
from core import filetools
|
||||
title = filetools.write("%s.bk" % fname, data)
|
||||
if title != "":
|
||||
@@ -150,16 +148,15 @@ def check_to_backup(data, fname, dict_data):
|
||||
|
||||
def update_node(dict_node, name_file, node, path=None, silent=False):
|
||||
"""
|
||||
actualiza el json_data de un fichero con el diccionario pasado
|
||||
update the json_data of a file with the last dictionary
|
||||
|
||||
@param dict_node: diccionario con el nodo
|
||||
@param dict_node: dictionary with node
|
||||
@type dict_node: dict
|
||||
@param name_file: Puede ser el nombre de un canal o server (sin incluir extension)
|
||||
o bien el nombre de un archivo json (con extension)
|
||||
@param name_file: It can be the name of a channel or server (not including extension) or the name of a json file (with extension)
|
||||
@type name_file: str
|
||||
@param node: nodo a actualizar
|
||||
@param path: Ruta base del archivo json. Por defecto la ruta de settings_channels.
|
||||
@return result: Devuelve True si se ha escrito correctamente o False si ha dado un error
|
||||
@param node: node to update
|
||||
@param path: Base path of the json file. By default the path of settings_channels.
|
||||
@return result: Returns True if it was written correctly or False if it gave an error
|
||||
@rtype: bool
|
||||
@return json_data
|
||||
@rtype: dict
|
||||
@@ -182,7 +179,7 @@ def update_node(dict_node, name_file, node, path=None, silent=False):
|
||||
try:
|
||||
data = filetools.read(fname)
|
||||
dict_data = load(data)
|
||||
# es un dict
|
||||
# it's a dict
|
||||
if dict_data:
|
||||
if node in dict_data:
|
||||
if not silent: logger.debug(" the key exists %s" % node)
|
||||
|
||||
+32
-34
@@ -9,8 +9,7 @@ from core.item import InfoLabels
|
||||
from platformcode import config, logger
|
||||
from platformcode import platformtools
|
||||
|
||||
# Este modulo es una interface para poder implementar diferentes scrapers
|
||||
# contendra todos las funciones comunes
|
||||
# This module is an interface to implement different scrapers, it will contain all the common functions
|
||||
|
||||
dict_default = None
|
||||
scraper = None
|
||||
@@ -18,36 +17,35 @@ scraper = None
|
||||
|
||||
def find_and_set_infoLabels(item):
|
||||
"""
|
||||
función que se llama para buscar y setear los infolabels
|
||||
function called to search and set infolabels
|
||||
:param item:
|
||||
:return: boleano que indica si se ha podido encontrar el 'code'
|
||||
:return: Boolean indicating if the 'code' could be found
|
||||
"""
|
||||
global scraper
|
||||
scraper = None
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
|
||||
list_opciones_cuadro = [config.get_localized_string(60223), config.get_localized_string(60224)]
|
||||
# Si se añaden más scrapers hay q declararlos aqui-> "modulo_scraper": "Texto_en_cuadro"
|
||||
scrapers_disponibles = {'tmdb': config.get_localized_string(60225),
|
||||
'tvdb': config.get_localized_string(60226)}
|
||||
# If more scrapers are added, they must be declared here-> "modulo_scraper": "Text_in_box"
|
||||
scrapers_disponibles = {'tmdb': config.get_localized_string(60225), 'tvdb': config.get_localized_string(60226)}
|
||||
|
||||
# Obtener el Scraper por defecto de la configuracion segun el tipo de contenido
|
||||
# Get the default Scraper of the configuration according to the content type
|
||||
if item.contentType == "movie":
|
||||
scraper_actual = ['tmdb'][config.get_setting("scraper_movies", "videolibrary")]
|
||||
tipo_contenido = config.get_localized_string(70283)
|
||||
title = item.contentTitle
|
||||
# Completar lista de opciones para este tipo de contenido
|
||||
# Complete list of options for this type of content
|
||||
list_opciones_cuadro.append(scrapers_disponibles['tmdb'])
|
||||
|
||||
else:
|
||||
scraper_actual = ['tmdb', 'tvdb'][config.get_setting("scraper_tvshows", "videolibrary")]
|
||||
tipo_contenido = "serie"
|
||||
title = item.contentSerieName
|
||||
# Completar lista de opciones para este tipo de contenido
|
||||
# Complete list of options for this type of content
|
||||
list_opciones_cuadro.append(scrapers_disponibles['tmdb'])
|
||||
list_opciones_cuadro.append(scrapers_disponibles['tvdb'])
|
||||
|
||||
# Importamos el scraper
|
||||
# We import the scraper
|
||||
try:
|
||||
scraper = __import__('core.%s' % scraper_actual, fromlist=["core.%s" % scraper_actual])
|
||||
except ImportError:
|
||||
@@ -57,34 +55,34 @@ def find_and_set_infoLabels(item):
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
while scraper:
|
||||
# Llamamos a la funcion find_and_set_infoLabels del scraper seleccionado
|
||||
# We call the find_and_set_infoLabels function of the selected scraper
|
||||
scraper_result = scraper.find_and_set_infoLabels(item)
|
||||
|
||||
# Verificar si existe 'code'
|
||||
# Check if there is a 'code'
|
||||
if scraper_result and item.infoLabels['code']:
|
||||
# code correcto
|
||||
# correct code
|
||||
logger.info("Identificador encontrado: %s" % item.infoLabels['code'])
|
||||
scraper.completar_codigos(item)
|
||||
return True
|
||||
elif scraper_result:
|
||||
# Contenido encontrado pero no hay 'code'
|
||||
# Content found but no 'code'
|
||||
msg = config.get_localized_string(60227) % title
|
||||
else:
|
||||
# Contenido no encontrado
|
||||
# Content not found
|
||||
msg = config.get_localized_string(60228) % title
|
||||
|
||||
logger.info(msg)
|
||||
# Mostrar cuadro con otras opciones:
|
||||
# Show box with other options:
|
||||
if scrapers_disponibles[scraper_actual] in list_opciones_cuadro:
|
||||
list_opciones_cuadro.remove(scrapers_disponibles[scraper_actual])
|
||||
index = platformtools.dialog_select(msg, list_opciones_cuadro)
|
||||
|
||||
if index < 0:
|
||||
logger.debug("Se ha pulsado 'cancelar' en la ventana '%s'" % msg)
|
||||
logger.debug("You have clicked 'cancel' in the window '%s'" % msg)
|
||||
return False
|
||||
|
||||
elif index == 0:
|
||||
# Pregunta el titulo
|
||||
# Ask the title
|
||||
title = platformtools.dialog_input(title, config.get_localized_string(60229) % tipo_contenido)
|
||||
if title:
|
||||
if item.contentType == "movie":
|
||||
@@ -92,25 +90,25 @@ def find_and_set_infoLabels(item):
|
||||
else:
|
||||
item.contentSerieName = title
|
||||
else:
|
||||
logger.debug("he pulsado 'cancelar' en la ventana 'Introduzca el nombre correcto'")
|
||||
logger.debug("I clicked 'cancel' in the window 'Enter the correct name'")
|
||||
return False
|
||||
|
||||
elif index == 1:
|
||||
# Hay q crear un cuadro de dialogo para introducir los datos
|
||||
logger.info("Completar información")
|
||||
# You have to create a dialog box to enter the data
|
||||
logger.info("Complete information")
|
||||
if cuadro_completar(item):
|
||||
# code correcto
|
||||
logger.info("Identificador encontrado: %s" % str(item.infoLabels['code']))
|
||||
# correct code
|
||||
logger.info("Identifier found: %s" % str(item.infoLabels['code']))
|
||||
return True
|
||||
# raise
|
||||
|
||||
elif list_opciones_cuadro[index] in list(scrapers_disponibles.values()):
|
||||
# Obtener el nombre del modulo del scraper
|
||||
# Get the name of the scraper module
|
||||
for k, v in list(scrapers_disponibles.items()):
|
||||
if list_opciones_cuadro[index] == v:
|
||||
if scrapers_disponibles[scraper_actual] not in list_opciones_cuadro:
|
||||
list_opciones_cuadro.append(scrapers_disponibles[scraper_actual])
|
||||
# Importamos el scraper k
|
||||
# We import the scraper k
|
||||
scraper_actual = k
|
||||
try:
|
||||
scraper = None
|
||||
@@ -119,7 +117,7 @@ def find_and_set_infoLabels(item):
|
||||
exec("import core." + scraper_actual + " as scraper_module")
|
||||
break
|
||||
|
||||
logger.error("Error al importar el modulo scraper %s" % scraper_actual)
|
||||
logger.error("Error importing the scraper module %s" % scraper_actual)
|
||||
|
||||
|
||||
def cuadro_completar(item):
|
||||
@@ -129,7 +127,7 @@ def cuadro_completar(item):
|
||||
dict_default = {}
|
||||
|
||||
COLOR = ["0xFF65B3DA", "0xFFFFFFFF"]
|
||||
# Creamos la lista de campos del infoLabel
|
||||
# We create the list of infoLabel fields
|
||||
controls = [("title", "text", config.get_localized_string(60230)),
|
||||
("originaltitle", "text", config.get_localized_string(60231)),
|
||||
("year", "text", config.get_localized_string(60232)),
|
||||
@@ -171,7 +169,7 @@ def cuadro_completar(item):
|
||||
if len(c) > 3:
|
||||
enabled += c[3]
|
||||
|
||||
# default para casos especiales
|
||||
# default for special cases
|
||||
if c[0] == "url_tmdb" and item.infoLabels["tmdb_id"] and 'tmdb' in item.infoLabels["url_scraper"]:
|
||||
dict_default[c[0]] = item.infoLabels["url_scraper"]
|
||||
|
||||
@@ -181,7 +179,7 @@ def cuadro_completar(item):
|
||||
if not dict_default[c[0]] or dict_default[c[0]] == 'None' or dict_default[c[0]] == 0:
|
||||
dict_default[c[0]] = ''
|
||||
elif isinstance(dict_default[c[0]], (int, float)) or (not PY3 and isinstance(dict_default[c[0]], (int, float, long))):
|
||||
# Si es numerico lo convertimos en str
|
||||
# If it is numerical we convert it into str
|
||||
dict_default[c[0]] = str(dict_default[c[0]])
|
||||
|
||||
listado_controles.append({'id': c[0],
|
||||
@@ -207,7 +205,7 @@ def callback_cuadro_completar(item, dict_values):
|
||||
global dict_default
|
||||
|
||||
if dict_values.get("title", None):
|
||||
# Adaptar dict_values a infoLabels validos
|
||||
# Adapt dict_values to valid infoLabels
|
||||
dict_values['mediatype'] = ['movie', 'tvshow'][dict_values['mediatype']]
|
||||
for k, v in list(dict_values.items()):
|
||||
if k in dict_default and dict_default[k] == dict_values[k]:
|
||||
@@ -229,16 +227,16 @@ def callback_cuadro_completar(item, dict_values):
|
||||
|
||||
def get_nfo(item):
|
||||
"""
|
||||
Devuelve la información necesaria para que se scrapee el resultado en la videoteca de kodi,
|
||||
Returns the information necessary for the result to be scraped into the kodi video library,
|
||||
|
||||
@param item: elemento que contiene los datos necesarios para generar la info
|
||||
@param item: element that contains the data necessary to generate the info
|
||||
@type item: Item
|
||||
@rtype: str
|
||||
@return:
|
||||
"""
|
||||
logger.info()
|
||||
if "infoLabels" in item and "noscrap_id" in item.infoLabels:
|
||||
# Crea el fichero xml con los datos que se obtiene de item ya que no hay ningún scraper activo
|
||||
# Create the xml file with the data obtained from the item since there is no active scraper
|
||||
info_nfo = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'
|
||||
|
||||
if "season" in item.infoLabels and "episode" in item.infoLabels:
|
||||
|
||||
+30
-30
@@ -56,15 +56,15 @@ def find_multiple_matches_groups(text, pattern):
|
||||
return [m.groupdict() for m in r.finditer(text)]
|
||||
|
||||
|
||||
# Convierte los codigos html "ñ" y lo reemplaza por "ñ" caracter unicode utf-8
|
||||
# Convert html codes "ñ" and replace it with "ñ" unicode utf-8 character
|
||||
def decodeHtmlentities(data):
|
||||
entity_re = re.compile("&(#?)(\d{1,5}|\w{1,8})(;?)")
|
||||
entity_re = re.compile(r"&(#?)(\d{1,5}|\w{1,8})(;?)")
|
||||
|
||||
def substitute_entity(match):
|
||||
ent = match.group(2) + match.group(3)
|
||||
res = ""
|
||||
while not ent in html5 and not ent.endswith(";") and match.group(1) != "#":
|
||||
# Excepción para cuando '&' se usa como argumento en la urls contenidas en los datos
|
||||
# Exception for when '&' is used as an argument in the urls contained in the data
|
||||
try:
|
||||
res = ent[-1] + res
|
||||
ent = ent[:-1]
|
||||
@@ -85,9 +85,9 @@ def decodeHtmlentities(data):
|
||||
|
||||
|
||||
def unescape(text):
|
||||
"""Removes HTML or XML character references
|
||||
and entities from a text string.
|
||||
keep &, >, < in the source code.
|
||||
"""
|
||||
Removes HTML or XML character references and entities from a text string.
|
||||
keep &, >, < in the source code.
|
||||
from Fredrik Lundh
|
||||
http://effbot.org/zone/re-sub.htm#unescape-html
|
||||
"""
|
||||
@@ -122,7 +122,7 @@ def unescape(text):
|
||||
|
||||
return re.sub("&#?\w+;", fixup, str(text))
|
||||
|
||||
# Convierte los codigos html "ñ" y lo reemplaza por "ñ" caracter unicode utf-8
|
||||
# Convert html codes "ñ" and replace it with "ñ" unicode utf-8 character
|
||||
|
||||
|
||||
# def decodeHtmlentities(string):
|
||||
@@ -277,7 +277,7 @@ def htmlclean(cadena):
|
||||
def slugify(title):
|
||||
# print title
|
||||
|
||||
# Sustituye acentos y eñes
|
||||
# Substitutes accents and eñes
|
||||
title = title.replace("Á", "a")
|
||||
title = title.replace("É", "e")
|
||||
title = title.replace("Í", "i")
|
||||
@@ -305,23 +305,23 @@ def slugify(title):
|
||||
title = title.replace("/", "-")
|
||||
title = title.replace("&", "&")
|
||||
|
||||
# Pasa a minúsculas
|
||||
# Lowercase
|
||||
title = title.lower().strip()
|
||||
|
||||
# Elimina caracteres no válidos
|
||||
# Remove invalid characters
|
||||
validchars = "abcdefghijklmnopqrstuvwxyz1234567890- "
|
||||
title = ''.join(c for c in title if c in validchars)
|
||||
|
||||
# Sustituye espacios en blanco duplicados y saltos de línea
|
||||
title = re.compile("\s+", re.DOTALL).sub(" ", title)
|
||||
# Replace duplicate blanks and line breaks
|
||||
title = re.compile(r"\s+", re.DOTALL).sub(" ", title)
|
||||
|
||||
# Sustituye espacios en blanco por guiones
|
||||
title = re.compile("\s", re.DOTALL).sub("-", title.strip())
|
||||
# Replace blanks with hyphens
|
||||
title = re.compile(r"\s", re.DOTALL).sub("-", title.strip())
|
||||
|
||||
# Sustituye espacios en blanco duplicados y saltos de línea
|
||||
title = re.compile("\-+", re.DOTALL).sub("-", title)
|
||||
# Replace duplicate blanks and line breaks
|
||||
title = re.compile(r"\-+", re.DOTALL).sub("-", title)
|
||||
|
||||
# Arregla casos especiales
|
||||
# Fix special cases
|
||||
if title.startswith("-"):
|
||||
title = title[1:]
|
||||
|
||||
@@ -337,10 +337,10 @@ def remove_htmltags(string):
|
||||
|
||||
def remove_show_from_title(title, show):
|
||||
# print slugify(title)+" == "+slugify(show)
|
||||
# Quita el nombre del programa del título
|
||||
# Remove program name from title
|
||||
if slugify(title).startswith(slugify(show)):
|
||||
|
||||
# Convierte a unicode primero, o el encoding se pierde
|
||||
# Convert to unicode first, or encoding is lost
|
||||
title = unicode(title, "utf-8", "replace")
|
||||
show = unicode(show, "utf-8", "replace")
|
||||
title = title[len(show):].strip()
|
||||
@@ -351,7 +351,7 @@ def remove_show_from_title(title, show):
|
||||
if title == "":
|
||||
title = str(time.time())
|
||||
|
||||
# Vuelve a utf-8
|
||||
# Return to utf-8
|
||||
title = title.encode("utf-8", "ignore")
|
||||
show = show.encode("utf-8", "ignore")
|
||||
|
||||
@@ -360,15 +360,15 @@ def remove_show_from_title(title, show):
|
||||
|
||||
def get_filename_from_url(url):
|
||||
if PY3:
|
||||
import urllib.parse as urlparse # Es muy lento en PY2. En PY3 es nativo
|
||||
import urllib.parse as urlparse # It is very slow in PY2. In PY3 it is native
|
||||
else:
|
||||
import urlparse # Usamos el nativo de PY2 que es más rápido
|
||||
import urlparse # We use the native of PY2 which is faster
|
||||
|
||||
parsed_url = urlparse.urlparse(url)
|
||||
try:
|
||||
filename = parsed_url.path
|
||||
except:
|
||||
# Si falla es porque la implementación de parsed_url no reconoce los atributos como "path"
|
||||
# If it fails it is because the implementation of parsed_url does not recognize the attributes as "path"
|
||||
if len(parsed_url) >= 4:
|
||||
filename = parsed_url[2]
|
||||
else:
|
||||
@@ -382,15 +382,15 @@ def get_filename_from_url(url):
|
||||
|
||||
def get_domain_from_url(url):
|
||||
if PY3:
|
||||
import urllib.parse as urlparse # Es muy lento en PY2. En PY3 es nativo
|
||||
import urllib.parse as urlparse # It is very slow in PY2. In PY3 it is native
|
||||
else:
|
||||
import urlparse # Usamos el nativo de PY2 que es más rápido
|
||||
import urlparse # We use the native of PY2 which is faster
|
||||
|
||||
parsed_url = urlparse.urlparse(url)
|
||||
try:
|
||||
filename = parsed_url.netloc
|
||||
except:
|
||||
# Si falla es porque la implementación de parsed_url no reconoce los atributos como "path"
|
||||
# If it fails it is because the implementation of parsed_url does not recognize the attributes as "path"
|
||||
if len(parsed_url) >= 4:
|
||||
filename = parsed_url[1]
|
||||
else:
|
||||
@@ -401,8 +401,8 @@ def get_domain_from_url(url):
|
||||
|
||||
def get_season_and_episode(title):
|
||||
"""
|
||||
Retorna el numero de temporada y de episodio en formato "1x01" obtenido del titulo de un episodio
|
||||
Ejemplos de diferentes valores para title y su valor devuelto:
|
||||
Returns the season and episode number in "1x01" format obtained from the title of an episode
|
||||
Examples of different values for title and its return value:
|
||||
"serie 101x1.strm", "s101e1.avi", "t101e1.avi" -> '101x01'
|
||||
"Name TvShow 1x6.avi" -> '1x06'
|
||||
"Temp 3 episodio 2.avi" -> '3x02'
|
||||
@@ -412,9 +412,9 @@ def get_season_and_episode(title):
|
||||
"Episodio 25: titulo episodio" -> '' (no existe el numero de temporada)
|
||||
"Serie X Temporada 1" -> '' (no existe el numero del episodio)
|
||||
@type title: str
|
||||
@param title: titulo del episodio de una serie
|
||||
@param title: title of a series episode
|
||||
@rtype: str
|
||||
@return: Numero de temporada y episodio en formato "1x01" o cadena vacia si no se han encontrado
|
||||
@return: Nseason and episode number in "1x01" format or empty string if not found
|
||||
"""
|
||||
filename = ""
|
||||
|
||||
|
||||
+161
-178
@@ -12,9 +12,9 @@ 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 urlparse # Es muy lento en PY2. En PY3 es nativo
|
||||
import urllib.parse as urlparse #It is very slow in PY2. In PY3 it is native
|
||||
else:
|
||||
import urlparse # Usamos el nativo de PY2 que es más rápido
|
||||
import urlparse # We use the native of PY2 which is faster
|
||||
|
||||
from future.builtins import range
|
||||
from past.utils import old_div
|
||||
@@ -35,38 +35,38 @@ server_list = {}
|
||||
|
||||
def find_video_items(item=None, data=None):
|
||||
"""
|
||||
Función genérica para buscar vídeos en una página, devolviendo un itemlist con los items listos para usar.
|
||||
- Si se pasa un Item como argumento, a los items resultantes mantienen los parametros del item pasado
|
||||
- Si no se pasa un Item, se crea uno nuevo, pero no contendra ningun parametro mas que los propios del servidor.
|
||||
Generic function to search for videos on a page, returning an itemlist with the ready-to-use items.
|
||||
- If an Item is passed as an argument, the resulting items keep the parameters of the last item
|
||||
- If an Item is not passed, a new one is created, but it will not contain any parameters other than those of the server.
|
||||
|
||||
@param item: Item al cual se quieren buscar vídeos, este debe contener la url válida
|
||||
@param item: Item to which you want to search for videos, this must contain the valid url
|
||||
@type item: Item
|
||||
@param data: Cadena con el contendio de la página ya descargado (si no se pasa item)
|
||||
@param data: String with the page content already downloaded (if item is not passed)
|
||||
@type data: str
|
||||
|
||||
@return: devuelve el itemlist con los resultados
|
||||
@return: returns the itemlist with the results
|
||||
@rtype: list
|
||||
"""
|
||||
logger.info()
|
||||
itemlist = []
|
||||
|
||||
# Descarga la página
|
||||
# Download the page
|
||||
if data is None:
|
||||
data = httptools.downloadpage(item.url).data
|
||||
|
||||
data = unshortenit.findlinks(data)
|
||||
|
||||
# Crea un item si no hay item
|
||||
# Create an item if there is no item
|
||||
if item is None:
|
||||
item = Item()
|
||||
# Pasa los campos thumbnail y title a contentThumbnail y contentTitle
|
||||
# Pass the thumbnail and title fields to contentThumbnail and contentTitle
|
||||
else:
|
||||
if not item.contentThumbnail:
|
||||
item.contentThumbnail = item.thumbnail
|
||||
if not item.contentTitle:
|
||||
item.contentTitle = item.title
|
||||
|
||||
# Busca los enlaces a los videos
|
||||
# Find the links to the videos
|
||||
for label, url, server, thumbnail in findvideos(data):
|
||||
title = config.get_localized_string(70206) % label
|
||||
itemlist.append(
|
||||
@@ -77,29 +77,28 @@ def find_video_items(item=None, data=None):
|
||||
|
||||
def get_servers_itemlist(itemlist, fnc=None, sort=False):
|
||||
"""
|
||||
Obtiene el servidor para cada uno de los items, en funcion de su url.
|
||||
- Asigna el servidor, la url modificada, el thumbnail (si el item no contiene contentThumbnail se asigna el del thumbnail)
|
||||
- Si se pasa una funcion por el argumento fnc, esta se ejecuta pasando el item como argumento,
|
||||
el resultado de esa funcion se asigna al titulo del item
|
||||
- En esta funcion podemos modificar cualquier cosa del item
|
||||
- Esta funcion siempre tiene que devolver el item.title como resultado
|
||||
- Si no se encuentra servidor para una url, se asigna "directo"
|
||||
|
||||
@param itemlist: listado de items
|
||||
Get the server for each of the items, based on their url.
|
||||
- Assign the server, the modified url, the thumbnail (if the item does not contain contentThumbnail the thumbnail is assigned)
|
||||
- If a function is passed through the fnc argument, it is executed by passing the item as an argument, the result of that function is assigned to the title of the item
|
||||
- In this function we can modify anything of the item
|
||||
- This function always has to return the item.title as a result
|
||||
- If no server is found for a url, it is assigned "direct"
|
||||
|
||||
@param itemlist: item list
|
||||
@type itemlist: list
|
||||
@param fnc: función para ejecutar con cada item (para asignar el titulo)
|
||||
@param fnc: function to execute with each item (to assign the title)
|
||||
@type fnc: function
|
||||
@param sort: indica si el listado resultante se ha de ordenar en funcion de la lista de servidores favoritos
|
||||
@param sort: indicates whether the resulting list should be ordered based on the list of favorite servers
|
||||
@type sort: bool
|
||||
"""
|
||||
# Recorre los servidores
|
||||
# Roam the servers
|
||||
for serverid in list(get_servers_list().keys()):
|
||||
server_parameters = get_server_parameters(serverid)
|
||||
|
||||
# Recorre los patrones
|
||||
# Walk the patterns
|
||||
for pattern in server_parameters.get("find_videos", {}).get("patterns", []):
|
||||
logger.info(pattern["pattern"])
|
||||
# Recorre los resultados
|
||||
# Scroll through the results
|
||||
for match in re.compile(pattern["pattern"], re.DOTALL).finditer(
|
||||
"\n".join([item.url.split('|')[0] for item in itemlist if not item.server])):
|
||||
url = pattern["url"]
|
||||
@@ -117,13 +116,13 @@ def get_servers_itemlist(itemlist, fnc=None, sort=False):
|
||||
else:
|
||||
item.url = url
|
||||
|
||||
# Eliminamos los servidores desactivados
|
||||
#itemlist = filter(lambda i: not i.server or is_server_enabled(i.server), itemlist)
|
||||
# Filtrar si es necesario
|
||||
# We remove the deactivated servers
|
||||
# itemlist = filter(lambda i: not i.server or is_server_enabled(i.server), itemlist)
|
||||
# Filter if necessary
|
||||
itemlist = filter_servers(itemlist)
|
||||
|
||||
for item in itemlist:
|
||||
# Asignamos "directo" en caso de que el server no se encuentre en Alfa
|
||||
# We assign "direct" in case the server is not in KoD
|
||||
if not item.server and item.url:
|
||||
item.server = "directo"
|
||||
|
||||
@@ -131,7 +130,7 @@ def get_servers_itemlist(itemlist, fnc=None, sort=False):
|
||||
item.title = fnc(item)
|
||||
|
||||
|
||||
# Ordenar segun favoriteslist si es necesario
|
||||
# Sort according to favoriteslist if necessary
|
||||
if sort:
|
||||
itemlist = sort_servers(itemlist)
|
||||
|
||||
@@ -140,11 +139,11 @@ def get_servers_itemlist(itemlist, fnc=None, sort=False):
|
||||
|
||||
def findvideos(data, skip=False):
|
||||
"""
|
||||
Recorre la lista de servidores disponibles y ejecuta la funcion findvideosbyserver para cada uno de ellos
|
||||
:param data: Texto donde buscar los enlaces
|
||||
:param skip: Indica un limite para dejar de recorrer la lista de servidores. Puede ser un booleano en cuyo caso
|
||||
seria False para recorrer toda la lista (valor por defecto) o True para detenerse tras el primer servidor que
|
||||
retorne algun enlace. Tambien puede ser un entero mayor de 1, que representaria el numero maximo de enlaces a buscar.
|
||||
Scroll through the list of available servers and run the findvideosbyserver function for each of them
|
||||
:param data: Text where to look for the links
|
||||
:param skip: Indicates a limit to stop scrolling through the list of servers. It can be a boolean in which case
|
||||
It would be False to go through the whole list (default value) or True to stop after the first server that
|
||||
return some link. It can also be an integer greater than 1, which would represent the maximum number of links to search.
|
||||
:return:
|
||||
"""
|
||||
logger.info()
|
||||
@@ -155,7 +154,7 @@ def findvideos(data, skip=False):
|
||||
|
||||
is_filter_servers = False
|
||||
|
||||
# Ejecuta el findvideos en cada servidor activo
|
||||
# Run findvideos on each active server
|
||||
for serverid in servers_list:
|
||||
'''if not is_server_enabled(serverid):
|
||||
continue'''
|
||||
@@ -183,16 +182,16 @@ def findvideosbyserver(data, serverid):
|
||||
return []
|
||||
devuelve = []
|
||||
if "find_videos" in server_parameters:
|
||||
# Recorre los patrones
|
||||
# Walk the patterns
|
||||
for pattern in server_parameters["find_videos"].get("patterns", []):
|
||||
msg = "%s\npattern: %s" % (serverid, pattern["pattern"])
|
||||
# Recorre los resultados
|
||||
# Scroll through the results
|
||||
for match in re.compile(pattern["pattern"], re.DOTALL).finditer(data):
|
||||
url = pattern["url"]
|
||||
# Crea la url con los datos
|
||||
# Create the url with the data
|
||||
for x in range(len(match.groups())):
|
||||
url = url.replace("\\%s" % (x + 1), match.groups()[x])
|
||||
msg += "\nurl encontrada: %s" % url
|
||||
msg += "\nfound url: %s" % url
|
||||
value = server_parameters["name"], url, serverid, server_parameters.get("thumbnail", "")
|
||||
if value not in devuelve and url not in server_parameters["find_videos"].get("ignore_urls", []):
|
||||
devuelve.append(value)
|
||||
@@ -211,7 +210,7 @@ def get_server_from_url(url):
|
||||
logger.info()
|
||||
servers_list = list(get_servers_list().keys())
|
||||
|
||||
# Ejecuta el findvideos en cada servidor activo
|
||||
# Run findvideos on each active server
|
||||
for serverid in servers_list:
|
||||
'''if not is_server_enabled(serverid):
|
||||
continue'''
|
||||
@@ -224,18 +223,18 @@ def get_server_from_url(url):
|
||||
if not server_parameters["active"]:
|
||||
continue
|
||||
if "find_videos" in server_parameters:
|
||||
# Recorre los patrones
|
||||
# Walk the patterns
|
||||
for n, pattern in enumerate(server_parameters["find_videos"].get("patterns", [])):
|
||||
msg = "%s\npattern: %s" % (serverid, pattern["pattern"])
|
||||
if not "pattern_compiled" in pattern:
|
||||
# logger.info('compiled ' + serverid)
|
||||
pattern["pattern_compiled"] = re.compile(pattern["pattern"])
|
||||
dict_servers_parameters[serverid]["find_videos"]["patterns"][n]["pattern_compiled"] = pattern["pattern_compiled"]
|
||||
# Recorre los resultados
|
||||
# Scroll through the results
|
||||
match = re.search(pattern["pattern_compiled"], url)
|
||||
if match:
|
||||
url = pattern["url"]
|
||||
# Crea la url con los datos
|
||||
# Create the url with the data
|
||||
for x in range(len(match.groups())):
|
||||
url = url.replace("\\%s" % (x + 1), match.groups()[x])
|
||||
msg += "\nurl encontrada: %s" % url
|
||||
@@ -249,19 +248,19 @@ def get_server_from_url(url):
|
||||
|
||||
def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialogo=False, background_dialog=False):
|
||||
"""
|
||||
Función para obtener la url real del vídeo
|
||||
@param server: Servidor donde está alojado el vídeo
|
||||
Function to get the real url of the video
|
||||
@param server: Server where the video is hosted
|
||||
@type server: str
|
||||
@param url: url del vídeo
|
||||
@param url: video url
|
||||
@type url: str
|
||||
@param video_password: Password para el vídeo
|
||||
@param video_password: Password for the video
|
||||
@type video_password: str
|
||||
@param muestra_dialogo: Muestra el diálogo de progreso
|
||||
@param muestra_dialogo: Show progress dialog
|
||||
@type muestra_dialogo: bool
|
||||
@type background_dialog: bool
|
||||
@param background_dialog: if progress dialog should be in background
|
||||
|
||||
@return: devuelve la url del video
|
||||
@return: returns the url of the video
|
||||
@rtype: list
|
||||
"""
|
||||
logger.info("Server: %s, Url: %s" % (server, url))
|
||||
@@ -273,14 +272,14 @@ def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialo
|
||||
error_messages = []
|
||||
opciones = []
|
||||
|
||||
# Si el vídeo es "directo" o "local", no hay que buscar más
|
||||
# If the video is "direct" or "local", look no further
|
||||
if server == "directo" or server == "local":
|
||||
if isinstance(video_password, list):
|
||||
return video_password, len(video_password) > 0, "<br/>".join(error_messages)
|
||||
logger.info("Server: %s, la url es la buena" % server)
|
||||
logger.info("Server: %s, url is good" % server)
|
||||
video_urls.append(["%s [%s]" % (urlparse.urlparse(url)[2][-4:], server), url])
|
||||
|
||||
# Averigua la URL del vídeo
|
||||
# Find out the video URL
|
||||
else:
|
||||
if server:
|
||||
server_parameters = get_server_parameters(server)
|
||||
@@ -288,12 +287,11 @@ def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialo
|
||||
server_parameters = {}
|
||||
|
||||
if server_parameters:
|
||||
# Muestra un diágo de progreso
|
||||
# Show a progress dialog
|
||||
if muestra_dialogo:
|
||||
progreso = (platformtools.dialog_progress_bg if background_dialog else platformtools.dialog_progress)(config.get_localized_string(20000),
|
||||
config.get_localized_string(70180) % server_parameters["name"])
|
||||
progreso = (platformtools.dialog_progress_bg if background_dialog else platformtools.dialog_progress)(config.get_localized_string(20000), config.get_localized_string(70180) % server_parameters["name"])
|
||||
|
||||
# Cuenta las opciones disponibles, para calcular el porcentaje
|
||||
# Count the available options, to calculate the percentage
|
||||
|
||||
orden = [
|
||||
["free"] + [server] + [premium for premium in server_parameters["premium"] if not premium == server],
|
||||
@@ -309,76 +307,76 @@ def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialo
|
||||
priority = int(config.get_setting("resolve_priority"))
|
||||
opciones = sorted(opciones, key=lambda x: orden[priority].index(x))
|
||||
|
||||
logger.info("Opciones disponibles: %s | %s" % (len(opciones), opciones))
|
||||
logger.info("Available options: %s | %s" % (len(opciones), opciones))
|
||||
else:
|
||||
logger.error("No existe conector para el servidor %s" % server)
|
||||
logger.error("There is no connector for the server %s" % server)
|
||||
error_messages.append(config.get_localized_string(60004) % server)
|
||||
muestra_dialogo = False
|
||||
|
||||
# Importa el server
|
||||
# Import the server
|
||||
try:
|
||||
server_module = __import__('servers.%s' % server, None, None, ["servers.%s" % server])
|
||||
logger.info("Servidor importado: %s" % server_module)
|
||||
logger.info("Imported server: %s" % server_module)
|
||||
except:
|
||||
server_module = None
|
||||
if muestra_dialogo:
|
||||
progreso.close()
|
||||
logger.error("No se ha podido importar el servidor: %s" % server)
|
||||
logger.error("Could not import server: %s" % server)
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Si tiene una función para ver si el vídeo existe, lo comprueba ahora
|
||||
# If it has a function to see if the video exists, check it now
|
||||
if hasattr(server_module, 'test_video_exists'):
|
||||
logger.info("Invocando a %s.test_video_exists" % server)
|
||||
logger.info("Invoking a %s.test_video_exists" % server)
|
||||
try:
|
||||
video_exists, message = server_module.test_video_exists(page_url=url)
|
||||
|
||||
if not video_exists:
|
||||
error_messages.append(message)
|
||||
logger.info("test_video_exists dice que el video no existe")
|
||||
logger.info("test_video_exists says video doesn't exist")
|
||||
if muestra_dialogo:
|
||||
progreso.close()
|
||||
else:
|
||||
logger.info("test_video_exists dice que el video SI existe")
|
||||
logger.info("test_video_exists says the video DOES exist")
|
||||
except:
|
||||
logger.error("No se ha podido comprobar si el video existe")
|
||||
logger.error("Could not verify if the video exists")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Si el video existe y el modo free está disponible, obtenemos la url
|
||||
# If the video exists and the free mode is available, we get the url
|
||||
if video_exists:
|
||||
for opcion in opciones:
|
||||
# Opcion free y premium propio usa el mismo server
|
||||
# Own free and premium option uses the same server
|
||||
if opcion == "free" or opcion == server:
|
||||
serverid = server_module
|
||||
server_name = server_parameters["name"]
|
||||
|
||||
# Resto de opciones premium usa un debrider
|
||||
# Rest of premium options use a debrider
|
||||
else:
|
||||
serverid = __import__('servers.debriders.%s' % opcion, None, None,
|
||||
["servers.debriders.%s" % opcion])
|
||||
server_name = get_server_parameters(opcion)["name"]
|
||||
|
||||
# Muestra el progreso
|
||||
# Show progress
|
||||
if muestra_dialogo:
|
||||
progreso.update((old_div(100, len(opciones))) * opciones.index(opcion), config.get_localized_string(70180) % server_name)
|
||||
|
||||
# Modo free
|
||||
# Free mode
|
||||
if opcion == "free":
|
||||
try:
|
||||
logger.info("Invocando a %s.get_video_url" % server)
|
||||
logger.info("Invoking a %s.get_video_url" % server)
|
||||
response = serverid.get_video_url(page_url=url, video_password=video_password)
|
||||
video_urls.extend(response)
|
||||
except:
|
||||
logger.error("Error al obtener la url en modo free")
|
||||
logger.error("Error getting url in free mode")
|
||||
error_messages.append(config.get_localized_string(60006) % server_name)
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Modo premium
|
||||
# Premium mode
|
||||
else:
|
||||
try:
|
||||
logger.info("Invocando a %s.get_video_url" % opcion)
|
||||
logger.info("Invoking a %s.get_video_url" % opcion)
|
||||
response = serverid.get_video_url(page_url=url, premium=True,
|
||||
user=config.get_setting("user", server=opcion),
|
||||
password=config.get_setting("password", server=opcion),
|
||||
@@ -390,27 +388,27 @@ def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialo
|
||||
else:
|
||||
error_messages.append(config.get_localized_string(60006) % server_name)
|
||||
except:
|
||||
logger.error("Error en el servidor: %s" % opcion)
|
||||
logger.error("Server errorr: %s" % opcion)
|
||||
error_messages.append(config.get_localized_string(60006) % server_name)
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Si ya tenemos URLS, dejamos de buscar
|
||||
# If we already have URLS, we stop searching
|
||||
if video_urls and config.get_setting("resolve_stop") == True:
|
||||
break
|
||||
|
||||
# Cerramos el progreso
|
||||
# We close progress
|
||||
if muestra_dialogo:
|
||||
progreso.update(100, config.get_localized_string(60008))
|
||||
progreso.close()
|
||||
|
||||
# Si no hay opciones disponibles mostramos el aviso de las cuentas premium
|
||||
# If there are no options available, we show the notice of premium accounts
|
||||
if video_exists and not opciones and server_parameters.get("premium"):
|
||||
listapremium = [get_server_parameters(premium)["name"] for premium in server_parameters["premium"]]
|
||||
error_messages.append(
|
||||
config.get_localized_string(60009) % (server, " o ".join(listapremium)))
|
||||
|
||||
# Si no tenemos urls ni mensaje de error, ponemos uno generico
|
||||
# If we do not have urls or error messages, we put a generic one
|
||||
elif not video_urls and not error_messages:
|
||||
error_messages.append(config.get_localized_string(60006) % get_server_parameters(server)["name"])
|
||||
|
||||
@@ -419,51 +417,51 @@ def resolve_video_urls_for_playing(server, url, video_password="", muestra_dialo
|
||||
|
||||
def get_server_name(serverid):
|
||||
"""
|
||||
Función obtener el nombre del servidor real a partir de una cadena.
|
||||
@param serverid: Cadena donde mirar
|
||||
Function get real server name from string.
|
||||
@param serverid: Chain where to look
|
||||
@type serverid: str
|
||||
|
||||
@return: Nombre del servidor
|
||||
@return: Server name
|
||||
@rtype: str
|
||||
"""
|
||||
serverid = serverid.lower().split(".")[0]
|
||||
|
||||
# Obtenemos el listado de servers
|
||||
# We get the list of servers
|
||||
server_list = list(get_servers_list().keys())
|
||||
|
||||
# Si el nombre está en la lista
|
||||
# If the name is in the list
|
||||
if serverid in server_list:
|
||||
return serverid
|
||||
|
||||
# Recorre todos los servers buscando el nombre
|
||||
# Browse all servers looking for the name
|
||||
for server in server_list:
|
||||
params = get_server_parameters(server)
|
||||
# Si la nombre esta en el listado de ids
|
||||
# If the name is in the list of ids
|
||||
if serverid in params["id"]:
|
||||
return server
|
||||
# Si el nombre es mas de una palabra, comprueba si algun id esta dentro del nombre:
|
||||
# If the name is more than one word, check if any id is inside the name:
|
||||
elif len(serverid.split()) > 1:
|
||||
for id in params["id"]:
|
||||
if id in serverid:
|
||||
return server
|
||||
|
||||
# Si no se encuentra nada se devuelve una cadena vacia
|
||||
# If nothing is found an empty string is returned
|
||||
return ""
|
||||
|
||||
|
||||
def is_server_enabled(server):
|
||||
"""
|
||||
Función comprobar si un servidor está segun la configuración establecida
|
||||
@param server: Nombre del servidor
|
||||
Function check if a server is according to the established configuration
|
||||
@param server: Server name
|
||||
@type server: str
|
||||
|
||||
@return: resultado de la comprobación
|
||||
@return: check result
|
||||
@rtype: bool
|
||||
"""
|
||||
|
||||
server = get_server_name(server)
|
||||
|
||||
# El server no existe
|
||||
# The server does not exist
|
||||
if not server:
|
||||
return False
|
||||
|
||||
@@ -481,11 +479,11 @@ def is_server_enabled(server):
|
||||
|
||||
def get_server_parameters(server):
|
||||
"""
|
||||
Obtiene los datos del servidor
|
||||
@param server: Nombre del servidor
|
||||
Get data from server
|
||||
@param server: Server name
|
||||
@type server: str
|
||||
|
||||
@return: datos del servidor
|
||||
@return: server data
|
||||
@rtype: dict
|
||||
"""
|
||||
# logger.info("server %s" % server)
|
||||
@@ -503,12 +501,11 @@ def get_server_parameters(server):
|
||||
# Debriders
|
||||
elif filetools.isfile(filetools.join(config.get_runtime_path(), "servers", "debriders", server + ".json")):
|
||||
path = filetools.join(config.get_runtime_path(), "servers", "debriders", server + ".json")
|
||||
#
|
||||
#Cuando no está bien definido el server en el canal (no existe conector), muestra error por no haber "path" y se tiene que revisar el canal
|
||||
#
|
||||
|
||||
# When the server is not well defined in the channel (there is no connector), it shows an error because there is no "path" and the channel has to be checked
|
||||
dict_server = jsontools.load(filetools.read(path))
|
||||
|
||||
# Imagenes: se admiten url y archivos locales dentro de "resources/images"
|
||||
# Images: url and local files are allowed inside "resources / images"
|
||||
if dict_server.get("thumbnail") and "://" not in dict_server["thumbnail"]:
|
||||
dict_server["thumbnail"] = filetools.join(config.get_runtime_path(), "resources", "media",
|
||||
"servers", dict_server["thumbnail"])
|
||||
@@ -573,7 +570,7 @@ def get_server_controls_settings(server_name):
|
||||
# Conversion de str a bool, etc...
|
||||
for c in list_controls:
|
||||
if 'id' not in c or 'type' not in c or 'default' not in c:
|
||||
# Si algun control de la lista no tiene id, type o default lo ignoramos
|
||||
# If any control in the list does not have id, type or default, we ignore it
|
||||
continue
|
||||
|
||||
# new dict with key(id) and value(default) from settings
|
||||
@@ -584,28 +581,28 @@ def get_server_controls_settings(server_name):
|
||||
|
||||
def get_server_setting(name, 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 propia del servidor 'server'.
|
||||
Returns the value of the parameter 'name' in the own configuration of the server 'server'.
|
||||
|
||||
Busca en la ruta \addon_data\plugin.video.addon\settings_servers el archivo server_data.json y lee
|
||||
el valor del parametro 'name'. Si el archivo server_data.json no existe busca en la carpeta servers el archivo
|
||||
server.json y crea un archivo server_data.json antes de retornar el valor solicitado. Si el parametro 'name'
|
||||
tampoco existe en el el archivo server.json se devuelve el parametro default.
|
||||
Look in the path \addon_data\plugin.video.addon\settings_servers for the file server_data.json and read
|
||||
the value of the parameter 'name'. If the server_data.json file does not exist look in the servers folder for the file
|
||||
server.json and create a server_data.json file before returning the requested value. If the parameter 'name'
|
||||
also does not exist in the server.json file the default parameter is returned.
|
||||
|
||||
|
||||
@param name: nombre del parametro
|
||||
@param name: parameter name
|
||||
@type name: 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 parameter value 'name'
|
||||
@rtype: any
|
||||
|
||||
"""
|
||||
# Creamos la carpeta si no existe
|
||||
# We create the folder if it does not exist
|
||||
if not filetools.exists(filetools.join(config.get_data_path(), "settings_servers")):
|
||||
filetools.mkdir(filetools.join(config.get_data_path(), "settings_servers"))
|
||||
|
||||
@@ -613,34 +610,34 @@ def get_server_setting(name, server, default=None):
|
||||
dict_settings = {}
|
||||
dict_file = {}
|
||||
if filetools.exists(file_settings):
|
||||
# Obtenemos configuracion guardada de ../settings/channel_data.json
|
||||
# We get saved configuration from ../settings/channel_data.json
|
||||
try:
|
||||
dict_file = jsontools.load(filetools.read(file_settings))
|
||||
if isinstance(dict_file, dict) and 'settings' in dict_file:
|
||||
dict_settings = dict_file['settings']
|
||||
except EnvironmentError:
|
||||
logger.info("ERROR al leer el archivo: %s" % file_settings)
|
||||
logger.info("ERROR when reading the file: %s" % file_settings)
|
||||
|
||||
if not dict_settings or name not in dict_settings:
|
||||
# Obtenemos controles del archivo ../servers/server.json
|
||||
# We get controls from the file ../servers/server.json
|
||||
try:
|
||||
list_controls, default_settings = get_server_controls_settings(server)
|
||||
except:
|
||||
default_settings = {}
|
||||
if name in default_settings: # Si el parametro existe en el server.json creamos el server_data.json
|
||||
if name in default_settings: # If the parameter exists in the server.json we create the server_data.json
|
||||
default_settings.update(dict_settings)
|
||||
dict_settings = default_settings
|
||||
dict_file['settings'] = dict_settings
|
||||
# Creamos el archivo ../settings/channel_data.json
|
||||
# We create the file ../settings/channel_data.json
|
||||
if not filetools.write(file_settings, jsontools.dump(dict_file)):
|
||||
logger.info("ERROR al salvar el archivo: %s" % file_settings)
|
||||
logger.info("ERROR saving file: %s" % file_settings)
|
||||
|
||||
# Devolvemos el valor del parametro local 'name' si existe, si no se devuelve default
|
||||
# We return the value of the local parameter 'name' if it exists, if default is not returned
|
||||
return dict_settings.get(name, default)
|
||||
|
||||
|
||||
def set_server_setting(name, value, server):
|
||||
# Creamos la carpeta si no existe
|
||||
# We create the folder if it does not exist
|
||||
if not filetools.exists(filetools.join(config.get_data_path(), "settings_servers")):
|
||||
filetools.mkdir(filetools.join(config.get_data_path(), "settings_servers"))
|
||||
|
||||
@@ -650,24 +647,24 @@ def set_server_setting(name, value, server):
|
||||
dict_file = None
|
||||
|
||||
if filetools.exists(file_settings):
|
||||
# Obtenemos configuracion guardada de ../settings/channel_data.json
|
||||
# We get saved configuration from ../settings/channel_data.json
|
||||
try:
|
||||
dict_file = jsontools.load(filetools.read(file_settings))
|
||||
dict_settings = dict_file.get('settings', {})
|
||||
except EnvironmentError:
|
||||
logger.info("ERROR al leer el archivo: %s" % file_settings)
|
||||
logger.info("ERROR when reading the file: %s" % file_settings)
|
||||
|
||||
dict_settings[name] = value
|
||||
|
||||
# comprobamos si existe dict_file y es un diccionario, sino lo creamos
|
||||
# we check if dict_file exists and it is a dictionary, if not we create it
|
||||
if dict_file is None or not dict_file:
|
||||
dict_file = {}
|
||||
|
||||
dict_file['settings'] = dict_settings
|
||||
|
||||
# Creamos el archivo ../settings/channel_data.json
|
||||
# We create the file ../settings/channel_data.json
|
||||
if not filetools.write(file_settings, jsontools.dump(dict_file)):
|
||||
logger.info("ERROR al salvar el archivo: %s" % file_settings)
|
||||
logger.info("ERROR saving file: %s" % file_settings)
|
||||
return None
|
||||
|
||||
return value
|
||||
@@ -675,10 +672,9 @@ def set_server_setting(name, value, server):
|
||||
|
||||
def get_servers_list():
|
||||
"""
|
||||
Obtiene un diccionario con todos los servidores disponibles
|
||||
Get a dictionary with all available servers
|
||||
|
||||
@return: Diccionario cuyas claves son los nombre de los servidores (nombre del json)
|
||||
y como valor un diccionario con los parametros del servidor.
|
||||
@return: Diccionario cuyas claves son los nombre de los servidores (nombre del json) and as a value a dictionary with the server parameters.
|
||||
@rtype: dict
|
||||
"""
|
||||
global server_list
|
||||
@@ -693,10 +689,9 @@ def get_servers_list():
|
||||
|
||||
def get_debriders_list():
|
||||
"""
|
||||
Obtiene un diccionario con todos los debriders disponibles
|
||||
Get a dictionary with all available debriders
|
||||
|
||||
@return: Diccionario cuyas claves son los nombre de los debriders (nombre del json)
|
||||
y como valor un diccionario con los parametros del servidor.
|
||||
@return: Dictionary whose keys are the names of the debriders (name of the json) and as a value a dictionary with the server parameters.
|
||||
@rtype: dict
|
||||
"""
|
||||
server_list = {}
|
||||
@@ -711,60 +706,52 @@ def get_debriders_list():
|
||||
|
||||
def sort_servers(servers_list):
|
||||
"""
|
||||
Si esta activada la opcion "Ordenar servidores" en la configuracion de servidores y existe un listado de servidores
|
||||
favoritos en la configuracion lo utiliza para ordenar la lista servers_list
|
||||
:param servers_list: Listado de servidores para ordenar. Los elementos de la lista servers_list pueden ser strings
|
||||
u objetos Item. En cuyo caso es necesario q tengan un atributo item.server del tipo str.
|
||||
:return: Lista del mismo tipo de objetos que servers_list ordenada en funcion de los servidores favoritos.
|
||||
If the option "Order servers" is activated in the server configuration and there is a list of servers
|
||||
favorites in settings use it to sort the servers_list list
|
||||
:param servers_list: List of servers to order. The items in the servers_list can be strings or Item objects. In which case it is necessary that they have an item.server attribute of type str.
|
||||
:return: List of the same type of objects as servers_list ordered according to the favorite servers.
|
||||
"""
|
||||
if servers_list and config.get_setting('favorites_servers'):
|
||||
if isinstance(servers_list[0], Item):
|
||||
servers_list = sorted(servers_list,
|
||||
key=lambda x: config.get_setting("favorites_servers_list", server=x.server) or 100)
|
||||
servers_list = sorted(servers_list, key=lambda x: config.get_setting("favorites_servers_list", server=x.server) or 100)
|
||||
else:
|
||||
servers_list = sorted(servers_list,
|
||||
key=lambda x: config.get_setting("favorites_servers_list", server=x) or 100)
|
||||
servers_list = sorted(servers_list, key=lambda x: config.get_setting("favorites_servers_list", server=x) or 100)
|
||||
|
||||
return servers_list
|
||||
|
||||
|
||||
def filter_servers(servers_list):
|
||||
"""
|
||||
Si esta activada la opcion "Filtrar por servidores" en la configuracion de servidores, elimina de la lista
|
||||
de entrada los servidores incluidos en la Lista Negra.
|
||||
:param servers_list: Listado de servidores para filtrar. Los elementos de la lista servers_list pueden ser strings
|
||||
u objetos Item. En cuyo caso es necesario q tengan un atributo item.server del tipo str.
|
||||
:return: Lista del mismo tipo de objetos que servers_list filtrada en funcion de la Lista Negra.
|
||||
If the option "Filter by servers" is activated in the server configuration, removes the servers included in the Black List from the entry list.
|
||||
:param servers_list: List of servers to filter. The items in the servers_list can be strings or Item objects. In which case it is necessary that they have an item.server attribute of type str.
|
||||
:return: List of the same type of objects as servers_list filtered based on the Black List.
|
||||
"""
|
||||
#Eliminamos los inactivos
|
||||
# We eliminate the inactive
|
||||
if servers_list:
|
||||
servers_list = [i for i in servers_list if not i.server or is_server_enabled(i.server)]
|
||||
|
||||
|
||||
|
||||
if servers_list and config.get_setting('filter_servers'):
|
||||
if isinstance(servers_list[0], Item):
|
||||
servers_list_filter = [x for x in servers_list if not config.get_setting("black_list", server=x.server)]
|
||||
else:
|
||||
servers_list_filter = [x for x in servers_list if not config.get_setting("black_list", server=x)]
|
||||
|
||||
# Si no hay enlaces despues de filtrarlos
|
||||
if servers_list_filter or not platformtools.dialog_yesno(config.get_localized_string(60000),
|
||||
config.get_localized_string(60010),
|
||||
config.get_localized_string(70281)):
|
||||
# If there are no links after filtering
|
||||
if servers_list_filter or not platformtools.dialog_yesno(config.get_localized_string(60000), config.get_localized_string(60010), config.get_localized_string(70281)):
|
||||
servers_list = servers_list_filter
|
||||
|
||||
|
||||
return servers_list
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# Comprobación de enlaces
|
||||
# -----------------------
|
||||
|
||||
# Checking links
|
||||
|
||||
def check_list_links(itemlist, numero='', timeout=3):
|
||||
"""
|
||||
Comprueba una lista de enlaces a videos y la devuelve modificando el titulo con la verificacion.
|
||||
El parámetro numero indica cuantos enlaces hay que verificar (0:5, 1:10, 2:15, 3:20)
|
||||
El parámetro timeout indica un tope de espera para descargar la página
|
||||
Check a list of video links and return it by modifying the title with verification.
|
||||
The number parameter indicates how many links to check (0:5, 1:10, 2:15, 3:20)
|
||||
The timeout parameter indicates a waiting limit to download the page
|
||||
"""
|
||||
numero = numero if numero > 4 else ((int(numero) + 1) * 5) if numero != '' else 5
|
||||
import sys
|
||||
@@ -790,47 +777,43 @@ def check_list_links(itemlist, numero='', timeout=3):
|
||||
|
||||
def check_video_link(item, timeout=3):
|
||||
"""
|
||||
Comprueba si el enlace a un video es valido y devuelve un string de 2 posiciones con la verificacion.
|
||||
:param url, server: Link y servidor
|
||||
:return: str(2) '??':No se ha podido comprobar. 'Ok':Parece que el link funciona. 'NO':Parece que no funciona.
|
||||
"""
|
||||
Check if the link to a video is valid and return a 2-position string with verification.
|
||||
:param url, server: Link and server
|
||||
:return: str(2) '??':Could not be verified. 'Ok': The link seems to work. 'NO': It doesn't seem to work.
|
||||
"""
|
||||
url = item.url
|
||||
server = item.server
|
||||
|
||||
|
||||
NK = "[COLOR 0xFFF9B613][B]" + u"\u2022".encode('utf-8') + "[/B][/COLOR]"
|
||||
OK = "[COLOR 0xFF00C289][B]" + u"\u2022".encode('utf-8') + "[/B][/COLOR]"
|
||||
KO = "[COLOR 0xFFC20000][B]" + u"\u2022".encode('utf-8') + "[/B][/COLOR]"
|
||||
|
||||
# NK = "[COLOR 0xFFF9B613][B]♥[/B][/COLOR]"
|
||||
# OK = "[COLOR 0xFF00C289][B]♥[/B][/COLOR]"
|
||||
# KO = "[COLOR 0xFFC20000][B]♥[/B][/COLOR]"
|
||||
|
||||
try:
|
||||
server_module = __import__('servers.%s' % server, None, None, ["servers.%s" % server])
|
||||
except:
|
||||
server_module = None
|
||||
logger.info("[check_video_link] No se puede importar el servidor! %s" % server)
|
||||
logger.info("[check_video_link] Cannot import server! %s" % server)
|
||||
return item, NK
|
||||
|
||||
|
||||
if hasattr(server_module, 'test_video_exists'):
|
||||
ant_timeout = httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT
|
||||
httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT = timeout # Limitar tiempo de descarga
|
||||
|
||||
httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT = timeout # Limit download time
|
||||
|
||||
try:
|
||||
video_exists, message = server_module.test_video_exists(page_url=url)
|
||||
if not video_exists:
|
||||
logger.info("[check_video_link] No existe! %s %s %s" % (message, server, url))
|
||||
logger.info("[check_video_link] Does not exist! %s %s %s" % (message, server, url))
|
||||
resultado = KO
|
||||
else:
|
||||
logger.info("[check_video_link] comprobacion OK %s %s" % (server, url))
|
||||
logger.info("[check_video_link] check ok %s %s" % (server, url))
|
||||
resultado = OK
|
||||
except:
|
||||
logger.info("[check_video_link] No se puede comprobar ahora! %s %s" % (server, url))
|
||||
logger.info("[check_video_link] Can't check now! %s %s" % (server, url))
|
||||
resultado = NK
|
||||
|
||||
finally:
|
||||
httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT = ant_timeout # Restaurar tiempo de descarga
|
||||
httptools.HTTPTOOLS_DEFAULT_DOWNLOAD_TIMEOUT = ant_timeout # Restore download time
|
||||
return item, resultado
|
||||
|
||||
logger.info("[check_video_link] No hay test_video_exists para servidor: %s" % server)
|
||||
logger.info("[check_video_link] There is no test_video_exists for server: %s" % server)
|
||||
return item, NK
|
||||
|
||||
+97
-90
@@ -668,11 +668,12 @@ def stayonline(id):
|
||||
return data
|
||||
|
||||
|
||||
def menuItem(itemlist, filename, title='', action='', url='', contentType='movie', args=[]):
|
||||
def menuItem(itemlist, filename, title='', action='', url='', contentType='movie', args=[], style=True):
|
||||
# Function to simplify menu creation
|
||||
|
||||
# Call typo function
|
||||
title = typo(title)
|
||||
if style:
|
||||
title = typo(title)
|
||||
|
||||
if contentType == 'movie': extra = 'movie'
|
||||
else: extra = 'tvshow'
|
||||
@@ -687,11 +688,6 @@ def menuItem(itemlist, filename, title='', action='', url='', contentType='movie
|
||||
contentType = contentType
|
||||
))
|
||||
|
||||
# Apply auto Thumbnails at the menus
|
||||
from channelselector import thumb
|
||||
thumb(itemlist)
|
||||
return itemlist
|
||||
|
||||
|
||||
def menu(func):
|
||||
def wrapper(*args):
|
||||
@@ -699,17 +695,19 @@ def menu(func):
|
||||
args = func(*args)
|
||||
|
||||
item = args['item']
|
||||
log(item.channel + ' start')
|
||||
host = func.__globals__['host']
|
||||
list_servers = func.__globals__['list_servers'] if 'list_servers' in func.__globals__ else ['directo']
|
||||
list_quality = func.__globals__['list_quality'] if 'list_quality' in func.__globals__ else ['default']
|
||||
log('LIST QUALITY', list_quality)
|
||||
filename = func.__module__.split('.')[1]
|
||||
global_search = False
|
||||
single_search = False
|
||||
# listUrls = ['film', 'filmSub', 'tvshow', 'tvshowSub', 'anime', 'animeSub', 'search', 'top', 'topSub']
|
||||
listUrls = ['top', 'film', 'tvshow', 'anime', 'search']
|
||||
listUrls_extra = []
|
||||
dictUrl = {}
|
||||
|
||||
global_search = item.global_search
|
||||
|
||||
# Main options
|
||||
itemlist = []
|
||||
@@ -722,61 +720,71 @@ def menu(func):
|
||||
if name == 'anime': title = 'Anime'
|
||||
|
||||
if name == 'search' and dictUrl[name] is not None:
|
||||
global_search = True
|
||||
single_search = True
|
||||
|
||||
# Make TOP MENU
|
||||
elif name == 'top' and dictUrl[name] is not None:
|
||||
for sub, var in dictUrl['top']:
|
||||
menuItem(itemlist, filename,
|
||||
title = sub + ' italic bold',
|
||||
url = host + var[0] if len(var) > 0 else '',
|
||||
action = var[1] if len(var) > 1 else 'peliculas',
|
||||
args=var[2] if len(var) > 2 else '',
|
||||
contentType= var[3] if len(var) > 3 else 'movie')
|
||||
if not global_search:
|
||||
for sub, var in dictUrl['top']:
|
||||
menuItem(itemlist, filename,
|
||||
title = sub + '{italic bold}',
|
||||
url = host + var[0] if len(var) > 0 else '',
|
||||
action = var[1] if len(var) > 1 else 'peliculas',
|
||||
args=var[2] if len(var) > 2 else '',
|
||||
contentType= var[3] if len(var) > 3 else 'movie')
|
||||
|
||||
# Make MAIN MENU
|
||||
elif dictUrl[name] is not None:
|
||||
if len(dictUrl[name]) == 0: url = ''
|
||||
else: url = dictUrl[name][0] if type(dictUrl[name][0]) is not tuple and len(dictUrl[name][0]) > 0 else ''
|
||||
menuItem(itemlist, filename,
|
||||
title + ' bullet bold', 'peliculas',
|
||||
host + url,
|
||||
contentType='movie' if name == 'film' else 'tvshow')
|
||||
if len(dictUrl[name]) > 0:
|
||||
if type(dictUrl[name][0]) is not tuple and type(dictUrl[name]) is not str: dictUrl[name].pop(0)
|
||||
if dictUrl[name] is not None and type(dictUrl[name]) is not str:
|
||||
for sub, var in dictUrl[name]:
|
||||
menuItem(itemlist, filename,
|
||||
title = sub + ' submenu {' + title + '}',
|
||||
url = host + var[0] if len(var) > 0 else '',
|
||||
action = var[1] if len(var) > 1 else 'peliculas',
|
||||
args=var[2] if len(var) > 2 else '',
|
||||
contentType= var[3] if len(var) > 3 else 'movie' if name == 'film' else 'tvshow')
|
||||
if len(dictUrl[name]) == 0:
|
||||
url = ''
|
||||
else:
|
||||
url = dictUrl[name][0] if type(dictUrl[name][0]) is not tuple and len(dictUrl[name][0]) > 0 else ''
|
||||
|
||||
if not global_search:
|
||||
menuItem(itemlist, filename,
|
||||
title + '{bullet bold}', 'peliculas',
|
||||
host + url,
|
||||
contentType='movie' if name == 'film' else 'tvshow')
|
||||
if len(dictUrl[name]) > 0:
|
||||
if type(dictUrl[name][0]) is not tuple and type(dictUrl[name]) is not str: dictUrl[name].pop(0)
|
||||
if dictUrl[name] is not None and type(dictUrl[name]) is not str:
|
||||
for sub, var in dictUrl[name]:
|
||||
menuItem(itemlist, filename,
|
||||
title = sub + '{submenu} {' + title + '}',
|
||||
url = host + var[0] if len(var) > 0 else '',
|
||||
action = var[1] if len(var) > 1 else 'peliculas',
|
||||
args=var[2] if len(var) > 2 else '',
|
||||
contentType= var[3] if len(var) > 3 else 'movie' if name == 'film' else 'tvshow')
|
||||
# add search menu for category
|
||||
if 'search' not in args: menuItem(itemlist, filename, config.get_localized_string(70741) % title + ' … submenu bold', 'search', host + url, contentType='movie' if name == 'film' else 'tvshow')
|
||||
if 'search' not in args: menuItem(itemlist, filename, config.get_localized_string(70741) % title + '… {submenu bold}', 'search', host + url, contentType='movie' if name == 'film' else 'tvshow', style=not global_search)
|
||||
|
||||
# Make EXTRA MENU (on bottom)
|
||||
for name, var in args.items():
|
||||
if name not in listUrls and name != 'item':
|
||||
listUrls_extra.append(name)
|
||||
for name in listUrls_extra:
|
||||
dictUrl[name] = args[name] if name in args else None
|
||||
for sub, var in dictUrl[name]:
|
||||
menuItem(itemlist, filename,
|
||||
title = sub + ' ',
|
||||
url = host + var[0] if len(var) > 0 else '',
|
||||
action = var[1] if len(var) > 1 else 'peliculas',
|
||||
args=var[2] if len(var) > 2 else '',
|
||||
contentType= var[3] if len(var) > 3 else 'movie',)
|
||||
if not global_search:
|
||||
for name, var in args.items():
|
||||
if name not in listUrls and name != 'item':
|
||||
listUrls_extra.append(name)
|
||||
for name in listUrls_extra:
|
||||
dictUrl[name] = args[name] if name in args else None
|
||||
for sub, var in dictUrl[name]:
|
||||
menuItem(itemlist, filename,
|
||||
title = sub + ' ',
|
||||
url = host + var[0] if len(var) > 0 else '',
|
||||
action = var[1] if len(var) > 1 else 'peliculas',
|
||||
args=var[2] if len(var) > 2 else '',
|
||||
contentType= var[3] if len(var) > 3 else 'movie',)
|
||||
|
||||
if global_search:
|
||||
menuItem(itemlist, filename, config.get_localized_string(70741) % '… bold', 'search', host + dictUrl['search'])
|
||||
if single_search:
|
||||
menuItem(itemlist, filename, config.get_localized_string(70741) % '… {bold}', 'search', host + dictUrl['search'], style=not global_search)
|
||||
|
||||
if 'get_channel_results' not in inspect.stack()[1][3]:
|
||||
if not global_search:
|
||||
autoplay.init(item.channel, list_servers, list_quality)
|
||||
autoplay.show_option(item.channel, itemlist)
|
||||
channel_config(item, itemlist)
|
||||
channel_config(item, itemlist)
|
||||
|
||||
# Apply auto Thumbnails at the menus
|
||||
from channelselector import thumb
|
||||
thumb(itemlist)
|
||||
log(item.channel + ' end')
|
||||
return itemlist
|
||||
|
||||
return wrapper
|
||||
@@ -785,57 +793,56 @@ def menu(func):
|
||||
def typo(string, typography=''):
|
||||
|
||||
kod_color = '0xFF65B3DA' #'0xFF0081C2'
|
||||
|
||||
try: string = str(string)
|
||||
except: string = str(string.encode('utf8'))
|
||||
|
||||
if typography:
|
||||
string = string + ' ' + typography
|
||||
if config.get_localized_string(30992) in string:
|
||||
string = string + ' >'
|
||||
|
||||
# If there are no attributes, it applies the default ones
|
||||
attribute = ['[]','()','submenu','color','bold','italic','_','--','[B]','[I]','[COLOR]']
|
||||
if int(config.get_setting('view_mode_channel').split(',')[-1]) in [0, 50, 55]:
|
||||
VLT = True
|
||||
else:
|
||||
VLT = False
|
||||
# Otherwise it uses the typographical attributes of the string
|
||||
# else:
|
||||
if 'capitalize' in string.lower():
|
||||
string = re.sub(r'\s*capitalize','',string).capitalize()
|
||||
if 'uppercase' in string.lower():
|
||||
string = re.sub(r'\s*uppercase','',string).upper()
|
||||
if 'lowercase' in string.lower():
|
||||
string = re.sub(r'\s*lowercase','',string).lower()
|
||||
if '[]' in string:
|
||||
string = '[' + re.sub(r'\s*\[\]','',string) + ']'
|
||||
if '()' in string:
|
||||
string = '(' + re.sub(r'\s*\(\)','',string) + ')'
|
||||
if 'submenu' in string:
|
||||
if VLT:
|
||||
string = "•• " + re.sub(r'\s*submenu','',string)
|
||||
else:
|
||||
string = re.sub(r'\s*submenu','',string)
|
||||
if 'color' in string:
|
||||
color = scrapertools.find_single_match(string, 'color ([a-z]+)')
|
||||
if color == 'kod' or '': color = kod_color
|
||||
string = '[COLOR '+ color +']' + re.sub(r'\scolor\s([a-z]+)','',string) + '[/COLOR]'
|
||||
if 'bold' in string:
|
||||
string = '[B]' + re.sub(r'\s*bold','',string) + '[/B]'
|
||||
if 'italic' in string:
|
||||
string = '[I]' + re.sub(r'\s*italic','',string) + '[/I]'
|
||||
if '_' in string:
|
||||
string = ' ' + re.sub(r'\s*_','',string)
|
||||
if '--' in string:
|
||||
string = ' - ' + re.sub(r'\s*--','',string)
|
||||
if 'bullet' in string:
|
||||
if VLT:
|
||||
string = '[B]' + "•" + '[/B] ' + re.sub(r'\s*bullet','',string)
|
||||
else:
|
||||
string = re.sub(r'\s*bullet','',string)
|
||||
if '{}' in string:
|
||||
string = re.sub(r'\s*\{\}','',string)
|
||||
|
||||
|
||||
if not typography and '{' in string:
|
||||
typography = string.split('{')[1].strip(' }').lower()
|
||||
string = string.replace('{' + typography + '}','').strip()
|
||||
else:
|
||||
string = string.strip()
|
||||
typography.lower()
|
||||
|
||||
|
||||
if 'capitalize' in typography:
|
||||
string = string.capitalize()
|
||||
if 'uppercase' in typography:
|
||||
string = string.upper()
|
||||
if 'lowercase' in typography:
|
||||
string = string.lower()
|
||||
if '[]' in typography:
|
||||
string = '[' + string + ']'
|
||||
if '()' in typography:
|
||||
string = '(' + string + ')'
|
||||
if 'submenu' in typography:
|
||||
if VLT: string = "•• " + string
|
||||
else: string = string
|
||||
if 'color kod' in typography:
|
||||
string = '[COLOR ' + kod_color + ']' + string + '[/COLOR]'
|
||||
elif 'color' in typography:
|
||||
color = scrapertools.find_single_match(typography, 'color ([a-zA-Z0-9]+)')
|
||||
string = '[COLOR ' + color + ']' + string + '[/COLOR]'
|
||||
if 'bold' in typography:
|
||||
string = '[B]' + string + '[/B]'
|
||||
if 'italic' in typography:
|
||||
string = '[I]' + string + '[/I]'
|
||||
if '_' in typography:
|
||||
string = ' ' + string
|
||||
if '--' in typography:
|
||||
string = ' - ' + string
|
||||
if 'bullet' in typography:
|
||||
if VLT: string = '[B]' + "•" + '[/B] ' + string
|
||||
else: string = string
|
||||
return string
|
||||
|
||||
|
||||
@@ -1169,7 +1176,7 @@ def server(item, data='', itemlist=[], headers='', AutoPlay=True, CheckLinks=Tru
|
||||
AP, HS = autoplay.get_channel_AP_HS(item)
|
||||
|
||||
# Check Links
|
||||
if not AP and (config.get_setting('checklinks') or config.get_setting('checklinks', item.channel)):
|
||||
if not AP and not item.global_search and (config.get_setting('checklinks') or config.get_setting('checklinks', item.channel)):
|
||||
if config.get_setting('checklinks', item.channel):
|
||||
checklinks_number = config.get_setting('checklinks_number', item.channel)
|
||||
elif config.get_setting('checklinks'):
|
||||
|
||||
+255
-328
File diff suppressed because it is too large
Load Diff
+197
-220
@@ -8,19 +8,12 @@ import sys
|
||||
PY3 = False
|
||||
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
|
||||
|
||||
import errno
|
||||
import math
|
||||
import traceback
|
||||
import re
|
||||
import os
|
||||
import errno, math, traceback, re, os
|
||||
|
||||
from core import filetools
|
||||
from core import scraper
|
||||
from core import scrapertools
|
||||
from core import filetools, scraper, scrapertools
|
||||
from core.item import Item
|
||||
from lib import generictools
|
||||
from platformcode import config, logger
|
||||
from platformcode import platformtools
|
||||
from platformcode import config, logger, platformtools
|
||||
|
||||
FOLDER_MOVIES = config.get_setting("folder_movies")
|
||||
FOLDER_TVSHOWS = config.get_setting("folder_tvshows")
|
||||
@@ -37,15 +30,13 @@ addon_name = "plugin://plugin.video.%s/" % config.PLUGIN_NAME
|
||||
|
||||
def read_nfo(path_nfo, item=None):
|
||||
"""
|
||||
Metodo para leer archivos nfo.
|
||||
Los arcivos nfo tienen la siguiente extructura: url_scraper | xml + item_json
|
||||
[url_scraper] y [xml] son opcionales, pero solo uno de ellos ha de existir siempre.
|
||||
@param path_nfo: ruta absoluta al archivo nfo
|
||||
Method to read nfo files.
|
||||
Nfo files have the following structure: url_scraper | xml + item_json [url_scraper] and [xml] are optional, but only one of them must always exist.
|
||||
@param path_nfo: absolute path to nfo file
|
||||
@type path_nfo: str
|
||||
@param item: Si se pasa este parametro el item devuelto sera una copia de este con
|
||||
los valores de 'infoLabels', 'library_playcounts' y 'path' leidos del nfo
|
||||
@param item: If this parameter is passed the returned item will be a copy of it with the values of 'infoLabels', 'library_playcounts' and 'path' read from the nfo
|
||||
@type: Item
|
||||
@return: Una tupla formada por la cabecera (head_nfo ='url_scraper'|'xml') y el objeto 'item_json'
|
||||
@return: A tuple consisting of the header (head_nfo = 'url_scraper' | 'xml') and the object 'item_json'
|
||||
@rtype: tuple (str, Item)
|
||||
"""
|
||||
head_nfo = ""
|
||||
@@ -77,15 +68,15 @@ def read_nfo(path_nfo, item=None):
|
||||
|
||||
def save_movie(item, silent=False):
|
||||
"""
|
||||
guarda en la libreria de peliculas el elemento item, con los valores que contiene.
|
||||
saves the item element in the movie library, with the values it contains.
|
||||
@type item: item
|
||||
@param item: elemento que se va a guardar.
|
||||
@param item: item to be saved.
|
||||
@rtype insertados: int
|
||||
@return: el número de elementos insertados
|
||||
@return: the number of elements inserted
|
||||
@rtype sobreescritos: int
|
||||
@return: el número de elementos sobreescritos
|
||||
@return: the number of overwritten elements
|
||||
@rtype fallidos: int
|
||||
@return: el número de elementos fallidos o -1 si ha fallado todo
|
||||
@return: the number of failed items or -1 if all failed
|
||||
"""
|
||||
logger.info()
|
||||
# logger.debug(item.tostring('\n'))
|
||||
@@ -94,34 +85,32 @@ def save_movie(item, silent=False):
|
||||
fallidos = 0
|
||||
path = ""
|
||||
|
||||
# Itentamos obtener el titulo correcto:
|
||||
# 1. contentTitle: Este deberia ser el sitio correcto, ya que title suele contener "Añadir a la videoteca..."
|
||||
# We try to obtain the correct title:
|
||||
# 1. contentTitle: This should be the correct site, since the title usually contains "Add to the video library..."
|
||||
# 2. fulltitle
|
||||
# 3. title
|
||||
# if item.contentTitle: item.title = item.contentTitle
|
||||
# elif item.fulltitle: item.title = item.fulltitle
|
||||
|
||||
if not item.contentTitle:
|
||||
# Colocamos el titulo correcto en su sitio para que scraper lo localize
|
||||
# We put the correct title on your site so that scraper can locate it
|
||||
if item.fulltitle:
|
||||
item.contentTitle = item.fulltitle
|
||||
else:
|
||||
item.contentTitle = item.title
|
||||
|
||||
# Si llegados a este punto no tenemos titulo, salimos
|
||||
# If at this point we do not have a title, we leave
|
||||
if not item.contentTitle or not item.channel:
|
||||
logger.debug("contentTitle NOT FOUND")
|
||||
return 0, 0, -1, path # Salimos sin guardar
|
||||
|
||||
scraper_return = scraper.find_and_set_infoLabels(item)
|
||||
|
||||
# Llegados a este punto podemos tener:
|
||||
# scraper_return = True: Un item con infoLabels con la información actualizada de la peli
|
||||
# scraper_return = False: Un item sin información de la peli (se ha dado a cancelar en la ventana)
|
||||
# item.infoLabels['code'] == "" : No se ha encontrado el identificador de IMDB necesario para continuar, salimos
|
||||
# At this point we can have:
|
||||
# scraper_return = True: An item with infoLabels with the updated information of the movie
|
||||
# scraper_return = False: An item without movie information (it has been canceled in the window)
|
||||
# item.infoLabels['code'] == "" : The required IMDB identifier was not found to continue, we quit
|
||||
if not scraper_return or not item.infoLabels['code']:
|
||||
# TODO de momento si no hay resultado no añadimos nada,
|
||||
# aunq podriamos abrir un cuadro para introducir el identificador/nombre a mano
|
||||
logger.debug("NOT FOUND IN SCRAPER OR DO NOT HAVE code")
|
||||
return 0, 0, -1, path
|
||||
|
||||
@@ -153,7 +142,7 @@ def save_movie(item, silent=False):
|
||||
break
|
||||
|
||||
if not path:
|
||||
# Crear carpeta
|
||||
# Create folder
|
||||
path = filetools.join(MOVIES_PATH, ("%s [%s]" % (base_name, _id)).strip())
|
||||
logger.info("Creating movie directory:" + path)
|
||||
if not filetools.mkdir(path):
|
||||
@@ -169,7 +158,7 @@ def save_movie(item, silent=False):
|
||||
json_exists = filetools.exists(json_path)
|
||||
|
||||
if not nfo_exists:
|
||||
# Creamos .nfo si no existe
|
||||
# We create .nfo if it doesn't exist
|
||||
logger.info("Creating .nfo: " + nfo_path)
|
||||
head_nfo = scraper.get_nfo(item)
|
||||
|
||||
@@ -178,18 +167,18 @@ def save_movie(item, silent=False):
|
||||
library_urls={})
|
||||
|
||||
else:
|
||||
# Si existe .nfo, pero estamos añadiendo un nuevo canal lo abrimos
|
||||
# If .nfo exists, but we are adding a new channel we open it
|
||||
head_nfo, item_nfo = read_nfo(nfo_path)
|
||||
|
||||
if not strm_exists:
|
||||
# Crear base_name.strm si no existe
|
||||
# Create base_name.strm if you do not exist
|
||||
item_strm = Item(channel='videolibrary', action='play_from_library',
|
||||
strm_path=strm_path.replace(MOVIES_PATH, ""), contentType='movie',
|
||||
contentTitle=item.contentTitle)
|
||||
strm_exists = filetools.write(strm_path, '%s?%s' % (addon_name, item_strm.tourl()))
|
||||
item_nfo.strm_path = strm_path.replace(MOVIES_PATH, "")
|
||||
|
||||
# Solo si existen item_nfo y .strm continuamos
|
||||
# Only if item_nfo and .strm exist we continue
|
||||
if item_nfo and strm_exists:
|
||||
|
||||
if json_exists:
|
||||
@@ -198,7 +187,7 @@ def save_movie(item, silent=False):
|
||||
else:
|
||||
insertados += 1
|
||||
|
||||
# Si se ha marcado la opción de url de emergencia, se añade ésta a la película después de haber ejecutado Findvideos del canal
|
||||
# If the emergency url option has been checked, it is added to the movie after running Findvideos from the channel
|
||||
try:
|
||||
headers = {}
|
||||
if item.headers:
|
||||
@@ -221,7 +210,7 @@ def save_movie(item, silent=False):
|
||||
|
||||
if filetools.write(nfo_path, head_nfo + item_nfo.tojson()):
|
||||
#logger.info("FOLDER_MOVIES : %s" % FOLDER_MOVIES)
|
||||
# actualizamos la videoteca de Kodi con la pelicula
|
||||
# We update the Kodi video library with the movie
|
||||
if config.is_xbmc() and config.get_setting("videolibrary_kodi") and not silent:
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.update()
|
||||
@@ -229,7 +218,7 @@ def save_movie(item, silent=False):
|
||||
if not silent: p_dialog.close()
|
||||
return insertados, sobreescritos, fallidos, path
|
||||
|
||||
# Si llegamos a este punto es por q algo ha fallado
|
||||
# If we get to this point it is because something has gone wrong
|
||||
logger.error("Could not save %s in the video library" % item.contentTitle)
|
||||
if not silent:
|
||||
p_dialog.update(100, config.get_localized_string(60063), item.contentTitle)
|
||||
@@ -423,37 +412,35 @@ def filter_list(episodelist, action=None, path=None):
|
||||
|
||||
def save_tvshow(item, episodelist, silent=False):
|
||||
"""
|
||||
guarda en la libreria de series la serie con todos los capitulos incluidos en la lista episodelist
|
||||
stores in the series library the series with all the chapters included in the episodelist
|
||||
@type item: item
|
||||
@param item: item que representa la serie a guardar
|
||||
@param item: item that represents the series to save
|
||||
@type episodelist: list
|
||||
@param episodelist: listado de items que representan los episodios que se van a guardar.
|
||||
@param episodelist: list of items that represent the episodes to be saved.
|
||||
@rtype insertados: int
|
||||
@return: el número de episodios insertados
|
||||
@return: the number of episodes inserted
|
||||
@rtype sobreescritos: int
|
||||
@return: el número de episodios sobreescritos
|
||||
@return: the number of overwritten episodes
|
||||
@rtype fallidos: int
|
||||
@return: el número de episodios fallidos o -1 si ha fallado toda la serie
|
||||
@return: the number of failed episodes or -1 if the entire series has failed
|
||||
@rtype path: str
|
||||
@return: directorio serie
|
||||
@return: serial directory
|
||||
"""
|
||||
logger.info()
|
||||
# logger.debug(item.tostring('\n'))
|
||||
path = ""
|
||||
|
||||
# Si llegados a este punto no tenemos titulo o code, salimos
|
||||
# If at this point we do not have a title or code, we leave
|
||||
if not (item.contentSerieName or item.infoLabels['code']) or not item.channel:
|
||||
logger.debug("NOT FOUND contentSerieName or code")
|
||||
return 0, 0, -1, path # Salimos sin guardar
|
||||
|
||||
scraper_return = scraper.find_and_set_infoLabels(item)
|
||||
# Llegados a este punto podemos tener:
|
||||
# scraper_return = True: Un item con infoLabels con la información actualizada de la serie
|
||||
# scraper_return = False: Un item sin información de la peli (se ha dado a cancelar en la ventana)
|
||||
# item.infoLabels['code'] == "" : No se ha encontrado el identificador de IMDB necesario para continuar, salimos
|
||||
# At this point we can have:
|
||||
# scraper_return = True: An item with infoLabels with the updated information of the series
|
||||
# scraper_return = False: An item without movie information (it has been canceled in the window)
|
||||
# item.infoLabels['code'] == "" :T he required IMDB identifier was not found to continue, we quit
|
||||
if not scraper_return or not item.infoLabels['code']:
|
||||
# TODO de momento si no hay resultado no añadimos nada,
|
||||
# aunq podriamos abrir un cuadro para introducir el identificador/nombre a mano
|
||||
logger.debug("NOT FOUND IN SCRAPER OR DO NOT HAVE code")
|
||||
return 0, 0, -1, path
|
||||
|
||||
@@ -464,8 +451,7 @@ def save_tvshow(item, episodelist, silent=False):
|
||||
elif item.infoLabels['code'][2] and item.infoLabels['code'][2] != 'None':
|
||||
_id = item.infoLabels['code'][2]
|
||||
else:
|
||||
logger.error("NO ENCONTRADO EN SCRAPER O NO TIENE code: " + item.url
|
||||
+ ' / ' + item.infoLabels['code'])
|
||||
logger.error("NOT FOUND IN SCRAPER OR HAS NO CODE: " + item.url + ' / ' + item.infoLabels['code'])
|
||||
return 0, 0, -1, path
|
||||
|
||||
if config.get_setting("original_title_folder", "videolibrary") and item.infoLabels['originaltitle']:
|
||||
@@ -504,7 +490,7 @@ def save_tvshow(item, episodelist, silent=False):
|
||||
|
||||
tvshow_path = filetools.join(path, "tvshow.nfo")
|
||||
if not filetools.exists(tvshow_path):
|
||||
# Creamos tvshow.nfo, si no existe, con la head_nfo, info de la serie y marcas de episodios vistos
|
||||
# We create tvshow.nfo, if it does not exist, with the head_nfo, series info and watched episode marks
|
||||
logger.info("Creating tvshow.nfo: " + tvshow_path)
|
||||
head_nfo = scraper.get_nfo(item)
|
||||
item.infoLabels['mediatype'] = "tvshow"
|
||||
@@ -516,7 +502,7 @@ def save_tvshow(item, episodelist, silent=False):
|
||||
item_tvshow.library_urls = {item.channel: item.url}
|
||||
|
||||
else:
|
||||
# Si existe tvshow.nfo, pero estamos añadiendo un nuevo canal actualizamos el listado de urls
|
||||
# If tvshow.nfo exists, but we are adding a new channel we update the list of urls
|
||||
head_nfo, item_tvshow = read_nfo(tvshow_path)
|
||||
item_tvshow.fulltitle = item.fulltitle
|
||||
item_tvshow.channel = "videolibrary"
|
||||
@@ -524,15 +510,15 @@ def save_tvshow(item, episodelist, silent=False):
|
||||
item_tvshow.library_urls[item.channel] = item.url
|
||||
|
||||
# FILTERTOOLS
|
||||
# si el canal tiene filtro de idiomas, añadimos el canal y el show
|
||||
# if the channel has a language filter, we add the channel and the show
|
||||
if episodelist and "list_language" in episodelist[0]:
|
||||
# si ya hemos añadido un canal previamente con filtro, añadimos o actualizamos el canal y show
|
||||
# if we have already added a previously filtered channel, we add or update the channel and show
|
||||
if "library_filter_show" in item_tvshow:
|
||||
if item.title_from_channel:
|
||||
item_tvshow.library_filter_show[item.channel] = item.title_from_channel
|
||||
else:
|
||||
item_tvshow.library_filter_show[item.channel] = item.show
|
||||
# no habia ningún canal con filtro y lo generamos por primera vez
|
||||
# there was no filter channel and we generated it for the first time
|
||||
else:
|
||||
if item.title_from_channel:
|
||||
item_tvshow.library_filter_show = {item.channel: item.title_from_channel}
|
||||
@@ -540,15 +526,15 @@ def save_tvshow(item, episodelist, silent=False):
|
||||
item_tvshow.library_filter_show = {item.channel: item.show}
|
||||
|
||||
if item.channel != "downloads":
|
||||
item_tvshow.active = 1 # para que se actualice a diario cuando se llame a service
|
||||
item_tvshow.active = 1 # to be updated daily when service is called
|
||||
|
||||
filetools.write(tvshow_path, head_nfo + item_tvshow.tojson())
|
||||
|
||||
if not episodelist:
|
||||
# La lista de episodios esta vacia
|
||||
# The episode list is empty
|
||||
return 0, 0, 0, path
|
||||
|
||||
# Guardar los episodios
|
||||
# Save the episodes
|
||||
'''import time
|
||||
start_time = time.time()'''
|
||||
insertados, sobreescritos, fallidos = save_episodes(path, episodelist, item, silent=silent)
|
||||
@@ -561,27 +547,27 @@ def save_tvshow(item, episodelist, silent=False):
|
||||
|
||||
def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
"""
|
||||
guarda en la ruta indicada todos los capitulos incluidos en la lista episodelist
|
||||
saves in the indicated path all the chapters included in the episodelist
|
||||
@type path: str
|
||||
@param path: ruta donde guardar los episodios
|
||||
@param path: path to save the episodes
|
||||
@type episodelist: list
|
||||
@param episodelist: listado de items que representan los episodios que se van a guardar.
|
||||
@param episodelist: list of items that represent the episodes to be saved.
|
||||
@type serie: item
|
||||
@param serie: serie de la que se van a guardar los episodios
|
||||
@param serie: series from which to save the episodes
|
||||
@type silent: bool
|
||||
@param silent: establece si se muestra la notificación
|
||||
@param overwrite: permite sobreescribir los ficheros existentes
|
||||
@param silent: sets whether notification is displayed
|
||||
@param overwrite: allows to overwrite existing files
|
||||
@type overwrite: bool
|
||||
@rtype insertados: int
|
||||
@return: el número de episodios insertados
|
||||
@return: the number of episodes inserted
|
||||
@rtype sobreescritos: int
|
||||
@return: el número de episodios sobreescritos
|
||||
@return: the number of overwritten episodes
|
||||
@rtype fallidos: int
|
||||
@return: el número de episodios fallidos
|
||||
@return: the number of failed episodes
|
||||
"""
|
||||
logger.info()
|
||||
episodelist = filter_list(episodelist, serie.action, path)
|
||||
# No hay lista de episodios, no hay nada que guardar
|
||||
# No episode list, nothing to save
|
||||
if not len(episodelist):
|
||||
logger.info("There is no episode list, we go out without creating strm")
|
||||
return 0, 0, 0
|
||||
@@ -606,27 +592,27 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
sobreescritos = 0
|
||||
fallidos = 0
|
||||
news_in_playcounts = {}
|
||||
# Listamos todos los ficheros de la serie, asi evitamos tener que comprobar si existe uno por uno
|
||||
# We list all the files in the series, so we avoid having to check if they exist one by one
|
||||
raiz, carpetas_series, ficheros = next(filetools.walk(path))
|
||||
ficheros = [filetools.join(path, f) for f in ficheros]
|
||||
|
||||
# Silent es para no mostrar progreso (para service)
|
||||
# Silent is to show no progress (for service)
|
||||
if not silent:
|
||||
# progress dialog
|
||||
p_dialog = platformtools.dialog_progress(config.get_localized_string(20000), config.get_localized_string(60064))
|
||||
p_dialog.update(0, config.get_localized_string(60065))
|
||||
|
||||
channel_alt = generictools.verify_channel(serie.channel) #Preparamos para añadir las urls de emergencia
|
||||
emergency_urls_stat = config.get_setting("emergency_urls", channel_alt) #El canal quiere urls de emergencia?
|
||||
channel_alt = generictools.verify_channel(serie.channel) # We prepare to add the emergency urls
|
||||
emergency_urls_stat = config.get_setting("emergency_urls", channel_alt) # Does the channel want emergency urls?
|
||||
emergency_urls_succ = False
|
||||
try: channel = __import__('specials.%s' % channel_alt, fromlist=["specials.%s" % channel_alt])
|
||||
except: channel = __import__('channels.%s' % channel_alt, fromlist=["channels.%s" % channel_alt])
|
||||
if serie.torrent_caching_fail: #Si el proceso de conversión ha fallado, no se cachean
|
||||
if serie.torrent_caching_fail: # If the conversion process has failed, they are not cached
|
||||
emergency_urls_stat = 0
|
||||
del serie.torrent_caching_fail
|
||||
|
||||
new_episodelist = []
|
||||
# Obtenemos el numero de temporada y episodio y descartamos los q no lo sean
|
||||
# We obtain the season and episode number and discard those that are not
|
||||
|
||||
for e in episodelist:
|
||||
headers = {}
|
||||
@@ -636,52 +622,52 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
try:
|
||||
season_episode = scrapertools.get_season_and_episode(e.title)
|
||||
|
||||
# Si se ha marcado la opción de url de emergencia, se añade ésta a cada episodio después de haber ejecutado Findvideos del canal
|
||||
if e.emergency_urls and isinstance(e.emergency_urls, dict): del e.emergency_urls #Borramos trazas anteriores
|
||||
json_path = filetools.join(path, ("%s [%s].json" % (season_episode, e.channel)).lower()) #Path del .json del episodio
|
||||
if emergency_urls_stat == 1 and not e.emergency_urls and e.contentType == 'episode': #Guardamos urls de emergencia?
|
||||
# If the emergency url option has been checked, it is added to each episode after running Findvideos from the channel
|
||||
if e.emergency_urls and isinstance(e.emergency_urls, dict): del e.emergency_urls # We erase previous traces
|
||||
json_path = filetools.join(path, ("%s [%s].json" % (season_episode, e.channel)).lower()) # Path of the episode .json
|
||||
if emergency_urls_stat == 1 and not e.emergency_urls and e.contentType == 'episode': # Do we keep emergency urls?
|
||||
if not silent:
|
||||
p_dialog.update(0, 'Cacheando enlaces y archivos .torrent...', e.title) #progress dialog
|
||||
if json_path in ficheros: #Si existe el .json sacamos de ahí las urls
|
||||
if overwrite: #pero solo si se se sobrescriben los .json
|
||||
json_epi = Item().fromjson(filetools.read(json_path)) #Leemos el .json
|
||||
if json_epi.emergency_urls: #si existen las urls de emergencia...
|
||||
e.emergency_urls = json_epi.emergency_urls #... las copiamos
|
||||
else: #y si no...
|
||||
e = emergency_urls(e, channel, json_path, headers=headers) #... las generamos
|
||||
p_dialog.update(0, 'Caching links and .torren filest...', e.title) # progress dialog
|
||||
if json_path in ficheros: # If there is the .json we get the urls from there
|
||||
if overwrite: # but only if .json are overwritten
|
||||
json_epi = Item().fromjson(filetools.read(json_path)) #We read the .json
|
||||
if json_epi.emergency_urls: # if there are emergency urls ...
|
||||
e.emergency_urls = json_epi.emergency_urls # ... we copy them
|
||||
else: # if not...
|
||||
e = emergency_urls(e, channel, json_path, headers=headers) # ... we generate them
|
||||
else:
|
||||
e = emergency_urls(e, channel, json_path, headers=headers) #Si el episodio no existe, generamos las urls
|
||||
if e.emergency_urls: #Si ya tenemos urls...
|
||||
emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo
|
||||
elif emergency_urls_stat == 2 and e.contentType == 'episode': #Borramos urls de emergencia?
|
||||
e = emergency_urls(e, channel, json_path, headers=headers) # If the episode does not exist, we generate the urls
|
||||
if e.emergency_urls: #If we already have urls...
|
||||
emergency_urls_succ = True # ... is a success and we are going to mark the .nfo
|
||||
elif emergency_urls_stat == 2 and e.contentType == 'episode': # Do we delete emergency urls?
|
||||
if e.emergency_urls: del e.emergency_urls
|
||||
emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo
|
||||
elif emergency_urls_stat == 3 and e.contentType == 'episode': #Actualizamos urls de emergencia?
|
||||
emergency_urls_succ = True # ... is a success and we are going to mark the .nfo
|
||||
elif emergency_urls_stat == 3 and e.contentType == 'episode': # Do we update emergency urls?
|
||||
if not silent:
|
||||
p_dialog.update(0, 'Cacheando enlaces y archivos .torrent...', e.title) #progress dialog
|
||||
e = emergency_urls(e, channel, json_path, headers=headers) #generamos las urls
|
||||
if e.emergency_urls: #Si ya tenemos urls...
|
||||
emergency_urls_succ = True #... es un éxito y vamos a marcar el .nfo
|
||||
p_dialog.update(0, 'Caching links and .torrent files...', e.title) # progress dialog
|
||||
e = emergency_urls(e, channel, json_path, headers=headers) # we generate the urls
|
||||
if e.emergency_urls: # If we already have urls...
|
||||
emergency_urls_succ = True # ... is a success and we are going to mark the .nfo
|
||||
|
||||
if not e.infoLabels["tmdb_id"] or (serie.infoLabels["tmdb_id"] and e.infoLabels["tmdb_id"] != serie.infoLabels["tmdb_id"]): #en series multicanal, prevalece el infolabels...
|
||||
e.infoLabels = serie.infoLabels #... del canal actual y no el del original
|
||||
e.infoLabels = serie.infoLabels # ... dthe current channel and not the original one
|
||||
e.contentSeason, e.contentEpisodeNumber = season_episode.split("x")
|
||||
if e.videolibray_emergency_urls:
|
||||
del e.videolibray_emergency_urls
|
||||
if e.channel_redir:
|
||||
del e.channel_redir #... y se borran las marcas de redirecciones
|
||||
del e.channel_redir # ... and redirect marks are erased
|
||||
new_episodelist.append(e)
|
||||
except:
|
||||
if e.contentType == 'episode':
|
||||
logger.error("Unable to save %s emergency urls in the video library" % e.contentTitle)
|
||||
continue
|
||||
|
||||
# No hay lista de episodios, no hay nada que guardar
|
||||
# No episode list, nothing to save
|
||||
if not len(new_episodelist):
|
||||
logger.info("There is no episode list, we go out without creating strm")
|
||||
return 0, 0, 0
|
||||
|
||||
# fix float porque la division se hace mal en python 2.x
|
||||
# fix float because division is done poorly in python 2.x
|
||||
try:
|
||||
t = float(100) / len(new_episodelist)
|
||||
except:
|
||||
@@ -718,9 +704,8 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
json_exists = json_path in ficheros
|
||||
|
||||
if not strm_exists:
|
||||
# Si no existe season_episode.strm añadirlo
|
||||
item_strm = Item(action='play_from_library', channel='videolibrary',
|
||||
strm_path=strm_path.replace(TVSHOWS_PATH, ""), infoLabels={})
|
||||
# If there is no season_episode.strm add it
|
||||
item_strm = Item(action='play_from_library', channel='videolibrary', strm_path=strm_path.replace(TVSHOWS_PATH, ""), infoLabels={})
|
||||
item_strm.contentSeason = e.contentSeason
|
||||
item_strm.contentEpisodeNumber = e.contentEpisodeNumber
|
||||
item_strm.contentType = e.contentType
|
||||
@@ -728,7 +713,7 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
|
||||
# FILTERTOOLS
|
||||
if item_strm.list_language:
|
||||
# si tvshow.nfo tiene filtro se le pasa al item_strm que se va a generar
|
||||
# if tvshow.nfo has a filter it is passed to the item_strm to be generated
|
||||
if "library_filter_show" in serie:
|
||||
item_strm.library_filter_show = serie.library_filter_show
|
||||
|
||||
@@ -741,38 +726,36 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
|
||||
item_nfo = None
|
||||
if not nfo_exists and e.infoLabels["code"]:
|
||||
# Si no existe season_episode.nfo añadirlo
|
||||
# If there is no season_episode.nfo add it
|
||||
scraper.find_and_set_infoLabels(e)
|
||||
head_nfo = scraper.get_nfo(e)
|
||||
|
||||
item_nfo = e.clone(channel="videolibrary", url="", action='findvideos',
|
||||
strm_path=strm_path.replace(TVSHOWS_PATH, ""))
|
||||
item_nfo = e.clone(channel="videolibrary", url="", action='findvideos', strm_path=strm_path.replace(TVSHOWS_PATH, ""))
|
||||
if item_nfo.emergency_urls:
|
||||
del item_nfo.emergency_urls #Solo se mantiene en el .json del episodio
|
||||
del item_nfo.emergency_urls # It only stays in the episode's .json
|
||||
|
||||
nfo_exists = filetools.write(nfo_path, head_nfo + item_nfo.tojson())
|
||||
|
||||
# Solo si existen season_episode.nfo y season_episode.strm continuamos
|
||||
# Only if there are season_episode.nfo and season_episode.strm we continue
|
||||
if nfo_exists and strm_exists:
|
||||
if not json_exists or overwrite:
|
||||
# Obtenemos infoLabel del episodio
|
||||
# We get infoLabel from the episode
|
||||
if not item_nfo:
|
||||
head_nfo, item_nfo = read_nfo(nfo_path)
|
||||
|
||||
# En series multicanal, prevalece el infolabels del canal actual y no el del original
|
||||
if not e.infoLabels["tmdb_id"] or (item_nfo.infoLabels["tmdb_id"] \
|
||||
and e.infoLabels["tmdb_id"] != item_nfo.infoLabels["tmdb_id"]):
|
||||
# In multichannel series, the infolabels of the current channel prevail and not that of the original
|
||||
if not e.infoLabels["tmdb_id"] or (item_nfo.infoLabels["tmdb_id"] and e.infoLabels["tmdb_id"] != item_nfo.infoLabels["tmdb_id"]):
|
||||
e.infoLabels = item_nfo.infoLabels
|
||||
|
||||
if filetools.write(json_path, e.tojson()):
|
||||
if not json_exists:
|
||||
logger.info("Inserted: %s" % json_path)
|
||||
insertados += 1
|
||||
# Marcamos episodio como no visto
|
||||
# We mark episode as unseen
|
||||
news_in_playcounts[season_episode] = 0
|
||||
# Marcamos la temporada como no vista
|
||||
# We mark the season as unseen
|
||||
news_in_playcounts["season %s" % e.contentSeason] = 0
|
||||
# Marcamos la serie como no vista
|
||||
# We mark the series as unseen
|
||||
# logger.debug("serie " + serie.tostring('\n'))
|
||||
news_in_playcounts[serie.contentSerieName] = 0
|
||||
|
||||
@@ -796,25 +779,25 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
p_dialog.close()
|
||||
|
||||
if news_in_playcounts or emergency_urls_succ or serie.infoLabels["status"] == "Ended" or serie.infoLabels["status"] == "Canceled":
|
||||
# Si hay nuevos episodios los marcamos como no vistos en tvshow.nfo ...
|
||||
# If there are new episodes we mark them as unseen on tvshow.nfo ...
|
||||
tvshow_path = filetools.join(path, "tvshow.nfo")
|
||||
try:
|
||||
import datetime
|
||||
head_nfo, tvshow_item = read_nfo(tvshow_path)
|
||||
tvshow_item.library_playcounts.update(news_in_playcounts)
|
||||
|
||||
#Si la operación de insertar/borrar urls de emergencia en los .jsons de los episodios ha tenido éxito, se marca el .nfo
|
||||
# If the emergency url insert / delete operation in the .jsons of the episodes was successful, the .nfo is checked
|
||||
if emergency_urls_succ:
|
||||
if tvshow_item.emergency_urls and not isinstance(tvshow_item.emergency_urls, dict):
|
||||
del tvshow_item.emergency_urls
|
||||
if emergency_urls_stat in [1, 3]: #Operación de guardar/actualizar enlaces
|
||||
if emergency_urls_stat in [1, 3]: # Save / update links operation
|
||||
if not tvshow_item.emergency_urls:
|
||||
tvshow_item.emergency_urls = dict()
|
||||
if tvshow_item.library_urls.get(serie.channel, False):
|
||||
tvshow_item.emergency_urls.update({serie.channel: True})
|
||||
elif emergency_urls_stat == 2: #Operación de Borrar enlaces
|
||||
elif emergency_urls_stat == 2: # Delete links operation
|
||||
if tvshow_item.emergency_urls and tvshow_item.emergency_urls.get(serie.channel, False):
|
||||
tvshow_item.emergency_urls.pop(serie.channel, None) #borramos la entrada del .nfo
|
||||
tvshow_item.emergency_urls.pop(serie.channel, None) # delete the entry of the .nfo
|
||||
|
||||
if tvshow_item.active == 30:
|
||||
tvshow_item.active = 1
|
||||
@@ -822,12 +805,9 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
tvshow_item.infoLabels = serie.infoLabels
|
||||
tvshow_item.infoLabels["title"] = tvshow_item.infoLabels["tvshowtitle"]
|
||||
|
||||
if max_sea == high_sea and max_epi == high_epi and (tvshow_item.infoLabels["status"] == "Ended"
|
||||
or tvshow_item.infoLabels["status"] == "Canceled") and insertados == 0 and fallidos == 0 \
|
||||
and not tvshow_item.local_episodes_path:
|
||||
tvshow_item.active = 0 # ... no la actualizaremos más
|
||||
logger.debug("%s [%s]: serie 'Terminada' o 'Cancelada'. Se desactiva la actualización periódica" % \
|
||||
(serie.contentSerieName, serie.channel))
|
||||
if max_sea == high_sea and max_epi == high_epi and (tvshow_item.infoLabels["status"] == "Ended" or tvshow_item.infoLabels["status"] == "Canceled") and insertados == 0 and fallidos == 0 and not tvshow_item.local_episodes_path:
|
||||
tvshow_item.active = 0 # ... nor we will update it more
|
||||
logger.debug("%s [%s]: 'Finished' or 'Canceled' series. Periodic update is disabled" % (serie.contentSerieName, serie.channel))
|
||||
|
||||
update_last = datetime.date.today()
|
||||
tvshow_item.update_last = update_last.strftime('%Y-%m-%d')
|
||||
@@ -841,7 +821,7 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
logger.error(traceback.format_exc())
|
||||
fallidos = -1
|
||||
else:
|
||||
# ... si ha sido correcto actualizamos la videoteca de Kodi
|
||||
# ... if it was correct we update the Kodi video library
|
||||
if config.is_xbmc() and config.get_setting("videolibrary_kodi") and not silent:
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.update()
|
||||
@@ -849,8 +829,7 @@ def save_episodes(path, episodelist, serie, silent=False, overwrite=True):
|
||||
if fallidos == len(episodelist):
|
||||
fallidos = -1
|
||||
|
||||
logger.debug("%s [%s]: inserted= %s, overwritten= %s, failed= %s" %
|
||||
(serie.contentSerieName, serie.channel, insertados, sobreescritos, fallidos))
|
||||
logger.debug("%s [%s]: inserted= %s, overwritten= %s, failed= %s" % (serie.contentSerieName, serie.channel, insertados, sobreescritos, fallidos))
|
||||
return insertados, sobreescritos, fallidos
|
||||
|
||||
|
||||
@@ -924,65 +903,63 @@ def process_local_episodes(local_episodes_path, path):
|
||||
|
||||
def add_movie(item):
|
||||
"""
|
||||
guarda una pelicula en la libreria de cine. La pelicula puede ser un enlace dentro de un canal o un video
|
||||
descargado previamente.
|
||||
Keep a movie at the movie library. The movie can be a link within a channel or a previously downloaded video.
|
||||
|
||||
Para añadir episodios descargados en local, el item debe tener exclusivamente:
|
||||
- contentTitle: titulo de la pelicula
|
||||
- title: titulo a mostrar junto al listado de enlaces -findvideos- ("Reproducir video local HD")
|
||||
- infoLabels["tmdb_id"] o infoLabels["imdb_id"]
|
||||
To add locally downloaded episodes, the item must have exclusively:
|
||||
- contentTitle: title of the movie
|
||||
- title: title to show next to the list of links -findvideos- ("Play local HD video")
|
||||
- infoLabels ["tmdb_id"] o infoLabels ["imdb_id"]
|
||||
- contentType == "movie"
|
||||
- channel = "downloads"
|
||||
- url : ruta local al video
|
||||
- url: local path to the video
|
||||
|
||||
@type item: item
|
||||
@param item: elemento que se va a guardar.
|
||||
@param item: item to be saved.
|
||||
"""
|
||||
logger.info()
|
||||
|
||||
#Para desambiguar títulos, se provoca que TMDB pregunte por el título realmente deseado
|
||||
#El usuario puede seleccionar el título entre los ofrecidos en la primera pantalla
|
||||
#o puede cancelar e introducir un nuevo título en la segunda pantalla
|
||||
#Si lo hace en "Introducir otro nombre", TMDB buscará automáticamente el nuevo título
|
||||
#Si lo hace en "Completar Información", cambia parcialmente al nuevo título, pero no busca en TMDB. Hay que hacerlo
|
||||
#Si se cancela la segunda pantalla, la variable "scraper_return" estará en False. El usuario no quiere seguir
|
||||
# To disambiguate titles, TMDB is caused to ask for the really desired title
|
||||
# The user can select the title among those offered on the first screen
|
||||
# or you can cancel and enter a new title on the second screen
|
||||
# If you do it in "Enter another name", TMDB will automatically search for the new title
|
||||
# If you do it in "Complete Information", it partially changes to the new title, but does not search TMDB. We have to do it
|
||||
# If the second screen is canceled, the variable "scraper_return" will be False. The user does not want to continue
|
||||
|
||||
item = generictools.update_title(item) #Llamamos al método que actualiza el título con tmdb.find_and_set_infoLabels
|
||||
item = generictools.update_title(item) # We call the method that updates the title with tmdb.find_and_set_infoLabels
|
||||
#if item.tmdb_stat:
|
||||
# del item.tmdb_stat #Limpiamos el status para que no se grabe en la Videoteca
|
||||
# del item.tmdb_stat # We clean the status so that it is not recorded in the Video Library
|
||||
|
||||
new_item = item.clone(action="findvideos")
|
||||
insertados, sobreescritos, fallidos, path = save_movie(new_item)
|
||||
|
||||
if fallidos == 0:
|
||||
platformtools.dialog_ok(config.get_localized_string(30131),
|
||||
config.get_localized_string(30135) % new_item.contentTitle) # 'se ha añadido a la videoteca'
|
||||
config.get_localized_string(30135) % new_item.contentTitle) # 'has been added to the video library'
|
||||
else:
|
||||
filetools.rmdirtree(path)
|
||||
platformtools.dialog_ok(config.get_localized_string(30131),
|
||||
config.get_localized_string(60066) % new_item.contentTitle) #"ERROR, la pelicula NO se ha añadido a la videoteca")
|
||||
config.get_localized_string(60066) % new_item.contentTitle) # "ERROR, the movie has NOT been added to the video library")
|
||||
|
||||
|
||||
def add_tvshow(item, channel=None):
|
||||
"""
|
||||
Guarda contenido en la libreria de series. Este contenido puede ser uno de estos dos:
|
||||
- La serie con todos los capitulos incluidos en la lista episodelist.
|
||||
- Un solo capitulo descargado previamente en local.
|
||||
Save content in the series library. This content can be one of these two:
|
||||
- The series with all the chapters included in the episodelist.
|
||||
- A single chapter previously downloaded locally.
|
||||
|
||||
Para añadir episodios descargados en local, el item debe tener exclusivamente:
|
||||
- contentSerieName (o show): Titulo de la serie
|
||||
- contentTitle: titulo del episodio para extraer season_and_episode ("1x01 Piloto")
|
||||
- title: titulo a mostrar junto al listado de enlaces -findvideos- ("Reproducir video local")
|
||||
- infoLabels["tmdb_id"] o infoLabels["imdb_id"]
|
||||
To add locally downloaded episodes, the item must have exclusively:
|
||||
- contentSerieName (or show): Title of the series
|
||||
- contentTitle: title of the episode to extract season_and_episode ("1x01 Pilot")
|
||||
- title: title to show next to the list of links -findvideos- ("Play local video")
|
||||
- infoLabels ["tmdb_id"] o infoLabels ["imdb_id"]
|
||||
- contentType != "movie"
|
||||
- channel = "downloads"
|
||||
- url : ruta local al video
|
||||
- url: local path to the video
|
||||
|
||||
@type item: item
|
||||
@param item: item que representa la serie a guardar
|
||||
@param item: item that represents the series to save
|
||||
@type channel: modulo
|
||||
@param channel: canal desde el que se guardara la serie.
|
||||
Por defecto se importara item.from_channel o item.channel
|
||||
@param channel: channel from which the series will be saved. By default, item.from_channel or item.channel will be imported.
|
||||
|
||||
"""
|
||||
logger.info("show=#" + item.show + "#")
|
||||
@@ -991,7 +968,7 @@ def add_tvshow(item, channel=None):
|
||||
itemlist = [item.clone()]
|
||||
|
||||
else:
|
||||
# Esta marca es porque el item tiene algo más aparte en el atributo "extra"
|
||||
# This mark is because the item has something else apart in the "extra" attribute
|
||||
item.action = item.extra if item.extra else item.action
|
||||
if isinstance(item.extra, str) and "###" in item.extra:
|
||||
item.action = item.extra.split("###")[0]
|
||||
@@ -1009,18 +986,18 @@ def add_tvshow(item, channel=None):
|
||||
except ImportError:
|
||||
exec("import channels." + item.channel + " as channel")
|
||||
|
||||
#Para desambiguar títulos, se provoca que TMDB pregunte por el título realmente deseado
|
||||
#El usuario puede seleccionar el título entre los ofrecidos en la primera pantalla
|
||||
#o puede cancelar e introducir un nuevo título en la segunda pantalla
|
||||
#Si lo hace en "Introducir otro nombre", TMDB buscará automáticamente el nuevo título
|
||||
#Si lo hace en "Completar Información", cambia parcialmente al nuevo título, pero no busca en TMDB. Hay que hacerlo
|
||||
#Si se cancela la segunda pantalla, la variable "scraper_return" estará en False. El usuario no quiere seguir
|
||||
# To disambiguate titles, TMDB is caused to ask for the really desired title
|
||||
# The user can select the title among those offered on the first screen
|
||||
# or you can cancel and enter a new title on the second screen
|
||||
# If you do it in "Enter another name", TMDB will automatically search for the new title
|
||||
# If you do it in "Complete Information", it partially changes to the new title, but does not search TMDB. We have to do it
|
||||
# If the second screen is canceled, the variable "scraper_return" will be False. The user does not want to continue
|
||||
|
||||
item = generictools.update_title(item) #Llamamos al método que actualiza el título con tmdb.find_and_set_infoLabels
|
||||
item = generictools.update_title(item) # We call the method that updates the title with tmdb.find_and_set_infoLabels
|
||||
#if item.tmdb_stat:
|
||||
# del item.tmdb_stat #Limpiamos el status para que no se grabe en la Videoteca
|
||||
# del item.tmdb_stat # We clean the status so that it is not recorded in the Video Library
|
||||
|
||||
# Obtiene el listado de episodios
|
||||
# Get the episode list
|
||||
|
||||
itemlist = getattr(channel, item.action)(item)
|
||||
if itemlist and not scrapertools.find_single_match(itemlist[0].title, r'(\d+.\d+)'):
|
||||
@@ -1040,34 +1017,34 @@ def add_tvshow(item, channel=None):
|
||||
if not insertados and not sobreescritos and not fallidos:
|
||||
filetools.rmdirtree(path)
|
||||
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60067) % item.show)
|
||||
logger.error("La serie %s no se ha podido añadir a la videoteca. No se ha podido obtener ningun episodio" % item.show)
|
||||
logger.error("The string %s could not be added to the video library. Could not get any episode" % item.show)
|
||||
|
||||
elif fallidos == -1:
|
||||
filetools.rmdirtree(path)
|
||||
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60068) % item.show)
|
||||
logger.error("La serie %s no se ha podido añadir a la videoteca" % item.show)
|
||||
logger.error("The string %s could not be added to the video library" % item.show)
|
||||
|
||||
elif fallidos == -2:
|
||||
filetools.rmdirtree(path)
|
||||
|
||||
elif fallidos > 0:
|
||||
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60069) % item.show)
|
||||
logger.error("No se han podido añadir %s episodios de la serie %s a la videoteca" % (fallidos, item.show))
|
||||
logger.error("Could not add %s episodes of series %s to the video library" % (fallidos, item.show))
|
||||
|
||||
else:
|
||||
platformtools.dialog_ok(config.get_localized_string(30131), config.get_localized_string(60070) % item.show)
|
||||
logger.info("Se han añadido %s episodios de la serie %s a la videoteca" % (insertados, item.show))
|
||||
logger.info("%s episodes of series %s have been added to the video library" % (insertados, item.show))
|
||||
if config.is_xbmc():
|
||||
if config.get_setting("sync_trakt_new_tvshow", "videolibrary"):
|
||||
import xbmc
|
||||
from platformcode import xbmc_videolibrary
|
||||
if config.get_setting("sync_trakt_new_tvshow_wait", "videolibrary"):
|
||||
# Comprobar que no se esta buscando contenido en la videoteca de Kodi
|
||||
# Check that you are not looking for content in the Kodi video library
|
||||
while xbmc.getCondVisibility('Library.IsScanningVideo()'):
|
||||
xbmc.sleep(1000)
|
||||
# Se lanza la sincronizacion para la videoteca de Kodi
|
||||
# Synchronization for Kodi video library launched
|
||||
xbmc_videolibrary.sync_trakt_kodi()
|
||||
# Se lanza la sincronización para la videoteca del addon
|
||||
# Synchronization for the addon video library is launched
|
||||
xbmc_videolibrary.sync_trakt_addon(path)
|
||||
|
||||
|
||||
@@ -1079,52 +1056,52 @@ def emergency_urls(item, channel=None, path=None, headers={}):
|
||||
magnet_caching_e = magnet_caching
|
||||
except:
|
||||
magnet_caching_e = True
|
||||
|
||||
"""
|
||||
Llamamos a Findvideos del canal con la variable "item.videolibray_emergency_urls = True" para obtener la variable
|
||||
"item.emergency_urls" con la lista de listas de tuplas de los enlaces torrent y de servidores directos para ese episodio o película
|
||||
En la lista [0] siempre deben ir los enlaces torrents, si los hay. Si se desea cachear los .torrents, la búsqueda va contra esa lista.
|
||||
En la lista dos irán los enlaces de servidores directos, pero también pueden ir enlaces magnet (que no son cacheables)
|
||||
|
||||
"""
|
||||
#lanazamos un "lookup" en el "findvideos" del canal para obtener los enlaces de emergencia
|
||||
We call Findvideos of the channel with the variable "item.videolibray_emergency_urls = True" to get the variable
|
||||
"item.emergency_urls" with the list of tuple lists of torrent links and direct servers for that episode or movie
|
||||
Torrents should always go in list [0], if any. If you want to cache the .torrents, the search goes against that list.
|
||||
List two will include direct server links, but also magnet links (which are not cacheable).
|
||||
"""
|
||||
# we launched a "lookup" in the "findvideos" of the channel to obtain the emergency links
|
||||
try:
|
||||
if channel == None: #Si el llamador no ha aportado la estructura de channel, se crea
|
||||
channel = generictools.verify_channel(item.channel) #Se verifica si es un clon, que devuelva "newpct1"
|
||||
if channel == None: # If the caller has not provided the channel structure, it is created
|
||||
channel = generictools.verify_channel(item.channel) # It is verified if it is a clone, which returns "newpct1"
|
||||
#channel = __import__('channels.%s' % channel, fromlist=["channels.%s" % channel])
|
||||
channel = __import__('specials.%s' % channel_alt, fromlist=["specials.%s" % channel_alt])
|
||||
if hasattr(channel, 'findvideos'): #Si el canal tiene "findvideos"...
|
||||
item.videolibray_emergency_urls = True #... se marca como "lookup"
|
||||
channel_save = item.channel #... guarda el canal original por si hay fail-over en Newpct1
|
||||
category_save = item.category #... guarda la categoría original por si hay fail-over o redirección en Newpct1
|
||||
if item.channel_redir: #... si hay un redir, se restaura temporamente el canal alternativo
|
||||
item.channel = scrapertools.find_single_match(item.url, 'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').lower()
|
||||
item.category = scrapertools.find_single_match(item.url, 'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').capitalize()
|
||||
item_res = getattr(channel, 'findvideos')(item) #... se procesa Findvideos
|
||||
item_res.channel = channel_save #... restaura el canal original por si hay fail-over en Newpct1
|
||||
item_res.category = category_save #... restaura la categoría original por si hay fail-over o redirección en Newpct1
|
||||
item.category = category_save #... restaura la categoría original por si hay fail-over o redirección en Newpct1
|
||||
del item_res.videolibray_emergency_urls #... y se borra la marca de lookup
|
||||
if hasattr(channel, 'findvideos'): # If the channel has "findvideos" ...
|
||||
item.videolibray_emergency_urls = True # ... marks itself as "lookup"
|
||||
channel_save = item.channel # ... save the original channel in case of fail-over in Newpct1
|
||||
category_save = item.category # ... save the original category in case of fail-over or redirection in Newpct1
|
||||
if item.channel_redir: # ... if there is a redir, the alternate channel is temporarily restored
|
||||
item.channel = scrapertools.find_single_match(item.url, r'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').lower()
|
||||
item.category = scrapertools.find_single_match(item.url, r'http.?\:\/\/(?:www.)?(\w+)\.\w+\/').capitalize()
|
||||
item_res = getattr(channel, 'findvideos')(item) # ... the process of Findvideos
|
||||
item_res.channel = channel_save # ... restore the original channel in case there is a fail-over in Newpct1
|
||||
item_res.category = category_save # ... restore the original category in case there is a fail-over or redirection in Newpct1
|
||||
item.category = category_save # ... restore the original category in case there is a fail-over or redirection in Newpct1
|
||||
del item_res.videolibray_emergency_urls # ... and the lookup mark is erased
|
||||
if item.videolibray_emergency_urls:
|
||||
del item.videolibray_emergency_urls #... y se borra la marca de lookup original
|
||||
del item.videolibray_emergency_urls # ... and the original lookup mark is erased
|
||||
except:
|
||||
logger.error('ERROR when processing the title in Findvideos del Canal: ' + item.channel + ' / ' + item.title)
|
||||
logger.error(traceback.format_exc())
|
||||
item.channel = channel_save #... restaura el canal original por si hay fail-over o redirección en Newpct1
|
||||
item.category = category_save #... restaura la categoría original por si hay fail-over o redirección en Newpct1
|
||||
item_res = item.clone() #Si ha habido un error, se devuelve el Item original
|
||||
item.channel = channel_save # ... restore the original channel in case of fail-over or redirection in Newpct1
|
||||
item.category = category_save # ... restore the original category in case there is a fail-over or redirection in Newpct1
|
||||
item_res = item.clone() # If there has been an error, the original Item is returned
|
||||
if item_res.videolibray_emergency_urls:
|
||||
del item_res.videolibray_emergency_urls #... y se borra la marca de lookup
|
||||
del item_res.videolibray_emergency_urls # ... and the lookup mark is erased
|
||||
if item.videolibray_emergency_urls:
|
||||
del item.videolibray_emergency_urls #... y se borra la marca de lookup original
|
||||
del item.videolibray_emergency_urls # ... and the original lookup mark is erased
|
||||
|
||||
#Si el usuario ha activado la opción "emergency_urls_torrents", se descargarán los archivos .torrent de cada título
|
||||
else: #Si se han cacheado con éxito los enlaces...
|
||||
# If the user has activated the option "emergency_urls_torrents", the .torrent files of each title will be downloaded
|
||||
else: # If the links have been successfully cached ...
|
||||
try:
|
||||
referer = None
|
||||
post = None
|
||||
channel_bis = generictools.verify_channel(item.channel)
|
||||
if config.get_setting("emergency_urls_torrents", channel_bis) and item_res.emergency_urls and path != None:
|
||||
videolibrary_path = config.get_videolibrary_path() #detectamos el path absoluto del título
|
||||
videolibrary_path = config.get_videolibrary_path() # we detect the absolute path of the title
|
||||
movies = config.get_setting("folder_movies")
|
||||
series = config.get_setting("folder_tvshows")
|
||||
if movies in path:
|
||||
@@ -1135,16 +1112,16 @@ def emergency_urls(item, channel=None, path=None, headers={}):
|
||||
i = 1
|
||||
if item_res.referer: referer = item_res.referer
|
||||
if item_res.post: post = item_res.post
|
||||
for url in item_res.emergency_urls[0]: #Recorremos las urls de emergencia...
|
||||
for url in item_res.emergency_urls[0]: # We go through the emergency urls ...
|
||||
torrents_path = re.sub(r'(?:\.\w+$)', '_%s.torrent' % str(i).zfill(2), path)
|
||||
path_real = ''
|
||||
if magnet_caching_e or not url.startswith('magnet'):
|
||||
path_real = torrent.caching_torrents(url, referer, post, torrents_path=torrents_path, headers=headers) #... para descargar los .torrents
|
||||
if path_real: #Si ha tenido éxito...
|
||||
item_res.emergency_urls[0][i-1] = path_real.replace(videolibrary_path, '') #se guarda el "path" relativo
|
||||
path_real = torrent.caching_torrents(url, referer, post, torrents_path=torrents_path, headers=headers) # ... to download the .torrents
|
||||
if path_real: # If you have been successful ...
|
||||
item_res.emergency_urls[0][i-1] = path_real.replace(videolibrary_path, '') # if it looks at the relative "path"
|
||||
i += 1
|
||||
|
||||
#Restauramos variables originales
|
||||
# We restore original variables
|
||||
if item.referer:
|
||||
item_res.referer = item.referer
|
||||
elif item_res.referer:
|
||||
@@ -1158,7 +1135,7 @@ def emergency_urls(item, channel=None, path=None, headers={}):
|
||||
except:
|
||||
logger.error('ERROR when caching the .torrent of: ' + item.channel + ' / ' + item.title)
|
||||
logger.error(traceback.format_exc())
|
||||
item_res = item.clone() #Si ha habido un error, se devuelve el Item original
|
||||
item_res = item.clone() # If there has been an error, the original Item is returned
|
||||
|
||||
#logger.debug(item_res.emergency_urls)
|
||||
return item_res #Devolvemos el Item actualizado con los enlaces de emergencia
|
||||
return item_res # We return the updated Item with the emergency links
|
||||
|
||||
+8
-11
@@ -17,8 +17,8 @@ from core import filetools
|
||||
|
||||
class ziptools(object):
|
||||
def extract(self, file, dir, folder_to_extract="", overwrite_question=False, backup=False):
|
||||
logger.info("file=%s" % file)
|
||||
logger.info("dir=%s" % dir)
|
||||
logger.info("file= %s" % file)
|
||||
logger.info("dir= %s" % dir)
|
||||
|
||||
if not dir.endswith(':') and not filetools.exists(dir):
|
||||
filetools.mkdir(dir)
|
||||
@@ -32,7 +32,7 @@ class ziptools(object):
|
||||
name = nameo.replace(':', '_').replace('<', '_').replace('>', '_').replace('|', '_').replace('"', '_').replace('?', '_').replace('*', '_')
|
||||
logger.info("name=%s" % nameo)
|
||||
if not name.endswith('/'):
|
||||
logger.info("no es un directorio")
|
||||
logger.info("it's not a directory")
|
||||
try:
|
||||
(path, filename) = filetools.split(filetools.join(dir, name))
|
||||
logger.info("path=%s" % path)
|
||||
@@ -53,31 +53,28 @@ class ziptools(object):
|
||||
try:
|
||||
if filetools.exists(outfilename) and overwrite_question:
|
||||
from platformcode import platformtools
|
||||
dyesno = platformtools.dialog_yesno("El archivo ya existe",
|
||||
"El archivo %s a descomprimir ya existe" \
|
||||
", ¿desea sobrescribirlo?" \
|
||||
% filetools.basename(outfilename))
|
||||
dyesno = platformtools.dialog_yesno("File already exists "," File %s to unzip already exists, do you want to overwrite it?" % filetools.basename(outfilename))
|
||||
if not dyesno:
|
||||
break
|
||||
if backup:
|
||||
import time
|
||||
hora_folder = "Copia seguridad [%s]" % time.strftime("%d-%m_%H-%M", time.localtime())
|
||||
hora_folder = "Backup [%s]" % time.strftime("%d-%m_%H-%M", time.localtime())
|
||||
backup = filetools.join(config.get_data_path(), 'backups', hora_folder, folder_to_extract)
|
||||
if not filetools.exists(backup):
|
||||
filetools.mkdir(backup)
|
||||
filetools.copy(outfilename, filetools.join(backup, filetools.basename(outfilename)))
|
||||
|
||||
if not filetools.write(outfilename, zf.read(nameo), silent=True, vfs=VFS): #TRUNCA en FINAL en Kodi 19 con VFS
|
||||
logger.error("Error en fichero " + nameo)
|
||||
logger.error("File error " + nameo)
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error("Error en fichero " + nameo)
|
||||
logger.error("File error " + nameo)
|
||||
|
||||
try:
|
||||
zf.close()
|
||||
except:
|
||||
logger.info("Error cerrando .zip " + file)
|
||||
logger.info("Error closing .zip " + file)
|
||||
|
||||
def _createstructure(self, file, dir):
|
||||
self._makedirs(self._listdirs(file), dir)
|
||||
|
||||
Reference in New Issue
Block a user