KoD 1.7.3

- fix vari\n\n
This commit is contained in:
marco
2021-11-27 18:08:14 +01:00
parent b0325f61d6
commit 7eebb48b89
60 changed files with 10634 additions and 630 deletions

View File

@@ -438,4 +438,8 @@ def verify_directories_created():
def get_online_server_thumb(server):
return "https://raw.github.com/kodiondemand/media/master/resources/servers/" + server.lower().replace('_server','') + '.png'
return "https://raw.github.com/kodiondemand/media/master/resources/servers/" + server.lower().replace('_server','') + '.png'
def get_language():
return get_localized_string(20001)

View File

@@ -222,6 +222,13 @@ def run(item=None):
# Special action for findvideos, where the plugin looks for known urls
elif item.action == "findvideos":
from core import servertools
from core import db
if db['OnPlay'].get('addon', False):
item.autoplay = True
db['OnPlay']['addon'] = False
platformtools.fakeVideo()
db.close()
# First checks if channel has a "findvideos" function
if hasattr(channel, 'findvideos'):
@@ -292,9 +299,20 @@ def run(item=None):
platformtools.render_items(itemlist, item)
# For all other actions
# For all other actions
else:
# import web_pdb; web_pdb.set_trace()
if item.channel == 'filmontv':
reload = False
from core import db
if db['OnPlay'].get('addon', False):
reload = True
db['OnPlay']['addon'] = False
db.close()
if reload:
platformtools.fakeVideo()
import xbmc
return xbmc.executebuiltin("Container.Update(" + sys.argv[0] + "?" + item.tourl() + ")")
logger.debug("Executing channel '%s' method" % item.action)
itemlist = getattr(channel, item.action)(item)
if config.get_setting('trakt_sync'):
@@ -468,12 +486,8 @@ def play_from_library(item):
import xbmcgui, xbmcplugin, xbmc
from time import sleep
# logger.debug("item: \n" + item.tostring('\n'))
# xbmc.Player().play(os.path.join(config.get_runtime_path(), "resources", "kod.mp4"))
if not item.autoplay and not item.next_ep:
platformtools.fakeVideo()
# from core.support import dbg;dbg()
# platformtools.prevent_busy(item)
itemlist=[]

View File

@@ -408,7 +408,8 @@ def viewmodeMonitor():
if currentMode != defaultMode:
# logger.debug('viewmode changed: ' + currentModeName + '-' + str(currentMode) + ' - content: ' + content)
config.set_setting('view_mode_%s' % content, currentModeName + ', ' + str(currentMode))
dialog_notification(config.get_localized_string(70153),
if config.get_setting('viewchange_notify'):
dialog_notification(config.get_localized_string(70153),
config.get_localized_string(70187) % (content, currentModeName),
sound=False)
except:
@@ -1138,8 +1139,8 @@ def show_video_info(*args, **kwargs):
def show_recaptcha(key, referer):
from platformcode.recaptcha import Recaptcha
return Recaptcha("Recaptcha.xml", config.get_runtime_path()).Start(key, referer)
from platformcode.recaptcha import Kodi
return Kodi(key, referer).run()
def alert_no_disponible_server(server):
@@ -1489,39 +1490,27 @@ def play_torrent(item, xlistitem, mediaurl):
selection = 0
if selection >= 0:
prevent_busy()
prevent_busy(item)
mediaurl = urllib.quote_plus(item.url)
torr_client = torrent_options[selection][0]
if torr_client in ['elementum'] and item.infoLabels['tmdb_id']:
if item.contentType == 'episode' and "elementum" not in torr_client:
mediaurl += "&episode=%s&library=&season=%s&show=%s&tmdb=%s&type=episode" % (item.infoLabels['episode'], item.infoLabels['season'], item.infoLabels['tmdb_id'], item.infoLabels['tmdb_id'])
mediaurl += "&episode=%s&season=%s&show=%s&tmdb=%s&type=episode" % (item.infoLabels['episode'], item.infoLabels['season'], item.infoLabels['tmdb_id'], item.infoLabels['tmdb_id'])
elif item.contentType == 'movie':
mediaurl += "&library=&tmdb=%s&type=movie" % (item.infoLabels['tmdb_id'])
mediaurl += "&tmdb=%s&type=movie" % (item.infoLabels['tmdb_id'])
if torr_client in ['elementum'] and item.downloadFilename:
torrent.elementum_download(item)
else:
# xbmc.executebuiltin("PlayMedia(" + torrent_options[selection][1] % mediaurl + ")")
if (item.fromLibrary and item.play_from == 'window') or item.window:
xlistitem.setPath(torrent_options[selection][1] % mediaurl)
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
playlist.clear()
playlist.add(torrent_options[selection][1] % mediaurl, xlistitem)
xbmc_player.play(playlist, xlistitem)
else:
if not item.autoplay and item.channel != 'videolibrary': fakeVideo()
if xbmc.getCondVisibility("system.platform.android"): xbmc.sleep(3000)
xbmc.executebuiltin("PlayMedia(" + torrent_options[selection][1] % mediaurl + ")")
# torrent.mark_auto_as_watched(item)
# if not item.globalsearch:
# while is_playing() and not xbmc.Monitor().abortRequested():
# time.sleep(3)
import xbmcaddon
addon = xbmcaddon.Addon(id='plugin.video.elementum')
if addon.getSetting('download_storage') == '0':
addon.setSetting('download_storage', '1')
xbmc.sleep(3000)
xbmc.executebuiltin("PlayMedia(" + torrent_options[selection][1] % mediaurl + ")")
def resume_playback(played_time):
class ResumePlayback(xbmcgui.WindowXMLDialog):
@@ -1597,173 +1586,38 @@ def install_inputstream():
def install_widevine():
platform = get_platform()
if platform['os'] != 'android':
from core.httptools import downloadpage
from xbmcaddon import Addon
from core import jsontools
from distutils.version import LooseVersion
path = xbmc.translatePath(Addon('inputstream.adaptive').getSetting('DECRYPTERPATH'))
# if Widevine CDM is not installed
if not os.path.exists(path) or not os.listdir(path):
select = dialog_yesno('Widevine CDM', config.get_localized_string(70808))
if select > 0:
if not 'arm' in platform['arch']:
last_version = downloadpage('https://dl.google.com/widevine-cdm/versions.txt').data.split()[-1]
download_widevine(last_version, platform, path)
else:
json = downloadpage('https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.json').data
devices = jsontools.load(json)
download_chromeos_image(devices, platform, path)
# if Widevine CDM is outdated
elif platform['os'] != 'android':
if not 'arm' in platform['arch']:
last_version = downloadpage('https://dl.google.com/widevine-cdm/versions.txt').data.split()[-1]
current_version = jsontools.load(open(os.path.join(path, 'manifest.json')).read())['version']
if LooseVersion(last_version) > LooseVersion(current_version):
select = dialog_yesno(config.get_localized_string(70810),config.get_localized_string(70809))
if select > 0: download_widevine(last_version, platform, path)
else:
devices = jsontools.load(downloadpage('https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.json').data)
current_version = jsontools.load(open(os.path.join(path, 'config.json')).read())['version']
last_version = best_chromeos_image(devices)['version']
if LooseVersion(last_version) > LooseVersion(current_version):
select = dialog_yesno(config.get_localized_string(70810),config.get_localized_string(70809))
if select > 0:download_chromeos_image(devices, platform, path)
def download_widevine(version, platform, path):
# for x86 architectures
from zipfile import ZipFile
from core import downloadtools
archiveName = 'https://dl.google.com/widevine-cdm/' + version + '-' + platform['os'] + '-' + platform['arch'] + '.zip'
fileName = config.get_temp_file('widevine.zip')
if not os.path.exists(archiveName):
if not os.path.exists(fileName):
downloadtools.downloadfile(archiveName, fileName, header='Download Widevine CDM')
zip_obj = ZipFile(fileName)
for filename in zip_obj.namelist():
zip_obj.extract(filename, path)
zip_obj.close()
os.remove(fileName)
def download_chromeos_image(devices, platform, path):
# for arm architectures
from core import downloadtools
from core import jsontools
best = best_chromeos_image(devices)
archiveName = best['url']
version = best['version']
fileName = config.get_temp_file(archiveName.split('/')[-1])
if not os.path.exists(fileName):
downloadtools.downloadfile(archiveName, fileName, header='Download Widevine CDM')
from lib.arm_chromeos import ChromeOSImage
ChromeOSImage(fileName).extract_file(
filename='libwidevinecdm.so',
extract_path=os.path.join(path),
progress=dialog_progress(config.get_localized_string(70811),config.get_localized_string(70812)))
recovery_file = os.path.join(path, os.path.basename('https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.json'))
config_file = os.path.join(path, 'config.json')
if not os.path.exists(path):
os.mkdir(path)
with open(recovery_file, 'w') as reco_file:
reco_file.write(jsontools.dump(devices, indent=4))
reco_file.close()
with open(config_file, 'w') as conf_file:
conf_file.write(jsontools.dump(best))
conf_file.close()
os.remove(fileName)
def best_chromeos_image(devices):
best = None
for device in devices:
# Select ARM hardware only
for arm_hwid in ['BIG','BLAZE','BOB','DRUWL','DUMO','ELM','EXPRESSO','FIEVEL','HANA','JAQ','JERRY','KEVIN','KITTY','MICKEY','MIGHTY','MINNIE','PHASER','PHASER360','PI','PIT','RELM','SCARLET','SKATE','SNOW','SPEEDY','SPRING','TIGER']:
if arm_hwid in device['hwidmatch']:
hwid = arm_hwid
break # We found an ARM device, rejoice !
else:
continue # Not ARM, skip this device
device['hwid'] = hwid
# Select the first ARM device
if best is None:
best = device
continue # Go to the next device
# Skip identical hwid
if hwid == best['hwid']:
continue
# Select the newest version
from distutils.version import LooseVersion # pylint: disable=import-error,no-name-in-module,useless-suppression
if LooseVersion(device['version']) > LooseVersion(best['version']):
logger.info('%s (%s) is newer than %s (%s)' % (device['hwid'], device['version'], best['hwid'], best['version']))
best = device
# Select the smallest image (disk space requirement)
elif LooseVersion(device['version']) == LooseVersion(best['version']):
if int(device['filesize']) + int(device['zipfilesize']) < int(best['filesize']) + int(best['zipfilesize']):
logger.info('%s (%d) is smaller than %s (%d)' % (device['hwid'], int(device['filesize']) + int(device['zipfilesize']), best['hwid'], int(best['filesize']) + int(best['zipfilesize'])))
best = device
return best
def get_platform():
import platform
build = xbmc.getInfoLabel("System.BuildVersion")
kodi_version = int(build.split()[0][:2])
ret = {
"auto_arch": sys.maxsize > 2 ** 32 and "64-bit" or "32-bit",
"arch": sys.maxsize > 2 ** 32 and "x64" or "ia32",
"os": "",
"version": platform.release(),
"kodi": kodi_version,
"build": build
}
# Not necessary on Android devices
if xbmc.getCondVisibility("system.platform.android"):
ret["os"] = "android"
if "arm" in platform.machine() or "aarch" in platform.machine():
ret["arch"] = "arm"
if "64" in platform.machine() and ret["auto_arch"] == "64-bit":
ret["arch"] = "arm64"
elif xbmc.getCondVisibility("system.platform.linux"):
ret["os"] = "linux"
if "aarch" in platform.machine() or "arm64" in platform.machine():
if xbmc.getCondVisibility("system.platform.linux.raspberrypi"):
ret["arch"] = "armv7"
elif ret["auto_arch"] == "32-bit":
ret["arch"] = "armv7"
elif ret["auto_arch"] == "64-bit":
ret["arch"] = "arm64"
elif platform.architecture()[0].startswith("32"):
ret["arch"] = "arm"
else:
ret["arch"] = "arm64"
elif "armv7" in platform.machine():
ret["arch"] = "armv7"
elif "arm" in platform.machine():
ret["arch"] = "arm"
elif xbmc.getCondVisibility("system.platform.xbox"):
ret["os"] = "win"
ret["arch"] = "x64"
elif xbmc.getCondVisibility("system.platform.windows"):
ret["os"] = "win"
if platform.machine().endswith('64'):
ret["arch"] = "x64"
elif xbmc.getCondVisibility("system.platform.osx"):
ret["os"] = "mac"
ret["arch"] = "x64"
elif xbmc.getCondVisibility("system.platform.ios"):
ret["os"] = "ios"
ret["arch"] = "arm"
return
return ret
# For all other devices use InputSeream Helper to install or update Widevine
from core import filetools
from xbmcaddon import Addon
addonName = 'script.module.inputstreamhelper'
def isHelper():
# If InputStream Helper is not installed requires installation
ret = False
if filetools.exists(xbmc.translatePath('special://home/addons/{}'.format(addonName))):
ret = True
else:
xbmc.executebuiltin('InstallAddon({})'.format(addonName), wait=True)
try:
addon = Addon(id=addonName)
ret = True
except:
pass
return ret
# If InputStream Helper is installed, install or update Widevine
if isHelper():
addon = Addon(id=addonName)
path = filetools.join(addon.getAddonInfo('Path'), 'lib')
sys.path.append(path)
from inputstreamhelper import Helper
helper = Helper('mpd', drm='widevine')
if not helper._check_widevine():
helper.install_widevine()
def get_played_time(item):
@@ -1831,7 +1685,9 @@ def prevent_busy(item=None):
def fakeVideo():
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, xbmcgui.ListItem(path=os.path.join(config.get_runtime_path(), "resources", "kod.mp4")))
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True,
xbmcgui.ListItem(path=os.path.join(config.get_runtime_path(), "resources", "kod.mp4")))
sleep = 200
while not is_playing():
xbmc.sleep(10)
xbmc.Player().stop()
xbmc.sleep(sleep)
xbmc.Player().stop()

View File

@@ -1,77 +1,211 @@
# -*- coding: utf-8 -*-
import sys
import time
from threading import Thread
from builtins import range
import xbmcgui
from core import httptools
from core import scrapertools
from platformcode import config
from platformcode import platformtools
from core import filetools
from platformcode import config, platformtools
from platformcode import logger
from lib.librecaptcha.recaptcha import ReCaptcha, Solver, DynamicSolver, MultiCaptchaSolver, Solution, \
ImageGridChallenge
lang = 'it'
temp_dir = config.get_temp_file('reCAPTCHA/')
tiles_pos = (75+390, 90+40)
grid_width = 450
tiles_texture_focus = 'white.png'
tiles_texture_checked = 'Controls/check_mark.png'
cur_tmp = 0
class Recaptcha(xbmcgui.WindowXMLDialog):
def Start(self, key, referer):
self.referer = referer
self.key = key
self.headers = {'Referer': self.referer}
TITLE = 10
PANEL = 11
IMAGE = 12
CONTROL = 1
api_js = httptools.downloadpage("https://www.google.com/recaptcha/api.js?hl=" + lang).data
version = scrapertools.find_single_match(api_js, 'po.src\s*=\s*\'(.*?)\';').split("/")[5]
self.url = "https://www.google.com/recaptcha/api/fallback?k=" + self.key + "&hl=" + lang + "&v=" + version + "&t=2&ff=true"
self.doModal()
# Reload
if self.result == {}:
self.result = Recaptcha("Recaptcha.xml", config.get_runtime_path()).Start(self.key, self.referer)
OK = 21
CANCEL = 22
# RELOAD = 23
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[a-z-]*">(.*?)(?:</label>|</div>)').replace(
"<strong>", "[B]").replace("</strong>", "[/B]")
self.token = scrapertools.find_single_match(data, 'name="c" value="([^"]+)"')
self.image = "https://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 get_temp():
global cur_tmp
cur_tmp += 1
if not filetools.isdir(temp_dir):
filetools.mkdir(temp_dir)
return temp_dir + str(cur_tmp) + '.png'
class Kodi:
def __init__(self, key, referer):
if sys.version_info[0] < 3:
self.rc = None
platformtools.dialog_ok('reCAPTCHA', 'Il sito sta mostrando la schermata "Non sono un robot".\nQuesta schermata tuttavia è superabile solo da kodi 19')
else:
prog = platformtools.dialog_progress('Caricamento reCAPTCHA', 'Il sito sta mostrando la schermata "Non sono un robot"')
filetools.rmdirtree(temp_dir)
self.rc = ReCaptcha(
api_key=key,
site_url=referer,
user_agent=httptools.get_user_agent(),
lang=lang
)
prog.close()
def run(self):
if not self.rc:
return None
result = self.rc.first_solver()
while not isinstance(result, str) and result is not False:
solution = self.run_solver(result)
if solution:
result = self.rc.send_solution(solution)
logger.debug(result)
else:
return False
platformtools.dialog_notification("Captcha corretto", "Verifica conclusa")
return result
def run_solver(self, solver: Solver) -> Solution:
selected_solver = {
DynamicSolver: DynamicKodi,
MultiCaptchaSolver: MultiCaptchaKodi,
}[type(solver)]("Recaptcha.xml", config.get_runtime_path())
selected_solver.solver = solver
return selected_solver.run()
class SolverKodi(xbmcgui.WindowXMLDialog):
def __init__(self, *args, **kwargs):
self.mensaje = kwargs.get("mensaje")
self.imagen = kwargs.get("imagen")
self.goal = ""
self.closed = False
self.result = None
self.image_path = ''
self.indices = {}
self.num_rows = 3
self.num_columns = 3
self.num_tiles = 9
logger.debug()
def show_image(self, image, goal):
self.image_path = get_temp()
filetools.write(self.image_path, image)
self.goal = goal.replace('<strong>', '[B]').replace('</strong>', '[/B]')
self.doModal()
def onInit(self):
#### Kodi 18 compatibility ####
if config.get_platform(True)['num_version'] < 18:
self.setCoordinateResolution(2)
self.update_window()
logger.debug(self.image_path)
items=[]
self.getControl(IMAGE).setImage(self.image_path, False)
self.getControl(TITLE).setLabel(self.goal)
for x in range(self.num_tiles):
item = xbmcgui.ListItem('')
item.setProperty('selected', 'false')
items.append(item)
self.getControl(PANEL).reset()
self.getControl(PANEL).addItems(items)
class MultiCaptchaKodi(SolverKodi):
"""
multicaptcha challenges present you with one large image split into a grid of tiles and ask you to select the tiles that contain a given object.
Occasionally, the image will not contain the object, but rather something that looks similar.
It is possible to select no tiles in this case, but reCAPTCHA may have been fooled by the similar-looking object and would reject a selection of no tiles.
"""
def run(self):
result = self.solver.first_challenge()
while not isinstance(result, Solution):
if not isinstance(result, ImageGridChallenge):
raise TypeError("Unexpected type: {}".format(type(result)))
indices = self.handle_challenge(result)
if self.closed:
return False
result = self.solver.select_indices(indices)
return result
def handle_challenge(self, challenge: ImageGridChallenge):
goal = challenge.goal.plain
self.num_rows = challenge.dimensions.rows
self.num_columns = challenge.dimensions.columns
logger.debug('RIGHE',self.num_rows, 'COLONNE',self.num_columns)
self.num_tiles = challenge.dimensions.count
image = challenge.image
self.show_image(image, goal)
if self.closed:
return False
return self.result
def onClick(self, control):
if control == 10003:
self.result = None
if control == CANCEL:
self.closed = True
self.close()
elif control == 10004:
self.result = {}
# elif control == RELOAD:
# self.closed = True
# self.close()
elif control == OK:
self.result = [int(k) for k in range(self.num_tiles) if self.indices.get(k, False)]
self.close()
elif control == 10002:
self.result = [int(k) for k in range(9) if self.result.get(k, False)]
post = {
"c": self.token,
"response": self.result
}
data = httptools.downloadpage(self.url, post=post, headers=self.headers).data
from platformcode import logger
logger.debug(data)
self.result = scrapertools.find_single_match(data, '<div class="fbc-verification-token">.*?>([^<]+)<')
if self.result:
platformtools.dialog_notification("Captcha corretto", "Verifica conclusa")
self.close()
else:
self.result = {}
self.close()
else:
self.result[control - 10005] = not self.result.get(control - 10005, False)
item = self.getControl(PANEL)
index = item.getSelectedPosition()
selected = True if item.getSelectedItem().getProperty('selected') == 'false' else False
item.getSelectedItem().setProperty('selected', str(selected).lower())
self.indices[index] = selected
class DynamicKodi(SolverKodi):
"""
dynamic challenges present you with a grid of different images and ask you to select the images that match the given description.
Each time you click an image, a new one takes its place. Usually, three images from the initial set match the description,
and at least one of the replacement images does as well.
"""
def run(self):
challenge = self.solver.get_challenge()
image = challenge.image
goal = challenge.goal.raw
self.num_rows = challenge.dimensions.rows
self.num_columns = challenge.dimensions.columns
self.num_tiles = challenge.dimensions.count
logger.debug('RIGHE',self.num_rows, 'COLONNE',self.num_columns)
self.show_image(image, goal)
if self.closed:
return False
return self.result
def changeTile(self, item, path, delay):
cur_delay = delay
while cur_delay > 0:
# todo: show time
item.setLabel('{:.1f}'.format(cur_delay))
time.sleep(0.1)
cur_delay -= 0.1
item.setLabel('')
item.setArt({'image': path})
def onClick(self, control):
if control == CANCEL:
self.closed = True
self.close()
# elif control == RELOAD:
# self.result = None
# self.close()
elif control == OK:
self.result = self.solver.finish()
self.close()
else:
panel = self.getControl(PANEL)
item = panel.getSelectedItem()
if not item.getLabel():
index = panel.getSelectedPosition()
tile = self.solver.select_tile(index)
path = get_temp()
filetools.write(path, tile.image)
Thread(target=self.changeTile, args=(item, path, tile.delay)).start()

View File

@@ -306,7 +306,7 @@ def updateFromZip(message=config.get_localized_string(80050)):
def refreshLang():
from platformcode import config
language = config.get_localized_string(20001)
language = config.get_language()
if language == 'eng':
xbmc.executebuiltin("SetGUILanguage(resource.language.it_it)")
xbmc.executebuiltin("SetGUILanguage(resource.language.en_en)")

View File

@@ -34,6 +34,7 @@ def mark_auto_as_watched(item):
sync = False
next_episode = None
show_server = True
mark_time = 0
percentage = float(config.get_setting("watched_setting")) / 100
time_from_end = config.get_setting('next_ep_seconds')
@@ -85,7 +86,7 @@ def mark_auto_as_watched(item):
break
# if item.options['continue']:
if actual_time < mark_time:
if actual_time < mark_time and mark_time:
item.played_time = actual_time
else: item.played_time = 0
platformtools.set_played_time(item)