* fix next page

* testing new filmontv

* Wstream quick fix, no resolution displayed :(

* new filmontv

* now regex is ok

* fix .po files

* +netlovers

* working on filmontv

* fix debriders

* new updater

* updater

* fix crash

* fix updater and re-add dev mode

* new url eurostreaming

* Delete netlovers.py

* Delete netlovers.json

* -net from menù

* fix eurostreaming: numero stagione e newest (#50)

* fix canale

* fix newest

* fix numero puntata

* cleanup

* cleanup 2

* fix updater crash on windows

* Fix Animeworld

* Nuovo Autorenumber

* initial background downloader support

* ops

* Update channels.json

* Update channels.json

* fix openload

* move json update to cohesist with updater

* disable json url updates

* fix typo

* fix typo 2

* Add files via upload

* Add files via upload

* fix autoplay in community channels

* fix toonitalia

* Fix Toonitalia

* workaround serietvsubita

* Nuova Rinumerazione Automatica

* Fix per Rinumerazione Automatica

* workaround updater

* Fix on air

* ops

* Personalizzazione sezione "Oggi in TV"

* Aggiunto orario sezione Oggi in TV

* aggiunto bit.ly (#56)

* aggiunto bit.ly

* Aggiunta personalizzazione homepage

* Revert "initial background downloader support"

This reverts commit f676ab0f

* KoD 0.4
This commit is contained in:
mac12m99
2019-06-30 10:35:48 +02:00
committed by GitHub
parent 7551aed8ba
commit 3fb9b068d9
47 changed files with 1340 additions and 664 deletions

View File

@@ -64,6 +64,10 @@ def run(item=None):
else:
item = Item(channel="channelselector", action="getmainlist", viewmode="movie")
if not config.get_setting('show_once'):
if not os.path.isdir(config.get_runtime_path() + '/.git'):
logger.info("DEV MODE OFF")
from platformcode import updater
updater.calcCurrHash()
from platformcode import xbmc_videolibrary
xbmc_videolibrary.ask_set_content(1, config.get_setting('videolibrary_kodi_force'))
config.set_setting('show_once', True)
@@ -75,9 +79,6 @@ def run(item=None):
if item.action == "":
logger.info("Item sin accion")
return
if item.action == "update":
updater.update()
# Action for main menu in channelselector
elif item.action == "getmainlist":

View File

@@ -1,314 +1,314 @@
# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------------
# Updater (kodi)
# --------------------------------------------------------------------------------
import json
import hashlib
import os
import sys
import threading
import time
import urllib
import shutil
import zipfile
from core import httptools, filetools, downloadtools
from platformcode import logger, platformtools, config
import json
import xbmc
import re
import xbmcaddon
from core import ziptools
from platformcode import config, logger
addon = xbmcaddon.Addon('plugin.video.kod')
_hdr_pat = re.compile("^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@.*")
branch = 'master'
user = 'kodiondemand'
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():
logger.info()
# Subtarea de monitor. Se activa cada X horas para comprobar si hay FIXES al addon
# def check_addon_monitor():
# logger.info()
#
# # Obtiene el íntervalo entre actualizaciones y si se quieren mensajes
# try:
# timer = int(config.get_setting('addon_update_timer')) # Intervalo entre actualizaciones, en Ajustes de Alfa
# if timer <= 0:
# return # 0. No se quieren actualizaciones
# verbose = config.get_setting('addon_update_message')
# except:
# timer = 12 # Por defecto cada 12 horas
# verbose = False # Por defecto, sin mensajes
# timer = timer * 3600 # Lo pasamos a segundos
#
# if config.get_platform(True)['num_version'] >= 14: # Si es Kodi, lanzamos el monitor
# import xbmc
# monitor = xbmc.Monitor()
# else: # Lanzamos solo una actualización y salimos
# check_addon_updates(verbose) # Lanza la actualización
# return
#
# while not monitor.abortRequested(): # Loop infinito hasta cancelar Kodi
#
# check_addon_updates(verbose) # Lanza la actualización
#
# if monitor.waitForAbort(timer): # Espera el tiempo programado o hasta que cancele Kodi
# break # Cancelación de Kodi, salimos
#
# return
#
# # Lanzamos en Servicio de actualización de FIXES
# try:
# threading.Thread(target=check_addon_monitor).start() # Creamos un Thread independiente, hasta el fin de Kodi
# time.sleep(5) # Dejamos terminar la primera verificación...
# except: # Si hay problemas de threading, se llama una sola vez
# try:
# timer = int(config.get_setting('addon_update_timer')) # Intervalo entre actualizaciones, en Ajustes de Alfa
# if timer <= 0:
# return # 0. No se quieren actualizaciones
# verbose = config.get_setting('addon_update_message')
# except:
# verbose = False # Por defecto, sin mensajes
# pass
# check_addon_updates(verbose) # Lanza la actualización, en Ajustes de Alfa
# time.sleep(5) # Dejamos terminar la primera verificación...
return
def 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)
if not addon.getSetting('addon_update_enabled'):
return False
logger.info('Cerco aggiornamenti..')
commits = loadCommits()
try:
numero_remote_version = int(remote_version)
numero_version_local = int(version_local)
localCommitFile = open(addonDir+trackingFile, 'r+')
except:
import traceback
logger.info(traceback.format_exc())
remote_version = ""
version_local = ""
calcCurrHash()
localCommitFile = open(addonDir + trackingFile, 'r+')
localCommitSha = localCommitFile.read()
localCommitSha = localCommitSha.replace('\n', '') # da testare
logger.info('Commit locale: ' + localCommitSha)
updated = False
if remote_version=="" or version_local=="":
arraydescargada = tag_version.split(".")
arraylocal = tag_local.split(".")
# local 2.8.0 - descargada 2.8.0 -> no descargar
# 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
pos = None
for n, c in enumerate(commits):
if c['sha'] == localCommitSha:
pos = n
break
else:
hayqueactualizar = (numero_remote_version > numero_version_local)
# evitiamo che dia errore perchè il file è già in uso
localCommitFile.close()
updateFromZip()
return True
if hayqueactualizar:
if plugin_mode:
logger.info("kodiondemand.core.updater actualizacion disponible")
# Añade al listado de XBMC
import xbmcgui
#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.")
if pos > 0:
changelog = ''
nCommitApplied = 0
for c in reversed(commits[:pos]):
commit = httptools.downloadpage(c['url']).data
commitJson = json.loads(commit)
logger.info('aggiornando a' + commitJson['sha'])
alreadyApplied = True
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
if addon.getSetting("addon_update_message"):
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()
logger.info('tree hash: ' + treeHash)
commits = loadCommits()
lastCommitSha = commits[0]['sha']
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:
page += 1
commits = loadCommits(page)
import xbmcgui
yes_pressed = xbmcgui.Dialog().yesno( "Versione "+tag_version+" disponibile" , "Installarla?" )
if yes_pressed:
params = {"version":tag_version}
update(params)
if found:
break
else:
logger.info('Non sono riuscito a trovare il commit attuale, scarico lo zip')
updateFromZip()
# se ha scaricato lo zip si trova di sicuro all'ultimo commit
localCommitFile = open(addonDir + trackingFile, 'w')
localCommitFile.write(lastCommitSha)
localCommitFile.close()
def update():
# Descarga el ZIP
logger.info("kodiondemand.core.updater update")
remotefilename = REMOTE_FILE
# https://gist.github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc Grazie!
def apply_patch(s,patch,revert=False):
"""
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"
logger.info("kodiondemand.core.updater remotefilename=%s" % remotefilename)
logger.info("kodiondemand.core.updater localfilename=%s" % localfilename)
logger.info("kodiondemand.core.updater descarga fichero...")
urllib.urlretrieve(remotefilename,localfilename)
#from core import downloadtools
#downloadtools.downloadfile(remotefilename, localfilename, continuar=False)
import urllib
urllib.urlretrieve(remotefilename, localfilename)
# Lo descomprime
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)
unzipper.extract(localfilename,destpathname, os.path.join(xbmc.translatePath("special://home/addons/"), "plugin.video.kod/"))
unzipper = ziptools()
unzipper.extract(localfilename, destpathname)
# puliamo tutto
shutil.rmtree(addonDir)
filetools.rename(destpathname + "addon-" + branch, addonDir)
temp_dir = os.path.join(destpathname,"addon-master")
files = os.listdir(temp_dir)
#for f in files:
# shutil.move(os.path.join(temp_dir, f), os.path.join(xbmc.translatePath("special://home/addons/"), "plugin.video.kod/", f))
# Borra el zip descargado
logger.info("kodiondemand.core.updater borra fichero...")
os.remove(localfilename)
#os.remove(temp_dir)
logger.info("kodiondemand.core.updater ...fichero borrado")
# os.remove(temp_dir)
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'
ADDON_UPDATES_ZIP = 'https://extra.alfa-addon.com/addon_updates/updates.zip'
class ziptools:
def extract(self, file, dir, folder_to_extract="", overwrite_question=False, backup=False):
logger.info("file=%s" % file)
logger.info("dir=%s" % dir)
try:
last_fix_json = os.path.join(config.get_runtime_path(), 'last_fix.json') # información de la versión fixeada del usuario
# Se guarda en get_runtime_path en lugar de get_data_path para que se elimine al cambiar de versión
if not dir.endswith(':') and not os.path.exists(dir):
os.mkdir(dir)
# Descargar json con las posibles actualizaciones
# -----------------------------------------------
data = httptools.downloadpage(ADDON_UPDATES_JSON, timeout=2).data
if data == '':
logger.info('No se encuentran actualizaciones del addon')
if verbose:
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668))
return False
zf = zipfile.ZipFile(file)
if not folder_to_extract:
self._createstructure(file, dir)
num_files = len(zf.namelist())
data = jsontools.load(data)
if 'addon_version' not in data or 'fix_version' not in data:
logger.info('No hay actualizaciones del addon')
if verbose:
platformtools.dialog_notification(config.get_localized_string(70667), config.get_localized_string(70668))
return False
for nameo in zf.namelist():
name = nameo.replace(':', '_').replace('<', '_').replace('>', '_').replace('|', '_').replace('"', '_').replace('?', '_').replace('*', '_')
logger.info("name=%s" % nameo)
if not name.endswith('/'):
logger.info("no es un directorio")
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:
logger.error('last_fix.json: ERROR desconocido')
lastfix = {}
outfilename = os.path.join(dir, name)
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
# -------------------------------------
localfilename = os.path.join(config.get_data_path(), 'temp_updates.zip')
if os.path.exists(localfilename): os.remove(localfilename)
outfile = open(outfilename, 'wb')
outfile.write(zf.read(nameo))
except:
logger.error("Error en fichero " + nameo)
downloadtools.downloadfile(ADDON_UPDATES_ZIP, localfilename, silent=True)
# Descomprimir zip dentro del addon
# ---------------------------------
def _createstructure(self, file, dir):
self._makedirs(self._listdirs(file), dir)
def create_necessary_paths(filename):
try:
unzipper = ziptools.ziptools()
unzipper.extract(localfilename, config.get_runtime_path())
(path, name) = os.path.split(filename)
os.makedirs(path)
except:
import xbmc
xbmc.executebuiltin('XBMC.Extract("%s", "%s")' % (localfilename, config.get_runtime_path()))
time.sleep(1)
# Borrar el zip descargado
# ------------------------
os.remove(localfilename)
# Guardar información de la versión fixeada
# -----------------------------------------
if 'files' in data: data.pop('files', None)
filetools.write(last_fix_json, jsontools.dump(data))
logger.info(config.get_localized_string(70672) % (data['addon_version'], data['fix_version']))
if verbose:
platformtools.dialog_notification(config.get_localized_string(70673), config.get_localized_string(70671) % (data['addon_version'], data['fix_version']))
return True
pass
except:
logger.error('Error al comprobar actualizaciones del addon!')
logger.error(traceback.format_exc())
if verbose:
platformtools.dialog_notification(config.get_localized_string(70674), config.get_localized_string(70675))
return False
'''
def _makedirs(self, directories, basedir):
for dir in directories:
curdir = os.path.join(basedir, dir)
if not os.path.exists(curdir):
os.mkdir(curdir)
def _listdirs(self, file):
zf = zipfile.ZipFile(file)
dirs = []
for name in zf.namelist():
if name.endswith('/'):
dirs.append(name)
dirs.sort()
return dirs