new updater

This commit is contained in:
marco
2019-06-14 20:43:41 +02:00
parent 53cc111e74
commit 0cc1e53857
2 changed files with 269 additions and 275 deletions
+2 -3
View File
@@ -64,6 +64,8 @@ def run(item=None):
else: else:
item = Item(channel="channelselector", action="getmainlist", viewmode="movie") item = Item(channel="channelselector", action="getmainlist", viewmode="movie")
if not config.get_setting('show_once'): if not config.get_setting('show_once'):
from platformcode import updater
updater.calcCurrHash()
from platformcode import xbmc_videolibrary from platformcode import xbmc_videolibrary
xbmc_videolibrary.ask_set_content(1, config.get_setting('videolibrary_kodi_force')) xbmc_videolibrary.ask_set_content(1, config.get_setting('videolibrary_kodi_force'))
config.set_setting('show_once', True) config.set_setting('show_once', True)
@@ -75,9 +77,6 @@ def run(item=None):
if item.action == "": if item.action == "":
logger.info("Item sin accion") logger.info("Item sin accion")
return return
if item.action == "update":
updater.update()
# Action for main menu in channelselector # Action for main menu in channelselector
elif item.action == "getmainlist": elif item.action == "getmainlist":
+267 -272
View File
@@ -1,314 +1,309 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------- import hashlib
# Updater (kodi)
# --------------------------------------------------------------------------------
import json
import os import os
import sys import shutil
import threading import zipfile
import time
import urllib
from core import httptools, filetools, downloadtools
from platformcode import logger, platformtools, config
import json
import xbmc import xbmc
import re
import xbmcaddon
from core import ziptools addon = xbmcaddon.Addon('plugin.video.kod')
from platformcode import config, logger
_hdr_pat = re.compile("^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@.*")
# branch = 'stable'
branch = 'updater'
# user = 'kodiondemand'
user = 'mac12m99'
repo = 'addon'
addonDir = xbmc.translatePath("special://home/addons/") + "plugin.video.kod/"
maxPage = 5 # le api restituiscono 30 commit per volta, quindi se si è rimasti troppo indietro c'è bisogno di andare avanti con le pagine
trackingFile = "last_commit.txt"
def loadCommits(page=1):
apiLink = 'https://api.github.com/repos/' + user + '/' + repo + '/commits?sha=' + branch + "&page=" + str(page)
commitsLink = httptools.downloadpage(apiLink).data
logger.info(apiLink)
return json.loads(commitsLink)
REMOTE_FILE = "https://github.com/kodiondemand/addon/archive/master.zip"
DESTINATION_FOLDER = xbmc.translatePath("special://home/addons") + "/plugin.video.kod"
REMOTE_VERSION_FILE = "https://raw.githubusercontent.com/kodiondemand/addon/master/version.json"
def check_addon_init(): def check_addon_init():
logger.info() # if not addon.getSetting('addon_update_enabled'):
# return False
# Subtarea de monitor. Se activa cada X horas para comprobar si hay FIXES al addon logger.info('Cerco aggiornamenti..')
# def check_addon_monitor(): commits = loadCommits()
# logger.info()
#
# # Obtiene el íntervalo entre actualizaciones y si se quieren mensajes
# try:
# timer = int(config.get_setting('addon_update_timer')) # Intervalo entre actualizaciones, en Ajustes de Alfa
# if timer <= 0:
# return # 0. No se quieren actualizaciones
# verbose = config.get_setting('addon_update_message')
# except:
# timer = 12 # Por defecto cada 12 horas
# verbose = False # Por defecto, sin mensajes
# timer = timer * 3600 # Lo pasamos a segundos
#
# if config.get_platform(True)['num_version'] >= 14: # Si es Kodi, lanzamos el monitor
# import xbmc
# monitor = xbmc.Monitor()
# else: # Lanzamos solo una actualización y salimos
# check_addon_updates(verbose) # Lanza la actualización
# return
#
# while not monitor.abortRequested(): # Loop infinito hasta cancelar Kodi
#
# check_addon_updates(verbose) # Lanza la actualización
#
# if monitor.waitForAbort(timer): # Espera el tiempo programado o hasta que cancele Kodi
# break # Cancelación de Kodi, salimos
#
# return
#
# # Lanzamos en Servicio de actualización de FIXES
# try:
# threading.Thread(target=check_addon_monitor).start() # Creamos un Thread independiente, hasta el fin de Kodi
# time.sleep(5) # Dejamos terminar la primera verificación...
# except: # Si hay problemas de threading, se llama una sola vez
# try:
# timer = int(config.get_setting('addon_update_timer')) # Intervalo entre actualizaciones, en Ajustes de Alfa
# if timer <= 0:
# return # 0. No se quieren actualizaciones
# verbose = config.get_setting('addon_update_message')
# except:
# verbose = False # Por defecto, sin mensajes
# pass
# check_addon_updates(verbose) # Lanza la actualización, en Ajustes de Alfa
# time.sleep(5) # Dejamos terminar la primera verificación...
return
def checkforupdates(plugin_mode=True):
logger.info("kodiondemand.core.updater checkforupdates")
response = urllib.urlopen(REMOTE_VERSION_FILE)
data = json.loads(response.read())
'''
{
"update": {
"name": "Kodi on Demand",
"tag": "1.0.0",
"version": "1000",
"date": "03/05/2019",
"changes": "Added Updater"
}
}
'''
# remote is addon version without dots
remote_version = data["update"]["version"]
# tag version is version with dots used to a betterview gui
tag_version = data["update"]["tag"]
logger.info("kodiondemand.core.updater version remota="+tag_version+" "+remote_version)
'''
# Lee el fichero con la versión instalada
localFileName = LOCAL_VERSION_FILE
logger.info("kodiondemand.core.updater fichero local version: "+localFileName)
infile = open( localFileName )
data = infile.read()
infile.close()
#logger.info("xml local="+data)
'''
path_local = xbmc.translatePath("special://home/addons/") + "plugin.video.kod/version.json"
data = json.loads(open(path_local).read())
version_local = data["update"]["version"]
tag_local = data["update"]["tag"]
logger.info("kodiondemand.core.updater version local="+tag_local+" "+version_local)
try: try:
numero_remote_version = int(remote_version) localCommitFile = open(addonDir+trackingFile, 'r+')
numero_version_local = int(version_local)
except: except:
import traceback calcCurrHash()
logger.info(traceback.format_exc()) localCommitFile = open(addonDir + trackingFile, 'r+')
remote_version = "" localCommitSha = localCommitFile.read()
version_local = "" localCommitSha = localCommitSha.replace('\n', '') # da testare
logger.info('Commit locale: ' + localCommitSha)
updated = False
if remote_version=="" or version_local=="": pos = None
arraydescargada = tag_version.split(".") for n, c in enumerate(commits):
arraylocal = tag_local.split(".") if c['sha'] == localCommitSha:
pos = n
# local 2.8.0 - descargada 2.8.0 -> no descargar break
# local 2.9.0 - descargada 2.8.0 -> no descargar
# local 2.8.0 - descargada 2.9.0 -> descargar
if len(arraylocal) == len(arraydescargada):
logger.info("caso 1")
hayqueactualizar = False
for i in range(0, len(arraylocal)):
print arraylocal[i], arraydescargada[i], int(arraydescargada[i]) > int(arraylocal[i])
if int(arraydescargada[i]) > int(arraylocal[i]):
hayqueactualizar = True
# local 2.8.0 - descargada 2.8 -> no descargar
# local 2.9.0 - descargada 2.8 -> no descargar
# local 2.8.0 - descargada 2.9 -> descargar
if len(arraylocal) > len(arraydescargada):
logger.info("caso 2")
hayqueactualizar = False
for i in range(0, len(arraydescargada)):
#print arraylocal[i], arraydescargada[i], int(arraydescargada[i]) > int(arraylocal[i])
if int(arraydescargada[i]) > int(arraylocal[i]):
hayqueactualizar = True
# local 2.8 - descargada 2.8.8 -> descargar
# local 2.9 - descargada 2.8.8 -> no descargar
# local 2.10 - descargada 2.9.9 -> no descargar
# local 2.5 - descargada 3.0.0
if len(arraylocal) < len(arraydescargada):
logger.info("caso 3")
hayqueactualizar = True
for i in range(0, len(arraylocal)):
#print arraylocal[i], arraydescargada[i], int(arraylocal[i])>int(arraydescargada[i])
if int(arraylocal[i]) > int(arraydescargada[i]):
hayqueactualizar = False
elif int(arraylocal[i]) < int(arraydescargada[i]):
hayqueactualizar = True
break
else: else:
hayqueactualizar = (numero_remote_version > numero_version_local) updateFromZip()
return True
if hayqueactualizar: if pos > 0:
changelog = ''
if plugin_mode: nCommitApplied = 0
for c in reversed(commits[:pos]):
logger.info("kodiondemand.core.updater actualizacion disponible") commit = httptools.downloadpage(c['url']).data
commitJson = json.loads(commit)
# Añade al listado de XBMC logger.info('aggiornando a' + commitJson['sha'])
import xbmcgui alreadyApplied = True
#thumbnail = IMAGES_PATH+"Crystal_Clear_action_info.png"
thumbnail = os.path.join(config.get_runtime_path() , "resources" , "images", "service_update.png")
logger.info("thumbnail="+thumbnail)
listitem = xbmcgui.ListItem( "Scarica la versione "+tag_version, thumbnailImage=thumbnail )
itemurl = '%s?action=update&version=%s' % ( sys.argv[ 0 ] , tag_version )
import xbmcplugin
xbmcplugin.addDirectoryItem( handle = int(sys.argv[ 1 ]), url = itemurl , listitem=listitem, isFolder=True)
# Avisa con un popup
dialog = xbmcgui.Dialog()
dialog.ok("Versione "+tag_version+" disponibile","E' possibile scaricare la nuova versione del plugin\nattraverso l'opzione nel menù principale.")
for file in commitJson['files']:
if file["filename"] == trackingFile: # il file di tracking non si modifica
continue
else:
logger.info(file["filename"])
if file['status'] == 'modified' or file['status'] == 'added':
if 'patch' in file:
text = ""
try:
localFile = open(addonDir + file["filename"], 'r+')
for line in localFile:
text += line
except IOError: # nuovo file
localFile = open(addonDir + file["filename"], 'w')
patched = apply_patch(text, (file['patch']+'\n').encode('utf-8'))
if patched != text: # non eseguo se già applicata (es. scaricato zip da github)
if getSha(patched) == file['sha']:
localFile.seek(0)
localFile.truncate()
localFile.writelines(patched)
localFile.close()
alreadyApplied = False
else: # nel caso ci siano stati problemi
logger.info('lo sha non corrisponde, scarico il file')
downloadtools.downloadfile(file['raw_url'], addonDir + file['filename'],
silent=True, continuar=True)
else: # è un file NON testuale, lo devo scaricare
# se non è già applicato
if not (filetools.isfile(addonDir + file['filename']) and getSha(
filetools.read(addonDir + file['filename']) == file['sha'])):
downloadtools.downloadfile(file['raw_url'], addonDir + file['filename'], silent=True, continuar=True)
alreadyApplied = False
elif file['status'] == 'removed':
try:
filetools.remove(addonDir+file["filename"])
alreadyApplied = False
except:
pass
elif file['status'] == 'renamed':
# se non è già applicato
if not (filetools.isfile(addonDir + file['filename']) and getSha(
filetools.read(addonDir + file['filename']) == file['sha'])):
dirs = file['filename'].split('/')
for d in dirs[:-1]:
if not filetools.isdir(addonDir + d):
filetools.mkdir(addonDir + d)
filetools.move(addonDir + file['previous_filename'], addonDir + file['filename'])
alreadyApplied = False
if not alreadyApplied: # non mando notifica se già applicata (es. scaricato zip da github)
changelog += commitJson['commit']['message'] + " | "
nCommitApplied += 1
time = nCommitApplied * 2000 if nCommitApplied < 10 else 20000
platformtools.dialog_notification('Kodi on Demand', changelog, time)
localCommitFile.seek(0)
localCommitFile.truncate()
localCommitFile.writelines(c['sha'])
localCommitFile.close()
else:
logger.info('Nessun nuovo aggiornamento')
return updated
def calcCurrHash():
from lib import githash
treeHash = githash.tree_hash(addonDir).hexdigest()
commits = loadCommits()
page = 1
while commits and page <= maxPage:
found = False
for n, c in enumerate(commits):
if c['commit']['tree']['sha'] == treeHash:
localCommitFile = open(addonDir + trackingFile, 'w')
localCommitFile.write(c['sha'])
localCommitFile.close()
found = True
break
else: else:
page += 1
commits = loadCommits(page)
import xbmcgui if found:
yes_pressed = xbmcgui.Dialog().yesno( "Versione "+tag_version+" disponibile" , "Installarla?" ) break
else:
if yes_pressed: logger.info('Non sono riuscito a trovare il commit attuale, scarico lo zip')
params = {"version":tag_version} updateFromZip()
update(params) calcCurrHash()
def update(): # https://gist.github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc Grazie!
# Descarga el ZIP
logger.info("kodiondemand.core.updater update") def apply_patch(s,patch,revert=False):
remotefilename = REMOTE_FILE """
Apply unified diff patch to string s to recover newer string.
If revert is True, treat s as the newer string, recover older string.
"""
s = s.splitlines(True)
p = patch.splitlines(True)
t = ''
i = sl = 0
(midx,sign) = (1,'+') if not revert else (3,'-')
while i < len(p) and p[i].startswith(("---","+++")): i += 1 # skip header lines
while i < len(p):
m = _hdr_pat.match(p[i])
if not m: raise Exception("Cannot process diff")
i += 1
l = int(m.group(midx))-1 + (m.group(midx+1) == '0')
t += ''.join(s[sl:l])
sl = l
while i < len(p) and p[i][0] != '@':
if i+1 < len(p) and p[i+1][0] == '\\': line = p[i][:-1]; i += 2
else: line = p[i]; i += 1
if len(line) > 0:
if line[0] == sign or line[0] == ' ': t += line[1:]
sl += (line[0] != sign)
t += ''.join(s[sl:])
return t
def getSha(fileText):
return hashlib.sha1("blob " + str(len(fileText)) + "\0" + fileText).hexdigest()
def updateFromZip():
platformtools.dialog_notification('Kodi on Demand', 'Aggiornamento in corso...')
remotefilename = 'https://github.com/' + user + "/" + repo + "/archive/" + branch + ".zip"
localfilename = xbmc.translatePath("special://home/addons/") + "plugin.video.kod.update.zip" localfilename = xbmc.translatePath("special://home/addons/") + "plugin.video.kod.update.zip"
logger.info("kodiondemand.core.updater remotefilename=%s" % remotefilename) logger.info("kodiondemand.core.updater remotefilename=%s" % remotefilename)
logger.info("kodiondemand.core.updater localfilename=%s" % localfilename) logger.info("kodiondemand.core.updater localfilename=%s" % localfilename)
logger.info("kodiondemand.core.updater descarga fichero...") logger.info("kodiondemand.core.updater descarga fichero...")
urllib.urlretrieve(remotefilename,localfilename) import urllib
#from core import downloadtools urllib.urlretrieve(remotefilename, localfilename)
#downloadtools.downloadfile(remotefilename, localfilename, continuar=False)
# Lo descomprime # Lo descomprime
logger.info("kodiondemand.core.updater descomprime fichero...") logger.info("kodiondemand.core.updater descomprime fichero...")
unzipper = ziptools.ziptools() destpathname = xbmc.translatePath("special://home/addons/")
destpathname = xbmc.translatePath("special://home/addons/")
logger.info("kodiondemand.core.updater destpathname=%s" % destpathname) logger.info("kodiondemand.core.updater destpathname=%s" % destpathname)
unzipper.extract(localfilename,destpathname, os.path.join(xbmc.translatePath("special://home/addons/"), "plugin.video.kod/"))
temp_dir = os.path.join(destpathname,"addon-master") # puliamo tutto
files = os.listdir(temp_dir) shutil.rmtree(addonDir)
#for f in files:
# shutil.move(os.path.join(temp_dir, f), os.path.join(xbmc.translatePath("special://home/addons/"), "plugin.video.kod/", f)) unzipper = ziptools()
unzipper.extract(localfilename, destpathname)
filetools.rename(destpathname + "addon-" + branch, addonDir)
# Borra el zip descargado # Borra el zip descargado
logger.info("kodiondemand.core.updater borra fichero...") logger.info("kodiondemand.core.updater borra fichero...")
os.remove(localfilename) os.remove(localfilename)
#os.remove(temp_dir) # os.remove(temp_dir)
logger.info("kodiondemand.core.updater ...fichero borrado") platformtools.dialog_notification('Kodi on Demand', 'Aggiornamento completato!')
'''
def check_addon_updates(verbose=False):
logger.info()
ADDON_UPDATES_JSON = 'https://extra.alfa-addon.com/addon_updates/updates.json' class ziptools:
ADDON_UPDATES_ZIP = 'https://extra.alfa-addon.com/addon_updates/updates.zip' def extract(self, file, dir, folder_to_extract="", overwrite_question=False, backup=False):
logger.info("file=%s" % file)
logger.info("dir=%s" % dir)
try: if not dir.endswith(':') and not os.path.exists(dir):
last_fix_json = os.path.join(config.get_runtime_path(), 'last_fix.json') # información de la versión fixeada del usuario os.mkdir(dir)
# Se guarda en get_runtime_path en lugar de get_data_path para que se elimine al cambiar de versión
# Descargar json con las posibles actualizaciones zf = zipfile.ZipFile(file)
# ----------------------------------------------- if not folder_to_extract:
data = httptools.downloadpage(ADDON_UPDATES_JSON, timeout=2).data self._createstructure(file, dir)
if data == '': num_files = len(zf.namelist())
logger.info('No se encuentran actualizaciones del addon')
if verbose:
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668))
return False
data = jsontools.load(data) for nameo in zf.namelist():
if 'addon_version' not in data or 'fix_version' not in data: name = nameo.replace(':', '_').replace('<', '_').replace('>', '_').replace('|', '_').replace('"', '_').replace('?', '_').replace('*', '_')
logger.info('No hay actualizaciones del addon') logger.info("name=%s" % nameo)
if verbose: if not name.endswith('/'):
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668)) logger.info("no es un directorio")
return False try:
(path, filename) = os.path.split(os.path.join(dir, name))
logger.info("path=%s" % path)
logger.info("name=%s" % name)
if folder_to_extract:
if path != os.path.join(dir, folder_to_extract):
break
else:
os.makedirs(path)
except:
pass
if folder_to_extract:
outfilename = os.path.join(dir, filename)
# Comprobar versión que tiene instalada el usuario con versión de la actualización
# --------------------------------------------------------------------------------
current_version = config.get_addon_version(with_fix=False)
if current_version != data['addon_version']:
logger.info('No hay actualizaciones para la versión %s del addon' % current_version)
if verbose:
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668))
return False
if os.path.exists(last_fix_json):
try:
lastfix = {}
lastfix = jsontools.load(filetools.read(last_fix_json))
if lastfix['addon_version'] == data['addon_version'] and lastfix['fix_version'] == data['fix_version']:
logger.info(config.get_localized_string(70670) % (data['addon_version'], data['fix_version']))
if verbose:
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70671) % (data['addon_version'], data['fix_version']))
return False
except:
if lastfix:
logger.error('last_fix.json: ERROR en: ' + str(lastfix))
else: else:
logger.error('last_fix.json: ERROR desconocido') outfilename = os.path.join(dir, name)
lastfix = {} logger.info("outfilename=%s" % outfilename)
try:
if os.path.exists(outfilename) and overwrite_question:
from platformcode import platformtools
dyesno = platformtools.dialog_yesno("El archivo ya existe",
"El archivo %s a descomprimir ya existe" \
", ¿desea sobrescribirlo?" \
% os.path.basename(outfilename))
if not dyesno:
break
if backup:
import time
import shutil
hora_folder = "Copia seguridad [%s]" % time.strftime("%d-%m_%H-%M", time.localtime())
backup = os.path.join(config.get_data_path(), 'backups', hora_folder, folder_to_extract)
if not os.path.exists(backup):
os.makedirs(backup)
shutil.copy2(outfilename, os.path.join(backup, os.path.basename(outfilename)))
# Descargar zip con las actualizaciones outfile = open(outfilename, 'wb')
# ------------------------------------- outfile.write(zf.read(nameo))
localfilename = os.path.join(config.get_data_path(), 'temp_updates.zip') except:
if os.path.exists(localfilename): os.remove(localfilename) logger.error("Error en fichero " + nameo)
downloadtools.downloadfile(ADDON_UPDATES_ZIP, localfilename, silent=True) def _createstructure(self, file, dir):
self._makedirs(self._listdirs(file), dir)
# Descomprimir zip dentro del addon
# --------------------------------- def create_necessary_paths(filename):
try: try:
unzipper = ziptools.ziptools() (path, name) = os.path.split(filename)
unzipper.extract(localfilename, config.get_runtime_path()) os.makedirs(path)
except: except:
import xbmc pass
xbmc.executebuiltin('XBMC.Extract("%s", "%s")' % (localfilename, config.get_runtime_path()))
time.sleep(1)
# Borrar el zip descargado
# ------------------------
os.remove(localfilename)
# Guardar información de la versión fixeada
# -----------------------------------------
if 'files' in data: data.pop('files', None)
filetools.write(last_fix_json, jsontools.dump(data))
logger.info(config.get_localized_string(70672) % (data['addon_version'], data['fix_version']))
if verbose:
platformtools.dialog_notification(config.get_localized_string(70673), config.get_localized_string(70671) % (data['addon_version'], data['fix_version']))
return True
except: def _makedirs(self, directories, basedir):
logger.error('Error al comprobar actualizaciones del addon!') for dir in directories:
logger.error(traceback.format_exc()) curdir = os.path.join(basedir, dir)
if verbose: if not os.path.exists(curdir):
platformtools.dialog_notification(config.get_localized_string(70674), config.get_localized_string(70675)) os.mkdir(curdir)
return False
''' def _listdirs(self, file):
zf = zipfile.ZipFile(file)
dirs = []
for name in zf.namelist():
if name.endswith('/'):
dirs.append(name)
dirs.sort()
return dirs