updates
This commit is contained in:
12
plugin.video.alfa/platformcode/__init__.py
Executable file
12
plugin.video.alfa/platformcode/__init__.py
Executable file
@@ -0,0 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Appends the main plugin dir to the PYTHONPATH if an internal package cannot be imported.
|
||||
# Examples: In Plex Media Server all modules are under "Code.*" package, and in Enigma2 under "Plugins.Extensions.*"
|
||||
try:
|
||||
# from core import logger
|
||||
import core
|
||||
except:
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
351
plugin.video.alfa/platformcode/download_and_play.py
Executable file
351
plugin.video.alfa/platformcode/download_and_play.py
Executable file
@@ -0,0 +1,351 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# Download and play
|
||||
# ------------------------------------------------------------
|
||||
# Based on code from the Mega add-on (xbmchub.com)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
from core import config
|
||||
from core import downloadtools
|
||||
from core import logger
|
||||
|
||||
|
||||
# Download a file and start playing while downloading
|
||||
def download_and_play(url, file_name, download_path):
|
||||
# Lanza thread
|
||||
logger.info("Active threads " + str(threading.active_count()))
|
||||
logger.info("" + repr(threading.enumerate()))
|
||||
logger.info("Starting download thread...")
|
||||
download_thread = DownloadThread(url, file_name, download_path)
|
||||
download_thread.start()
|
||||
logger.info("Download thread started")
|
||||
logger.info("Active threads " + str(threading.active_count()))
|
||||
logger.info("" + repr(threading.enumerate()))
|
||||
|
||||
# Espera
|
||||
logger.info("Waiting...")
|
||||
|
||||
while True:
|
||||
cancelled = False
|
||||
dialog = xbmcgui.DialogProgress()
|
||||
dialog.create('Descargando...', 'Cierra esta ventana para empezar la reproducción')
|
||||
dialog.update(0)
|
||||
|
||||
while not cancelled and download_thread.isAlive():
|
||||
dialog.update(download_thread.get_progress(), "Cancela esta ventana para empezar la reproducción",
|
||||
"Velocidad: " + str(int(download_thread.get_speed() / 1024)) + " KB/s " + str(
|
||||
download_thread.get_actual_size()) + "MB de " + str(
|
||||
download_thread.get_total_size()) + "MB",
|
||||
"Tiempo restante: " + str(downloadtools.sec_to_hms(download_thread.get_remaining_time())))
|
||||
xbmc.sleep(1000)
|
||||
|
||||
if dialog.iscanceled():
|
||||
cancelled = True
|
||||
break
|
||||
|
||||
dialog.close()
|
||||
|
||||
logger.info("End of waiting")
|
||||
|
||||
# Lanza el reproductor
|
||||
player = CustomPlayer()
|
||||
player.set_download_thread(download_thread)
|
||||
player.PlayStream(download_thread.get_file_name())
|
||||
|
||||
# Fin de reproducción
|
||||
logger.info("Fin de reproducción")
|
||||
|
||||
if player.is_stopped():
|
||||
logger.info("Terminado por el usuario")
|
||||
break
|
||||
else:
|
||||
if not download_thread.isAlive():
|
||||
logger.info("La descarga ha terminado")
|
||||
break
|
||||
else:
|
||||
logger.info("Continua la descarga")
|
||||
|
||||
# Cuando el reproductor acaba, si continúa descargando lo para ahora
|
||||
logger.info("Download thread alive=" + str(download_thread.isAlive()))
|
||||
if download_thread.isAlive():
|
||||
logger.info("Killing download thread")
|
||||
download_thread.force_stop()
|
||||
|
||||
|
||||
class CustomPlayer(xbmc.Player):
|
||||
def __init__(self, *args, **kwargs):
|
||||
logger.info()
|
||||
self.actualtime = 0
|
||||
self.totaltime = 0
|
||||
self.stopped = False
|
||||
xbmc.Player.__init__(self)
|
||||
|
||||
def PlayStream(self, url):
|
||||
logger.info("url=" + url)
|
||||
self.play(url)
|
||||
self.actualtime = 0
|
||||
self.url = url
|
||||
while self.isPlaying():
|
||||
self.actualtime = self.getTime()
|
||||
self.totaltime = self.getTotalTime()
|
||||
logger.info("actualtime=" + str(self.actualtime) + " totaltime=" + str(self.totaltime))
|
||||
xbmc.sleep(3000)
|
||||
|
||||
def set_download_thread(self, download_thread):
|
||||
logger.info()
|
||||
self.download_thread = download_thread
|
||||
|
||||
def force_stop_download_thread(self):
|
||||
logger.info()
|
||||
|
||||
if self.download_thread.isAlive():
|
||||
logger.info("Killing download thread")
|
||||
self.download_thread.force_stop()
|
||||
|
||||
# while self.download_thread.isAlive():
|
||||
# xbmc.sleep(1000)
|
||||
|
||||
def onPlayBackStarted(self):
|
||||
logger.info("PLAYBACK STARTED")
|
||||
|
||||
def onPlayBackEnded(self):
|
||||
logger.info("PLAYBACK ENDED")
|
||||
|
||||
def onPlayBackStopped(self):
|
||||
logger.info("PLAYBACK STOPPED")
|
||||
self.stopped = True
|
||||
self.force_stop_download_thread()
|
||||
|
||||
def is_stopped(self):
|
||||
return self.stopped
|
||||
|
||||
|
||||
# Download in background
|
||||
class DownloadThread(threading.Thread):
|
||||
def __init__(self, url, file_name, download_path):
|
||||
logger.info(repr(file))
|
||||
self.url = url
|
||||
self.download_path = download_path
|
||||
self.file_name = os.path.join(download_path, file_name)
|
||||
self.progress = 0
|
||||
self.force_stop_file_name = os.path.join(self.download_path, "force_stop.tmp")
|
||||
self.velocidad = 0
|
||||
self.tiempofalta = 0
|
||||
self.actual_size = 0
|
||||
self.total_size = 0
|
||||
|
||||
if os.path.exists(self.force_stop_file_name):
|
||||
os.remove(self.force_stop_file_name)
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
logger.info("Download starts...")
|
||||
|
||||
if "megacrypter.com" in self.url:
|
||||
self.download_file_megacrypter()
|
||||
else:
|
||||
self.download_file()
|
||||
logger.info("Download ends")
|
||||
|
||||
def force_stop(self):
|
||||
logger.info()
|
||||
force_stop_file = open(self.force_stop_file_name, "w")
|
||||
force_stop_file.write("0")
|
||||
force_stop_file.close()
|
||||
|
||||
def get_progress(self):
|
||||
return self.progress
|
||||
|
||||
def get_file_name(self):
|
||||
return self.file_name
|
||||
|
||||
def get_speed(self):
|
||||
return self.velocidad
|
||||
|
||||
def get_remaining_time(self):
|
||||
return self.tiempofalta
|
||||
|
||||
def get_actual_size(self):
|
||||
return self.actual_size
|
||||
|
||||
def get_total_size(self):
|
||||
return self.total_size
|
||||
|
||||
def download_file_megacrypter(self):
|
||||
logger.info()
|
||||
|
||||
comando = "./megacrypter.sh"
|
||||
logger.info("comando=" + comando)
|
||||
|
||||
oldcwd = os.getcwd()
|
||||
logger.info("oldcwd=" + oldcwd)
|
||||
|
||||
cwd = os.path.join(config.get_runtime_path(), "tools")
|
||||
logger.info("cwd=" + cwd)
|
||||
os.chdir(cwd)
|
||||
logger.info("directory changed to=" + os.getcwd())
|
||||
|
||||
logger.info("destino=" + self.download_path)
|
||||
|
||||
os.system(comando + " '" + self.url + "' \"" + self.download_path + "\"")
|
||||
# p = subprocess.Popen([comando , self.url , self.download_path], cwd=cwd, stdout=subprocess.PIPE , stderr=subprocess.PIPE )
|
||||
# out, err = p.communicate()
|
||||
# logger.info("DownloadThread.download_file out="+out)
|
||||
|
||||
os.chdir(oldcwd)
|
||||
|
||||
def download_file(self):
|
||||
logger.info("Direct download")
|
||||
|
||||
headers = []
|
||||
|
||||
# Se asegura de que el fichero se podrá crear
|
||||
logger.info("nombrefichero=" + self.file_name)
|
||||
self.file_name = xbmc.makeLegalFilename(self.file_name)
|
||||
logger.info("nombrefichero=" + self.file_name)
|
||||
logger.info("url=" + self.url)
|
||||
|
||||
# Crea el fichero
|
||||
existSize = 0
|
||||
f = open(self.file_name, 'wb')
|
||||
grabado = 0
|
||||
|
||||
# Interpreta las cabeceras en una URL como en XBMC
|
||||
if "|" in self.url:
|
||||
additional_headers = self.url.split("|")[1]
|
||||
if "&" in additional_headers:
|
||||
additional_headers = additional_headers.split("&")
|
||||
else:
|
||||
additional_headers = [additional_headers]
|
||||
|
||||
for additional_header in additional_headers:
|
||||
logger.info("additional_header: " + additional_header)
|
||||
name = re.findall("(.*?)=.*?", additional_header)[0]
|
||||
value = urllib.unquote_plus(re.findall(".*?=(.*?)$", additional_header)[0])
|
||||
headers.append([name, value])
|
||||
|
||||
self.url = self.url.split("|")[0]
|
||||
logger.info("url=" + self.url)
|
||||
|
||||
# Timeout del socket a 60 segundos
|
||||
socket.setdefaulttimeout(60)
|
||||
|
||||
# Crea la petición y añade las cabeceras
|
||||
h = urllib2.HTTPHandler(debuglevel=0)
|
||||
request = urllib2.Request(self.url)
|
||||
for header in headers:
|
||||
logger.info("Header=" + header[0] + ": " + header[1])
|
||||
request.add_header(header[0], header[1])
|
||||
|
||||
# Lanza la petición
|
||||
opener = urllib2.build_opener(h)
|
||||
urllib2.install_opener(opener)
|
||||
try:
|
||||
connexion = opener.open(request)
|
||||
except urllib2.HTTPError, e:
|
||||
logger.error("error %d (%s) al abrir la url %s" % (e.code, e.msg, self.url))
|
||||
# print e.code
|
||||
# print e.msg
|
||||
# print e.hdrs
|
||||
# print e.fp
|
||||
f.close()
|
||||
|
||||
# El error 416 es que el rango pedido es mayor que el fichero => es que ya está completo
|
||||
if e.code == 416:
|
||||
return 0
|
||||
else:
|
||||
return -2
|
||||
|
||||
try:
|
||||
totalfichero = int(connexion.headers["Content-Length"])
|
||||
except:
|
||||
totalfichero = 1
|
||||
|
||||
self.total_size = int(float(totalfichero) / float(1024 * 1024))
|
||||
|
||||
logger.info("Content-Length=%s" % totalfichero)
|
||||
blocksize = 100 * 1024
|
||||
|
||||
bloqueleido = connexion.read(blocksize)
|
||||
logger.info("Iniciando descarga del fichero, bloqueleido=%s" % len(bloqueleido))
|
||||
|
||||
maxreintentos = 10
|
||||
|
||||
while len(bloqueleido) > 0:
|
||||
try:
|
||||
if os.path.exists(self.force_stop_file_name):
|
||||
logger.info("Detectado fichero force_stop, se interrumpe la descarga")
|
||||
f.close()
|
||||
|
||||
xbmc.executebuiltin((u'XBMC.Notification("Cancelado", "Descarga en segundo plano cancelada", 300)'))
|
||||
|
||||
return
|
||||
|
||||
# Escribe el bloque leido
|
||||
# try:
|
||||
# import xbmcvfs
|
||||
# f.write( bloqueleido )
|
||||
# except:
|
||||
f.write(bloqueleido)
|
||||
grabado = grabado + len(bloqueleido)
|
||||
logger.info("grabado=%d de %d" % (grabado, totalfichero))
|
||||
percent = int(float(grabado) * 100 / float(totalfichero))
|
||||
self.progress = percent;
|
||||
totalmb = float(float(totalfichero) / (1024 * 1024))
|
||||
descargadosmb = float(float(grabado) / (1024 * 1024))
|
||||
self.actual_size = int(descargadosmb)
|
||||
|
||||
# Lee el siguiente bloque, reintentando para no parar todo al primer timeout
|
||||
reintentos = 0
|
||||
while reintentos <= maxreintentos:
|
||||
try:
|
||||
|
||||
before = time.time()
|
||||
bloqueleido = connexion.read(blocksize)
|
||||
after = time.time()
|
||||
if (after - before) > 0:
|
||||
self.velocidad = len(bloqueleido) / ((after - before))
|
||||
falta = totalfichero - grabado
|
||||
if self.velocidad > 0:
|
||||
self.tiempofalta = falta / self.velocidad
|
||||
else:
|
||||
self.tiempofalta = 0
|
||||
break
|
||||
except:
|
||||
reintentos = reintentos + 1
|
||||
logger.info("ERROR en la descarga del bloque, reintento %d" % reintentos)
|
||||
for line in sys.exc_info():
|
||||
logger.error("%s" % line)
|
||||
|
||||
# Ha habido un error en la descarga
|
||||
if reintentos > maxreintentos:
|
||||
logger.error("ERROR en la descarga del fichero")
|
||||
f.close()
|
||||
|
||||
return -2
|
||||
|
||||
except:
|
||||
import traceback, sys
|
||||
from pprint import pprint
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
lines = traceback.format_exception(exc_type, exc_value, exc_tb)
|
||||
for line in lines:
|
||||
line_splits = line.split("\n")
|
||||
for line_split in line_splits:
|
||||
logger.error(line_split)
|
||||
|
||||
f.close()
|
||||
return -2
|
||||
|
||||
return
|
||||
452
plugin.video.alfa/platformcode/launcher.py
Executable file
452
plugin.video.alfa/platformcode/launcher.py
Executable file
@@ -0,0 +1,452 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# XBMC Launcher (xbmc / kodi)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import urllib2
|
||||
|
||||
from core import channeltools
|
||||
from core import config
|
||||
from core import videolibrarytools
|
||||
from core import logger
|
||||
from core import scrapertools
|
||||
from core import servertools
|
||||
from core.item import Item
|
||||
from platformcode import platformtools
|
||||
|
||||
|
||||
def start():
|
||||
""" Primera funcion que se ejecuta al entrar en el plugin.
|
||||
Dentro de esta funcion deberian ir todas las llamadas a las
|
||||
funciones que deseamos que se ejecuten nada mas abrir el plugin.
|
||||
"""
|
||||
logger.info()
|
||||
|
||||
# Test if all the required directories are created
|
||||
config.verify_directories_created()
|
||||
|
||||
|
||||
def run(item=None):
|
||||
logger.info()
|
||||
|
||||
if not item:
|
||||
# Extract item from sys.argv
|
||||
if sys.argv[2]:
|
||||
item = Item().fromurl(sys.argv[2])
|
||||
|
||||
# If no item, this is mainlist
|
||||
else:
|
||||
item = Item(channel="channelselector", action="getmainlist", viewmode="movie")
|
||||
|
||||
logger.info(item.tostring())
|
||||
|
||||
try:
|
||||
|
||||
# If item has no action, stops here
|
||||
if item.action == "":
|
||||
logger.info("Item sin accion")
|
||||
return
|
||||
|
||||
# Action for main menu in channelselector
|
||||
if item.action == "getmainlist":
|
||||
import channelselector
|
||||
|
||||
# # Check for updates only on first screen
|
||||
# if config.get_setting("check_for_plugin_updates") == True:
|
||||
# logger.info("Check for plugin updates enabled")
|
||||
# from core import updater
|
||||
#
|
||||
# try:
|
||||
# config.set_setting("plugin_updates_available", 0)
|
||||
# new_published_version_tag, number_of_updates = updater.get_available_updates()
|
||||
#
|
||||
# config.set_setting("plugin_updates_available", number_of_updates)
|
||||
# itemlist = channelselector.getmainlist()
|
||||
#
|
||||
# if new_published_version_tag != "":
|
||||
# platformtools.dialog_notification(new_published_version_tag + " disponible",
|
||||
# "Ya puedes descargar la nueva versión del plugin\n"
|
||||
# "desde el listado principal")
|
||||
#
|
||||
# itemlist = channelselector.getmainlist()
|
||||
# itemlist.insert(0, Item(title="Descargar version " + new_published_version_tag,
|
||||
# version=new_published_version_tag, channel="updater",
|
||||
# action="update",
|
||||
# thumbnail=channelselector.get_thumb("thumb_update.png")))
|
||||
# except:
|
||||
# import traceback
|
||||
# logger.error(traceback.format_exc())
|
||||
# platformtools.dialog_ok("No se puede conectar", "No ha sido posible comprobar",
|
||||
# "si hay actualizaciones")
|
||||
# logger.error("Fallo al verificar la actualización")
|
||||
# config.set_setting("plugin_updates_available", 0)
|
||||
# itemlist = channelselector.getmainlist()
|
||||
#
|
||||
# else:
|
||||
# logger.info("Check for plugin updates disabled")
|
||||
# config.set_setting("plugin_updates_available", 0)
|
||||
# itemlist = channelselector.getmainlist()
|
||||
|
||||
itemlist = channelselector.getmainlist()
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# # Action for updating plugin
|
||||
# elif item.action == "update":
|
||||
#
|
||||
# from core import updater
|
||||
# updater.update(item)
|
||||
# config.set_setting("plugin_updates_available", 0)
|
||||
#
|
||||
# import xbmc
|
||||
# xbmc.executebuiltin("Container.Refresh")
|
||||
|
||||
# Action for channel types on channelselector: movies, series, etc.
|
||||
elif item.action == "getchanneltypes":
|
||||
import channelselector
|
||||
itemlist = channelselector.getchanneltypes()
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# Action for channel listing on channelselector
|
||||
elif item.action == "filterchannels":
|
||||
import channelselector
|
||||
itemlist = channelselector.filterchannels(item.channel_type)
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# Special action for playing a video from the library
|
||||
elif item.action == "play_from_library":
|
||||
play_from_library(item)
|
||||
return
|
||||
|
||||
# Action in certain channel specified in "action" and "channel" parameters
|
||||
else:
|
||||
|
||||
# Entry point for a channel is the "mainlist" action, so here we check parental control
|
||||
if item.action == "mainlist":
|
||||
|
||||
# Parental control
|
||||
# If it is an adult channel, and user has configured pin, asks for it
|
||||
if channeltools.is_adult(item.channel) and config.get_setting("adult_pin") != "":
|
||||
tecleado = platformtools.dialog_input("", "Contraseña para canales de adultos", True)
|
||||
if tecleado is None or tecleado != config.get_setting("adult_pin"):
|
||||
return
|
||||
|
||||
# # Actualiza el canal individual
|
||||
# if (item.action == "mainlist" and item.channel != "channelselector" and
|
||||
# config.get_setting("check_for_channel_updates") == True):
|
||||
# from core import updater
|
||||
# updater.update_channel(item.channel)
|
||||
|
||||
# Checks if channel exists
|
||||
channel_file = os.path.join(config.get_runtime_path(),
|
||||
'channels', item.channel + ".py")
|
||||
logger.info("channel_file=%s" % channel_file)
|
||||
|
||||
channel = None
|
||||
|
||||
if os.path.exists(channel_file):
|
||||
try:
|
||||
channel = __import__('channels.%s' % item.channel, None,
|
||||
None, ["channels.%s" % item.channel])
|
||||
except ImportError:
|
||||
exec "import channels." + item.channel + " as channel"
|
||||
|
||||
logger.info("Running channel %s | %s" % (channel.__name__, channel.__file__))
|
||||
|
||||
# Special play action
|
||||
if item.action == "play":
|
||||
logger.info("item.action=%s" % item.action.upper())
|
||||
# logger.debug("item_toPlay: " + "\n" + item.tostring('\n'))
|
||||
|
||||
# First checks if channel has a "play" function
|
||||
if hasattr(channel, 'play'):
|
||||
logger.info("Executing channel 'play' method")
|
||||
itemlist = channel.play(item)
|
||||
b_favourite = item.isFavourite
|
||||
# Play should return a list of playable URLS
|
||||
if len(itemlist) > 0 and isinstance(itemlist[0], Item):
|
||||
item = itemlist[0]
|
||||
if b_favourite:
|
||||
item.isFavourite = True
|
||||
platformtools.play_video(item)
|
||||
|
||||
# Permitir varias calidades desde play en el canal
|
||||
elif len(itemlist) > 0 and isinstance(itemlist[0], list):
|
||||
item.video_urls = itemlist
|
||||
platformtools.play_video(item)
|
||||
|
||||
# If not, shows user an error message
|
||||
else:
|
||||
platformtools.dialog_ok("alfa", "No hay nada para reproducir")
|
||||
|
||||
# If player don't have a "play" function, not uses the standard play from platformtools
|
||||
else:
|
||||
logger.info("Executing core 'play' method")
|
||||
platformtools.play_video(item)
|
||||
|
||||
# Special action for findvideos, where the plugin looks for known urls
|
||||
elif item.action == "findvideos":
|
||||
|
||||
# First checks if channel has a "findvideos" function
|
||||
if hasattr(channel, 'findvideos'):
|
||||
itemlist = getattr(channel, item.action)(item)
|
||||
itemlist = servertools.filter_servers(itemlist)
|
||||
|
||||
# If not, uses the generic findvideos function
|
||||
else:
|
||||
logger.info("No channel 'findvideos' method, "
|
||||
"executing core method")
|
||||
itemlist = servertools.find_video_items(item)
|
||||
|
||||
if config.get_setting("max_links", "videolibrary") != 0:
|
||||
itemlist = limit_itemlist(itemlist)
|
||||
|
||||
from platformcode import subtitletools
|
||||
subtitletools.saveSubtitleName(item)
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# Special action for adding a movie to the library
|
||||
elif item.action == "add_pelicula_to_library":
|
||||
videolibrarytools.add_movie(item)
|
||||
|
||||
# Special action for adding a serie to the library
|
||||
elif item.action == "add_serie_to_library":
|
||||
videolibrarytools.add_tvshow(item, channel)
|
||||
|
||||
# Special action for downloading all episodes from a serie
|
||||
elif item.action == "download_all_episodes":
|
||||
from channels import downloads
|
||||
item.action = item.extra
|
||||
del item.extra
|
||||
downloads.save_download(item)
|
||||
|
||||
# Special action for searching, first asks for the words then call the "search" function
|
||||
elif item.action == "search":
|
||||
logger.info("item.action=%s" % item.action.upper())
|
||||
|
||||
last_search = ""
|
||||
last_search_active = config.get_setting("last_search", "search")
|
||||
if last_search_active:
|
||||
try:
|
||||
current_saved_searches_list = list(config.get_setting("saved_searches_list", "search"))
|
||||
last_search = current_saved_searches_list[0]
|
||||
except:
|
||||
pass
|
||||
|
||||
tecleado = platformtools.dialog_input(last_search)
|
||||
if tecleado is not None:
|
||||
if last_search_active and not tecleado.startswith("http"):
|
||||
from channels import search
|
||||
search.save_search(tecleado)
|
||||
|
||||
itemlist = channel.search(item, tecleado)
|
||||
else:
|
||||
return
|
||||
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
# For all other actions
|
||||
else:
|
||||
logger.info("Executing channel '%s' method" % item.action)
|
||||
itemlist = getattr(channel, item.action)(item)
|
||||
platformtools.render_items(itemlist, item)
|
||||
|
||||
except urllib2.URLError, e:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# Grab inner and third party errors
|
||||
if hasattr(e, 'reason'):
|
||||
logger.error("Razon del error, codigo: %s | Razon: %s" % (str(e.reason[0]), str(e.reason[1])))
|
||||
texto = config.get_localized_string(30050) # "No se puede conectar con el sitio web"
|
||||
platformtools.dialog_ok("alfa", texto)
|
||||
|
||||
# Grab server response errors
|
||||
elif hasattr(e, 'code'):
|
||||
logger.error("Codigo de error HTTP : %d" % e.code)
|
||||
# "El sitio web no funciona correctamente (error http %d)"
|
||||
platformtools.dialog_ok("alfa", config.get_localized_string(30051) % e.code)
|
||||
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
patron = 'File "' + os.path.join(config.get_runtime_path(), "channels", "").replace("\\",
|
||||
"\\\\") + '([^.]+)\.py"'
|
||||
canal = scrapertools.find_single_match(traceback.format_exc(), patron)
|
||||
|
||||
try:
|
||||
import xbmc
|
||||
if config.get_platform(True)['num_version'] < 14:
|
||||
log_name = "xbmc.log"
|
||||
else:
|
||||
log_name = "kodi.log"
|
||||
log_message = "Ruta: " + xbmc.translatePath("special://logpath") + log_name
|
||||
except:
|
||||
log_message = ""
|
||||
|
||||
if canal:
|
||||
platformtools.dialog_ok(
|
||||
"Error inesperado en el canal " + canal,
|
||||
"Puede deberse a un fallo de conexión, la web del canal "
|
||||
"ha cambiado su estructura, o un error interno de alfa.",
|
||||
"Para saber más detalles, consulta el log.", log_message)
|
||||
else:
|
||||
platformtools.dialog_ok(
|
||||
"Se ha producido un error en alfa",
|
||||
"Comprueba el log para ver mas detalles del error.",
|
||||
log_message)
|
||||
|
||||
|
||||
def reorder_itemlist(itemlist):
|
||||
logger.info()
|
||||
# logger.debug("Inlet itemlist size: %i" % len(itemlist))
|
||||
|
||||
new_list = []
|
||||
mod_list = []
|
||||
not_mod_list = []
|
||||
|
||||
modified = 0
|
||||
not_modified = 0
|
||||
|
||||
to_change = [['Ver en', '[V]'],
|
||||
['Descargar en', '[D]']]
|
||||
|
||||
for item in itemlist:
|
||||
old_title = unicode(item.title, "utf8").lower().encode("utf8")
|
||||
for before, after in to_change:
|
||||
if before in item.title:
|
||||
item.title = item.title.replace(before, after)
|
||||
break
|
||||
|
||||
new_title = unicode(item.title, "utf8").lower().encode("utf8")
|
||||
if old_title != new_title:
|
||||
mod_list.append(item)
|
||||
modified += 1
|
||||
else:
|
||||
not_mod_list.append(item)
|
||||
not_modified += 1
|
||||
|
||||
# logger.debug("OLD: %s | NEW: %s" % (old_title, new_title))
|
||||
|
||||
new_list.extend(mod_list)
|
||||
new_list.extend(not_mod_list)
|
||||
|
||||
logger.info("Titulos modificados:%i | No modificados:%i" % (modified, not_modified))
|
||||
|
||||
if len(new_list) == 0:
|
||||
new_list = itemlist
|
||||
|
||||
# logger.debug("Outlet itemlist size: %i" % len(new_list))
|
||||
return new_list
|
||||
|
||||
|
||||
def limit_itemlist(itemlist):
|
||||
logger.info()
|
||||
# logger.debug("Inlet itemlist size: %i" % len(itemlist))
|
||||
|
||||
try:
|
||||
opt = config.get_setting("max_links", "videolibrary")
|
||||
if opt == 0:
|
||||
new_list = itemlist
|
||||
else:
|
||||
i_max = 30 * opt
|
||||
new_list = itemlist[:i_max]
|
||||
|
||||
# logger.debug("Outlet itemlist size: %i" % len(new_list))
|
||||
return new_list
|
||||
except:
|
||||
return itemlist
|
||||
|
||||
|
||||
def play_from_library(item):
|
||||
"""
|
||||
Los .strm al reproducirlos desde kodi, este espera que sea un archivo "reproducible" asi que no puede contener
|
||||
más items, como mucho se puede colocar un dialogo de seleccion.
|
||||
Esto lo solucionamos "engañando a kodi" y haciendole creer que se ha reproducido algo, asi despues mediante
|
||||
"Container.Update()" cargamos el strm como si un item desde dentro del addon se tratara, quitando todas
|
||||
las limitaciones y permitiendo reproducir mediante la funcion general sin tener que crear nuevos métodos para
|
||||
la videoteca.
|
||||
@type item: item
|
||||
@param item: elemento con información
|
||||
"""
|
||||
logger.info()
|
||||
# logger.debug("item: \n" + item.tostring('\n'))
|
||||
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
import xbmc
|
||||
# Intentamos reproducir una imagen (esto no hace nada y ademas no da error)
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True,
|
||||
xbmcgui.ListItem(
|
||||
path=os.path.join(config.get_runtime_path(), "resources", "subtitle.mp4")))
|
||||
|
||||
# Por si acaso la imagen hiciera (en futuras versiones) le damos a stop para detener la reproduccion
|
||||
xbmc.Player().stop()
|
||||
|
||||
# modificamos el action (actualmente la videoteca necesita "findvideos" ya que es donde se buscan las fuentes
|
||||
item.action = "findvideos"
|
||||
|
||||
window_type = config.get_setting("window_type", "videolibrary")
|
||||
|
||||
# y volvemos a lanzar kodi
|
||||
if xbmc.getCondVisibility('Window.IsMedia') and not window_type == 1:
|
||||
# Ventana convencional
|
||||
xbmc.executebuiltin("Container.Update(" + sys.argv[0] + "?" + item.tourl() + ")")
|
||||
|
||||
else:
|
||||
# Ventana emergente
|
||||
from channels import videolibrary
|
||||
p_dialog = platformtools.dialog_progress_bg('alfa', 'Cargando...')
|
||||
p_dialog.update(0, '')
|
||||
|
||||
itemlist = videolibrary.findvideos(item)
|
||||
|
||||
p_dialog.update(50, '')
|
||||
|
||||
'''# Se filtran los enlaces segun la lista negra
|
||||
if config.get_setting('filter_servers', "servers"):
|
||||
itemlist = servertools.filter_servers(itemlist)'''
|
||||
|
||||
# Se limita la cantidad de enlaces a mostrar
|
||||
if config.get_setting("max_links", "videolibrary") != 0:
|
||||
itemlist = limit_itemlist(itemlist)
|
||||
|
||||
# Se "limpia" ligeramente la lista de enlaces
|
||||
if config.get_setting("replace_VD", "videolibrary") == 1:
|
||||
itemlist = reorder_itemlist(itemlist)
|
||||
|
||||
p_dialog.update(100, '')
|
||||
xbmc.sleep(500)
|
||||
p_dialog.close()
|
||||
|
||||
if len(itemlist) > 0:
|
||||
# El usuario elige el mirror
|
||||
opciones = []
|
||||
for item in itemlist:
|
||||
opciones.append(item.title)
|
||||
|
||||
# Se abre la ventana de seleccion
|
||||
if (item.contentSerieName != "" and
|
||||
item.contentSeason != "" and
|
||||
item.contentEpisodeNumber != ""):
|
||||
cabecera = ("%s - %sx%s -- %s" %
|
||||
(item.contentSerieName,
|
||||
item.contentSeason,
|
||||
item.contentEpisodeNumber,
|
||||
config.get_localized_string(30163)))
|
||||
else:
|
||||
cabecera = config.get_localized_string(30163)
|
||||
|
||||
seleccion = platformtools.dialog_select(cabecera, opciones)
|
||||
|
||||
if seleccion == -1:
|
||||
return
|
||||
else:
|
||||
item = videolibrary.play(itemlist[seleccion])[0]
|
||||
platformtools.play_video(item)
|
||||
737
plugin.video.alfa/platformcode/mct.py
Executable file
737
plugin.video.alfa/platformcode/mct.py
Executable file
@@ -0,0 +1,737 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# MCT - Mini Cliente Torrent
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
try:
|
||||
from python_libtorrent import get_libtorrent, get_platform
|
||||
|
||||
lt = get_libtorrent()
|
||||
except Exception, e:
|
||||
import libtorrent as lt
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
|
||||
from core import config
|
||||
from core import scrapertools
|
||||
from core import filetools
|
||||
|
||||
|
||||
def play(url, xlistitem={}, is_view=None, subtitle="", item=None):
|
||||
allocate = True
|
||||
try:
|
||||
import platform
|
||||
xbmc.log("XXX KODI XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
xbmc.log("OS platform: %s %s" % (platform.system(), platform.release()))
|
||||
xbmc.log("xbmc/kodi version: %s" % xbmc.getInfoLabel("System.BuildVersion"))
|
||||
xbmc_version = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
|
||||
xbmc.log("xbmc/kodi version number: %s" % xbmc_version)
|
||||
xbmc.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX KODI XXXX")
|
||||
|
||||
_platform = get_platform()
|
||||
if str(_platform['system']) in ["android_armv7", "linux_armv6", "linux_armv7"]:
|
||||
allocate = False
|
||||
# -- log ------------------------------------------------
|
||||
xbmc.log("XXX platform XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
xbmc.log("_platform['system']: %s" % _platform['system'])
|
||||
xbmc.log("allocate: %s" % allocate)
|
||||
xbmc.log("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX platform XXXX")
|
||||
# -- ----------------------------------------------------
|
||||
except:
|
||||
pass
|
||||
|
||||
DOWNLOAD_PATH = config.get_setting("downloadpath")
|
||||
|
||||
# -- adfly: ------------------------------------
|
||||
if url.startswith("http://adf.ly/"):
|
||||
try:
|
||||
data = scrapertools.downloadpage(url)
|
||||
url = decode_adfly(data)
|
||||
except:
|
||||
ddd = xbmcgui.Dialog()
|
||||
ddd.ok("alfa-MCT: Sin soporte adf.ly",
|
||||
"El script no tiene soporte para el acortador de urls adf.ly.", "", "url: " + url)
|
||||
return
|
||||
|
||||
# -- Necesario para algunas webs ----------------------------
|
||||
if not url.endswith(".torrent") and not url.startswith("magnet"):
|
||||
t_file = scrapertools.get_header_from_response(url, header_to_get="location")
|
||||
if len(t_file) > 0:
|
||||
url = t_file
|
||||
t_file = scrapertools.get_header_from_response(url, header_to_get="location")
|
||||
if len(t_file) > 0:
|
||||
url = t_file
|
||||
|
||||
# -- Crear dos carpetas en descargas para los archivos ------
|
||||
save_path_videos = os.path.join(DOWNLOAD_PATH, "torrent-videos")
|
||||
save_path_torrents = os.path.join(DOWNLOAD_PATH, "torrent-torrents")
|
||||
if not os.path.exists(save_path_torrents): os.mkdir(save_path_torrents)
|
||||
|
||||
# -- Usar - archivo torrent desde web, magnet o HD ---------
|
||||
if not os.path.isfile(url) and not url.startswith("magnet"):
|
||||
# -- http - crear archivo torrent -----------------------
|
||||
data = url_get(url)
|
||||
# -- El nombre del torrent será el que contiene en los --
|
||||
# -- datos. -
|
||||
re_name = urllib.unquote(scrapertools.get_match(data, ':name\d+:(.*?)\d+:'))
|
||||
torrent_file = filetools.join(save_path_torrents, filetools.encode(re_name + '.torrent'))
|
||||
|
||||
f = open(torrent_file, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
elif os.path.isfile(url):
|
||||
# -- file - para usar torrens desde el HD ---------------
|
||||
torrent_file = url
|
||||
else:
|
||||
# -- magnet ---------------------------------------------
|
||||
torrent_file = url
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# -- MCT - MiniClienteTorrent -------------------------------
|
||||
ses = lt.session()
|
||||
|
||||
# -- log ----------------------------------------------------
|
||||
xbmc.log("### Init session ########")
|
||||
xbmc.log(lt.version)
|
||||
xbmc.log("#########################")
|
||||
# -- --------------------------------------------------------
|
||||
|
||||
ses.add_dht_router("router.bittorrent.com", 6881)
|
||||
ses.add_dht_router("router.utorrent.com", 6881)
|
||||
ses.add_dht_router("dht.transmissionbt.com", 6881)
|
||||
|
||||
trackers = [
|
||||
"udp://tracker.openbittorrent.com:80/announce",
|
||||
"http://tracker.torrentbay.to:6969/announce",
|
||||
"http://tracker.pow7.com/announce",
|
||||
"udp://tracker.ccc.de:80/announce",
|
||||
"udp://open.demonii.com:1337",
|
||||
|
||||
"http://9.rarbg.com:2710/announce",
|
||||
"http://bt.careland.com.cn:6969/announce",
|
||||
"http://explodie.org:6969/announce",
|
||||
"http://mgtracker.org:2710/announce",
|
||||
"http://tracker.best-torrents.net:6969/announce",
|
||||
"http://tracker.tfile.me/announce",
|
||||
"http://tracker1.wasabii.com.tw:6969/announce",
|
||||
"udp://9.rarbg.com:2710/announce",
|
||||
"udp://9.rarbg.me:2710/announce",
|
||||
"udp://coppersurfer.tk:6969/announce",
|
||||
|
||||
"http://www.spanishtracker.com:2710/announce",
|
||||
"http://www.todotorrents.com:2710/announce",
|
||||
]
|
||||
|
||||
video_file = ""
|
||||
# -- magnet2torrent -----------------------------------------
|
||||
if torrent_file.startswith("magnet"):
|
||||
try:
|
||||
import zlib
|
||||
btih = hex(zlib.crc32(
|
||||
scrapertools.get_match(torrent_file, 'magnet:\?xt=urn:(?:[A-z0-9:]+|)([A-z0-9]{32})')) & 0xffffffff)
|
||||
files = [f for f in os.listdir(save_path_torrents) if os.path.isfile(os.path.join(save_path_torrents, f))]
|
||||
for file in files:
|
||||
if btih in os.path.basename(file):
|
||||
torrent_file = os.path.join(save_path_torrents, file)
|
||||
except:
|
||||
pass
|
||||
|
||||
if torrent_file.startswith("magnet"):
|
||||
try:
|
||||
tempdir = tempfile.mkdtemp()
|
||||
except IOError:
|
||||
tempdir = os.path.join(save_path_torrents, "temp")
|
||||
if not os.path.exists(tempdir):
|
||||
os.mkdir(tempdir)
|
||||
params = {
|
||||
'save_path': tempdir,
|
||||
'trackers': trackers,
|
||||
'storage_mode': lt.storage_mode_t.storage_mode_allocate,
|
||||
'paused': False,
|
||||
'auto_managed': True,
|
||||
'duplicate_is_error': True
|
||||
}
|
||||
h = lt.add_magnet_uri(ses, torrent_file, params)
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
while not h.has_metadata():
|
||||
message, porcent, msg_file, s, download = getProgress(h, "Creando torrent desde magnet")
|
||||
dp.update(porcent, message, msg_file)
|
||||
if s.state == 1: download = 1
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
h.force_dht_announce()
|
||||
xbmc.sleep(1000)
|
||||
|
||||
dp.close()
|
||||
info = h.get_torrent_info()
|
||||
data = lt.bencode(lt.create_torrent(info).generate())
|
||||
|
||||
torrent_file = os.path.join(save_path_torrents,
|
||||
unicode(info.name() + "-" + btih, "'utf-8'", errors="replace") + ".torrent")
|
||||
f = open(torrent_file, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
ses.remove_torrent(h)
|
||||
shutil.rmtree(tempdir)
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# -- Archivos torrent ---------------------------------------
|
||||
e = lt.bdecode(open(torrent_file, 'rb').read())
|
||||
info = lt.torrent_info(e)
|
||||
|
||||
# -- El más gordo o uno de los más gordo se entiende que es -
|
||||
# -- el vídeo o es el vídeo que se usará como referencia -
|
||||
# -- para el tipo de archivo -
|
||||
xbmc.log("##### Archivos ## %s ##" % len(info.files()))
|
||||
_index_file, _video_file, _size_file = get_video_file(info)
|
||||
|
||||
# -- Prioritarizar/Seleccionar archivo-----------------------
|
||||
_index, video_file, video_size, len_files = get_video_files_sizes(info)
|
||||
if len_files == 0:
|
||||
dp = xbmcgui.Dialog().ok("No se puede reproducir", "El torrent no contiene ningún archivo de vídeo")
|
||||
|
||||
if _index == -1:
|
||||
_index = _index_file
|
||||
video_file = _video_file
|
||||
video_size = _size_file
|
||||
|
||||
_video_file_ext = os.path.splitext(_video_file)[1]
|
||||
xbmc.log("##### _video_file_ext ## %s ##" % _video_file_ext)
|
||||
if (_video_file_ext == ".avi" or _video_file_ext == ".mp4") and allocate:
|
||||
xbmc.log("##### storage_mode_t.storage_mode_allocate (" + _video_file_ext + ") #####")
|
||||
h = ses.add_torrent({'ti': info, 'save_path': save_path_videos, 'trackers': trackers,
|
||||
'storage_mode': lt.storage_mode_t.storage_mode_allocate})
|
||||
else:
|
||||
xbmc.log("##### storage_mode_t.storage_mode_sparse (" + _video_file_ext + ") #####")
|
||||
h = ses.add_torrent({'ti': info, 'save_path': save_path_videos, 'trackers': trackers,
|
||||
'storage_mode': lt.storage_mode_t.storage_mode_sparse})
|
||||
allocate = True
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# -- Descarga secuencial - trozo 1, trozo 2, ... ------------
|
||||
h.set_sequential_download(True)
|
||||
|
||||
h.force_reannounce()
|
||||
h.force_dht_announce()
|
||||
|
||||
# -- Inicio de variables para 'pause' automático cuando el -
|
||||
# -- el vídeo se acerca a una pieza sin completar -
|
||||
is_greater_num_pieces = False
|
||||
is_greater_num_pieces_plus = False
|
||||
is_greater_num_pieces_pause = False
|
||||
|
||||
porcent4first_pieces = int(video_size * 0.000000005)
|
||||
if porcent4first_pieces < 10: porcent4first_pieces = 10
|
||||
if porcent4first_pieces > 100: porcent4first_pieces = 100
|
||||
porcent4last_pieces = int(porcent4first_pieces / 2)
|
||||
|
||||
num_pieces_to_resume = int(video_size * 0.0000000025)
|
||||
if num_pieces_to_resume < 5: num_pieces_to_resume = 5
|
||||
if num_pieces_to_resume > 25: num_pieces_to_resume = 25
|
||||
|
||||
xbmc.log("##### porcent4first_pieces ## %s ##" % porcent4first_pieces)
|
||||
xbmc.log("##### porcent4last_pieces ## %s ##" % porcent4last_pieces)
|
||||
xbmc.log("##### num_pieces_to_resume ## %s ##" % num_pieces_to_resume)
|
||||
|
||||
# -- Prioritarizar o seleccionar las piezas del archivo que -
|
||||
# -- se desea reproducir con 'file_priorities' -
|
||||
piece_set = set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate)
|
||||
|
||||
# -- Crear diálogo de progreso para el primer bucle ---------
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
|
||||
_pieces_info = {}
|
||||
|
||||
# -- Doble bucle anidado ------------------------------------
|
||||
# -- Descarga - Primer bucle -
|
||||
while not h.is_seed():
|
||||
s = h.status()
|
||||
|
||||
xbmc.sleep(100)
|
||||
|
||||
# -- Recuperar los datos del progreso -------------------
|
||||
message, porcent, msg_file, s, download = getProgress(h, video_file, _pf=_pieces_info)
|
||||
|
||||
# -- Si hace 'checking' existe descarga -----------------
|
||||
# -- 'download' Se usará para saber si hay datos -
|
||||
# -- descargados para el diálogo de 'remove_files' -
|
||||
if s.state == 1: download = 1
|
||||
|
||||
# -- Player - play --------------------------------------
|
||||
# -- Comprobar si se han completado las piezas para el -
|
||||
# -- inicio del vídeo -
|
||||
first_pieces = True
|
||||
|
||||
_c = 0
|
||||
for i in range(piece_set[0], piece_set[porcent4first_pieces]):
|
||||
first_pieces &= h.have_piece(i)
|
||||
if h.have_piece(i): _c += 1
|
||||
_pieces_info = {'current': 0, 'continuous': "%s/%s" % (_c, porcent4first_pieces), 'continuous2': "",
|
||||
'have': h.status().num_pieces, 'len': len(piece_set)}
|
||||
|
||||
last_pieces = True
|
||||
if not allocate:
|
||||
_c = len(piece_set) - 1;
|
||||
_cc = 0
|
||||
for i in range(len(piece_set) - porcent4last_pieces, len(piece_set)):
|
||||
last_pieces &= h.have_piece(i)
|
||||
if h.have_piece(i): _c -= 1; _cc += 1
|
||||
_pieces_info['continuous2'] = "[%s/%s] " % (_cc, porcent4last_pieces)
|
||||
|
||||
if is_view != "Ok" and first_pieces and last_pieces:
|
||||
_pieces_info['continuous2'] = ""
|
||||
xbmc.log("##### porcent [%.2f%%]" % (s.progress * 100))
|
||||
is_view = "Ok"
|
||||
dp.close()
|
||||
|
||||
# -- Player - Ver el vídeo --------------------------
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
|
||||
ren_video_file = os.path.join(save_path_videos, video_file)
|
||||
try:
|
||||
playlist.add(ren_video_file, xlistitem)
|
||||
except:
|
||||
playlist.add(ren_video_file)
|
||||
|
||||
if xbmc_version < 17:
|
||||
player = play_video(xbmc.PLAYER_CORE_AUTO)
|
||||
else:
|
||||
player = play_video()
|
||||
player.play(playlist)
|
||||
|
||||
# -- Contador de cancelaciones para la ventana de -
|
||||
# -- 'pause' automático -
|
||||
is_greater_num_pieces_canceled = 0
|
||||
continuous_pieces = 0
|
||||
porcent_time = 0.00
|
||||
current_piece = 0
|
||||
set_next_continuous_pieces = porcent4first_pieces
|
||||
|
||||
# -- Impedir que kodi haga 'resume' a un archivo ----
|
||||
# -- que se reprodujo con anterioridad y que se -
|
||||
# -- eliminó para impedir que intente la reprucción -
|
||||
# -- en una pieza que aún no se ha completado y se -
|
||||
# -- active 'pause' automático -
|
||||
not_resume = True
|
||||
|
||||
# -- Bandera subTítulos
|
||||
_sub = False
|
||||
|
||||
# -- Segundo bucle - Player - Control de eventos ----
|
||||
while player.isPlaying():
|
||||
xbmc.sleep(100)
|
||||
|
||||
# -- Añadir subTítulos
|
||||
if subtitle != "" and not _sub:
|
||||
_sub = True
|
||||
player.setSubtitles(subtitle)
|
||||
|
||||
# -- Impedir que kodi haga 'resume' al inicio ---
|
||||
# -- de la descarga de un archivo conocido -
|
||||
if not_resume:
|
||||
player.seekTime(0)
|
||||
not_resume = False
|
||||
|
||||
# -- Control 'pause' automático -
|
||||
continuous_pieces = count_completed_continuous_pieces(h, piece_set)
|
||||
|
||||
if xbmc.Player().isPlaying():
|
||||
|
||||
# -- Porcentage del progreso del vídeo ------
|
||||
player_getTime = player.getTime()
|
||||
player_getTotalTime = player.getTotalTime()
|
||||
porcent_time = player_getTime / player_getTotalTime * 100
|
||||
|
||||
# -- Pieza que se está reproduciendo --------
|
||||
current_piece = int(porcent_time / 100 * len(piece_set))
|
||||
|
||||
# -- Banderas de control --------------------
|
||||
is_greater_num_pieces = (current_piece > continuous_pieces - num_pieces_to_resume)
|
||||
is_greater_num_pieces_plus = (current_piece + porcent4first_pieces > continuous_pieces)
|
||||
is_greater_num_pieces_finished = (current_piece + porcent4first_pieces >= len(piece_set))
|
||||
|
||||
# -- Activa 'pause' automático --------------
|
||||
if is_greater_num_pieces and not player.paused and not is_greater_num_pieces_finished:
|
||||
is_greater_num_pieces_pause = True
|
||||
player.pause()
|
||||
|
||||
if continuous_pieces >= set_next_continuous_pieces:
|
||||
set_next_continuous_pieces = continuous_pieces + num_pieces_to_resume
|
||||
next_continuous_pieces = str(continuous_pieces - current_piece) + "/" + str(
|
||||
set_next_continuous_pieces - current_piece)
|
||||
_pieces_info = {'current': current_piece, 'continuous': next_continuous_pieces,
|
||||
'continuous2': _pieces_info['continuous2'], 'have': h.status().num_pieces,
|
||||
'len': len(piece_set)}
|
||||
|
||||
# si es un archivo de la videoteca enviar a marcar como visto
|
||||
if item.strm_path:
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.mark_auto_as_watched(item)
|
||||
|
||||
# -- Cerrar el diálogo de progreso --------------
|
||||
if player.resumed:
|
||||
dp.close()
|
||||
|
||||
# -- Mostrar el diálogo de progreso -------------
|
||||
if player.paused:
|
||||
# -- Crear diálogo si no existe -------------
|
||||
if not player.statusDialogoProgress:
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
player.setDialogoProgress()
|
||||
|
||||
# -- Diálogos de estado en el visionado -----
|
||||
if not h.is_seed():
|
||||
# -- Recuperar los datos del progreso ---
|
||||
message, porcent, msg_file, s, download = getProgress(h, video_file, _pf=_pieces_info)
|
||||
dp.update(porcent, message, msg_file)
|
||||
else:
|
||||
dp.update(100, "Descarga completa: " + video_file)
|
||||
|
||||
# -- Se canceló el progreso en el visionado -
|
||||
# -- Continuar -
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
player.pause()
|
||||
|
||||
# -- Se canceló el progreso en el visionado -
|
||||
# -- en la ventana de 'pause' automático. -
|
||||
# -- Parar si el contador llega a 3 -
|
||||
if dp.iscanceled() and is_greater_num_pieces_pause:
|
||||
is_greater_num_pieces_canceled += 1
|
||||
if is_greater_num_pieces_canceled == 3:
|
||||
player.stop()
|
||||
|
||||
# -- Desactiva 'pause' automático y ---------
|
||||
# -- reinicia el contador de cancelaciones -
|
||||
if not dp.iscanceled() and not is_greater_num_pieces_plus and is_greater_num_pieces_pause:
|
||||
dp.close()
|
||||
player.pause()
|
||||
is_greater_num_pieces_pause = False
|
||||
is_greater_num_pieces_canceled = 0
|
||||
|
||||
# -- El usuario cancelo el visionado --------
|
||||
# -- Terminar -
|
||||
if player.ended:
|
||||
# -- Diálogo eliminar archivos ----------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
|
||||
# -- Kodi - Se cerró el visionado -----------------------
|
||||
# -- Continuar | Terminar -
|
||||
if is_view == "Ok" and not xbmc.Player().isPlaying():
|
||||
|
||||
if info.num_files() == 1:
|
||||
# -- Diálogo continuar o terminar ---------------
|
||||
d = xbmcgui.Dialog()
|
||||
ok = d.yesno('alfa-MCT', 'XBMC-Kodi Cerró el vídeo.', '¿Continuar con la sesión?')
|
||||
else:
|
||||
ok = False
|
||||
# -- SI ---------------------------------------------
|
||||
if ok:
|
||||
# -- Continuar: ---------------------------------
|
||||
is_view = None
|
||||
else:
|
||||
# -- Terminar: ----------------------------------
|
||||
# -- Comprobar si el vídeo pertenece a una ------
|
||||
# -- lista de archivos -
|
||||
_index, video_file, video_size, len_files = get_video_files_sizes(info)
|
||||
if _index == -1 or len_files == 1:
|
||||
# -- Diálogo eliminar archivos --------------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
else:
|
||||
# -- Lista de archivos. Diálogo de opciones -
|
||||
piece_set = set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate)
|
||||
is_view = None
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
|
||||
# -- Mostar progeso antes del visionado -----------------
|
||||
if is_view != "Ok":
|
||||
dp.update(porcent, message, msg_file)
|
||||
|
||||
# -- Se canceló el progreso antes del visionado ---------
|
||||
# -- Terminar -
|
||||
if dp.iscanceled():
|
||||
dp.close()
|
||||
# -- Comprobar si el vídeo pertenece a una lista de -
|
||||
# -- archivos -
|
||||
_index, video_file, video_size, len_files = get_video_files_sizes(info)
|
||||
if _index == -1 or len_files == 1:
|
||||
# -- Diálogo eliminar archivos ------------------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
return
|
||||
else:
|
||||
# -- Lista de archivos. Diálogo de opciones -----
|
||||
piece_set = set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate)
|
||||
is_view = None
|
||||
dp = xbmcgui.DialogProgress()
|
||||
dp.create('alfa-MCT')
|
||||
|
||||
# -- Kodi - Error? - No debería llegar aquí -----------------
|
||||
if is_view == "Ok" and not xbmc.Player().isPlaying():
|
||||
dp.close()
|
||||
# -- Diálogo eliminar archivos --------------------------
|
||||
remove_files(download, torrent_file, video_file, ses, h)
|
||||
|
||||
return
|
||||
|
||||
|
||||
# -- Progreso de la descarga ------------------------------------
|
||||
def getProgress(h, video_file, _pf={}):
|
||||
if len(_pf) > 0:
|
||||
_pf_msg = "[%s] [%s] %s[%s] [%s]" % (
|
||||
_pf['current'], _pf['continuous'], _pf['continuous2'], _pf['have'], _pf['len'])
|
||||
else:
|
||||
_pf_msg = ""
|
||||
|
||||
s = h.status()
|
||||
|
||||
state_str = ['queued', 'checking', 'downloading metadata', \
|
||||
'downloading', 'finished', 'seeding', 'allocating', 'checking fastresume']
|
||||
|
||||
message = '%.2f%% d:%.1f kb/s u:%.1f kb/s p:%d s:%d %s' % \
|
||||
(s.progress * 100, s.download_rate / 1000, s.upload_rate / 1000, \
|
||||
s.num_peers, s.num_seeds, state_str[s.state])
|
||||
porcent = int(s.progress * 100)
|
||||
|
||||
download = (s.progress * 100)
|
||||
|
||||
if "/" in video_file: video_file = video_file.split("/")[1]
|
||||
msg_file = video_file
|
||||
|
||||
if len(msg_file) > 50:
|
||||
msg_file = msg_file.replace(video_file,
|
||||
os.path.splitext(video_file)[0][:40] + "... " + os.path.splitext(video_file)[1])
|
||||
msg_file = msg_file + "[CR]" + "%.2f MB" % (s.total_wanted / 1048576.0) + " - " + _pf_msg
|
||||
|
||||
return (message, porcent, msg_file, s, download)
|
||||
|
||||
|
||||
# -- Clase play_video - Controlar eventos -----------------------
|
||||
class play_video(xbmc.Player):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.paused = False
|
||||
self.resumed = True
|
||||
self.statusDialogoProgress = False
|
||||
self.ended = False
|
||||
|
||||
def onPlayBackPaused(self):
|
||||
self.paused = True
|
||||
self.resumed = False
|
||||
|
||||
def onPlayBackResumed(self):
|
||||
self.paused = False
|
||||
self.resumed = True
|
||||
self.statusDialogoProgress = False
|
||||
|
||||
def is_paused(self):
|
||||
return self.paused
|
||||
|
||||
def setDialogoProgress(self):
|
||||
self.statusDialogoProgress = True
|
||||
|
||||
def is_started(self):
|
||||
self.ended = False
|
||||
|
||||
def is_ended(self):
|
||||
self.ended = True
|
||||
|
||||
|
||||
# -- Conseguir el nombre un alchivo de vídeo del metadata -------
|
||||
# -- El más gordo o uno de los más gordo se entiende que es el -
|
||||
# -- vídeo o es vídeo que se usará como referencia para el tipo -
|
||||
# -- de archivo -
|
||||
def get_video_file(info):
|
||||
size_file = 0
|
||||
for i, f in enumerate(info.files()):
|
||||
if f.size > size_file:
|
||||
video_file = f.path.replace("\\", "/")
|
||||
size_file = f.size
|
||||
index_file = i
|
||||
return index_file, video_file, size_file
|
||||
|
||||
|
||||
# -- Listado de selección del vídeo a prioritarizar -------------
|
||||
def get_video_files_sizes(info):
|
||||
opciones = {}
|
||||
vfile_name = {}
|
||||
vfile_size = {}
|
||||
|
||||
# -- Eliminar errores con tíldes -----------------------------
|
||||
for i, f in enumerate(info.files()):
|
||||
_title = unicode(f.path, "iso-8859-1", errors="replace")
|
||||
_title = unicode(f.path, "'utf-8'", errors="replace")
|
||||
|
||||
extensions_list = ['.aaf', '.3gp', '.asf', '.avi', '.flv', '.mpeg',
|
||||
'.m1v', '.m2v', '.m4v', '.mkv', '.mov', '.mpg',
|
||||
'.mpe', '.mp4', '.ogg', '.rar', '.wmv', '.zip']
|
||||
|
||||
for i, f in enumerate(info.files()):
|
||||
_index = int(i)
|
||||
_title = f.path.replace("\\", "/")
|
||||
_size = f.size
|
||||
|
||||
_file_name = os.path.splitext(_title)[0]
|
||||
if "/" in _file_name: _file_name = _file_name.split('/')[1]
|
||||
|
||||
_file_ext = os.path.splitext(_title)[1]
|
||||
|
||||
if _file_ext in extensions_list:
|
||||
index = len(opciones)
|
||||
_caption = str(index) + \
|
||||
" - " + \
|
||||
_file_name + _file_ext + \
|
||||
" - %.2f MB" % (_size / 1048576.0)
|
||||
|
||||
vfile_name[index] = _title
|
||||
vfile_size[index] = _size
|
||||
|
||||
opciones[i] = _caption
|
||||
|
||||
if len(opciones) > 1:
|
||||
d = xbmcgui.Dialog()
|
||||
seleccion = d.select("alfa-MCT: Lista de vídeos", opciones.values())
|
||||
else:
|
||||
seleccion = 0
|
||||
|
||||
index = opciones.keys()[seleccion]
|
||||
if seleccion == -1:
|
||||
vfile_name[seleccion] = ""
|
||||
vfile_size[seleccion] = 0
|
||||
index = seleccion
|
||||
|
||||
return index, vfile_name[seleccion], vfile_size[seleccion], len(opciones)
|
||||
|
||||
|
||||
# -- Preguntar si se desea borrar lo descargado -----------------
|
||||
def remove_files(download, torrent_file, video_file, ses, h):
|
||||
dialog_view = False
|
||||
torrent = False
|
||||
|
||||
if os.path.isfile(torrent_file):
|
||||
dialog_view = True
|
||||
torrent = True
|
||||
|
||||
if download > 0:
|
||||
dialog_view = True
|
||||
|
||||
if "/" in video_file: video_file = video_file.split("/")[0]
|
||||
|
||||
if dialog_view:
|
||||
d = xbmcgui.Dialog()
|
||||
ok = d.yesno('alfa-MCT', 'Borrar las descargas del video', video_file)
|
||||
|
||||
# -- SI -------------------------------------------------
|
||||
if ok:
|
||||
# -- Borrar archivo - torrent -----------------------
|
||||
if torrent:
|
||||
os.remove(torrent_file)
|
||||
# -- Borrar carpeta/archivos y sesión - vídeo -------
|
||||
ses.remove_torrent(h, 1)
|
||||
xbmc.log("### End session #########")
|
||||
else:
|
||||
# -- Borrar sesión ----------------------------------
|
||||
ses.remove_torrent(h)
|
||||
xbmc.log("### End session #########")
|
||||
else:
|
||||
# -- Borrar sesión --------------------------------------
|
||||
ses.remove_torrent(h)
|
||||
xbmc.log("### End session #########")
|
||||
|
||||
return
|
||||
|
||||
|
||||
# -- Descargar de la web los datos para crear el torrent --------
|
||||
# -- Si queremos aligerar el script mct.py se puede importar la -
|
||||
# -- función del conentor torrent.py -
|
||||
def url_get(url, params={}, headers={}):
|
||||
from contextlib import closing
|
||||
|
||||
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:20.0) Gecko/20100101 Firefox/20.0"
|
||||
|
||||
if params:
|
||||
import urllib
|
||||
url = "%s?%s" % (url, urllib.urlencode(params))
|
||||
|
||||
req = urllib2.Request(url)
|
||||
req.add_header("User-Agent", USER_AGENT)
|
||||
|
||||
for k, v in headers.items():
|
||||
req.add_header(k, v)
|
||||
|
||||
try:
|
||||
with closing(urllib2.urlopen(req)) as response:
|
||||
data = response.read()
|
||||
if response.headers.get("Content-Encoding", "") == "gzip":
|
||||
import zlib
|
||||
return zlib.decompressobj(16 + zlib.MAX_WBITS).decompress(data)
|
||||
return data
|
||||
except urllib2.HTTPError:
|
||||
return None
|
||||
|
||||
|
||||
# -- Contar las piezas contiguas completas del vídeo ------------
|
||||
def count_completed_continuous_pieces(h, piece_set):
|
||||
not_zero = 0
|
||||
for i, _set in enumerate(piece_set):
|
||||
if not h.have_piece(_set):
|
||||
break
|
||||
else:
|
||||
not_zero = 1
|
||||
return i + not_zero
|
||||
|
||||
|
||||
# -- Prioritarizar o seleccionar las piezas del archivo que se -
|
||||
# -- desea reproducir con 'file_priorities' estableciendo a 1 -
|
||||
# -- el archivo deseado y a 0 el resto de archivos almacenando -
|
||||
# -- en una lista los índices de de las piezas del archivo -
|
||||
def set_priority_pieces(h, _index, video_file, video_size,
|
||||
porcent4first_pieces, porcent4last_pieces, allocate):
|
||||
for i, _set in enumerate(h.file_priorities()):
|
||||
if i != _index:
|
||||
h.file_priority(i, 0)
|
||||
else:
|
||||
h.file_priority(i, 1)
|
||||
|
||||
piece_set = []
|
||||
for i, _set in enumerate(h.piece_priorities()):
|
||||
if _set == 1: piece_set.append(i)
|
||||
|
||||
if not allocate:
|
||||
for i in range(0, porcent4first_pieces):
|
||||
h.set_piece_deadline(piece_set[i], 10000)
|
||||
|
||||
for i in range(len(piece_set) - porcent4last_pieces, len(piece_set)):
|
||||
h.set_piece_deadline(piece_set[i], 10000)
|
||||
|
||||
return piece_set
|
||||
|
||||
|
||||
def decode_adfly(data):
|
||||
import base64
|
||||
ysmm = scrapertools.find_single_match(data, "var ysmm = '([^']+)'")
|
||||
left = ''
|
||||
right = ''
|
||||
for c in [ysmm[i:i + 2] for i in range(0, len(ysmm), 2)]:
|
||||
left += c[0]
|
||||
right = c[1] + right
|
||||
|
||||
decoded_url = base64.b64decode(left.encode() + right.encode())[2:].decode()
|
||||
return decoded_url
|
||||
964
plugin.video.alfa/platformcode/platformtools.py
Executable file
964
plugin.video.alfa/platformcode/platformtools.py
Executable file
@@ -0,0 +1,964 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# platformtools
|
||||
# ------------------------------------------------------------
|
||||
# Herramientas responsables de adaptar los diferentes
|
||||
# cuadros de dialogo a una plataforma en concreto,
|
||||
# en este caso Kodi.
|
||||
# version 2.0
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
from core import config
|
||||
from core import logger
|
||||
from core.item import Item
|
||||
|
||||
|
||||
def dialog_ok(heading, line1, line2="", line3=""):
|
||||
dialog = xbmcgui.Dialog()
|
||||
return dialog.ok(heading, line1, line2, line3)
|
||||
|
||||
|
||||
def dialog_notification(heading, message, icon=0, time=5000, sound=True):
|
||||
dialog = xbmcgui.Dialog()
|
||||
try:
|
||||
l_icono = xbmcgui.NOTIFICATION_INFO, xbmcgui.NOTIFICATION_WARNING, xbmcgui.NOTIFICATION_ERROR
|
||||
dialog.notification(heading, message, l_icono[icon], time, sound)
|
||||
except:
|
||||
dialog_ok(heading, message)
|
||||
|
||||
|
||||
def dialog_yesno(heading, line1, line2="", line3="", nolabel="No", yeslabel="Si", autoclose=""):
|
||||
dialog = xbmcgui.Dialog()
|
||||
if autoclose:
|
||||
return dialog.yesno(heading, line1, line2, line3, nolabel, yeslabel, autoclose)
|
||||
else:
|
||||
return dialog.yesno(heading, line1, line2, line3, nolabel, yeslabel)
|
||||
|
||||
|
||||
def dialog_select(heading, _list):
|
||||
return xbmcgui.Dialog().select(heading, _list)
|
||||
|
||||
|
||||
def dialog_progress(heading, line1, line2=" ", line3=" "):
|
||||
dialog = xbmcgui.DialogProgress()
|
||||
dialog.create(heading, line1, line2, line3)
|
||||
return dialog
|
||||
|
||||
|
||||
def dialog_progress_bg(heading, message=""):
|
||||
try:
|
||||
dialog = xbmcgui.DialogProgressBG()
|
||||
dialog.create(heading, message)
|
||||
return dialog
|
||||
except:
|
||||
return dialog_progress(heading, message)
|
||||
|
||||
|
||||
def dialog_input(default="", heading="", hidden=False):
|
||||
keyboard = xbmc.Keyboard(default, heading, hidden)
|
||||
keyboard.doModal()
|
||||
if keyboard.isConfirmed():
|
||||
return keyboard.getText()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def dialog_numeric(_type, heading, default=""):
|
||||
dialog = xbmcgui.Dialog()
|
||||
d = dialog.numeric(_type, heading, default)
|
||||
return d
|
||||
|
||||
|
||||
def itemlist_refresh():
|
||||
xbmc.executebuiltin("Container.Refresh")
|
||||
|
||||
|
||||
def itemlist_update(item):
|
||||
xbmc.executebuiltin("Container.Update(" + sys.argv[0] + "?" + item.tourl() + ")")
|
||||
|
||||
|
||||
def render_items(itemlist, parent_item):
|
||||
"""
|
||||
Función encargada de mostrar el itemlist en kodi, se pasa como parametros el itemlist y el item del que procede
|
||||
@type itemlist: list
|
||||
@param itemlist: lista de elementos a mostrar
|
||||
|
||||
@type parent_item: item
|
||||
@param parent_item: elemento padre
|
||||
"""
|
||||
# Si el itemlist no es un list salimos
|
||||
if not type(itemlist) == list:
|
||||
return
|
||||
|
||||
# Si no hay ningun item, mostramos un aviso
|
||||
if not len(itemlist):
|
||||
itemlist.append(Item(title="No hay elementos que mostrar"))
|
||||
|
||||
# Recorremos el itemlist
|
||||
for item in itemlist:
|
||||
# logger.debug(item)
|
||||
# Si el item no contiene categoria, le ponemos la del item padre
|
||||
if item.category == "":
|
||||
item.category = parent_item.category
|
||||
|
||||
# Si el item no contiene fanart, le ponemos el del item padre
|
||||
if item.fanart == "":
|
||||
item.fanart = parent_item.fanart
|
||||
|
||||
# Formatear titulo
|
||||
if item.text_color:
|
||||
item.title = '[COLOR %s]%s[/COLOR]' % (item.text_color, item.title)
|
||||
if item.text_bold:
|
||||
item.title = '[B]%s[/B]' % item.title
|
||||
if item.text_italic:
|
||||
item.title = '[I]%s[/I]' % item.title
|
||||
|
||||
# Añade headers a las imagenes si estan en un servidor con cloudflare
|
||||
from core import httptools
|
||||
item.thumbnail = httptools.get_url_headers(item.thumbnail)
|
||||
item.fanart = httptools.get_url_headers(item.fanart)
|
||||
|
||||
# IconImage para folder y video
|
||||
if item.folder:
|
||||
icon_image = "DefaultFolder.png"
|
||||
else:
|
||||
icon_image = "DefaultVideo.png"
|
||||
|
||||
# Creamos el listitem
|
||||
listitem = xbmcgui.ListItem(item.title, iconImage=icon_image, thumbnailImage=item.thumbnail)
|
||||
|
||||
# Ponemos el fanart
|
||||
if item.fanart:
|
||||
listitem.setProperty('fanart_image', item.fanart)
|
||||
else:
|
||||
listitem.setProperty('fanart_image', os.path.join(config.get_runtime_path(), "fanart.jpg"))
|
||||
|
||||
# TODO: ¿Se puede eliminar esta linea? yo no he visto que haga ningun efecto.
|
||||
xbmcplugin.setPluginFanart(int(sys.argv[1]), os.path.join(config.get_runtime_path(), "fanart.jpg"))
|
||||
|
||||
# Añadimos los infoLabels
|
||||
set_infolabels(listitem, item)
|
||||
|
||||
# Montamos el menu contextual
|
||||
context_commands = set_context_commands(item, parent_item)
|
||||
|
||||
# Añadimos el item
|
||||
listitem.addContextMenuItems(context_commands, replaceItems=True)
|
||||
|
||||
if not item.totalItems:
|
||||
item.totalItems = 0
|
||||
xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url='%s?%s' % (sys.argv[0], item.tourl()),
|
||||
listitem=listitem, isFolder=item.folder,
|
||||
totalItems=item.totalItems)
|
||||
|
||||
# Fijar los tipos de vistas...
|
||||
if config.get_setting("forceview") == True:
|
||||
# ...forzamos segun el viewcontent
|
||||
xbmcplugin.setContent(int(sys.argv[1]), parent_item.viewcontent)
|
||||
# logger.debug(parent_item)
|
||||
elif parent_item.channel not in ["channelselector", ""]:
|
||||
# ... o segun el canal
|
||||
xbmcplugin.setContent(int(sys.argv[1]), "movies")
|
||||
|
||||
# Fijamos el "breadcrumb"
|
||||
xbmcplugin.setPluginCategory(handle=int(sys.argv[1]), category=parent_item.category.capitalize())
|
||||
|
||||
# No ordenar items
|
||||
xbmcplugin.addSortMethod(handle=int(sys.argv[1]), sortMethod=xbmcplugin.SORT_METHOD_NONE)
|
||||
|
||||
# Cerramos el directorio
|
||||
xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True)
|
||||
|
||||
# Fijar la vista
|
||||
if config.get_setting("forceview") == True:
|
||||
viewmode_id = get_viewmode_id(parent_item)
|
||||
xbmc.executebuiltin("Container.SetViewMode(%s)" % viewmode_id)
|
||||
|
||||
|
||||
def get_viewmode_id(parent_item):
|
||||
# viewmode_json habria q guardarlo en un archivo y crear un metodo para q el user fije sus preferencias en:
|
||||
# user_files, user_movies, user_tvshows, user_season y user_episodes.
|
||||
viewmode_json = {'skin.confluence': {'default_files': 50,
|
||||
'default_movies': 515,
|
||||
'default_tvshows': 508,
|
||||
'default_seasons': 503,
|
||||
'default_episodes': 504,
|
||||
'view_list': 50,
|
||||
'view_thumbnails': 500,
|
||||
'view_movie_with_plot': 503},
|
||||
'skin.estuary': {'default_files': 50,
|
||||
'default_movies': 54,
|
||||
'default_tvshows': 502,
|
||||
'default_seasons': 500,
|
||||
'default_episodes': 53,
|
||||
'view_list': 50,
|
||||
'view_thumbnails': 500,
|
||||
'view_movie_with_plot': 54}}
|
||||
|
||||
# Si el parent_item tenia fijado un viewmode usamos esa vista...
|
||||
if parent_item.viewmode == 'movie':
|
||||
# Remplazamos el antiguo viewmode 'movie' por 'thumbnails'
|
||||
parent_item.viewmode = 'thumbnails'
|
||||
|
||||
if parent_item.viewmode in ["list", "movie_with_plot", "thumbnails"]:
|
||||
view_name = "view_" + parent_item.viewmode
|
||||
|
||||
'''elif isinstance(parent_item.viewmode, int):
|
||||
# only for debug
|
||||
viewName = parent_item.viewmode'''
|
||||
|
||||
# ...sino ponemos la vista por defecto en funcion del viewcontent
|
||||
else:
|
||||
view_name = "default_" + parent_item.viewcontent
|
||||
|
||||
skin_name = xbmc.getSkinDir()
|
||||
if skin_name not in viewmode_json:
|
||||
skin_name = 'skin.confluence'
|
||||
view_skin = viewmode_json[skin_name]
|
||||
return view_skin.get(view_name, 50)
|
||||
|
||||
|
||||
def set_infolabels(listitem, item, player=False):
|
||||
"""
|
||||
Metodo para pasar la informacion al listitem (ver tmdb.set_InfoLabels() )
|
||||
item.infoLabels es un dicionario con los pares de clave/valor descritos en:
|
||||
http://mirrors.xbmc.org/docs/python-docs/14.x-helix/xbmcgui.html#ListItem-setInfo
|
||||
@param listitem: objeto xbmcgui.ListItem
|
||||
@type listitem: xbmcgui.ListItem
|
||||
@param item: objeto Item que representa a una pelicula, serie o capitulo
|
||||
@type item: item
|
||||
"""
|
||||
if item.infoLabels:
|
||||
if 'mediatype' not in item.infoLabels:
|
||||
item.infoLabels['mediatype'] = item.contentType
|
||||
listitem.setInfo("video", item.infoLabels)
|
||||
|
||||
if player and not item.contentTitle:
|
||||
if item.fulltitle:
|
||||
listitem.setInfo("video", {"Title": item.fulltitle})
|
||||
else:
|
||||
listitem.setInfo("video", {"Title": item.title})
|
||||
|
||||
elif not player:
|
||||
listitem.setInfo("video", {"Title": item.title})
|
||||
|
||||
# Añadido para Kodi Krypton (v17)
|
||||
if config.get_platform(True)['num_version'] >= 17.0:
|
||||
listitem.setArt({"poster": item.thumbnail})
|
||||
|
||||
|
||||
def set_context_commands(item, parent_item):
|
||||
"""
|
||||
Función para generar los menus contextuales.
|
||||
1. Partiendo de los datos de item.context
|
||||
a. Metodo antiguo item.context tipo str separando las opciones por "|" (ejemplo: item.context = "1|2|3")
|
||||
(solo predefinidos)
|
||||
b. Metodo list: item.context es un list con las diferentes opciones del menu:
|
||||
- Predefinidos: Se cargara una opcion predefinida con un nombre.
|
||||
item.context = ["1","2","3"]
|
||||
|
||||
- dict(): Se cargara el item actual modificando los campos que se incluyan en el dict() en caso de
|
||||
modificar los campos channel y action estos serán guardados en from_channel y from_action.
|
||||
item.context = [{"title":"Nombre del menu", "action": "action del menu",
|
||||
"channel":"channel del menu"}, {...}]
|
||||
|
||||
2. Añadiendo opciones segun criterios
|
||||
Se pueden añadir opciones al menu contextual a items que cumplan ciertas condiciones.
|
||||
|
||||
|
||||
3. Añadiendo opciones a todos los items
|
||||
Se pueden añadir opciones al menu contextual para todos los items
|
||||
|
||||
4. Se pueden deshabilitar las opciones del menu contextual añadiendo un comando 'no_context' al item.context.
|
||||
Las opciones que Kodi, el skin u otro añadido añada al menu contextual no se pueden deshabilitar.
|
||||
|
||||
@param item: elemento que contiene los menu contextuales
|
||||
@type item: item
|
||||
@param parent_item:
|
||||
@type parent_item: item
|
||||
"""
|
||||
context_commands = []
|
||||
num_version_xbmc = config.get_platform(True)['num_version']
|
||||
|
||||
# Creamos un list con las diferentes opciones incluidas en item.context
|
||||
if type(item.context) == str:
|
||||
context = item.context.split("|")
|
||||
elif type(item.context) == list:
|
||||
context = item.context
|
||||
else:
|
||||
context = []
|
||||
|
||||
# Opciones segun item.context
|
||||
for command in context:
|
||||
# Predefinidos
|
||||
if type(command) == str:
|
||||
if command == "no_context":
|
||||
return []
|
||||
|
||||
# Formato dict
|
||||
if type(command) == dict:
|
||||
# Los parametros del dict, se sobreescriben al nuevo context_item en caso de sobreescribir "action" y
|
||||
# "channel", los datos originales se guardan en "from_action" y "from_channel"
|
||||
if "action" in command:
|
||||
command["from_action"] = item.action
|
||||
if "channel" in command:
|
||||
command["from_channel"] = item.channel
|
||||
|
||||
if "goto" in command:
|
||||
context_commands.append((command["title"], "XBMC.Container.Refresh (%s?%s)" %
|
||||
(sys.argv[0], item.clone(**command).tourl())))
|
||||
else:
|
||||
context_commands.append(
|
||||
(command["title"], "XBMC.RunPlugin(%s?%s)" % (sys.argv[0], item.clone(**command).tourl())))
|
||||
|
||||
# Opciones segun criterios, solo si el item no es un tag (etiqueta), ni es "Añadir a la videoteca", etc...
|
||||
if item.action and item.action not in ["add_pelicula_to_library", "add_serie_to_library", "buscartrailer"]:
|
||||
# Mostrar informacion: si el item tiene plot suponemos q es una serie, temporada, capitulo o pelicula
|
||||
if item.infoLabels['plot'] and (num_version_xbmc < 17.0 or item.contentType == 'season'):
|
||||
context_commands.append(("Información", "XBMC.Action(Info)"))
|
||||
|
||||
# ExtendedInfo: Si esta instalado el addon y se cumplen una serie de condiciones
|
||||
if xbmc.getCondVisibility('System.HasAddon(script.extendedinfo)') \
|
||||
and config.get_setting("extended_info") == True:
|
||||
if item.contentType == "episode" and item.contentEpisodeNumber and item.contentSeason \
|
||||
and (item.infoLabels['tmdb_id'] or item.contentSerieName):
|
||||
param = "tvshow_id =%s, tvshow=%s, season=%s, episode=%s" \
|
||||
% (item.infoLabels['tmdb_id'], item.contentSerieName, item.contentSeason,
|
||||
item.contentEpisodeNumber)
|
||||
context_commands.append(("ExtendedInfo",
|
||||
"XBMC.RunScript(script.extendedinfo,info=extendedepisodeinfo,%s)" % param))
|
||||
|
||||
elif item.contentType == "season" and item.contentSeason \
|
||||
and (item.infoLabels['tmdb_id'] or item.contentSerieName):
|
||||
param = "tvshow_id =%s,tvshow=%s, season=%s" \
|
||||
% (item.infoLabels['tmdb_id'], item.contentSerieName, item.contentSeason)
|
||||
context_commands.append(("ExtendedInfo",
|
||||
"XBMC.RunScript(script.extendedinfo,info=seasoninfo,%s)" % param))
|
||||
|
||||
elif item.contentType == "tvshow" and (item.infoLabels['tmdb_id'] or item.infoLabels['tvdb_id'] or
|
||||
item.infoLabels['imdb_id'] or item.contentSerieName):
|
||||
param = "id =%s,tvdb_id=%s,imdb_id=%s,name=%s" \
|
||||
% (item.infoLabels['tmdb_id'], item.infoLabels['tvdb_id'], item.infoLabels['imdb_id'],
|
||||
item.contentSerieName)
|
||||
context_commands.append(("ExtendedInfo",
|
||||
"XBMC.RunScript(script.extendedinfo,info=extendedtvinfo,%s)" % param))
|
||||
|
||||
elif item.contentType == "movie" and (item.infoLabels['tmdb_id'] or item.infoLabels['imdb_id'] or
|
||||
item.contentTitle):
|
||||
param = "id =%s,imdb_id=%s,name=%s" \
|
||||
% (item.infoLabels['tmdb_id'], item.infoLabels['imdb_id'], item.contentTitle)
|
||||
context_commands.append(("ExtendedInfo",
|
||||
"XBMC.RunScript(script.extendedinfo,info=extendedinfo,%s)" % param))
|
||||
|
||||
# InfoPlus
|
||||
if config.get_setting("infoplus") == True:
|
||||
if item.infoLabels['tmdb_id'] or item.infoLabels['imdb_id'] or item.infoLabels['tvdb_id'] or \
|
||||
(item.contentTitle and item.infoLabels["year"]) or item.contentSerieName:
|
||||
context_commands.append(("InfoPlus", "XBMC.RunPlugin(%s?%s)" % (sys.argv[0], item.clone(
|
||||
channel="infoplus", action="start", from_channel=item.channel).tourl())))
|
||||
|
||||
# Ir al Menu Principal (channel.mainlist)
|
||||
if parent_item.channel not in ["news", "channelselector"] and item.action != "mainlist" \
|
||||
and parent_item.action != "mainlist":
|
||||
context_commands.append(("Ir al Menu Principal", "XBMC.Container.Refresh (%s?%s)" %
|
||||
(sys.argv[0], Item(channel=item.channel, action="mainlist").tourl())))
|
||||
|
||||
# Añadir a Favoritos
|
||||
if num_version_xbmc < 17.0 and \
|
||||
((item.channel not in ["favorites", "videolibrary", "help", ""]
|
||||
or item.action in ["update_videolibrary"]) and parent_item.channel != "favorites"):
|
||||
context_commands.append((config.get_localized_string(30155), "XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(channel="favoritos", action="addFavourite",
|
||||
from_channel=item.channel,
|
||||
from_action=item.action).tourl())))
|
||||
|
||||
if item.channel != "videolibrary":
|
||||
# Añadir Serie a la videoteca
|
||||
if item.action in ["episodios", "get_episodios"] and item.contentSerieName:
|
||||
context_commands.append(("Añadir Serie a Videoteca", "XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(action="add_serie_to_library",
|
||||
from_action=item.action).tourl())))
|
||||
# Añadir Pelicula a videoteca
|
||||
elif item.action in ["detail", "findvideos"] and item.contentType == 'movie' and item.contentTitle:
|
||||
context_commands.append(("Añadir Pelicula a Videoteca", "XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(action="add_pelicula_to_library",
|
||||
from_action=item.action).tourl())))
|
||||
|
||||
if item.channel != "downloads":
|
||||
# Descargar pelicula
|
||||
if item.contentType == "movie" and item.contentTitle:
|
||||
context_commands.append(("Descargar Pelicula", "XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(channel="downloads", action="save_download",
|
||||
from_channel=item.channel, from_action=item.action)
|
||||
.tourl())))
|
||||
|
||||
elif item.contentSerieName:
|
||||
# Descargar serie
|
||||
if item.contentType == "tvshow":
|
||||
context_commands.append(("Descargar Serie", "XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(channel="downloads", action="save_download",
|
||||
from_channel=item.channel,
|
||||
from_action=item.action).tourl())))
|
||||
|
||||
# Descargar episodio
|
||||
if item.contentType == "episode":
|
||||
context_commands.append(("Descargar Episodio", "XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(channel="downloads", action="save_download",
|
||||
from_channel=item.channel,
|
||||
from_action=item.action).tourl())))
|
||||
|
||||
# Descargar temporada
|
||||
if item.contentType == "season":
|
||||
context_commands.append(("Descargar Temporada", "XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(channel="downloads", action="save_download",
|
||||
from_channel=item.channel,
|
||||
from_action=item.action).tourl())))
|
||||
|
||||
# Abrir configuración
|
||||
if parent_item.channel not in ["setting", "news", "search"]:
|
||||
context_commands.append(("Abrir Configuración", "XBMC.Container.Update(%s?%s)" %
|
||||
(sys.argv[0], Item(channel="setting", action="mainlist").tourl())))
|
||||
|
||||
# Buscar Trailer
|
||||
if item.action == "findvideos" or "buscar_trailer" in context:
|
||||
context_commands.append(("Buscar Trailer", "XBMC.RunPlugin(%s?%s)" % (sys.argv[0], item.clone(
|
||||
channel="trailertools", action="buscartrailer", contextual=True).tourl())))
|
||||
|
||||
# Añadir SuperFavourites al menu contextual (1.0.53 o superior necesario)
|
||||
sf_file_path = xbmc.translatePath("special://home/addons/plugin.program.super.favourites/LaunchSFMenu.py")
|
||||
check_sf = os.path.exists(sf_file_path)
|
||||
if check_sf and xbmc.getCondVisibility('System.HasAddon("plugin.program.super.favourites")'):
|
||||
context_commands.append(("Super Favourites Menu",
|
||||
"XBMC.RunScript(special://home/addons/plugin.program.super.favourites/LaunchSFMenu.py)"))
|
||||
|
||||
return sorted(context_commands, key=lambda comand: comand[0])
|
||||
|
||||
|
||||
def is_playing():
|
||||
return xbmc.Player().isPlaying()
|
||||
|
||||
|
||||
def play_video(item, strm=False):
|
||||
logger.info()
|
||||
# logger.debug(item.tostring('\n'))
|
||||
|
||||
if item.channel == 'downloads':
|
||||
logger.info("Reproducir video local: %s [%s]" % (item.title, item.url))
|
||||
xlistitem = xbmcgui.ListItem(path=item.url, thumbnailImage=item.thumbnail)
|
||||
set_infolabels(xlistitem, item, True)
|
||||
xbmc.Player().play(item.url, xlistitem)
|
||||
return
|
||||
|
||||
default_action = config.get_setting("default_action")
|
||||
logger.info("default_action=%s" % default_action)
|
||||
|
||||
# Abre el diálogo de selección para ver las opciones disponibles
|
||||
opciones, video_urls, seleccion, salir = get_dialogo_opciones(item, default_action, strm)
|
||||
if salir:
|
||||
return
|
||||
|
||||
# se obtienen la opción predeterminada de la configuración del addon
|
||||
seleccion = get_seleccion(default_action, opciones, seleccion, video_urls)
|
||||
if seleccion < 0: # Cuadro cancelado
|
||||
return
|
||||
|
||||
logger.info("seleccion=%d" % seleccion)
|
||||
logger.info("seleccion=%s" % opciones[seleccion])
|
||||
|
||||
# se ejecuta la opcion disponible, jdwonloader, descarga, favoritos, añadir a la videoteca... SI NO ES PLAY
|
||||
salir = set_opcion(item, seleccion, opciones, video_urls)
|
||||
if salir:
|
||||
return
|
||||
|
||||
# obtenemos el video seleccionado
|
||||
mediaurl, view, mpd = get_video_seleccionado(item, seleccion, video_urls)
|
||||
if mediaurl == "":
|
||||
return
|
||||
|
||||
# se obtiene la información del video.
|
||||
if not item.contentThumbnail:
|
||||
xlistitem = xbmcgui.ListItem(path=mediaurl, thumbnailImage=item.thumbnail)
|
||||
else:
|
||||
xlistitem = xbmcgui.ListItem(path=mediaurl, thumbnailImage=item.contentThumbnail)
|
||||
set_infolabels(xlistitem, item, True)
|
||||
|
||||
# si se trata de un vídeo en formato mpd, se configura el listitem para reproducirlo
|
||||
# con el addon inpustreamaddon implementado en Kodi 17
|
||||
if mpd:
|
||||
xlistitem.setProperty('inputstreamaddon', 'inputstream.adaptive')
|
||||
xlistitem.setProperty('inputstream.adaptive.manifest_type', 'mpd')
|
||||
|
||||
# se lanza el reproductor
|
||||
set_player(item, xlistitem, mediaurl, view, strm)
|
||||
|
||||
|
||||
def stop_video():
|
||||
xbmc.Player().stop()
|
||||
|
||||
|
||||
def get_seleccion(default_action, opciones, seleccion, video_urls):
|
||||
# preguntar
|
||||
if default_action == 0:
|
||||
# "Elige una opción"
|
||||
seleccion = dialog_select(config.get_localized_string(30163), opciones)
|
||||
# Ver en calidad baja
|
||||
elif default_action == 1:
|
||||
seleccion = 0
|
||||
# Ver en alta calidad
|
||||
elif default_action == 2:
|
||||
seleccion = len(video_urls) - 1
|
||||
else:
|
||||
seleccion = 0
|
||||
return seleccion
|
||||
|
||||
|
||||
def show_channel_settings(**kwargs):
|
||||
"""
|
||||
Muestra un cuadro de configuracion personalizado para cada canal y guarda los datos al cerrarlo.
|
||||
|
||||
Los parámetros que se le pasan se puede ver en la el método al que se llama
|
||||
|
||||
@return: devuelve la ventana con los elementos
|
||||
@rtype: SettingsWindow
|
||||
"""
|
||||
from xbmc_config_menu import SettingsWindow
|
||||
return SettingsWindow("ChannelSettings.xml", config.get_runtime_path()).start(**kwargs)
|
||||
|
||||
|
||||
def show_video_info(*args, **kwargs):
|
||||
"""
|
||||
Muestra una ventana con la info del vídeo.
|
||||
|
||||
Los parámetros que se le pasan se puede ver en la el método al que se llama
|
||||
|
||||
@return: devuelve la ventana con los elementos
|
||||
@rtype: InfoWindow
|
||||
"""
|
||||
|
||||
from xbmc_info_window import InfoWindow
|
||||
return InfoWindow("InfoWindow.xml", config.get_runtime_path()).start(*args, **kwargs)
|
||||
|
||||
|
||||
def show_recaptcha(key, referer):
|
||||
from recaptcha import Recaptcha
|
||||
return Recaptcha("Recaptcha.xml", config.get_runtime_path()).Start(key, referer)
|
||||
|
||||
|
||||
def alert_no_disponible_server(server):
|
||||
# 'El vídeo ya no está en %s' , 'Prueba en otro servidor o en otro canal'
|
||||
dialog_ok(config.get_localized_string(30055), (config.get_localized_string(30057) % server),
|
||||
config.get_localized_string(30058))
|
||||
|
||||
|
||||
def alert_unsopported_server():
|
||||
# 'Servidor no soportado o desconocido' , 'Prueba en otro servidor o en otro canal'
|
||||
dialog_ok(config.get_localized_string(30065), config.get_localized_string(30058))
|
||||
|
||||
|
||||
def handle_wait(time_to_wait, title, text):
|
||||
logger.info("handle_wait(time_to_wait=%d)" % time_to_wait)
|
||||
espera = dialog_progress(' ' + title, "")
|
||||
|
||||
secs = 0
|
||||
increment = int(100 / time_to_wait)
|
||||
|
||||
cancelled = False
|
||||
while secs < time_to_wait:
|
||||
secs += 1
|
||||
percent = increment * secs
|
||||
secs_left = str((time_to_wait - secs))
|
||||
remaining_display = ' Espera ' + secs_left + ' segundos para que comience el vídeo...'
|
||||
espera.update(percent, ' ' + text, remaining_display)
|
||||
xbmc.sleep(1000)
|
||||
if espera.iscanceled():
|
||||
cancelled = True
|
||||
break
|
||||
|
||||
if cancelled:
|
||||
logger.info('Espera cancelada')
|
||||
return False
|
||||
else:
|
||||
logger.info('Espera finalizada')
|
||||
return True
|
||||
|
||||
|
||||
def get_dialogo_opciones(item, default_action, strm):
|
||||
logger.info()
|
||||
# logger.debug(item.tostring('\n'))
|
||||
from core import servertools
|
||||
|
||||
opciones = []
|
||||
error = False
|
||||
|
||||
try:
|
||||
item.server = item.server.lower()
|
||||
except AttributeError:
|
||||
item.server = ""
|
||||
|
||||
if item.server == "":
|
||||
item.server = "directo"
|
||||
|
||||
# Si no es el modo normal, no muestra el diálogo porque cuelga XBMC
|
||||
muestra_dialogo = (config.get_setting("player_mode") == 0 and not strm)
|
||||
|
||||
# Extrae las URL de los vídeos, y si no puedes verlo te dice el motivo
|
||||
# Permitir varias calidades para server "directo"
|
||||
if item.video_urls:
|
||||
video_urls, puedes, motivo = item.video_urls, True, ""
|
||||
else:
|
||||
video_urls, puedes, motivo = servertools.resolve_video_urls_for_playing(
|
||||
item.server, item.url, item.password, muestra_dialogo)
|
||||
|
||||
seleccion = 0
|
||||
# Si puedes ver el vídeo, presenta las opciones
|
||||
if puedes:
|
||||
for video_url in video_urls:
|
||||
opciones.append(config.get_localized_string(30151) + " " + video_url[0])
|
||||
|
||||
if item.server == "local":
|
||||
opciones.append(config.get_localized_string(30164))
|
||||
else:
|
||||
# "Descargar"
|
||||
opcion = config.get_localized_string(30153)
|
||||
opciones.append(opcion)
|
||||
|
||||
if item.isFavourite:
|
||||
# "Quitar de favoritos"
|
||||
opciones.append(config.get_localized_string(30154))
|
||||
else:
|
||||
# "Añadir a favoritos"
|
||||
opciones.append(config.get_localized_string(30155))
|
||||
|
||||
if not strm and item.contentType == 'movie':
|
||||
# "Añadir a videoteca"
|
||||
opciones.append(config.get_localized_string(30161))
|
||||
|
||||
if default_action == "3":
|
||||
seleccion = len(opciones) - 1
|
||||
|
||||
# Busqueda de trailers en youtube
|
||||
if item.channel not in ["Trailer", "ecarteleratrailers"]:
|
||||
# "Buscar Trailer"
|
||||
opciones.append(config.get_localized_string(30162))
|
||||
|
||||
# Si no puedes ver el vídeo te informa
|
||||
else:
|
||||
if item.server != "":
|
||||
if "<br/>" in motivo:
|
||||
dialog_ok("No puedes ver ese vídeo porque...", motivo.split("<br/>")[0], motivo.split("<br/>")[1],
|
||||
item.url)
|
||||
else:
|
||||
dialog_ok("No puedes ver ese vídeo porque...", motivo, item.url)
|
||||
else:
|
||||
dialog_ok("No puedes ver ese vídeo porque...", "El servidor donde está alojado no está",
|
||||
"soportado en alfa todavía", item.url)
|
||||
|
||||
if item.channel == "favoritos":
|
||||
# "Quitar de favoritos"
|
||||
opciones.append(config.get_localized_string(30154))
|
||||
|
||||
if len(opciones) == 0:
|
||||
error = True
|
||||
|
||||
return opciones, video_urls, seleccion, error
|
||||
|
||||
|
||||
def set_opcion(item, seleccion, opciones, video_urls):
|
||||
logger.info()
|
||||
# logger.debug(item.tostring('\n'))
|
||||
salir = False
|
||||
# No ha elegido nada, lo más probable porque haya dado al ESC
|
||||
|
||||
if seleccion == -1:
|
||||
# Para evitar el error "Uno o más elementos fallaron" al cancelar la selección desde fichero strm
|
||||
listitem = xbmcgui.ListItem(item.title, iconImage="DefaultVideo.png", thumbnailImage=item.thumbnail)
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
|
||||
|
||||
# "Descargar"
|
||||
elif opciones[seleccion] == config.get_localized_string(30153):
|
||||
from channels import downloads
|
||||
if item.contentType == "list" or item.contentType == "tvshow":
|
||||
item.contentType = "video"
|
||||
item.play_menu = True
|
||||
downloads.save_download(item)
|
||||
salir = True
|
||||
|
||||
# "Quitar de favoritos"
|
||||
elif opciones[seleccion] == config.get_localized_string(30154):
|
||||
from channels import favorites
|
||||
favorites.delFavourite(item)
|
||||
salir = True
|
||||
|
||||
# "Añadir a favoritos":
|
||||
elif opciones[seleccion] == config.get_localized_string(30155):
|
||||
from channels import favorites
|
||||
item.from_channel = "favoritos"
|
||||
favorites.addFavourite(item)
|
||||
salir = True
|
||||
|
||||
# "Añadir a videoteca":
|
||||
elif opciones[seleccion] == config.get_localized_string(30161):
|
||||
titulo = item.fulltitle
|
||||
if titulo == "":
|
||||
titulo = item.title
|
||||
|
||||
new_item = item.clone(title=titulo, action="play_from_library", category="Cine",
|
||||
fulltitle=item.fulltitle, channel=item.channel)
|
||||
|
||||
from core import videolibrarytools
|
||||
videolibrarytools.add_movie(new_item)
|
||||
|
||||
salir = True
|
||||
|
||||
# "Buscar Trailer":
|
||||
elif opciones[seleccion] == config.get_localized_string(30162):
|
||||
config.set_setting("subtitulo", False)
|
||||
xbmc.executebuiltin("XBMC.RunPlugin(%s?%s)" %
|
||||
(sys.argv[0], item.clone(channel="trailertools", action="buscartrailer",
|
||||
contextual=True).tourl()))
|
||||
salir = True
|
||||
|
||||
return salir
|
||||
|
||||
|
||||
def get_video_seleccionado(item, seleccion, video_urls):
|
||||
logger.info()
|
||||
mediaurl = ""
|
||||
view = False
|
||||
wait_time = 0
|
||||
mpd = False
|
||||
|
||||
# Ha elegido uno de los vídeos
|
||||
if seleccion < len(video_urls):
|
||||
mediaurl = video_urls[seleccion][1]
|
||||
if len(video_urls[seleccion]) > 4:
|
||||
wait_time = video_urls[seleccion][2]
|
||||
item.subtitle = video_urls[seleccion][3]
|
||||
mpd = True
|
||||
elif len(video_urls[seleccion]) > 3:
|
||||
wait_time = video_urls[seleccion][2]
|
||||
item.subtitle = video_urls[seleccion][3]
|
||||
elif len(video_urls[seleccion]) > 2:
|
||||
wait_time = video_urls[seleccion][2]
|
||||
view = True
|
||||
|
||||
# Si no hay mediaurl es porque el vídeo no está :)
|
||||
logger.info("mediaurl=" + mediaurl)
|
||||
if mediaurl == "":
|
||||
if item.server == "unknown":
|
||||
alert_unsopported_server()
|
||||
else:
|
||||
alert_no_disponible_server(item.server)
|
||||
|
||||
# Si hay un tiempo de espera (como en megaupload), lo impone ahora
|
||||
if wait_time > 0:
|
||||
continuar = handle_wait(wait_time, item.server, "Cargando vídeo...")
|
||||
if not continuar:
|
||||
mediaurl = ""
|
||||
|
||||
return mediaurl, view, mpd
|
||||
|
||||
|
||||
def set_player(item, xlistitem, mediaurl, view, strm):
|
||||
logger.info()
|
||||
logger.debug("item:\n" + item.tostring('\n'))
|
||||
|
||||
# Movido del conector "torrent" aqui
|
||||
if item.server == "torrent":
|
||||
play_torrent(item, xlistitem, mediaurl)
|
||||
return
|
||||
|
||||
# Si es un fichero strm no hace falta el play
|
||||
elif strm:
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, xlistitem)
|
||||
if item.subtitle != "":
|
||||
xbmc.sleep(2000)
|
||||
xbmc.Player().setSubtitles(item.subtitle)
|
||||
|
||||
else:
|
||||
logger.info("player_mode=%s" % config.get_setting("player_mode"))
|
||||
logger.info("mediaurl=" + mediaurl)
|
||||
if config.get_setting("player_mode") == 3 or "megacrypter.com" in mediaurl:
|
||||
import download_and_play
|
||||
download_and_play.download_and_play(mediaurl, "download_and_play.tmp", config.get_setting("downloadpath"))
|
||||
return
|
||||
|
||||
elif config.get_setting("player_mode") == 0 or \
|
||||
(config.get_setting("player_mode") == 3 and mediaurl.startswith("rtmp")):
|
||||
# Añadimos el listitem a una lista de reproducción (playlist)
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
playlist.add(mediaurl, xlistitem)
|
||||
|
||||
# Reproduce
|
||||
xbmc_player = xbmc.Player()
|
||||
xbmc_player.play(playlist, xlistitem)
|
||||
|
||||
elif config.get_setting("player_mode") == 1:
|
||||
logger.info("mediaurl :" + mediaurl)
|
||||
logger.info("Tras setResolvedUrl")
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, xbmcgui.ListItem(path=mediaurl))
|
||||
|
||||
elif config.get_setting("player_mode") == 2:
|
||||
xbmc.executebuiltin("PlayMedia(" + mediaurl + ")")
|
||||
|
||||
# TODO MIRAR DE QUITAR VIEW
|
||||
if item.subtitle != "" and view:
|
||||
logger.info("Subtítulos externos: " + item.subtitle)
|
||||
xbmc.sleep(2000)
|
||||
xbmc.Player().setSubtitles(item.subtitle)
|
||||
|
||||
# si es un archivo de la videoteca enviar a marcar como visto
|
||||
if strm or item.strm_path:
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.mark_auto_as_watched(item)
|
||||
|
||||
|
||||
def play_torrent(item, xlistitem, mediaurl):
|
||||
logger.info()
|
||||
# Opciones disponibles para Reproducir torrents
|
||||
torrent_options = list()
|
||||
torrent_options.append(["Cliente interno (necesario libtorrent)"])
|
||||
torrent_options.append(["Cliente interno MCT (necesario libtorrent)"])
|
||||
|
||||
# Plugins externos se encuentra en servers/torrent.json nodo clients
|
||||
from core import filetools
|
||||
from core import jsontools
|
||||
torrent_clients = jsontools.get_node_from_file("torrent.json", "clients", filetools.join(config.get_runtime_path(),
|
||||
"servers"))
|
||||
for client in torrent_clients:
|
||||
if xbmc.getCondVisibility('System.HasAddon("%s")' % client["id"]):
|
||||
torrent_options.append(["Plugin externo: %s" % client["name"], client["url"]])
|
||||
|
||||
# todo permitir elegir opción por defecto
|
||||
if len(torrent_options) > 1:
|
||||
seleccion = dialog_select("Abrir torrent con...", [opcion[0] for opcion in torrent_options])
|
||||
else:
|
||||
seleccion = 0
|
||||
|
||||
# Plugins externos
|
||||
if seleccion > 1:
|
||||
mediaurl = urllib.quote_plus(item.url)
|
||||
xbmc.executebuiltin("PlayMedia(" + torrent_options[seleccion][1] % mediaurl + ")")
|
||||
|
||||
if seleccion == 1:
|
||||
from platformcode import mct
|
||||
mct.play(mediaurl, xlistitem, subtitle=item.subtitle, item=item)
|
||||
|
||||
# Reproductor propio (libtorrent)
|
||||
if seleccion == 0:
|
||||
import time
|
||||
played = False
|
||||
debug = (config.get_setting("debug") == True)
|
||||
|
||||
# Importamos el cliente
|
||||
from btserver import Client
|
||||
|
||||
client_tmp_path = config.get_setting("downloadpath")
|
||||
if not client_tmp_path:
|
||||
client_tmp_path = config.get_data_path()
|
||||
|
||||
# Iniciamos el cliente:
|
||||
c = Client(url=mediaurl, is_playing_fnc=xbmc.Player().isPlaying, wait_time=None, timeout=10,
|
||||
temp_path=os.path.join(client_tmp_path, "alfa-torrent"), print_status=debug)
|
||||
|
||||
# Mostramos el progreso
|
||||
progreso = dialog_progress("Alfa - Torrent", "Iniciando...")
|
||||
|
||||
# Mientras el progreso no sea cancelado ni el cliente cerrado
|
||||
while not c.closed:
|
||||
try:
|
||||
# Obtenemos el estado del torrent
|
||||
s = c.status
|
||||
if debug:
|
||||
# Montamos las tres lineas con la info del torrent
|
||||
txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \
|
||||
(s.progress_file, s.file_size, s.str_state, s._download_rate)
|
||||
txt2 = 'S: %d(%d) P: %d(%d) | DHT:%s (%d) | Trakers: %d' % \
|
||||
(s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete, s.dht_state, s.dht_nodes,
|
||||
s.trackers)
|
||||
txt3 = 'Origen Peers TRK: %d DHT: %d PEX: %d LSD %d ' % \
|
||||
(s.trk_peers, s.dht_peers, s.pex_peers, s.lsd_peers)
|
||||
else:
|
||||
txt = '%.2f%% de %.1fMB %s | %.1f kB/s' % \
|
||||
(s.progress_file, s.file_size, s.str_state, s._download_rate)
|
||||
txt2 = 'S: %d(%d) P: %d(%d)' % (s.num_seeds, s.num_complete, s.num_peers, s.num_incomplete)
|
||||
try:
|
||||
txt3 = 'Deteniendo automaticamente en: %ss' % (int(s.timeout))
|
||||
except:
|
||||
txt3 = ''
|
||||
|
||||
progreso.update(s.buffer, txt, txt2, txt3)
|
||||
time.sleep(0.5)
|
||||
|
||||
if progreso.iscanceled():
|
||||
progreso.close()
|
||||
if s.buffer == 100:
|
||||
if dialog_yesno("Alfa - Torrent", "¿Deseas iniciar la reproduccion?"):
|
||||
played = False
|
||||
progreso = dialog_progress("Alfa - Torrent", "")
|
||||
progreso.update(s.buffer, txt, txt2, txt3)
|
||||
else:
|
||||
progreso = dialog_progress("Alfa - Torrent", "")
|
||||
break
|
||||
|
||||
else:
|
||||
if dialog_yesno("Alfa - Torrent", "¿Deseas cancelar el proceso?"):
|
||||
progreso = dialog_progress("Alfa - Torrent", "")
|
||||
break
|
||||
|
||||
else:
|
||||
progreso = dialog_progress("Alfa - Torrent", "")
|
||||
progreso.update(s.buffer, txt, txt2, txt3)
|
||||
|
||||
# Si el buffer se ha llenado y la reproduccion no ha sido iniciada, se inicia
|
||||
if s.buffer == 100 and not played:
|
||||
# Cerramos el progreso
|
||||
progreso.close()
|
||||
|
||||
# Obtenemos el playlist del torrent
|
||||
videourl = c.get_play_list()
|
||||
|
||||
# Iniciamos el reproductor
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
playlist.add(videourl, xlistitem)
|
||||
xbmc_player = xbmc.Player()
|
||||
xbmc_player.play(playlist)
|
||||
|
||||
# Marcamos como reproducido para que no se vuelva a iniciar
|
||||
played = True
|
||||
|
||||
# si es un archivo de la videoteca enviar a marcar como visto
|
||||
if item.strm_path:
|
||||
from platformcode import xbmc_videolibrary
|
||||
xbmc_videolibrary.mark_auto_as_watched(item)
|
||||
|
||||
# Y esperamos a que el reproductor se cierre
|
||||
while xbmc.Player().isPlaying():
|
||||
time.sleep(1)
|
||||
|
||||
# Cuando este cerrado, Volvemos a mostrar el dialogo
|
||||
progreso = dialog_progress("Alfa - Torrent", "")
|
||||
progreso.update(s.buffer, txt, txt2, txt3)
|
||||
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
break
|
||||
|
||||
progreso.update(100, "Terminando y eliminando datos", " ", " ")
|
||||
|
||||
# Detenemos el cliente
|
||||
if not c.closed:
|
||||
c.stop()
|
||||
|
||||
# Y cerramos el progreso
|
||||
progreso.close()
|
||||
71
plugin.video.alfa/platformcode/recaptcha.py
Executable file
71
plugin.video.alfa/platformcode/recaptcha.py
Executable file
@@ -0,0 +1,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import xbmcgui
|
||||
from core import config
|
||||
from core import httptools
|
||||
from core import scrapertools
|
||||
from platformcode import platformtools
|
||||
|
||||
|
||||
class Recaptcha(xbmcgui.WindowXMLDialog):
|
||||
def Start(self, key, referer):
|
||||
self.referer = referer
|
||||
self.key = key
|
||||
self.headers = {'Referer': self.referer}
|
||||
|
||||
api_js = httptools.downloadpage("http://www.google.com/recaptcha/api.js?hl=es").data
|
||||
version = scrapertools.find_single_match(api_js, 'po.src = \'(.*?)\';').split("/")[5]
|
||||
self.url = "http://www.google.com/recaptcha/api/fallback?k=%s&hl=es&v=%s&t=2&ff=true" % (self.key, version)
|
||||
self.doModal()
|
||||
# Reload
|
||||
if self.result == {}:
|
||||
self.result = Recaptcha("Recaptcha.xml", config.get_runtime_path()).Start(self.key, self.referer)
|
||||
|
||||
return self.result
|
||||
|
||||
def update_window(self):
|
||||
data = httptools.downloadpage(self.url, headers=self.headers).data
|
||||
self.message = scrapertools.find_single_match(data,
|
||||
'<div class="rc-imageselect-desc-no-canonical">(.*?)(?:</label>|</div>)').replace(
|
||||
"<strong>", "[B]").replace("</strong>", "[/B]")
|
||||
self.token = scrapertools.find_single_match(data, 'name="c" value="([^"]+)"')
|
||||
self.image = "http://www.google.com/recaptcha/api2/payload?k=%s&c=%s" % (self.key, self.token)
|
||||
self.result = {}
|
||||
self.getControl(10020).setImage(self.image)
|
||||
self.getControl(10000).setText(self.message)
|
||||
self.setFocusId(10005)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.mensaje = kwargs.get("mensaje")
|
||||
self.imagen = kwargs.get("imagen")
|
||||
|
||||
def onInit(self):
|
||||
self.setCoordinateResolution(2)
|
||||
self.update_window()
|
||||
|
||||
def onClick(self, control):
|
||||
if control == 10003:
|
||||
self.result = None
|
||||
self.close()
|
||||
|
||||
elif control == 10004:
|
||||
self.result = {}
|
||||
self.close()
|
||||
|
||||
elif control == 10002:
|
||||
self.result = [int(k) for k in range(9) if self.result.get(k, False) == True]
|
||||
post = "c=%s" % self.token
|
||||
|
||||
for r in self.result:
|
||||
post += "&response=%s" % r
|
||||
|
||||
data = httptools.downloadpage(self.url, post, headers=self.headers).data
|
||||
self.result = scrapertools.find_single_match(data, '<div class="fbc-verification-token">.*?>([^<]+)<')
|
||||
if self.result:
|
||||
platformtools.dialog_notification("Captcha Correcto", "La verificación ha concluido")
|
||||
self.close()
|
||||
else:
|
||||
self.result = {}
|
||||
self.close()
|
||||
else:
|
||||
self.result[control - 10005] = not self.result.get(control - 10005, False)
|
||||
269
plugin.video.alfa/platformcode/subtitletools.py
Executable file
269
plugin.video.alfa/platformcode/subtitletools.py
Executable file
@@ -0,0 +1,269 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import urllib
|
||||
from unicodedata import normalize
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
from core import config
|
||||
from core import logger
|
||||
|
||||
allchars = string.maketrans('', '')
|
||||
deletechars = ',\\/:*"<>|?'
|
||||
|
||||
|
||||
# Extraemos el nombre de la serie, temporada y numero de capitulo ejemplo: 'fringe 1x01'
|
||||
def regex_tvshow(compare, file, sub=""):
|
||||
regex_expressions = ['[Ss]([0-9]+)[][._-]*[Ee]([0-9]+)([^\\\\/]*)$',
|
||||
'[\._ \-]([0-9]+)x([0-9]+)([^\\/]*)', # foo.1x09
|
||||
'[\._ \-]([0-9]+)([0-9][0-9])([\._ \-][^\\/]*)', # foo.109
|
||||
'([0-9]+)([0-9][0-9])([\._ \-][^\\/]*)',
|
||||
'[\\\\/\\._ -]([0-9]+)([0-9][0-9])[^\\/]*',
|
||||
'Season ([0-9]+) - Episode ([0-9]+)[^\\/]*',
|
||||
'Season ([0-9]+) Episode ([0-9]+)[^\\/]*',
|
||||
'[\\\\/\\._ -][0]*([0-9]+)x[0]*([0-9]+)[^\\/]*',
|
||||
'[[Ss]([0-9]+)\]_\[[Ee]([0-9]+)([^\\/]*)', # foo_[s01]_[e01]
|
||||
'[\._ \-][Ss]([0-9]+)[\.\-]?[Ee]([0-9]+)([^\\/]*)', # foo, s01e01, foo.s01.e01, foo.s01-e01
|
||||
's([0-9]+)ep([0-9]+)[^\\/]*', # foo - s01ep03, foo - s1ep03
|
||||
'[Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+)([^\\\\/]*)$',
|
||||
'[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+)([^\\\\/]*)$',
|
||||
'[\\\\/\\._ \\[\\(-]([0-9]+)X([0-9]+)([^\\\\/]*)$'
|
||||
]
|
||||
sub_info = ""
|
||||
tvshow = 0
|
||||
|
||||
for regex in regex_expressions:
|
||||
response_file = re.findall(regex, file)
|
||||
if len(response_file) > 0:
|
||||
print "Regex File Se: %s, Ep: %s," % (str(response_file[0][0]), str(response_file[0][1]),)
|
||||
tvshow = 1
|
||||
if not compare:
|
||||
title = re.split(regex, file)[0]
|
||||
for char in ['[', ']', '_', '(', ')', '.', '-']:
|
||||
title = title.replace(char, ' ')
|
||||
if title.endswith(" "): title = title.strip()
|
||||
print "title: %s" % title
|
||||
return title, response_file[0][0], response_file[0][1]
|
||||
else:
|
||||
break
|
||||
|
||||
if (tvshow == 1):
|
||||
for regex in regex_expressions:
|
||||
response_sub = re.findall(regex, sub)
|
||||
if len(response_sub) > 0:
|
||||
try:
|
||||
sub_info = "Regex Subtitle Ep: %s," % (str(response_sub[0][1]),)
|
||||
if (int(response_sub[0][1]) == int(response_file[0][1])):
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
if compare:
|
||||
return True
|
||||
else:
|
||||
return "", "", ""
|
||||
|
||||
# Obtiene el nombre de la pelicula o capitulo de la serie guardado previamente en configuraciones del plugin
|
||||
# y luego lo busca en el directorio de subtitulos, si los encuentra los activa.
|
||||
|
||||
|
||||
def set_Subtitle():
|
||||
logger.info()
|
||||
|
||||
exts = [".srt", ".sub", ".txt", ".smi", ".ssa", ".ass"]
|
||||
subtitle_folder_path = os.path.join(config.get_data_path(), "subtitles")
|
||||
|
||||
subtitle_type = config.get_setting("subtitle_type")
|
||||
|
||||
if subtitle_type == "2":
|
||||
subtitle_path = config.get_setting("subtitlepath_file")
|
||||
logger.info("Con subtitulo : " + subtitle_path)
|
||||
xbmc.Player().setSubtitles(subtitle_path)
|
||||
else:
|
||||
if subtitle_type == "0":
|
||||
subtitle_path = config.get_setting("subtitlepath_folder")
|
||||
if subtitle_path == "":
|
||||
subtitle_path = subtitle_folder_path
|
||||
config.set_setting("subtitlepath_folder", subtitle_path)
|
||||
else:
|
||||
subtitle_path = config.get_setting("subtitlepath_keyboard")
|
||||
long = len(subtitle_path)
|
||||
if long > 0:
|
||||
if subtitle_path.startswith("http") or subtitle_path[long - 4, long] in exts:
|
||||
logger.info("Con subtitulo : " + subtitle_path)
|
||||
xbmc.Player().setSubtitles(subtitle_path)
|
||||
return
|
||||
else:
|
||||
subtitle_path = subtitle_folder_path
|
||||
config.set_setting("subtitlepath_keyboard", subtitle_path)
|
||||
|
||||
import glob
|
||||
|
||||
subtitle_name = config.get_setting("subtitle_name").replace("amp;", "")
|
||||
tvshow_title, season, episode = regex_tvshow(False, subtitle_name)
|
||||
try:
|
||||
if episode != "":
|
||||
Subnames = glob.glob(os.path.join(subtitle_path, "Tvshows", tvshow_title,
|
||||
"%s %sx%s" % (tvshow_title, season, episode) + "*.??.???"))
|
||||
else:
|
||||
Subnames = glob.glob(os.path.join(subtitle_path, "Movies", subtitle_name + "*.??.???"))
|
||||
for Subname in Subnames:
|
||||
if os.path.splitext(Subname)[1] in exts:
|
||||
logger.info("Con subtitulo : " + os.path.split(Subname)[1])
|
||||
xbmc.Player().setSubtitles((Subname))
|
||||
except:
|
||||
logger.error("error al cargar subtitulos")
|
||||
|
||||
# Limpia los caracteres unicode
|
||||
|
||||
|
||||
def _normalize(title, charset='utf-8'):
|
||||
'''Removes all accents and illegal chars for titles from the String'''
|
||||
if isinstance(title, unicode):
|
||||
title = string.translate(title, allchars, deletechars)
|
||||
try:
|
||||
title = title.encode("utf-8")
|
||||
title = normalize('NFKD', title).encode('ASCII', 'ignore')
|
||||
except UnicodeEncodeError:
|
||||
logger.error("Error de encoding")
|
||||
else:
|
||||
title = string.translate(title, allchars, deletechars)
|
||||
try:
|
||||
# iso-8859-1
|
||||
title = title.decode(charset).encode('utf-8')
|
||||
title = normalize('NFKD', unicode(title, 'utf-8'))
|
||||
title = title.encode('ASCII', 'ignore')
|
||||
except UnicodeEncodeError:
|
||||
logger.error("Error de encoding")
|
||||
return title
|
||||
|
||||
#
|
||||
|
||||
|
||||
def searchSubtitle(item):
|
||||
if config.get_setting("subtitle_type") == 0:
|
||||
subtitlepath = config.get_setting("subtitlepath_folder")
|
||||
if subtitlepath == "":
|
||||
subtitlepath = os.path.join(config.get_data_path(), "subtitles")
|
||||
config.set_setting("subtitlepath_folder", subtitlepath)
|
||||
|
||||
elif config.get_setting("subtitle_type") == 1:
|
||||
subtitlepath = config.get_setting("subtitlepath_keyboard")
|
||||
if subtitlepath == "":
|
||||
subtitlepath = os.path.join(config.get_data_path(), "subtitles")
|
||||
config.set_setting("subtitlepathkeyboard", subtitlepath)
|
||||
elif subtitlepath.startswith("http"):
|
||||
subtitlepath = config.get_setting("subtitlepath_folder")
|
||||
|
||||
else:
|
||||
subtitlepath = config.get_setting("subtitlepath_folder")
|
||||
if subtitlepath == "":
|
||||
subtitlepath = os.path.join(config.get_data_path(), "subtitles")
|
||||
config.set_setting("subtitlepath_folder", subtitlepath)
|
||||
if not os.path.exists(subtitlepath):
|
||||
try:
|
||||
os.mkdir(subtitlepath)
|
||||
except:
|
||||
logger.error("error no se pudo crear path subtitulos")
|
||||
return
|
||||
|
||||
path_movie_subt = xbmc.translatePath(os.path.join(subtitlepath, "Movies"))
|
||||
if not os.path.exists(path_movie_subt):
|
||||
try:
|
||||
os.mkdir(path_movie_subt)
|
||||
except:
|
||||
logger.error("error no se pudo crear el path Movies")
|
||||
return
|
||||
full_path_tvshow = ""
|
||||
path_tvshow_subt = xbmc.translatePath(os.path.join(subtitlepath, "Tvshows"))
|
||||
if not os.path.exists(path_tvshow_subt):
|
||||
try:
|
||||
os.mkdir(path_tvshow_subt)
|
||||
except:
|
||||
logger.error("error no pudo crear el path Tvshows")
|
||||
return
|
||||
if item.show in item.title:
|
||||
title_new = title = urllib.unquote_plus(item.title)
|
||||
else:
|
||||
title_new = title = urllib.unquote_plus(item.show + " - " + item.title)
|
||||
path_video_temp = xbmc.translatePath(os.path.join(config.get_runtime_path(), "resources", "subtitle.mp4"))
|
||||
if not os.path.exists(path_video_temp):
|
||||
logger.error("error : no existe el video temporal de subtitulos")
|
||||
return
|
||||
# path_video_temp = xbmc.translatePath(os.path.join( ,video_temp + ".mp4" ))
|
||||
|
||||
title_new = _normalize(title_new)
|
||||
tvshow_title, season, episode = regex_tvshow(False, title_new)
|
||||
if episode != "":
|
||||
full_path_tvshow = xbmc.translatePath(os.path.join(path_tvshow_subt, tvshow_title))
|
||||
if not os.path.exists(full_path_tvshow):
|
||||
os.mkdir(full_path_tvshow) # title_new + ".mp4"
|
||||
full_path_video_new = xbmc.translatePath(
|
||||
os.path.join(full_path_tvshow, "%s %sx%s.mp4" % (tvshow_title, season, episode)))
|
||||
logger.info(full_path_video_new)
|
||||
listitem = xbmcgui.ListItem(title_new, iconImage="DefaultVideo.png", thumbnailImage="")
|
||||
listitem.setInfo("video",
|
||||
{"Title": title_new, "Genre": "Tv shows", "episode": int(episode), "season": int(season),
|
||||
"tvshowtitle": tvshow_title})
|
||||
|
||||
else:
|
||||
full_path_video_new = xbmc.translatePath(os.path.join(path_movie_subt, title_new + ".mp4"))
|
||||
listitem = xbmcgui.ListItem(title, iconImage="DefaultVideo.png", thumbnailImage="")
|
||||
listitem.setInfo("video", {"Title": title_new, "Genre": "Movies"})
|
||||
|
||||
import shutil, time
|
||||
|
||||
try:
|
||||
shutil.copy(path_video_temp, full_path_video_new)
|
||||
copy = True
|
||||
logger.info("nuevo path =" + full_path_video_new)
|
||||
time.sleep(2)
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
playlist.add(full_path_video_new, listitem)
|
||||
# xbmcPlayer = xbmc.Player( xbmc.PLAYER_CORE_AUTO )
|
||||
xbmcPlayer = xbmc.Player()
|
||||
xbmcPlayer.play(playlist)
|
||||
|
||||
# xbmctools.launchplayer(full_path_video_new,listitem)
|
||||
except:
|
||||
copy = False
|
||||
logger.error("Error : no se pudo copiar")
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if copy:
|
||||
if xbmc.Player().isPlayingVideo():
|
||||
xbmc.executebuiltin("RunScript(script.xbmc.subtitles)")
|
||||
while xbmc.Player().isPlayingVideo():
|
||||
continue
|
||||
|
||||
time.sleep(1)
|
||||
os.remove(full_path_video_new)
|
||||
try:
|
||||
if full_path_tvshow != "":
|
||||
os.rmdir(full_path_tvshow)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def saveSubtitleName(item):
|
||||
if item.show in item.title:
|
||||
title = item.title
|
||||
else:
|
||||
title = item.show + " - " + item.title
|
||||
try:
|
||||
title = _normalize(title)
|
||||
except:
|
||||
pass
|
||||
|
||||
tvshow_title, season, episode = regex_tvshow(False, title)
|
||||
if episode != "":
|
||||
# title = "% %sx%s" %(tvshow_title,season,episode)
|
||||
config.set_setting("subtitle_name", title)
|
||||
else:
|
||||
config.set_setting("subtitle_name", title)
|
||||
return
|
||||
997
plugin.video.alfa/platformcode/xbmc_config_menu.py
Executable file
997
plugin.video.alfa/platformcode/xbmc_config_menu.py
Executable file
@@ -0,0 +1,997 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# XBMC Config Menu
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import inspect
|
||||
import os
|
||||
|
||||
import xbmcgui
|
||||
from core import channeltools
|
||||
from core import config
|
||||
from core import logger
|
||||
from core import servertools
|
||||
|
||||
|
||||
class SettingsWindow(xbmcgui.WindowXMLDialog):
|
||||
""" Clase derivada que permite utilizar cuadros de configuracion personalizados.
|
||||
|
||||
Esta clase deriva de xbmcgui.WindowXMLDialog y permite crear un cuadro de dialogo con controles del tipo:
|
||||
Radio Button (bool), Cuadro de texto (text), Lista (list) y Etiquetas informativas (label).
|
||||
Tambien podemos personalizar el cuadro añadiendole un titulo (title).
|
||||
|
||||
Metodo constructor:
|
||||
SettingWindow(listado_controles, dict_values, title, callback, item)
|
||||
Parametros:
|
||||
listado_controles: (list) Lista de controles a incluir en la ventana, segun el siguiente esquema:
|
||||
(opcional)list_controls= [
|
||||
{'id': "nameControl1",
|
||||
'type': "bool", # bool, text, list, label
|
||||
'label': "Control 1: tipo RadioButton",
|
||||
'color': '0xFFee66CC', # color del texto en formato ARGB hexadecimal
|
||||
'default': True,
|
||||
'enabled': True,
|
||||
'visible': True
|
||||
},
|
||||
{'id': "nameControl2",
|
||||
'type': "text", # bool, text, list, label
|
||||
'label': "Control 2: tipo Cuadro de texto",
|
||||
'color': '0xFFee66CC',
|
||||
'default': "Valor por defecto",
|
||||
'hidden': False, # only for type = text Indica si hay que ocultar
|
||||
el texto (para passwords)
|
||||
'enabled': True,
|
||||
'visible': True
|
||||
},
|
||||
{'id': "nameControl3",
|
||||
'type': "list", # bool, text, list, label
|
||||
'label': "Control 3: tipo Lista",
|
||||
'color': '0xFFee66CC',
|
||||
'default': 0, # Indice del valor por defecto en lvalues
|
||||
'enabled': True,
|
||||
'visible': True,
|
||||
'lvalues':["item1", "item2", "item3", "item4"], # only for type = list
|
||||
},
|
||||
{'id': "nameControl4",
|
||||
'type': "label", # bool, text, list, label
|
||||
'label': "Control 4: tipo Etiqueta",
|
||||
'color': '0xFFee66CC',
|
||||
'enabled': True,
|
||||
'visible': True
|
||||
}]
|
||||
Si no se incluye el listado_controles, se intenta obtener del json del canal desde donde se hace la
|
||||
llamada.
|
||||
|
||||
El formato de los controles en el json es:
|
||||
{
|
||||
...
|
||||
...
|
||||
"settings": [
|
||||
{
|
||||
"id": "name_control_1",
|
||||
"type": "bool",
|
||||
"label": "Control 1: tipo RadioButton",
|
||||
"default": false,
|
||||
"enabled": true,
|
||||
"visible": true,
|
||||
"color": "0xFFee66CC"
|
||||
},
|
||||
{
|
||||
"id": "name_control_2",
|
||||
"type": "text",
|
||||
"label": "Control 2: tipo Cuadro de texto",
|
||||
"default": "Valor por defecto",
|
||||
"hidden": true,
|
||||
"enabled": true,
|
||||
"visible": true,
|
||||
"color": "0xFFee66CC"
|
||||
},
|
||||
{
|
||||
"id": "name_control_3",
|
||||
"type": "list",
|
||||
"label": "Control 3: tipo Lista",
|
||||
"default": 0,
|
||||
"enabled": true,
|
||||
"visible": true,
|
||||
"color": "0xFFee66CC",
|
||||
"lvalues": [
|
||||
"item1",
|
||||
"item2",
|
||||
"item3",
|
||||
"item4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "name_control_4",
|
||||
"type": "label",
|
||||
"label": "Control 4: tipo Etiqueta",
|
||||
"enabled": true,
|
||||
"visible": true,
|
||||
"color": "0xFFee66CC"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
Los campos 'label', 'default' y 'lvalues' pueden ser un numero precedido de '@'. En cuyo caso se
|
||||
buscara el literal en el archivo string.xml del idioma seleccionado.
|
||||
Los campos 'enabled' y 'visible' admiten los comparadores eq(), gt() e it() y su funcionamiento se
|
||||
describe en: http://kodi.wiki/view/Add-on_settings#Different_types
|
||||
|
||||
(opcional)dict_values: (dict) Diccionario que representa el par (id: valor) de los controles de la
|
||||
lista.
|
||||
Si algun control de la lista no esta incluido en este diccionario se le asignara el valor por
|
||||
defecto.
|
||||
dict_values={"nameControl1": False,
|
||||
"nameControl2": "Esto es un ejemplo"}
|
||||
|
||||
(opcional) caption: (str) Titulo de la ventana de configuracion. Se puede localizar mediante un numero
|
||||
precedido de '@'
|
||||
(opcional) callback (str) Nombre de la funcion, del canal desde el que se realiza la llamada, que sera
|
||||
invocada al pulsar el boton aceptar de la ventana. A esta funcion se le pasara como parametros el
|
||||
objeto 'item' y el dicionario 'dict_values'. Si este parametro no existe, se busca en el canal una
|
||||
funcion llamada 'cb_validate_config' y si existe se utiliza como callback.
|
||||
|
||||
Retorno: Si se especifica 'callback' o el canal incluye 'cb_validate_config' se devolvera lo que devuelva
|
||||
esa funcion. Si no devolvera None
|
||||
|
||||
Ejemplos de uso:
|
||||
platformtools.show_channel_settings(): Así tal cual, sin pasar ningún argumento, la ventana detecta de que canal
|
||||
se ha hecho la llamada,
|
||||
y lee los ajustes del json y carga los controles, cuando le das a Aceptar los vuelve a guardar.
|
||||
|
||||
return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, callback='cb',
|
||||
item=item):
|
||||
Así abre la ventana con los controles pasados y los valores de dict_values, si no se pasa dict_values, carga
|
||||
los valores por defecto de los controles,
|
||||
cuando le das a aceptar, llama a la función 'callback' del canal desde donde se ha llamado, pasando como
|
||||
parámetros, el item y el dict_values
|
||||
"""
|
||||
|
||||
def start(self, list_controls=None, dict_values=None, caption="", callback=None, item=None,
|
||||
custom_button=None, channelpath=None):
|
||||
logger.info()
|
||||
|
||||
# Ruta para las imagenes de la ventana
|
||||
self.mediapath = os.path.join(config.get_runtime_path(), 'resources', 'skins', 'Default', 'media')
|
||||
|
||||
# Capturamos los parametros
|
||||
self.list_controls = list_controls
|
||||
self.values = dict_values
|
||||
self.caption = caption
|
||||
self.callback = callback
|
||||
self.item = item
|
||||
|
||||
if type(custom_button) == dict:
|
||||
self.custom_button = {}
|
||||
self.custom_button["label"] = custom_button.get("label", "")
|
||||
self.custom_button["function"] = custom_button.get("function", "")
|
||||
self.custom_button["visible"] = bool(custom_button.get("visible", True))
|
||||
self.custom_button["close"] = bool(custom_button.get("close", False))
|
||||
else:
|
||||
self.custom_button = None
|
||||
|
||||
# Obtenemos el canal desde donde se ha echo la llamada y cargamos los settings disponibles para ese canal
|
||||
if not channelpath:
|
||||
channelpath = inspect.currentframe().f_back.f_back.f_code.co_filename
|
||||
self.channel = os.path.basename(channelpath).replace(".py", "")
|
||||
self.ch_type = os.path.basename(os.path.dirname(channelpath))
|
||||
|
||||
# Si no tenemos list_controls, hay que sacarlos del json del canal
|
||||
if not self.list_controls:
|
||||
|
||||
# Si la ruta del canal esta en la carpeta "channels", obtenemos los controles y valores mediante chaneltools
|
||||
if os.path.join(config.get_runtime_path(), "channels") in channelpath:
|
||||
|
||||
# La llamada se hace desde un canal
|
||||
self.list_controls, default_values = channeltools.get_channel_controls_settings(self.channel)
|
||||
self.kwargs = {"channel": self.channel}
|
||||
|
||||
# Si la ruta del canal esta en la carpeta "servers", obtenemos los controles y valores mediante servertools
|
||||
elif os.path.join(config.get_runtime_path(), "servers") in channelpath:
|
||||
|
||||
# La llamada se hace desde un canal
|
||||
self.list_controls, default_values = servertools.get_server_controls_settings(self.channel)
|
||||
self.kwargs = {"server": self.channel}
|
||||
|
||||
# En caso contrario salimos
|
||||
else:
|
||||
return None
|
||||
|
||||
# Si no se pasan dict_values, creamos un dict en blanco
|
||||
if self.values is None:
|
||||
self.values = {}
|
||||
|
||||
# Ponemos el titulo
|
||||
if self.caption == "":
|
||||
self.caption = str(config.get_localized_string(30100)) + " -- " + self.channel.capitalize()
|
||||
|
||||
elif self.caption.startswith('@') and unicode(self.caption[1:]).isnumeric():
|
||||
self.caption = config.get_localized_string(int(self.caption[1:]))
|
||||
|
||||
# Muestra la ventana
|
||||
self.return_value = None
|
||||
self.doModal()
|
||||
return self.return_value
|
||||
|
||||
@staticmethod
|
||||
def set_enabled(c, val):
|
||||
if c["type"] == "list":
|
||||
c["control"].setEnabled(val)
|
||||
c["downBtn"].setEnabled(val)
|
||||
c["upBtn"].setEnabled(val)
|
||||
c["label"].setEnabled(val)
|
||||
else:
|
||||
c["control"].setEnabled(val)
|
||||
|
||||
@staticmethod
|
||||
def set_visible(c, val):
|
||||
if c["type"] == "list":
|
||||
c["control"].setVisible(val)
|
||||
c["downBtn"].setVisible(val)
|
||||
c["upBtn"].setVisible(val)
|
||||
c["label"].setVisible(val)
|
||||
else:
|
||||
c["control"].setVisible(val)
|
||||
|
||||
def evaluate_conditions(self):
|
||||
for c in self.list_controls:
|
||||
c["active"] = self.evaluate(self.list_controls.index(c), c["enabled"])
|
||||
self.set_enabled(c, c["active"])
|
||||
c["show"] = self.evaluate(self.list_controls.index(c), c["visible"])
|
||||
if not c["show"]:
|
||||
self.set_visible(c, c["show"])
|
||||
self.visible_controls = [c for c in self.list_controls if c["show"]]
|
||||
|
||||
def evaluate(self, index, cond):
|
||||
import re
|
||||
|
||||
# Si la condicion es True o False, no hay mas que evaluar, ese es el valor
|
||||
if type(cond) == bool:
|
||||
return cond
|
||||
|
||||
# Obtenemos las condiciones
|
||||
conditions = re.compile("(!?eq|!?gt|!?lt)?\(([^,]+),[\"|']?([^)|'|\"]*)['|\"]?\)[ ]*([+||])?").findall(cond)
|
||||
for operator, id, value, next in conditions:
|
||||
# El id tiene que ser un numero, sino, no es valido y devuelve False
|
||||
try:
|
||||
id = int(id)
|
||||
except:
|
||||
return False
|
||||
|
||||
# El control sobre el que evaluar, tiene que estar dentro del rango, sino devuelve False
|
||||
if index + id < 0 or index + id >= len(self.list_controls):
|
||||
return False
|
||||
|
||||
else:
|
||||
# Obtenemos el valor del control sobre el que se compara
|
||||
c = self.list_controls[index + id]
|
||||
if c["type"] == "bool":
|
||||
control_value = bool(c["control"].isSelected())
|
||||
if c["type"] == "text":
|
||||
control_value = c["control"].getText()
|
||||
if c["type"] == "list":
|
||||
control_value = c["label"].getLabel()
|
||||
if c["type"] == "label":
|
||||
control_value = c["control"].getLabel()
|
||||
|
||||
# Operaciones lt "menor que" y gt "mayor que", requieren que las comparaciones sean numeros, sino devuelve
|
||||
# False
|
||||
if operator in ["lt", "!lt", "gt", "!gt"]:
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
# Operacion eq "igual a"
|
||||
if operator in ["eq", "!eq"]:
|
||||
# valor int
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# valor bool
|
||||
if value.lower() == "true":
|
||||
value = True
|
||||
elif value.lower() == "false":
|
||||
value = False
|
||||
|
||||
# operacion "eq" "igual a"
|
||||
if operator == "eq":
|
||||
if control_value == value:
|
||||
ok = True
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# operacion "!eq" "no igual a"
|
||||
if operator == "!eq":
|
||||
if not control_value == value:
|
||||
ok = True
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# operacion "gt" "mayor que"
|
||||
if operator == "gt":
|
||||
if control_value > value:
|
||||
ok = True
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# operacion "!gt" "no mayor que"
|
||||
if operator == "!gt":
|
||||
if not control_value > value:
|
||||
ok = True
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# operacion "lt" "menor que"
|
||||
if operator == "lt":
|
||||
if control_value < value:
|
||||
ok = True
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# operacion "!lt" "no menor que"
|
||||
if operator == "!lt":
|
||||
if not control_value < value:
|
||||
ok = True
|
||||
else:
|
||||
ok = False
|
||||
|
||||
# Siguiente operación, si es "|" (or) y el resultado es True, no tiene sentido seguir, es True
|
||||
if next == "|" and ok is True:
|
||||
break
|
||||
# Siguiente operación, si es "+" (and) y el resultado es False, no tiene sentido seguir, es False
|
||||
if next == "+" and ok is False:
|
||||
break
|
||||
|
||||
# Siguiente operación, si es "+" (and) y el resultado es True, Seguira, para comprobar el siguiente valor
|
||||
# Siguiente operación, si es "|" (or) y el resultado es False, Seguira, para comprobar el siguiente valor
|
||||
|
||||
return ok
|
||||
|
||||
def add_control_label(self, c):
|
||||
control = xbmcgui.ControlLabel(0, -100, self.controls_width, 30, "", alignment=4, font=self.font,
|
||||
textColor=c["color"])
|
||||
|
||||
self.addControl(control)
|
||||
|
||||
control.setVisible(False)
|
||||
control.setLabel(c["label"])
|
||||
|
||||
# Lo añadimos al listado
|
||||
c["control"] = control
|
||||
|
||||
def add_control_list(self, c):
|
||||
control = xbmcgui.ControlButton(0, -100, self.controls_width, self.height_control,
|
||||
c["label"], os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'),
|
||||
os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'),
|
||||
0, textColor=c["color"],
|
||||
font=self.font)
|
||||
|
||||
label = xbmcgui.ControlLabel(0, -100, self.controls_width - 30, self.height_control,
|
||||
"", font=self.font, textColor=c["color"], alignment=4 | 1)
|
||||
|
||||
upBtn = xbmcgui.ControlButton(0, -100, 20, 15, "",
|
||||
focusTexture=os.path.join(self.mediapath, 'Controls', 'spinUp-Focus.png'),
|
||||
noFocusTexture=os.path.join(self.mediapath, 'Controls', 'spinUp-noFocus.png'))
|
||||
|
||||
downBtn = xbmcgui.ControlButton(0, -100 + 15, 20, 15, "",
|
||||
focusTexture=os.path.join(self.mediapath, 'Controls', 'spinDown-Focus.png'),
|
||||
noFocusTexture=os.path.join(self.mediapath, 'Controls', 'spinDown-noFocus.png'))
|
||||
|
||||
self.addControl(control)
|
||||
self.addControl(label)
|
||||
self.addControl(upBtn)
|
||||
self.addControl(downBtn)
|
||||
|
||||
control.setVisible(False)
|
||||
label.setVisible(False)
|
||||
upBtn.setVisible(False)
|
||||
downBtn.setVisible(False)
|
||||
label.setLabel(c["lvalues"][self.values[c["id"]]])
|
||||
|
||||
c["control"] = control
|
||||
c["label"] = label
|
||||
c["downBtn"] = downBtn
|
||||
c["upBtn"] = upBtn
|
||||
|
||||
def add_control_text(self, c):
|
||||
if xbmcgui.ControlEdit == ControlEdit:
|
||||
control = xbmcgui.ControlEdit(0, -100, self.controls_width, self.height_control,
|
||||
c["label"], os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'),
|
||||
os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'),
|
||||
0, textColor=c["color"],
|
||||
font=self.font, isPassword=c["hidden"], window=self)
|
||||
|
||||
else:
|
||||
control = xbmcgui.ControlEdit(0, -100, self.controls_width - 5, self.height_control,
|
||||
c["label"], self.font, c["color"], '', 4, isPassword=c["hidden"],
|
||||
focusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'),
|
||||
noFocusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'))
|
||||
|
||||
self.addControl(control)
|
||||
|
||||
control.setVisible(False)
|
||||
control.setLabel(c["label"])
|
||||
control.setText(self.values[c["id"]])
|
||||
control.setWidth(self.controls_width - 5)
|
||||
control.setHeight(self.height_control)
|
||||
|
||||
c["control"] = control
|
||||
|
||||
def add_control_bool(self, c):
|
||||
# Versiones antiguas no admite algunas texturas
|
||||
if xbmcgui.__version__ in ["1.2", "2.0"]:
|
||||
control = xbmcgui.ControlRadioButton(0 - 10, -100, self.controls_width + 10, self.height_control,
|
||||
label=c["label"], font=self.font, textColor=c["color"],
|
||||
focusTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'MenuItemFO.png'),
|
||||
noFocusTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'MenuItemNF.png'))
|
||||
else:
|
||||
control = xbmcgui.ControlRadioButton(0 - 10, -100, self.controls_width + 10,
|
||||
self.height_control, label=c["label"], font=self.font,
|
||||
textColor=c["color"],
|
||||
focusTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'MenuItemFO.png'),
|
||||
noFocusTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'MenuItemNF.png'),
|
||||
focusOnTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'radiobutton-focus.png'),
|
||||
noFocusOnTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'radiobutton-focus.png'),
|
||||
focusOffTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'radiobutton-nofocus.png'),
|
||||
noFocusOffTexture=os.path.join(self.mediapath, 'Controls',
|
||||
'radiobutton-nofocus.png'))
|
||||
|
||||
self.addControl(control)
|
||||
|
||||
control.setVisible(False)
|
||||
control.setRadioDimension(x=self.controls_width + 10 - (self.height_control - 5), y=0,
|
||||
width=self.height_control - 5, height=self.height_control - 5)
|
||||
control.setSelected(self.values[c["id"]])
|
||||
|
||||
c["control"] = control
|
||||
|
||||
def onInit(self):
|
||||
self.getControl(10004).setEnabled(False)
|
||||
self.getControl(10005).setEnabled(False)
|
||||
self.getControl(10006).setEnabled(False)
|
||||
self.ok_enabled = False
|
||||
self.default_enabled = False
|
||||
|
||||
if xbmcgui.__version__ == "1.2":
|
||||
self.setCoordinateResolution(1)
|
||||
else:
|
||||
self.setCoordinateResolution(5)
|
||||
|
||||
# Ponemos el título
|
||||
self.getControl(10002).setLabel(self.caption)
|
||||
|
||||
if self.custom_button is not None:
|
||||
if self.custom_button['visible']:
|
||||
self.getControl(10006).setLabel(self.custom_button['label'])
|
||||
else:
|
||||
self.getControl(10006).setVisible(False)
|
||||
self.getControl(10004).setPosition(self.getControl(10004).getPosition()[0] + 80,
|
||||
self.getControl(10004).getPosition()[1])
|
||||
self.getControl(10005).setPosition(self.getControl(10005).getPosition()[0] + 80,
|
||||
self.getControl(10005).getPosition()[1])
|
||||
|
||||
# Obtenemos las dimensiones del area de controles
|
||||
self.controls_width = self.getControl(10007).getWidth() - 20
|
||||
self.controls_height = self.getControl(10007).getHeight()
|
||||
self.controls_pos_x = self.getControl(10007).getPosition()[0] + self.getControl(10001).getPosition()[0] + 10
|
||||
self.controls_pos_y = self.getControl(10007).getPosition()[1] + self.getControl(10001).getPosition()[1]
|
||||
self.height_control = 35
|
||||
self.font = "font12"
|
||||
|
||||
# En versiones antiguas: creamos 5 controles, de lo conrtario al hacer click al segundo control,
|
||||
# automaticamente cambia el label del tercero a "Short By: Name" no se porque...
|
||||
if xbmcgui.ControlEdit == ControlEdit:
|
||||
for x in range(5):
|
||||
control = xbmcgui.ControlRadioButton(-500, 0, 0, 0, "")
|
||||
self.addControl(control)
|
||||
|
||||
for c in self.list_controls:
|
||||
# Saltamos controles que no tengan los valores adecuados
|
||||
if "type" not in c:
|
||||
continue
|
||||
if "label" not in c:
|
||||
continue
|
||||
if c["type"] != "label" and "id" not in c:
|
||||
continue
|
||||
if c["type"] == "list" and "lvalues" not in c:
|
||||
continue
|
||||
if c["type"] == "list" and not type(c["lvalues"]) == list:
|
||||
continue
|
||||
if c["type"] == "list" and not len(c["lvalues"]) > 0:
|
||||
continue
|
||||
if c["type"] != "label" and len(
|
||||
[control.get("id") for control in self.list_controls if c["id"] == control.get("id")]) > 1:
|
||||
continue
|
||||
|
||||
# Translation label y lvalues
|
||||
if c['label'].startswith('@') and unicode(c['label'][1:]).isnumeric():
|
||||
c['label'] = config.get_localized_string(int(c['label'][1:]))
|
||||
if c['type'] == 'list':
|
||||
lvalues = []
|
||||
for li in c['lvalues']:
|
||||
if li.startswith('@') and unicode(li[1:]).isnumeric():
|
||||
lvalues.append(config.get_localized_string(int(li[1:])))
|
||||
else:
|
||||
lvalues.append(li)
|
||||
c['lvalues'] = lvalues
|
||||
|
||||
# Valores por defecto en caso de que el control no disponga de ellos
|
||||
if c["type"] == "bool":
|
||||
default = False
|
||||
elif c["type"] == "list":
|
||||
default = 0
|
||||
else:
|
||||
# label or text
|
||||
default = ""
|
||||
|
||||
c["default"] = c.get("default", default)
|
||||
c["color"] = c.get("color", "0xFF0066CC")
|
||||
c["visible"] = c.get("visible", True)
|
||||
c["enabled"] = c.get("enabled", True)
|
||||
|
||||
if c["type"] == "label" and "id" not in c:
|
||||
c["id"] = None
|
||||
|
||||
if c["type"] == "text":
|
||||
c["hidden"] = c.get("hidden", False)
|
||||
|
||||
# Decidimos si usar el valor por defecto o el valor guardado
|
||||
if c["type"] in ["bool", "text", "list"]:
|
||||
if c["id"] not in self.values:
|
||||
if not self.callback:
|
||||
self.values[c["id"]] = config.get_setting(c["id"], **self.kwargs)
|
||||
else:
|
||||
self.values[c["id"]] = c["default"]
|
||||
|
||||
if c["type"] == "bool":
|
||||
self.add_control_bool(c)
|
||||
|
||||
elif c["type"] == 'text':
|
||||
self.add_control_text(c)
|
||||
|
||||
elif c["type"] == 'list':
|
||||
self.add_control_list(c)
|
||||
|
||||
elif c["type"] == 'label':
|
||||
self.add_control_label(c)
|
||||
|
||||
self.list_controls = [c for c in self.list_controls if "control" in c]
|
||||
|
||||
self.evaluate_conditions()
|
||||
self.index = -1
|
||||
self.dispose_controls(0)
|
||||
self.getControl(100010).setVisible(False)
|
||||
self.getControl(10004).setEnabled(True)
|
||||
self.getControl(10005).setEnabled(True)
|
||||
self.getControl(10006).setEnabled(True)
|
||||
self.ok_enabled = True
|
||||
self.default_enabled = True
|
||||
self.check_default()
|
||||
self.check_ok(self.values)
|
||||
|
||||
def dispose_controls(self, index, focus=False, force=False):
|
||||
show_controls = self.controls_height / self.height_control - 1
|
||||
|
||||
visible_count = 0
|
||||
|
||||
if focus:
|
||||
if not index >= self.index or not index <= self.index + show_controls:
|
||||
if index < self.index:
|
||||
new_index = index
|
||||
else:
|
||||
new_index = index - show_controls
|
||||
else:
|
||||
new_index = self.index
|
||||
else:
|
||||
|
||||
if index + show_controls >= len(self.visible_controls): index = len(
|
||||
self.visible_controls) - show_controls - 1
|
||||
if index < 0: index = 0
|
||||
new_index = index
|
||||
|
||||
if self.index <> new_index or force:
|
||||
for x, c in enumerate(self.visible_controls):
|
||||
if x < new_index or visible_count > show_controls or not c["show"]:
|
||||
self.set_visible(c, False)
|
||||
else:
|
||||
c["y"] = self.controls_pos_y + visible_count * self.height_control
|
||||
visible_count += 1
|
||||
|
||||
if c["type"] != "list":
|
||||
if c["type"] == "bool":
|
||||
c["control"].setPosition(self.controls_pos_x - 10, c["y"])
|
||||
else:
|
||||
c["control"].setPosition(self.controls_pos_x, c["y"])
|
||||
|
||||
else:
|
||||
c["control"].setPosition(self.controls_pos_x, c["y"])
|
||||
if xbmcgui.__version__ == "1.2":
|
||||
c["label"].setPosition(self.controls_pos_x + self.controls_width - 30, c["y"])
|
||||
else:
|
||||
c["label"].setPosition(self.controls_pos_x, c["y"])
|
||||
c["upBtn"].setPosition(self.controls_pos_x + c["control"].getWidth() - 25, c["y"] + 3)
|
||||
c["downBtn"].setPosition(self.controls_pos_x + c["control"].getWidth() - 25, c["y"] + 18)
|
||||
|
||||
self.set_visible(c, True)
|
||||
|
||||
# Calculamos la posicion y tamaño del ScrollBar
|
||||
hidden_controls = len(self.visible_controls) - show_controls - 1
|
||||
if hidden_controls < 0: hidden_controls = 0
|
||||
|
||||
scrollbar_height = self.getControl(10008).getHeight() - (hidden_controls * 3)
|
||||
scrollbar_y = self.getControl(10008).getPosition()[1] + (new_index * 3)
|
||||
self.getControl(10009).setPosition(self.getControl(10008).getPosition()[0], scrollbar_y)
|
||||
self.getControl(10009).setHeight(scrollbar_height)
|
||||
|
||||
self.index = new_index
|
||||
|
||||
if focus:
|
||||
self.setFocus(self.visible_controls[index]["control"])
|
||||
|
||||
def check_ok(self, dict_values=None):
|
||||
if not self.callback:
|
||||
if dict_values:
|
||||
self.init_values = dict_values.copy()
|
||||
self.getControl(10004).setEnabled(False)
|
||||
self.ok_enabled = False
|
||||
|
||||
else:
|
||||
if self.init_values == self.values:
|
||||
self.getControl(10004).setEnabled(False)
|
||||
self.ok_enabled = False
|
||||
else:
|
||||
self.getControl(10004).setEnabled(True)
|
||||
self.ok_enabled = True
|
||||
|
||||
def check_default(self):
|
||||
if self.custom_button is None:
|
||||
def_values = dict([[c["id"], c.get("default")] for c in self.list_controls if not c["type"] == "label"])
|
||||
|
||||
if def_values == self.values:
|
||||
self.getControl(10006).setEnabled(False)
|
||||
self.default_enabled = False
|
||||
else:
|
||||
self.getControl(10006).setEnabled(True)
|
||||
self.default_enabled = True
|
||||
|
||||
def onClick(self, id):
|
||||
# Valores por defecto
|
||||
if id == 10006:
|
||||
if self.custom_button is not None:
|
||||
if self.custom_button["close"]:
|
||||
self.close()
|
||||
|
||||
if '.' in self.callback:
|
||||
package, self.callback = self.callback.rsplit('.', 1)
|
||||
else:
|
||||
package = '%s.%s' % (self.ch_type, self.channel)
|
||||
|
||||
try:
|
||||
cb_channel = __import__(package, None, None, [package])
|
||||
except ImportError:
|
||||
logger.error('Imposible importar %s' % package)
|
||||
else:
|
||||
self.return_value = getattr(cb_channel, self.custom_button['function'])(self.item, self.values)
|
||||
if not self.custom_button["close"]:
|
||||
if isinstance(self.return_value, dict) and self.return_value.has_key("label"):
|
||||
self.getControl(10006).setLabel(self.return_value['label'])
|
||||
|
||||
for c in self.list_controls:
|
||||
if c["type"] == "text":
|
||||
c["control"].setText(self.values[c["id"]])
|
||||
if c["type"] == "bool":
|
||||
c["control"].setSelected(self.values[c["id"]])
|
||||
if c["type"] == "list":
|
||||
c["label"].setLabel(c["lvalues"][self.values[c["id"]]])
|
||||
|
||||
self.evaluate_conditions()
|
||||
self.dispose_controls(self.index, force=True)
|
||||
|
||||
else:
|
||||
for c in self.list_controls:
|
||||
if c["type"] == "text":
|
||||
c["control"].setText(c["default"])
|
||||
self.values[c["id"]] = c["default"]
|
||||
if c["type"] == "bool":
|
||||
c["control"].setSelected(c["default"])
|
||||
self.values[c["id"]] = c["default"]
|
||||
if c["type"] == "list":
|
||||
c["label"].setLabel(c["lvalues"][c["default"]])
|
||||
self.values[c["id"]] = c["default"]
|
||||
|
||||
self.evaluate_conditions()
|
||||
self.dispose_controls(self.index, force=True)
|
||||
self.check_default()
|
||||
self.check_ok()
|
||||
|
||||
# Boton Cancelar y [X]
|
||||
if id == 10003 or id == 10005:
|
||||
self.close()
|
||||
|
||||
# Boton Aceptar
|
||||
if id == 10004:
|
||||
self.close()
|
||||
if self.callback and '.' in self.callback:
|
||||
package, self.callback = self.callback.rsplit('.', 1)
|
||||
else:
|
||||
package = '%s.%s' % (self.ch_type, self.channel)
|
||||
|
||||
cb_channel = None
|
||||
try:
|
||||
cb_channel = __import__(package, None, None, [package])
|
||||
except ImportError:
|
||||
logger.error('Imposible importar %s' % package)
|
||||
|
||||
if self.callback:
|
||||
# Si existe una funcion callback la invocamos ...
|
||||
self.return_value = getattr(cb_channel, self.callback)(self.item, self.values)
|
||||
else:
|
||||
# si no, probamos si en el canal existe una funcion 'cb_validate_config' ...
|
||||
try:
|
||||
self.return_value = getattr(cb_channel, 'cb_validate_config')(self.item, self.values)
|
||||
except AttributeError:
|
||||
# ... si tampoco existe 'cb_validate_config'...
|
||||
for v in self.values:
|
||||
config.set_setting(v, self.values[v], **self.kwargs)
|
||||
|
||||
# Controles de ajustes, si se cambia el valor de un ajuste, cambiamos el valor guardado en el diccionario de
|
||||
# valores
|
||||
# Obtenemos el control sobre el que se ha echo click
|
||||
control = self.getControl(id)
|
||||
|
||||
# Lo buscamos en el listado de controles
|
||||
for cont in self.list_controls:
|
||||
|
||||
# Si el control es un "downBtn" o "upBtn" son los botones del "list"
|
||||
# en este caso cambiamos el valor del list
|
||||
if cont["type"] == "list" and (cont["downBtn"] == control or cont["upBtn"] == control):
|
||||
|
||||
# Para bajar una posicion
|
||||
if cont["downBtn"] == control:
|
||||
index = cont["lvalues"].index(cont["label"].getLabel())
|
||||
if index > 0:
|
||||
cont["label"].setLabel(cont["lvalues"][index - 1])
|
||||
|
||||
# Para subir una posicion
|
||||
elif cont["upBtn"] == control:
|
||||
index = cont["lvalues"].index(cont["label"].getLabel())
|
||||
if index < len(cont["lvalues"]) - 1:
|
||||
cont["label"].setLabel(cont["lvalues"][index + 1])
|
||||
|
||||
# Guardamos el nuevo valor en el diccionario de valores
|
||||
self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel())
|
||||
|
||||
# Si esl control es un "bool", guardamos el nuevo valor True/False
|
||||
if cont["type"] == "bool" and cont["control"] == control:
|
||||
self.values[cont["id"]] = bool(cont["control"].isSelected())
|
||||
|
||||
# Si esl control es un "text", guardamos el nuevo valor
|
||||
if cont["type"] == "text" and cont["control"] == control:
|
||||
# Versiones antiguas requieren abrir el teclado manualmente
|
||||
if xbmcgui.ControlEdit == ControlEdit:
|
||||
import xbmc
|
||||
keyboard = xbmc.Keyboard(cont["control"].getText(), cont["control"].getLabel(),
|
||||
cont["control"].isPassword)
|
||||
keyboard.setHiddenInput(cont["control"].isPassword)
|
||||
keyboard.doModal()
|
||||
if keyboard.isConfirmed():
|
||||
cont["control"].setText(keyboard.getText())
|
||||
|
||||
self.values[cont["id"]] = cont["control"].getText()
|
||||
|
||||
self.evaluate_conditions()
|
||||
self.dispose_controls(self.index, force=True)
|
||||
self.check_default()
|
||||
self.check_ok()
|
||||
|
||||
# Versiones antiguas requieren esta funcion
|
||||
def onFocus(self, a):
|
||||
pass
|
||||
|
||||
def onAction(self, raw_action):
|
||||
# Obtenemos el foco
|
||||
focus = self.getFocusId()
|
||||
|
||||
action = raw_action.getId()
|
||||
# Accion 1: Flecha izquierda
|
||||
if action == 1:
|
||||
# Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor
|
||||
if focus not in [10004, 10005, 10006]:
|
||||
control = self.getFocus()
|
||||
for cont in self.list_controls:
|
||||
if cont["type"] == "list" and cont["control"] == control:
|
||||
index = cont["lvalues"].index(cont["label"].getLabel())
|
||||
if index > 0:
|
||||
cont["label"].setLabel(cont["lvalues"][index - 1])
|
||||
|
||||
# Guardamos el nuevo valor en el listado de controles
|
||||
self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel())
|
||||
|
||||
self.evaluate_conditions()
|
||||
self.dispose_controls(self.index, force=True)
|
||||
self.check_default()
|
||||
self.check_ok()
|
||||
|
||||
# Si el foco está en alguno de los tres botones inferiores, movemos al siguiente
|
||||
else:
|
||||
if focus == 10006:
|
||||
self.setFocusId(10005)
|
||||
if focus == 10005 and self.ok_enabled:
|
||||
self.setFocusId(10004)
|
||||
|
||||
# Accion 1: Flecha derecha
|
||||
elif action == 2:
|
||||
# Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor
|
||||
if focus not in [10004, 10005, 10006]:
|
||||
control = self.getFocus()
|
||||
for cont in self.list_controls:
|
||||
if cont["type"] == "list" and cont["control"] == control:
|
||||
index = cont["lvalues"].index(cont["label"].getLabel())
|
||||
if index < len(cont["lvalues"]) - 1:
|
||||
cont["label"].setLabel(cont["lvalues"][index + 1])
|
||||
|
||||
# Guardamos el nuevo valor en el listado de controles
|
||||
self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel())
|
||||
|
||||
self.evaluate_conditions()
|
||||
self.dispose_controls(self.index, force=True)
|
||||
self.check_default()
|
||||
self.check_ok()
|
||||
|
||||
# Si el foco está en alguno de los tres botones inferiores, movemos al siguiente
|
||||
else:
|
||||
if focus == 10004:
|
||||
self.setFocusId(10005)
|
||||
if focus == 10005 and self.default_enabled:
|
||||
self.setFocusId(10006)
|
||||
|
||||
# Accion 4: Flecha abajo
|
||||
elif action == 4:
|
||||
# Si el foco no está en ninguno de los tres botones inferiores, bajamos el foco en los controles de ajustes
|
||||
if focus not in [10004, 10005, 10006]:
|
||||
try:
|
||||
focus_control = \
|
||||
[self.visible_controls.index(c) for c in self.visible_controls if c["control"] == self.getFocus()][
|
||||
0]
|
||||
focus_control += 1
|
||||
except:
|
||||
focus_control = 0
|
||||
|
||||
while not focus_control == len(self.visible_controls) and (
|
||||
self.visible_controls[focus_control]["type"] == "label" or not
|
||||
self.visible_controls[focus_control]["active"]):
|
||||
focus_control += 1
|
||||
|
||||
if focus_control >= len(self.visible_controls):
|
||||
self.setFocusId(10005)
|
||||
return
|
||||
|
||||
self.dispose_controls(focus_control, True)
|
||||
|
||||
# Accion 4: Flecha arriba
|
||||
elif action == 3:
|
||||
# Si el foco no está en ninguno de los tres botones inferiores, subimos el foco en los controles de ajustes
|
||||
if focus not in [10003, 10004, 10005, 10006]:
|
||||
try:
|
||||
focus_control = \
|
||||
[self.visible_controls.index(c) for c in self.visible_controls if c["control"] == self.getFocus()][
|
||||
0]
|
||||
focus_control -= 1
|
||||
|
||||
while not focus_control == -1 and (self.visible_controls[focus_control]["type"] == "label" or not
|
||||
self.visible_controls[focus_control]["active"]):
|
||||
focus_control -= 1
|
||||
|
||||
if focus_control < 0: focus_control = 0
|
||||
except:
|
||||
focus_control = 0
|
||||
|
||||
self.dispose_controls(focus_control, True)
|
||||
|
||||
# Si el foco está en alguno de los tres botones inferiores, ponemos el foco en el ultimo ajuste.
|
||||
else:
|
||||
focus_control = len(self.visible_controls) - 1
|
||||
while not focus_control == -1 and (self.visible_controls[focus_control]["type"] == "label" or not
|
||||
self.visible_controls[focus_control]["active"]):
|
||||
focus_control -= 1
|
||||
if focus_control < 0: focus_control = 0
|
||||
|
||||
self.setFocus(self.visible_controls[focus_control]["control"])
|
||||
|
||||
# Accion 104: Scroll arriba
|
||||
elif action == 104:
|
||||
self.dispose_controls(self.index - 1)
|
||||
|
||||
# Accion 105: Scroll abajo
|
||||
elif action == 105:
|
||||
self.dispose_controls(self.index + 1)
|
||||
|
||||
# ACTION_PREVIOUS_MENU 10
|
||||
# ACTION_NAV_BACK 92
|
||||
elif action in [10, 92]:
|
||||
self.close()
|
||||
|
||||
elif action == 504:
|
||||
|
||||
if self.xx > raw_action.getAmount2():
|
||||
if (self.xx - int(raw_action.getAmount2())) / self.height_control:
|
||||
self.xx -= self.height_control
|
||||
self.dispose_controls(self.index + 1)
|
||||
else:
|
||||
if (int(raw_action.getAmount2()) - self.xx) / self.height_control:
|
||||
self.xx += self.height_control
|
||||
self.dispose_controls(self.index - 1)
|
||||
return
|
||||
|
||||
elif action == 501:
|
||||
self.xx = int(raw_action.getAmount2())
|
||||
|
||||
|
||||
class ControlEdit(xbmcgui.ControlButton):
|
||||
def __new__(cls, *args, **kwargs):
|
||||
del kwargs["isPassword"]
|
||||
del kwargs["window"]
|
||||
args = list(args)
|
||||
return xbmcgui.ControlButton.__new__(cls, *args, **kwargs)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.isPassword = kwargs["isPassword"]
|
||||
self.window = kwargs["window"]
|
||||
self.label = ""
|
||||
self.text = ""
|
||||
self.textControl = xbmcgui.ControlLabel(self.getX(), self.getY(), self.getWidth(), self.getHeight(), self.text,
|
||||
font=kwargs["font"], textColor=kwargs["textColor"], alignment=4 | 1)
|
||||
self.window.addControl(self.textControl)
|
||||
|
||||
def setLabel(self, val):
|
||||
self.label = val
|
||||
xbmcgui.ControlButton.setLabel(self, val)
|
||||
|
||||
def getX(self):
|
||||
return xbmcgui.ControlButton.getPosition(self)[0]
|
||||
|
||||
def getY(self):
|
||||
return xbmcgui.ControlButton.getPosition(self)[1]
|
||||
|
||||
def setEnabled(self, e):
|
||||
xbmcgui.ControlButton.setEnabled(self, e)
|
||||
self.textControl.setEnabled(e)
|
||||
|
||||
def setWidth(self, w):
|
||||
xbmcgui.ControlButton.setWidth(self, w)
|
||||
self.textControl.setWidth(w / 2)
|
||||
|
||||
def setHeight(self, w):
|
||||
xbmcgui.ControlButton.setHeight(self, w)
|
||||
self.textControl.setHeight(w)
|
||||
|
||||
def setPosition(self, x, y):
|
||||
xbmcgui.ControlButton.setPosition(self, x, y)
|
||||
if xbmcgui.__version__ == "1.2":
|
||||
self.textControl.setPosition(x + self.getWidth(), y)
|
||||
else:
|
||||
self.textControl.setPosition(x + self.getWidth() / 2, y)
|
||||
|
||||
def setText(self, text):
|
||||
self.text = text
|
||||
if self.isPassword:
|
||||
self.textControl.setLabel("*" * len(self.text))
|
||||
else:
|
||||
self.textControl.setLabel(self.text)
|
||||
|
||||
def getText(self):
|
||||
return self.text
|
||||
|
||||
|
||||
if not hasattr(xbmcgui, "ControlEdit"):
|
||||
xbmcgui.ControlEdit = ControlEdit
|
||||
329
plugin.video.alfa/platformcode/xbmc_info_window.py
Executable file
329
plugin.video.alfa/platformcode/xbmc_info_window.py
Executable file
@@ -0,0 +1,329 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import xbmcgui
|
||||
from core import logger
|
||||
from core.tmdb import Tmdb
|
||||
|
||||
ID_BUTTON_CLOSE = 10003
|
||||
ID_BUTTON_PREVIOUS = 10025
|
||||
ID_BUTTON_NEXT = 10026
|
||||
ID_BUTTON_CANCEL = 10027
|
||||
ID_BUTTON_OK = 10028
|
||||
|
||||
|
||||
class InfoWindow(xbmcgui.WindowXMLDialog):
|
||||
otmdb = None
|
||||
|
||||
item_title = ""
|
||||
item_serie = ""
|
||||
item_temporada = 0
|
||||
item_episodio = 0
|
||||
result = {}
|
||||
|
||||
# PARA TMDB
|
||||
@staticmethod
|
||||
def get_language(lng):
|
||||
# Cambiamos el formato del Idioma
|
||||
languages = {
|
||||
'aa': 'Afar', 'ab': 'Abkhazian', 'af': 'Afrikaans', 'ak': 'Akan', 'sq': 'Albanian', 'am': 'Amharic',
|
||||
'ar': 'Arabic', 'an': 'Aragonese', 'as': 'Assamese', 'av': 'Avaric', 'ae': 'Avestan', 'ay': 'Aymara',
|
||||
'az': 'Azerbaijani', 'ba': 'Bashkir', 'bm': 'Bambara', 'eu': 'Basque', 'be': 'Belarusian', 'bn': 'Bengali',
|
||||
'bh': 'Bihari languages', 'bi': 'Bislama', 'bo': 'Tibetan', 'bs': 'Bosnian', 'br': 'Breton',
|
||||
'bg': 'Bulgarian', 'my': 'Burmese', 'ca': 'Catalan; Valencian', 'cs': 'Czech', 'ch': 'Chamorro',
|
||||
'ce': 'Chechen', 'zh': 'Chinese',
|
||||
'cu': 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic', 'cv': 'Chuvash',
|
||||
'kw': 'Cornish', 'co': 'Corsican', 'cr': 'Cree', 'cy': 'Welsh', 'da': 'Danish', 'de': 'German',
|
||||
'dv': 'Divehi; Dhivehi; Maldivian', 'nl': 'Dutch; Flemish', 'dz': 'Dzongkha', 'en': 'English',
|
||||
'eo': 'Esperanto', 'et': 'Estonian', 'ee': 'Ewe', 'fo': 'Faroese', 'fa': 'Persian', 'fj': 'Fijian',
|
||||
'fi': 'Finnish', 'fr': 'French', 'fy': 'Western Frisian', 'ff': 'Fulah', 'Ga': 'Georgian',
|
||||
'gd': 'Gaelic; Scottish Gaelic', 'ga': 'Irish', 'gl': 'Galician', 'gv': 'Manx',
|
||||
'el': 'Greek, Modern (1453-)', 'gn': 'Guarani', 'gu': 'Gujarati', 'ht': 'Haitian; Haitian Creole',
|
||||
'ha': 'Hausa', 'he': 'Hebrew', 'hz': 'Herero', 'hi': 'Hindi', 'ho': 'Hiri Motu', 'hr': 'Croatian',
|
||||
'hu': 'Hungarian', 'hy': 'Armenian', 'ig': 'Igbo', 'is': 'Icelandic', 'io': 'Ido',
|
||||
'ii': 'Sichuan Yi; Nuosu', 'iu': 'Inuktitut', 'ie': 'Interlingue; Occidental',
|
||||
'ia': 'Interlingua (International Auxiliary Language Association)', 'id': 'Indonesian', 'ik': 'Inupiaq',
|
||||
'it': 'Italian', 'jv': 'Javanese', 'ja': 'Japanese', 'kl': 'Kalaallisut; Greenlandic', 'kn': 'Kannada',
|
||||
'ks': 'Kashmiri', 'ka': 'Georgian', 'kr': 'Kanuri', 'kk': 'Kazakh', 'km': 'Central Khmer',
|
||||
'ki': 'Kikuyu; Gikuyu', 'rw': 'Kinyarwanda', 'ky': 'Kirghiz; Kyrgyz', 'kv': 'Komi', 'kg': 'Kongo',
|
||||
'ko': 'Korean', 'kj': 'Kuanyama; Kwanyama', 'ku': 'Kurdish', 'lo': 'Lao', 'la': 'Latin', 'lv': 'Latvian',
|
||||
'li': 'Limburgan; Limburger; Limburgish', 'ln': 'Lingala', 'lt': 'Lithuanian',
|
||||
'lb': 'Luxembourgish; Letzeburgesch', 'lu': 'Luba-Katanga', 'lg': 'Ganda', 'mk': 'Macedonian',
|
||||
'mh': 'Marshallese', 'ml': 'Malayalam', 'mi': 'Maori', 'mr': 'Marathi', 'ms': 'Malay', 'Mi': 'Micmac',
|
||||
'mg': 'Malagasy', 'mt': 'Maltese', 'mn': 'Mongolian', 'na': 'Nauru', 'nv': 'Navajo; Navaho',
|
||||
'nr': 'Ndebele, South; South Ndebele', 'nd': 'Ndebele, North; North Ndebele', 'ng': 'Ndonga',
|
||||
'ne': 'Nepali', 'nn': 'Norwegian Nynorsk; Nynorsk, Norwegian', 'nb': 'Bokmål, Norwegian; Norwegian Bokmål',
|
||||
'no': 'Norwegian', 'oc': 'Occitan (post 1500)', 'oj': 'Ojibwa', 'or': 'Oriya', 'om': 'Oromo',
|
||||
'os': 'Ossetian; Ossetic', 'pa': 'Panjabi; Punjabi', 'pi': 'Pali', 'pl': 'Polish', 'pt': 'Portuguese',
|
||||
'ps': 'Pushto; Pashto', 'qu': 'Quechua', 'ro': 'Romanian; Moldavian; Moldovan', 'rn': 'Rundi',
|
||||
'ru': 'Russian', 'sg': 'Sango', 'rm': 'Romansh', 'sa': 'Sanskrit', 'si': 'Sinhala; Sinhalese',
|
||||
'sk': 'Slovak', 'sl': 'Slovenian', 'se': 'Northern Sami', 'sm': 'Samoan', 'sn': 'Shona', 'sd': 'Sindhi',
|
||||
'so': 'Somali', 'st': 'Sotho, Southern', 'es': 'Spanish', 'sc': 'Sardinian', 'sr': 'Serbian', 'ss': 'Swati',
|
||||
'su': 'Sundanese', 'sw': 'Swahili', 'sv': 'Swedish', 'ty': 'Tahitian', 'ta': 'Tamil', 'tt': 'Tatar',
|
||||
'te': 'Telugu', 'tg': 'Tajik', 'tl': 'Tagalog', 'th': 'Thai', 'ti': 'Tigrinya',
|
||||
'to': 'Tonga (Tonga Islands)', 'tn': 'Tswana', 'ts': 'Tsonga', 'tk': 'Turkmen', 'tr': 'Turkish',
|
||||
'tw': 'Twi', 'ug': 'Uighur; Uyghur', 'uk': 'Ukrainian', 'ur': 'Urdu', 'uz': 'Uzbek', 've': 'Venda',
|
||||
'vi': 'Vietnamese', 'vo': 'Volapük', 'wa': 'Walloon', 'wo': 'Wolof', 'xh': 'Xhosa', 'yi': 'Yiddish',
|
||||
'yo': 'Yoruba', 'za': 'Zhuang; Chuang', 'zu': 'Zulu'}
|
||||
|
||||
return languages.get(lng, lng)
|
||||
|
||||
def get_scraper_data(self, data_in):
|
||||
self.otmdb = None
|
||||
# logger.debug(str(data_in))
|
||||
|
||||
if self.listData:
|
||||
# Datos comunes a todos los listados
|
||||
infoLabels = self.scraper().get_infoLabels(origen=data_in)
|
||||
|
||||
if "original_language" in infoLabels:
|
||||
infoLabels["language"] = self.get_language(infoLabels["original_language"])
|
||||
infoLabels["puntuacion"] = "%s/10 (%s)" % (infoLabels.get("rating", "?"), infoLabels.get("votes", "N/A"))
|
||||
|
||||
self.result = infoLabels
|
||||
|
||||
def start(self, data, caption="Información del vídeo", item=None, scraper=Tmdb):
|
||||
"""
|
||||
Muestra una ventana con la info del vídeo. Opcionalmente se puede indicar el titulo de la ventana mendiante
|
||||
el argumento 'caption'.
|
||||
|
||||
Si se pasa un item como argumento 'data' usa el scrapper Tmdb para buscar la info del vídeo
|
||||
En caso de peliculas:
|
||||
Coge el titulo de los siguientes campos (en este orden)
|
||||
1. contentTitle (este tiene prioridad 1)
|
||||
2. fulltitle (este tiene prioridad 2)
|
||||
3. title (este tiene prioridad 3)
|
||||
El primero que contenga "algo" lo interpreta como el titulo (es importante asegurarse que el titulo este en
|
||||
su sitio)
|
||||
|
||||
En caso de series:
|
||||
1. Busca la temporada y episodio en los campos contentSeason y contentEpisodeNumber
|
||||
2. Intenta Sacarlo del titulo del video (formato: 1x01)
|
||||
|
||||
Aqui hay dos opciones posibles:
|
||||
1. Tenemos Temporada y episodio
|
||||
Muestra la información del capitulo concreto
|
||||
2. NO Tenemos Temporada y episodio
|
||||
En este caso muestra la informacion generica de la serie
|
||||
|
||||
Si se pasa como argumento 'data' un objeto InfoLabels(ver item.py) muestra en la ventana directamente
|
||||
la información pasada (sin usar el scrapper)
|
||||
Formato:
|
||||
En caso de peliculas:
|
||||
infoLabels({
|
||||
"type" : "movie",
|
||||
"title" : "Titulo de la pelicula",
|
||||
"original_title" : "Titulo original de la pelicula",
|
||||
"date" : "Fecha de lanzamiento",
|
||||
"language" : "Idioma original de la pelicula",
|
||||
"rating" : "Puntuacion de la pelicula",
|
||||
"votes" : "Numero de votos",
|
||||
"genres" : "Generos de la pelicula",
|
||||
"thumbnail" : "Ruta para el thumbnail",
|
||||
"fanart" : "Ruta para el fanart",
|
||||
"plot" : "Sinopsis de la pelicula"
|
||||
}
|
||||
En caso de series:
|
||||
infoLabels({
|
||||
"type" : "tv",
|
||||
"title" : "Titulo de la serie",
|
||||
"episode_title" : "Titulo del episodio",
|
||||
"date" : "Fecha de emision",
|
||||
"language" : "Idioma original de la serie",
|
||||
"rating" : "Puntuacion de la serie",
|
||||
"votes" : "Numero de votos",
|
||||
"genres" : "Generos de la serie",
|
||||
"thumbnail" : "Ruta para el thumbnail",
|
||||
"fanart" : "Ruta para el fanart",
|
||||
"plot" : "Sinopsis de la del episodio o de la serie",
|
||||
"seasons" : "Numero de Temporadas",
|
||||
"season" : "Temporada",
|
||||
"episodes" : "Numero de episodios de la temporada",
|
||||
"episode" : "Episodio"
|
||||
}
|
||||
Si se pasa como argumento 'data' un listado de InfoLabels() con la estructura anterior, muestra los botones
|
||||
'Anterior' y 'Siguiente' para ir recorriendo la lista. Ademas muestra los botones 'Aceptar' y 'Cancelar' que
|
||||
llamaran a la funcion 'callback' del canal desde donde se realiza la llamada pasandole como parametros el elemento
|
||||
actual (InfoLabels()) o None respectivamente.
|
||||
|
||||
@param data: información para obtener datos del scraper.
|
||||
@type data: item, InfoLabels, list(InfoLabels)
|
||||
@param caption: titulo de la ventana.
|
||||
@type caption: str
|
||||
@param item: elemento del que se va a mostrar la ventana de información
|
||||
@type item: Item
|
||||
@param scraper: scraper que tiene los datos de las peliculas o series a mostrar en la ventana.
|
||||
@type scraper: Scraper
|
||||
"""
|
||||
|
||||
# Capturamos los parametros
|
||||
self.caption = caption
|
||||
self.item = item
|
||||
self.indexList = -1
|
||||
self.listData = None
|
||||
self.return_value = None
|
||||
self.scraper = scraper
|
||||
|
||||
logger.debug(data)
|
||||
if type(data) == list:
|
||||
self.listData = data
|
||||
self.indexList = 0
|
||||
data = self.listData[self.indexList]
|
||||
|
||||
self.get_scraper_data(data)
|
||||
|
||||
# Muestra la ventana
|
||||
self.doModal()
|
||||
return self.return_value
|
||||
|
||||
def __init__(self, *args):
|
||||
self.caption = ""
|
||||
self.item = None
|
||||
self.listData = None
|
||||
self.indexList = 0
|
||||
self.return_value = None
|
||||
self.scraper = Tmdb
|
||||
|
||||
def onInit(self):
|
||||
if xbmcgui.__version__ == "1.2":
|
||||
self.setCoordinateResolution(1)
|
||||
else:
|
||||
self.setCoordinateResolution(5)
|
||||
|
||||
# Ponemos el título y las imagenes
|
||||
self.getControl(10002).setLabel(self.caption)
|
||||
self.getControl(10004).setImage(self.result.get("fanart", ""))
|
||||
self.getControl(10005).setImage(self.result.get("thumbnail", "images/img_no_disponible.png"))
|
||||
|
||||
# Cargamos los datos para el formato pelicula
|
||||
if self.result.get("mediatype", "movie") == "movie":
|
||||
self.getControl(10006).setLabel("Título:")
|
||||
self.getControl(10007).setLabel(self.result.get("title", "N/A"))
|
||||
self.getControl(10008).setLabel("Título original:")
|
||||
self.getControl(10009).setLabel(self.result.get("originaltitle", "N/A"))
|
||||
self.getControl(100010).setLabel("Idioma original:")
|
||||
self.getControl(100011).setLabel(self.result.get("language", "N/A"))
|
||||
self.getControl(100012).setLabel("Puntuación:")
|
||||
self.getControl(100013).setLabel(self.result.get("puntuacion", "N/A"))
|
||||
self.getControl(100014).setLabel("Lanzamiento:")
|
||||
self.getControl(100015).setLabel(self.result.get("release_date", "N/A"))
|
||||
self.getControl(100016).setLabel("Géneros:")
|
||||
self.getControl(100017).setLabel(self.result.get("genre", "N/A"))
|
||||
|
||||
# Cargamos los datos para el formato serie
|
||||
else:
|
||||
self.getControl(10006).setLabel("Serie:")
|
||||
self.getControl(10007).setLabel(self.result.get("title", "N/A"))
|
||||
self.getControl(10008).setLabel("Idioma original:")
|
||||
self.getControl(10009).setLabel(self.result.get("language", "N/A"))
|
||||
self.getControl(100010).setLabel("Puntuación:")
|
||||
self.getControl(100011).setLabel(self.result.get("puntuacion", "N/A"))
|
||||
self.getControl(100012).setLabel("Géneros:")
|
||||
self.getControl(100013).setLabel(self.result.get("genre", "N/A"))
|
||||
|
||||
if self.result.get("season"):
|
||||
self.getControl(100014).setLabel("Título temporada:")
|
||||
self.getControl(100015).setLabel(self.result.get("temporada_nombre", "N/A"))
|
||||
self.getControl(100016).setLabel("Temporada:")
|
||||
self.getControl(100017).setLabel(self.result.get("season", "N/A") + " de " +
|
||||
self.result.get("seasons", "N/A"))
|
||||
if self.result.get("episode"):
|
||||
self.getControl(100014).setLabel("Título:")
|
||||
self.getControl(100015).setLabel(self.result.get("episode_title", "N/A"))
|
||||
self.getControl(100018).setLabel("Episodio:")
|
||||
self.getControl(100019).setLabel(self.result.get("episode", "N/A") + " de " +
|
||||
self.result.get("episodes", "N/A"))
|
||||
self.getControl(100020).setLabel("Emisión:")
|
||||
self.getControl(100021).setLabel(self.result.get("date", "N/A"))
|
||||
|
||||
# Sinopsis
|
||||
if self.result['plot']:
|
||||
self.getControl(100022).setLabel("Sinopsis:")
|
||||
self.getControl(100023).setText(self.result.get("plot", "N/A"))
|
||||
else:
|
||||
self.getControl(100022).setLabel("")
|
||||
self.getControl(100023).setText("")
|
||||
|
||||
# Cargamos los botones si es necesario
|
||||
self.getControl(10024).setVisible(self.indexList > -1) # Grupo de botones
|
||||
self.getControl(ID_BUTTON_PREVIOUS).setEnabled(self.indexList > 0) # Anterior
|
||||
|
||||
if self.listData:
|
||||
m = len(self.listData)
|
||||
else:
|
||||
m = 1
|
||||
|
||||
self.getControl(ID_BUTTON_NEXT).setEnabled(self.indexList + 1 != m) # Siguiente
|
||||
self.getControl(100029).setLabel("(%s/%s)" % (self.indexList + 1, m)) # x/m
|
||||
|
||||
# Ponemos el foco en el Grupo de botones, si estuviera desactivado "Anterior" iria el foco al boton "Siguiente"
|
||||
# si "Siguiente" tb estuviera desactivado pasara el foco al botón "Cancelar"
|
||||
self.setFocus(self.getControl(10024))
|
||||
|
||||
return self.return_value
|
||||
|
||||
def onClick(self, _id):
|
||||
logger.info("onClick id=" + repr(_id))
|
||||
if _id == ID_BUTTON_PREVIOUS and self.indexList > 0:
|
||||
self.indexList -= 1
|
||||
self.get_scraper_data(self.listData[self.indexList])
|
||||
self.onInit()
|
||||
|
||||
elif _id == ID_BUTTON_NEXT and self.indexList < len(self.listData) - 1:
|
||||
self.indexList += 1
|
||||
self.get_scraper_data(self.listData[self.indexList])
|
||||
self.onInit()
|
||||
|
||||
elif _id == ID_BUTTON_OK or _id == ID_BUTTON_CLOSE or _id == ID_BUTTON_CANCEL:
|
||||
self.close()
|
||||
|
||||
if _id == ID_BUTTON_OK:
|
||||
self.return_value = self.listData[self.indexList]
|
||||
else:
|
||||
self.return_value = None
|
||||
|
||||
def onAction(self, action):
|
||||
logger.info("action=" + repr(action.getId()))
|
||||
action = action.getId()
|
||||
|
||||
# Obtenemos el foco
|
||||
focus = self.getFocusId()
|
||||
|
||||
# Accion 1: Flecha izquierda
|
||||
if action == 1:
|
||||
|
||||
if focus == ID_BUTTON_OK:
|
||||
self.setFocus(self.getControl(ID_BUTTON_CANCEL))
|
||||
|
||||
elif focus == ID_BUTTON_CANCEL:
|
||||
if self.indexList + 1 != len(self.listData):
|
||||
# vamos al botón Siguiente
|
||||
self.setFocus(self.getControl(ID_BUTTON_NEXT))
|
||||
elif self.indexList > 0:
|
||||
# vamos al botón Anterior ya que Siguiente no está activo (estamos al final de la lista)
|
||||
self.setFocus(self.getControl(ID_BUTTON_PREVIOUS))
|
||||
|
||||
elif focus == ID_BUTTON_NEXT:
|
||||
if self.indexList > 0:
|
||||
# vamos al botón Anterior
|
||||
self.setFocus(self.getControl(ID_BUTTON_PREVIOUS))
|
||||
|
||||
# Accion 2: Flecha derecha
|
||||
elif action == 2:
|
||||
|
||||
if focus == ID_BUTTON_PREVIOUS:
|
||||
if self.indexList + 1 != len(self.listData):
|
||||
# vamos al botón Siguiente
|
||||
self.setFocus(self.getControl(ID_BUTTON_NEXT))
|
||||
else:
|
||||
# vamos al botón Cancelar ya que Siguiente no está activo (estamos al final de la lista)
|
||||
self.setFocus(self.getControl(ID_BUTTON_CANCEL))
|
||||
|
||||
elif focus == ID_BUTTON_NEXT:
|
||||
self.setFocus(self.getControl(ID_BUTTON_CANCEL))
|
||||
|
||||
elif focus == ID_BUTTON_CANCEL:
|
||||
self.setFocus(self.getControl(ID_BUTTON_OK))
|
||||
|
||||
# Pulsa ESC o Atrás, simula click en boton cancelar
|
||||
if action in [10, 92]:
|
||||
self.onClick(ID_BUTTON_CANCEL)
|
||||
828
plugin.video.alfa/platformcode/xbmc_videolibrary.py
Executable file
828
plugin.video.alfa/platformcode/xbmc_videolibrary.py
Executable file
@@ -0,0 +1,828 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ------------------------------------------------------------
|
||||
# XBMC Library Tools
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import urllib2
|
||||
|
||||
import xbmc
|
||||
from core import config
|
||||
from core import filetools
|
||||
from core import jsontools
|
||||
from core import logger
|
||||
from platformcode import platformtools
|
||||
|
||||
|
||||
def mark_auto_as_watched(item):
|
||||
def mark_as_watched_subThread(item):
|
||||
logger.info()
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
|
||||
condicion = config.get_setting("watched_setting", "videolibrary")
|
||||
|
||||
time_limit = time.time() + 30
|
||||
while not platformtools.is_playing() and time.time() < time_limit:
|
||||
time.sleep(1)
|
||||
|
||||
sync_with_trakt = False
|
||||
|
||||
while platformtools.is_playing():
|
||||
tiempo_actual = xbmc.Player().getTime()
|
||||
totaltime = xbmc.Player().getTotalTime()
|
||||
|
||||
mark_time = 0
|
||||
if condicion == 0: # '5 minutos'
|
||||
mark_time = 300
|
||||
elif condicion == 1: # '30%'
|
||||
mark_time = totaltime * 0.3
|
||||
elif condicion == 2: # '50%'
|
||||
mark_time = totaltime * 0.5
|
||||
elif condicion == 3: # '80%'
|
||||
mark_time = totaltime * 0.8
|
||||
elif condicion == 4: # '0 seg'
|
||||
mark_time = -1
|
||||
|
||||
# logger.debug(str(tiempo_actual))
|
||||
# logger.debug(str(mark_time))
|
||||
|
||||
if tiempo_actual > mark_time:
|
||||
logger.debug("marcado")
|
||||
item.playcount = 1
|
||||
sync_with_trakt = True
|
||||
from channels import videolibrary
|
||||
videolibrary.mark_content_as_watched(item)
|
||||
break
|
||||
|
||||
time.sleep(30)
|
||||
|
||||
# Sincronizacion silenciosa con Trakt
|
||||
if sync_with_trakt:
|
||||
if config.get_setting("sync_trakt_watched", "videolibrary"):
|
||||
sync_trakt_kodi()
|
||||
|
||||
# logger.debug("Fin del hilo")
|
||||
|
||||
# Si esta configurado para marcar como visto
|
||||
if config.get_setting("mark_as_watched", "videolibrary"):
|
||||
threading.Thread(target=mark_as_watched_subThread, args=[item]).start()
|
||||
|
||||
|
||||
def sync_trakt_addon(path_folder):
|
||||
"""
|
||||
Actualiza los valores de episodios vistos si
|
||||
"""
|
||||
logger.info()
|
||||
# si existe el addon hacemos la busqueda
|
||||
if xbmc.getCondVisibility('System.HasAddon("script.trakt")'):
|
||||
# importamos dependencias
|
||||
paths = ["special://home/addons/script.module.dateutil/lib/", "special://home/addons/script.module.six/lib/",
|
||||
"special://home/addons/script.module.arrow/lib/", "special://home/addons/script.module.trakt/lib/",
|
||||
"special://home/addons/script.trakt/"]
|
||||
|
||||
for path in paths:
|
||||
import sys
|
||||
sys.path.append(xbmc.translatePath(path))
|
||||
|
||||
# se obtiene las series vistas
|
||||
try:
|
||||
from resources.lib.traktapi import traktAPI
|
||||
traktapi = traktAPI()
|
||||
except:
|
||||
return
|
||||
|
||||
shows = traktapi.getShowsWatched({})
|
||||
shows = shows.items()
|
||||
|
||||
# obtenemos el id de la serie para comparar
|
||||
import re
|
||||
_id = re.findall("\[(.*?)\]", path_folder, flags=re.DOTALL)[0]
|
||||
logger.debug("el id es %s" % _id)
|
||||
|
||||
if "tt" in _id:
|
||||
type_id = "imdb"
|
||||
elif "tvdb_" in _id:
|
||||
_id = _id.strip("tvdb_")
|
||||
type_id = "tvdb"
|
||||
elif "tmdb_" in _id:
|
||||
type_id = "tmdb"
|
||||
_id = _id.strip("tmdb_")
|
||||
else:
|
||||
logger.error("No hay _id de la serie")
|
||||
return
|
||||
|
||||
# obtenemos los valores de la serie
|
||||
from core import videolibrarytools
|
||||
tvshow_file = filetools.join(path_folder, "tvshow.nfo")
|
||||
head_nfo, serie = videolibrarytools.read_nfo(tvshow_file)
|
||||
|
||||
# buscamos en las series de trakt
|
||||
for show in shows:
|
||||
show_aux = show[1].to_dict()
|
||||
|
||||
try:
|
||||
_id_trakt = show_aux['ids'].get(type_id, None)
|
||||
# logger.debug("ID ES %s" % _id_trakt)
|
||||
if _id_trakt:
|
||||
if _id == _id_trakt:
|
||||
logger.debug("ENCONTRADO!! %s" % show_aux)
|
||||
|
||||
# creamos el diccionario de trakt para la serie encontrada con el valor que tiene "visto"
|
||||
dict_trakt_show = {}
|
||||
|
||||
for idx_season, season in enumerate(show_aux['seasons']):
|
||||
for idx_episode, episode in enumerate(show_aux['seasons'][idx_season]['episodes']):
|
||||
sea_epi = "%sx%s" % (show_aux['seasons'][idx_season]['number'],
|
||||
str(show_aux['seasons'][idx_season]['episodes'][idx_episode][
|
||||
'number']).zfill(2))
|
||||
|
||||
dict_trakt_show[sea_epi] = show_aux['seasons'][idx_season]['episodes'][idx_episode][
|
||||
'watched']
|
||||
logger.debug("dict_trakt_show %s " % dict_trakt_show)
|
||||
|
||||
# obtenemos las keys que son episodios
|
||||
regex_epi = re.compile('\d+x\d+')
|
||||
keys_episodes = [key for key in serie.library_playcounts if regex_epi.match(key)]
|
||||
# obtenemos las keys que son temporadas
|
||||
keys_seasons = [key for key in serie.library_playcounts if 'season ' in key]
|
||||
# obtenemos los numeros de las keys temporadas
|
||||
seasons = [key.strip('season ') for key in keys_seasons]
|
||||
|
||||
# marcamos los episodios vistos
|
||||
for k in keys_episodes:
|
||||
serie.library_playcounts[k] = dict_trakt_show.get(k, 0)
|
||||
|
||||
for season in seasons:
|
||||
episodios_temporada = 0
|
||||
episodios_vistos_temporada = 0
|
||||
|
||||
# obtenemos las keys de los episodios de una determinada temporada
|
||||
keys_season_episodes = [key for key in keys_episodes if key.startswith("%sx" % season)]
|
||||
|
||||
for k in keys_season_episodes:
|
||||
episodios_temporada += 1
|
||||
if serie.library_playcounts[k] > 0:
|
||||
episodios_vistos_temporada += 1
|
||||
|
||||
# se comprueba que si todos los episodios están vistos, se marque la temporada como vista
|
||||
if episodios_temporada == episodios_vistos_temporada:
|
||||
serie.library_playcounts.update({"season %s" % season: 1})
|
||||
|
||||
temporada = 0
|
||||
temporada_vista = 0
|
||||
|
||||
for k in keys_seasons:
|
||||
temporada += 1
|
||||
if serie.library_playcounts[k] > 0:
|
||||
temporada_vista += 1
|
||||
|
||||
# se comprueba que si todas las temporadas están vistas, se marque la serie como vista
|
||||
if temporada == temporada_vista:
|
||||
serie.library_playcounts.update({serie.title: 1})
|
||||
|
||||
logger.debug("los valores nuevos %s " % serie.library_playcounts)
|
||||
filetools.write(tvshow_file, head_nfo + serie.tojson())
|
||||
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
else:
|
||||
logger.error("no se ha podido obtener el id, trakt tiene: %s" % show_aux['ids'])
|
||||
|
||||
except:
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def sync_trakt_kodi(silent=True):
|
||||
# Para que la sincronizacion no sea silenciosa vale con silent=False
|
||||
if xbmc.getCondVisibility('System.HasAddon("script.trakt")'):
|
||||
notificacion = True
|
||||
if (not config.get_setting("sync_trakt_notification", "videolibrary") and
|
||||
platformtools.is_playing()):
|
||||
notificacion = False
|
||||
|
||||
xbmc.executebuiltin('RunScript(script.trakt,action=sync,silent=%s)' % silent)
|
||||
logger.info("Sincronizacion con Trakt iniciada")
|
||||
|
||||
if notificacion:
|
||||
platformtools.dialog_notification("Alfa",
|
||||
"Sincronizacion con Trakt iniciada",
|
||||
icon=0,
|
||||
time=2000)
|
||||
|
||||
|
||||
def mark_content_as_watched_on_kodi(item, value=1):
|
||||
"""
|
||||
marca el contenido como visto o no visto en la libreria de Kodi
|
||||
@type item: item
|
||||
@param item: elemento a marcar
|
||||
@type value: int
|
||||
@param value: >0 para visto, 0 para no visto
|
||||
"""
|
||||
logger.info()
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
payload_f = ''
|
||||
|
||||
if item.contentType == "movie":
|
||||
movieid = 0
|
||||
payload = {"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies",
|
||||
"params": {"properties": ["title", "playcount", "originaltitle", "file"]},
|
||||
"id": 1}
|
||||
|
||||
data = get_data(payload)
|
||||
if 'result' in data and "movies" in data['result']:
|
||||
|
||||
filename = filetools.basename(item.strm_path)
|
||||
head, tail = filetools.split(filetools.split(item.strm_path)[0])
|
||||
path = filetools.join(tail, filename)
|
||||
|
||||
for d in data['result']['movies']:
|
||||
if d['file'].replace("/", "\\").endswith(path.replace("/", "\\")):
|
||||
# logger.debug("marco la pelicula como vista")
|
||||
movieid = d['movieid']
|
||||
break
|
||||
|
||||
if movieid != 0:
|
||||
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": {
|
||||
"movieid": movieid, "playcount": value}, "id": 1}
|
||||
|
||||
else: # item.contentType != 'movie'
|
||||
episodeid = 0
|
||||
payload = {"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes",
|
||||
"params": {"properties": ["title", "playcount", "showtitle", "file", "tvshowid"]},
|
||||
"id": 1}
|
||||
|
||||
data = get_data(payload)
|
||||
if 'result' in data and "episodes" in data['result']:
|
||||
|
||||
filename = filetools.basename(item.strm_path)
|
||||
head, tail = filetools.split(filetools.split(item.strm_path)[0])
|
||||
path = filetools.join(tail, filename)
|
||||
|
||||
for d in data['result']['episodes']:
|
||||
|
||||
if d['file'].replace("/", "\\").endswith(path.replace("/", "\\")):
|
||||
# logger.debug("marco el episodio como visto")
|
||||
episodeid = d['episodeid']
|
||||
break
|
||||
|
||||
if episodeid != 0:
|
||||
payload_f = {"jsonrpc": "2.0", "method": "VideoLibrary.SetEpisodeDetails", "params": {
|
||||
"episodeid": episodeid, "playcount": value}, "id": 1}
|
||||
|
||||
if payload_f:
|
||||
# Marcar como visto
|
||||
data = get_data(payload_f)
|
||||
# logger.debug(str(data))
|
||||
if data['result'] != 'OK':
|
||||
logger.error("ERROR al poner el contenido como visto")
|
||||
|
||||
|
||||
def mark_season_as_watched_on_kodi(item, value=1):
|
||||
"""
|
||||
marca toda la temporada como vista o no vista en la libreria de Kodi
|
||||
@type item: item
|
||||
@param item: elemento a marcar
|
||||
@type value: int
|
||||
@param value: >0 para visto, 0 para no visto
|
||||
"""
|
||||
logger.info()
|
||||
# logger.debug("item:\n" + item.tostring('\n'))
|
||||
|
||||
# Solo podemos marcar la temporada como vista en la BBDD de Kodi si la BBDD es local,
|
||||
# en caso de compartir BBDD esta funcionalidad no funcionara
|
||||
if config.get_setting("db_mode", "videolibrary"):
|
||||
return
|
||||
|
||||
if value == 0:
|
||||
value = 'Null'
|
||||
|
||||
request_season = ''
|
||||
if item.contentSeason > -1:
|
||||
request_season = ' and c12= %s' % item.contentSeason
|
||||
|
||||
tvshows_path = filetools.join(config.get_videolibrary_path(), config.get_setting("folder_tvshows"))
|
||||
item_path1 = "%" + item.path.replace("\\\\", "\\").replace(tvshows_path, "")
|
||||
if item_path1[:-1] != "\\":
|
||||
item_path1 += "\\"
|
||||
item_path2 = item_path1.replace("\\", "/")
|
||||
|
||||
sql = 'update files set playCount= %s where idFile in ' \
|
||||
'(select idfile from episode_view where strPath like "%s" or strPath like "%s"%s)' % \
|
||||
(value, item_path1, item_path2, request_season)
|
||||
|
||||
execute_sql_kodi(sql)
|
||||
|
||||
|
||||
def get_data(payload):
|
||||
"""
|
||||
obtiene la información de la llamada JSON-RPC con la información pasada en payload
|
||||
@type payload: dict
|
||||
@param payload: data
|
||||
:return:
|
||||
"""
|
||||
logger.info("payload: %s" % payload)
|
||||
# Required header for XBMC JSON-RPC calls, otherwise you'll get a 415 HTTP response code - Unsupported media type
|
||||
headers = {'content-type': 'application/json'}
|
||||
|
||||
if config.get_setting("db_mode", "videolibrary"):
|
||||
try:
|
||||
try:
|
||||
xbmc_port = config.get_setting("xbmc_puerto", "videolibrary")
|
||||
except:
|
||||
xbmc_port = 0
|
||||
|
||||
xbmc_json_rpc_url = "http://" + config.get_setting("xbmc_host", "videolibrary") + ":" + str(
|
||||
xbmc_port) + "/jsonrpc"
|
||||
req = urllib2.Request(xbmc_json_rpc_url, data=jsontools.dump(payload), headers=headers)
|
||||
f = urllib2.urlopen(req)
|
||||
response = f.read()
|
||||
f.close()
|
||||
|
||||
logger.info("get_data: response %s" % response)
|
||||
data = jsontools.load(response)
|
||||
except Exception, ex:
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex).__name__, ex.args)
|
||||
logger.error("error en xbmc_json_rpc_url: %s" % message)
|
||||
data = ["error"]
|
||||
else:
|
||||
try:
|
||||
data = jsontools.load(xbmc.executeJSONRPC(jsontools.dump(payload)))
|
||||
except Exception, ex:
|
||||
template = "An exception of type %s occured. Arguments:\n%r"
|
||||
message = template % (type(ex).__name__, ex.args)
|
||||
logger.error("error en xbmc.executeJSONRPC: %s" % message)
|
||||
data = ["error"]
|
||||
|
||||
logger.info("data: %s" % data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def update(folder_content=config.get_setting("folder_tvshows"), folder=""):
|
||||
"""
|
||||
Actualiza la libreria dependiendo del tipo de contenido y la ruta que se le pase.
|
||||
|
||||
@type folder_content: str
|
||||
@param folder_content: tipo de contenido para actualizar, series o peliculas
|
||||
@type folder: str
|
||||
@param folder: nombre de la carpeta a escanear.
|
||||
"""
|
||||
logger.info(folder)
|
||||
|
||||
if not folder:
|
||||
# Actualizar toda la coleccion
|
||||
while xbmc.getCondVisibility('Library.IsScanningVideo()'):
|
||||
xbmc.sleep(500)
|
||||
xbmc.executebuiltin('UpdateLibrary(video)')
|
||||
|
||||
else:
|
||||
# Actualizar una sola carpeta en un hilo independiente
|
||||
|
||||
def update_multi_threads(update_path, lock):
|
||||
lock.acquire()
|
||||
# logger.debug("%s\nINICIO" % update_path)
|
||||
payload = {"jsonrpc": "2.0",
|
||||
"method": "VideoLibrary.Scan",
|
||||
"params": {"directory": update_path}, "id": 1}
|
||||
|
||||
data = get_data(payload)
|
||||
lock.release()
|
||||
# logger.debug("%s\nFIN data: %s" % (update_path, data))
|
||||
|
||||
videolibrarypath = config.get_videolibrary_config_path()
|
||||
|
||||
if folder.endswith('/') or folder.endswith('\\'):
|
||||
folder = folder[:-1]
|
||||
|
||||
if videolibrarypath.startswith("special:"):
|
||||
if videolibrarypath.endswith('/'):
|
||||
videolibrarypath = videolibrarypath[:-1]
|
||||
update_path = videolibrarypath + "/" + folder_content + "/" + folder + "/"
|
||||
else:
|
||||
update_path = filetools.join(videolibrarypath, folder_content, folder) + "/"
|
||||
|
||||
t = threading.Thread(target=update_multi_threads, args=[update_path, threading.Lock()])
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
|
||||
def clean(mostrar_dialogo=False):
|
||||
"""
|
||||
limpia la libreria de elementos que no existen
|
||||
@param mostrar_dialogo: muestra el cuadro de progreso mientras se limpia la videoteca
|
||||
@type mostrar_dialogo: bool
|
||||
"""
|
||||
logger.info()
|
||||
payload = {"jsonrpc": "2.0", "method": "VideoLibrary.Clean", "id": 1,
|
||||
"params": {"showdialogs": mostrar_dialogo}}
|
||||
data = get_data(payload)
|
||||
|
||||
if data.get('result', False) == 'OK':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def search_library_path():
|
||||
sql = 'SELECT strPath FROM path WHERE strPath LIKE "special://%/plugin.video.alfa/library/" AND idParentPath ISNULL'
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records >= 1:
|
||||
logger.debug(records[0][0])
|
||||
return records[0][0]
|
||||
return None
|
||||
|
||||
|
||||
def set_content(content_type, silent=False):
|
||||
"""
|
||||
Procedimiento para auto-configurar la videoteca de kodi con los valores por defecto
|
||||
@type content_type: str ('movie' o 'tvshow')
|
||||
@param content_type: tipo de contenido para configurar, series o peliculas
|
||||
"""
|
||||
continuar = True
|
||||
msg_text = ""
|
||||
videolibrarypath = config.get_setting("videolibrarypath")
|
||||
|
||||
if content_type == 'movie':
|
||||
if not xbmc.getCondVisibility('System.HasAddon(metadata.themoviedb.org)'):
|
||||
if not silent:
|
||||
# Preguntar si queremos instalar metadata.themoviedb.org
|
||||
install = platformtools.dialog_yesno("The Movie Database",
|
||||
"No se ha encontrado el Scraper de películas de TheMovieDB.",
|
||||
"¿Desea instalarlo ahora?")
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Instalar metadata.themoviedb.org
|
||||
xbmc.executebuiltin('xbmc.installaddon(metadata.themoviedb.org)', True)
|
||||
logger.info("Instalado el Scraper de películas de TheMovieDB")
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and xbmc.getCondVisibility('System.HasAddon(metadata.themoviedb.org)'))
|
||||
if not continuar:
|
||||
msg_text = "The Movie Database no instalado."
|
||||
|
||||
else: # SERIES
|
||||
# Instalar The TVDB
|
||||
if not xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'):
|
||||
if not silent:
|
||||
# Preguntar si queremos instalar metadata.tvdb.com
|
||||
install = platformtools.dialog_yesno("The TVDB",
|
||||
"No se ha encontrado el Scraper de series de The TVDB.",
|
||||
"¿Desea instalarlo ahora?")
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Instalar metadata.tvdb.com
|
||||
xbmc.executebuiltin('xbmc.installaddon(metadata.tvdb.com)', True)
|
||||
logger.info("Instalado el Scraper de series de The TVDB")
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and xbmc.getCondVisibility('System.HasAddon(metadata.tvdb.com)'))
|
||||
if not continuar:
|
||||
msg_text = "The TVDB no instalado."
|
||||
|
||||
# Instalar TheMovieDB
|
||||
if continuar and not xbmc.getCondVisibility('System.HasAddon(metadata.tvshows.themoviedb.org)'):
|
||||
continuar = False
|
||||
if not silent:
|
||||
# Preguntar si queremos instalar metadata.tvshows.themoviedb.org
|
||||
install = platformtools.dialog_yesno("The Movie Database",
|
||||
"No se ha encontrado el Scraper de series de TheMovieDB.",
|
||||
"¿Desea instalarlo ahora?")
|
||||
else:
|
||||
install = True
|
||||
|
||||
if install:
|
||||
try:
|
||||
# Instalar metadata.tvshows.themoviedb.org
|
||||
# 1º Probar desde el repositorio ...
|
||||
xbmc.executebuiltin('xbmc.installaddon(metadata.tvshows.themoviedb.org)', True)
|
||||
if not xbmc.getCondVisibility('System.HasAddon(metadata.tvshows.themoviedb.org)'):
|
||||
# ...si no funciona descargar e instalar desde la web
|
||||
url = "http://mirrors.kodi.tv/addons/jarvis/metadata.tvshows.themoviedb.org/metadata.tvshows.themoviedb.org-1.3.1.zip"
|
||||
path_down = xbmc.translatePath(
|
||||
"special://home/addons/packages/metadata.tvshows.themoviedb.org-1.3.1.zip")
|
||||
path_unzip = xbmc.translatePath("special://home/addons/")
|
||||
header = ("User-Agent",
|
||||
"Kodi/15.2 (Windows NT 10.0; WOW64) App_Bitness/32 Version/15.2-Git:20151019-02e7013")
|
||||
|
||||
from core import downloadtools
|
||||
from core import ziptools
|
||||
|
||||
downloadtools.downloadfile(url, path_down, continuar=True, headers=[header])
|
||||
unzipper = ziptools.ziptools()
|
||||
unzipper.extract(path_down, path_unzip)
|
||||
xbmc.executebuiltin('UpdateLocalAddons')
|
||||
|
||||
strSettings = '<settings>\n' \
|
||||
' <setting id="fanart" value="true" />\n' \
|
||||
' <setting id="keeporiginaltitle" value="false" />\n' \
|
||||
' <setting id="language" value="es" />\n' \
|
||||
'</settings>'
|
||||
path_settings = xbmc.translatePath(
|
||||
"special://profile/addon_data/metadata.tvshows.themoviedb.org/settings.xml")
|
||||
tv_themoviedb_addon_path = filetools.dirname(path_settings)
|
||||
if not filetools.exists(tv_themoviedb_addon_path):
|
||||
filetools.mkdir(tv_themoviedb_addon_path)
|
||||
if filetools.write(path_settings, strSettings):
|
||||
continuar = True
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
continuar = (install and continuar)
|
||||
if not continuar:
|
||||
msg_text = "The Movie Database no instalado."
|
||||
|
||||
idPath = 0
|
||||
idParentPath = 0
|
||||
if continuar:
|
||||
continuar = False
|
||||
|
||||
# Buscamos el idPath
|
||||
sql = 'SELECT MAX(idPath) FROM path'
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
idPath = records[0][0] + 1
|
||||
|
||||
sql_videolibrarypath = videolibrarypath
|
||||
if sql_videolibrarypath.startswith("special://"):
|
||||
sql_videolibrarypath = sql_videolibrarypath.replace('/profile/', '/%/').replace('/home/userdata/', '/%/')
|
||||
sep = '/'
|
||||
elif sql_videolibrarypath.startswith("smb://"):
|
||||
sep = '/'
|
||||
else:
|
||||
sep = os.sep
|
||||
|
||||
if not sql_videolibrarypath.endswith(sep):
|
||||
sql_videolibrarypath += sep
|
||||
|
||||
# Buscamos el idParentPath
|
||||
sql = 'SELECT idPath, strPath FROM path where strPath LIKE "%s"' % sql_videolibrarypath
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
idParentPath = records[0][0]
|
||||
videolibrarypath = records[0][1][:-1]
|
||||
continuar = True
|
||||
else:
|
||||
# No existe videolibrarypath en la BD: la insertamos
|
||||
sql_videolibrarypath = videolibrarypath
|
||||
if not sql_videolibrarypath.endswith(sep):
|
||||
sql_videolibrarypath += sep
|
||||
|
||||
sql = 'INSERT INTO path (idPath, strPath, scanRecursive, useFolderNames, noUpdate, exclude) VALUES ' \
|
||||
'(%s, "%s", 0, 0, 0, 0)' % (idPath, sql_videolibrarypath)
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
continuar = True
|
||||
idParentPath = idPath
|
||||
idPath += 1
|
||||
else:
|
||||
msg_text = "Error al fijar videolibrarypath en BD"
|
||||
|
||||
if continuar:
|
||||
continuar = False
|
||||
|
||||
# Fijamos strContent, strScraper, scanRecursive y strSettings
|
||||
if content_type == 'movie':
|
||||
strContent = 'movies'
|
||||
strScraper = 'metadata.themoviedb.org'
|
||||
scanRecursive = 2147483647
|
||||
strSettings = "<settings><setting id='RatingS' value='TMDb' /><setting id='certprefix' value='Rated ' />" \
|
||||
"<setting id='fanart' value='true' /><setting id='keeporiginaltitle' value='false' />" \
|
||||
"<setting id='language' value='es' /><setting id='tmdbcertcountry' value='us' />" \
|
||||
"<setting id='trailer' value='true' /></settings>"
|
||||
strActualizar = "¿Desea configurar este Scraper en español como opción por defecto para películas?"
|
||||
if not videolibrarypath.endswith(sep):
|
||||
videolibrarypath += sep
|
||||
strPath = videolibrarypath + config.get_setting("folder_movies") + sep
|
||||
else:
|
||||
strContent = 'tvshows'
|
||||
strScraper = 'metadata.tvdb.com'
|
||||
scanRecursive = 0
|
||||
strSettings = "<settings><setting id='RatingS' value='TheTVDB' />" \
|
||||
"<setting id='absolutenumber' value='false' />" \
|
||||
"<setting id='dvdorder' value='false' />" \
|
||||
"<setting id='fallback' value='true' />" \
|
||||
"<setting id='fanart' value='true' />" \
|
||||
"<setting id='language' value='es' /></settings>"
|
||||
strActualizar = "¿Desea configurar este Scraper en español como opción por defecto para series?"
|
||||
if not videolibrarypath.endswith(sep):
|
||||
videolibrarypath += sep
|
||||
strPath = videolibrarypath + config.get_setting("folder_tvshows") + sep
|
||||
|
||||
logger.info("%s: %s" % (content_type, strPath))
|
||||
# Comprobamos si ya existe strPath en la BD para evitar duplicados
|
||||
sql = 'SELECT idPath FROM path where strPath="%s"' % strPath
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
sql = ""
|
||||
if nun_records == 0:
|
||||
# Insertamos el scraper
|
||||
sql = 'INSERT INTO path (idPath, strPath, strContent, strScraper, scanRecursive, useFolderNames, ' \
|
||||
'strSettings, noUpdate, exclude, idParentPath) VALUES (%s, "%s", "%s", "%s", %s, 0, ' \
|
||||
'"%s", 0, 0, %s)' % (
|
||||
idPath, strPath, strContent, strScraper, scanRecursive, strSettings, idParentPath)
|
||||
else:
|
||||
if not silent:
|
||||
# Preguntar si queremos configurar themoviedb.org como opcion por defecto
|
||||
actualizar = platformtools.dialog_yesno("The TVDB", strActualizar)
|
||||
else:
|
||||
actualizar = True
|
||||
|
||||
if actualizar:
|
||||
# Actualizamos el scraper
|
||||
idPath = records[0][0]
|
||||
sql = 'UPDATE path SET strContent="%s", strScraper="%s", scanRecursive=%s, strSettings="%s" ' \
|
||||
'WHERE idPath=%s' % (strContent, strScraper, scanRecursive, strSettings, idPath)
|
||||
|
||||
if sql:
|
||||
nun_records, records = execute_sql_kodi(sql)
|
||||
if nun_records == 1:
|
||||
continuar = True
|
||||
|
||||
if not continuar:
|
||||
msg_text = "Error al configurar el scraper en la BD."
|
||||
|
||||
if not continuar:
|
||||
heading = "Videoteca %s no configurada" % content_type
|
||||
elif content_type == 'SERIES' and not xbmc.getCondVisibility(
|
||||
'System.HasAddon(metadata.tvshows.themoviedb.org)'):
|
||||
heading = "Videoteca %s configurada" % content_type
|
||||
msg_text = "Es necesario reiniciar Kodi para que los cambios surtan efecto."
|
||||
else:
|
||||
heading = "Videoteca %s configurada" % content_type
|
||||
msg_text = "Felicidades la videoteca de Kodi ha sido configurada correctamente."
|
||||
platformtools.dialog_notification(heading, msg_text, icon=1, time=10000)
|
||||
logger.info("%s: %s" % (heading, msg_text))
|
||||
|
||||
|
||||
def execute_sql_kodi(sql):
|
||||
"""
|
||||
Ejecuta la consulta sql contra la base de datos de kodi
|
||||
@param sql: Consulta sql valida
|
||||
@type sql: str
|
||||
@return: Numero de registros modificados o devueltos por la consulta
|
||||
@rtype nun_records: int
|
||||
@return: lista con el resultado de la consulta
|
||||
@rtype records: list of tuples
|
||||
"""
|
||||
logger.info()
|
||||
file_db = ""
|
||||
nun_records = 0
|
||||
records = None
|
||||
|
||||
# Buscamos el archivo de la BBDD de videos segun la version de kodi
|
||||
video_db = config.get_platform(True)['video_db']
|
||||
if video_db:
|
||||
file_db = filetools.join(xbmc.translatePath("special://userdata/Database"), video_db)
|
||||
|
||||
# metodo alternativo para localizar la BBDD
|
||||
if not file_db or not filetools.exists(file_db):
|
||||
file_db = ""
|
||||
for f in filetools.listdir(xbmc.translatePath("special://userdata/Database")):
|
||||
path_f = filetools.join(xbmc.translatePath("special://userdata/Database"), f)
|
||||
|
||||
if filetools.isfile(path_f) and f.lower().startswith('myvideos') and f.lower().endswith('.db'):
|
||||
file_db = path_f
|
||||
break
|
||||
|
||||
if file_db:
|
||||
logger.info("Archivo de BD: %s" % file_db)
|
||||
conn = None
|
||||
try:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect(file_db)
|
||||
cursor = conn.cursor()
|
||||
|
||||
logger.info("Ejecutando sql: %s" % sql)
|
||||
cursor.execute(sql)
|
||||
conn.commit()
|
||||
|
||||
records = cursor.fetchall()
|
||||
if sql.lower().startswith("select"):
|
||||
nun_records = len(records)
|
||||
if nun_records == 1 and records[0][0] is None:
|
||||
nun_records = 0
|
||||
records = []
|
||||
else:
|
||||
nun_records = conn.total_changes
|
||||
|
||||
conn.close()
|
||||
logger.info("Consulta ejecutada. Registros: %s" % nun_records)
|
||||
|
||||
except:
|
||||
logger.error("Error al ejecutar la consulta sql")
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
else:
|
||||
logger.debug("Base de datos no encontrada")
|
||||
|
||||
return nun_records, records
|
||||
|
||||
|
||||
def add_sources(path):
|
||||
logger.info()
|
||||
from xml.dom import minidom
|
||||
|
||||
SOURCES_PATH = xbmc.translatePath("special://userdata/sources.xml")
|
||||
|
||||
if os.path.exists(SOURCES_PATH):
|
||||
xmldoc = minidom.parse(SOURCES_PATH)
|
||||
else:
|
||||
# Crear documento
|
||||
xmldoc = minidom.Document()
|
||||
nodo_sources = xmldoc.createElement("sources")
|
||||
|
||||
for type in ['programs', 'video', 'music', 'picture', 'files']:
|
||||
nodo_type = xmldoc.createElement(type)
|
||||
element_default = xmldoc.createElement("default")
|
||||
element_default.setAttribute("pathversion", "1")
|
||||
nodo_type.appendChild(element_default)
|
||||
nodo_sources.appendChild(nodo_type)
|
||||
xmldoc.appendChild(nodo_sources)
|
||||
|
||||
# Buscamos el nodo video
|
||||
nodo_video = xmldoc.childNodes[0].getElementsByTagName("video")[0]
|
||||
|
||||
# Buscamos el path dentro de los nodos_path incluidos en el nodo_video
|
||||
nodos_paths = nodo_video.getElementsByTagName("path")
|
||||
list_path = [p.firstChild.data for p in nodos_paths]
|
||||
logger.debug(list_path)
|
||||
if path in list_path:
|
||||
logger.debug("La ruta %s ya esta en sources.xml" % path)
|
||||
return
|
||||
logger.debug("La ruta %s NO esta en sources.xml" % path)
|
||||
|
||||
# Si llegamos aqui es por q el path no esta en sources.xml, asi q lo incluimos
|
||||
nodo_source = xmldoc.createElement("source")
|
||||
|
||||
# Nodo <name>
|
||||
nodo_name = xmldoc.createElement("name")
|
||||
sep = os.sep
|
||||
if path.startswith("special://") or path.startswith("smb://"):
|
||||
sep = "/"
|
||||
name = path
|
||||
if path.endswith(sep):
|
||||
name = path[:-1]
|
||||
nodo_name.appendChild(xmldoc.createTextNode(name.rsplit(sep)[-1]))
|
||||
nodo_source.appendChild(nodo_name)
|
||||
|
||||
# Nodo <path>
|
||||
nodo_path = xmldoc.createElement("path")
|
||||
nodo_path.setAttribute("pathversion", "1")
|
||||
nodo_path.appendChild(xmldoc.createTextNode(path))
|
||||
nodo_source.appendChild(nodo_path)
|
||||
|
||||
# Nodo <allowsharing>
|
||||
nodo_allowsharing = xmldoc.createElement("allowsharing")
|
||||
nodo_allowsharing.appendChild(xmldoc.createTextNode('true'))
|
||||
nodo_source.appendChild(nodo_allowsharing)
|
||||
|
||||
# Añadimos <source> a <video>
|
||||
nodo_video.appendChild(nodo_source)
|
||||
|
||||
# Guardamos los cambios
|
||||
filetools.write(SOURCES_PATH,
|
||||
'\n'.join([x for x in xmldoc.toprettyxml().encode("utf-8").splitlines() if x.strip()]))
|
||||
|
||||
|
||||
def ask_set_content(flag, silent=False):
|
||||
logger.info()
|
||||
logger.debug("videolibrary_kodi_flag %s" % config.get_setting("videolibrary_kodi_flag"))
|
||||
logger.debug("videolibrary_kodi %s" % config.get_setting("videolibrary_kodi"))
|
||||
|
||||
def do_config():
|
||||
logger.debug("hemos aceptado")
|
||||
config.set_setting("videolibrary_kodi", True)
|
||||
set_content("movie", silent=True)
|
||||
set_content("tvshow", silent=True)
|
||||
add_sources(config.get_setting("videolibrarypath"))
|
||||
add_sources(config.get_setting("downloadpath"))
|
||||
|
||||
if not silent:
|
||||
heading = "Alfa Auto-configuración"
|
||||
linea1 = "¿Desea que Alfa auto-configure la videteoca de Kodi?"
|
||||
linea2 = "Si pulsa 'No' podra hacerlo desde 'Configuración > Preferencia > Rutas'."
|
||||
if platformtools.dialog_yesno(heading, linea1, linea2):
|
||||
do_config()
|
||||
else:
|
||||
# no hemos aceptado
|
||||
config.set_setting("videolibrary_kodi", False)
|
||||
|
||||
else:
|
||||
do_config()
|
||||
|
||||
config.set_setting("videolibrary_kodi_flag", flag)
|
||||
Reference in New Issue
Block a user