# -*- coding: utf-8 -*- # ------------------------------------------------------------ # Configuracion # ------------------------------------------------------------ from __future__ import division #from builtins import str import sys PY3 = False if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int from builtins import range from past.utils import old_div from channelselector import get_thumb from core import filetools, servertools from core.item import Item from platformcode import config, logger, platformtools CHANNELNAME = "setting" def menu_channels(item): logger.info() itemlist = list() itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60545), action="conf_tools", folder=False, extra="channels_onoff", thumbnail=get_thumb("setting_0.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60546) + ":", action="", folder=False, text_bold = True, thumbnail=get_thumb("setting_0.png"))) # Home - Configurable channels import channelselector from core import channeltools channel_list = channelselector.filterchannels("all") for channel in channel_list: if not channel.channel: continue channel_parameters = channeltools.get_channel_parameters(channel.channel) if channel_parameters["has_settings"]: itemlist.append(Item(channel=CHANNELNAME, title=". " + config.get_localized_string(60547) % channel.title, action="channel_config", config=channel.channel, folder=False, thumbnail=channel.thumbnail)) # End - Configurable channels itemlist.append(Item(channel=CHANNELNAME, action="", title="", folder=False, thumbnail=get_thumb("setting_0.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60548) + ":", action="", folder=False, text_bold=True, thumbnail=get_thumb("channels.png"))) itemlist.append(Item(channel=CHANNELNAME, title=". " + config.get_localized_string(60549), action="conf_tools", folder=True, extra="lib_check_datajson", thumbnail=get_thumb("channels.png"))) return itemlist def channel_config(item): return platformtools.show_channel_settings(channelpath=filetools.join(config.get_runtime_path(), "channels", item.config)) # def setting_torrent(item): # logger.info() # LIBTORRENT_PATH = config.get_setting("libtorrent_path", server="torrent", default="") # LIBTORRENT_ERROR = config.get_setting("libtorrent_error", server="torrent", default="") # default = config.get_setting("torrent_client", server="torrent", default=0) # BUFFER = config.get_setting("mct_buffer", server="torrent", default="50") # DOWNLOAD_PATH = config.get_setting("mct_download_path", server="torrent", default=config.get_setting("downloadpath")) # if not DOWNLOAD_PATH: DOWNLOAD_PATH = filetools.join(config.get_data_path(), 'downloads') # BACKGROUND = config.get_setting("mct_background_download", server="torrent", default=True) # RAR = config.get_setting("mct_rar_unpack", server="torrent", default=True) # DOWNLOAD_LIMIT = config.get_setting("mct_download_limit", server="torrent", default="") # BUFFER_BT = config.get_setting("bt_buffer", server="torrent", default="50") # DOWNLOAD_PATH_BT = config.get_setting("bt_download_path", server="torrent", default=config.get_setting("downloadpath")) # if not DOWNLOAD_PATH_BT: DOWNLOAD_PATH_BT = filetools.join(config.get_data_path(), 'downloads') # MAGNET2TORRENT = config.get_setting("magnet2torrent", server="torrent", default=False) # torrent_options = [config.get_localized_string(30006), config.get_localized_string(70254), config.get_localized_string(70255)] # torrent_options.extend(platformtools.torrent_client_installed()) # list_controls = [ # { # "id": "libtorrent_path", # "type": "text", # "label": "Libtorrent path", # "default": LIBTORRENT_PATH, # "enabled": True, # "visible": False # }, # { # "id": "libtorrent_error", # "type": "text", # "label": "libtorrent error", # "default": LIBTORRENT_ERROR, # "enabled": True, # "visible": False # }, # { # "id": "list_torrent", # "type": "list", # "label": config.get_localized_string(70256), # "default": default, # "enabled": True, # "visible": True, # "lvalues": torrent_options # }, # { # "id": "mct_buffer", # "type": "text", # "label": "MCT - " + config.get_localized_string(70758), # "default": BUFFER, # "enabled": True, # "visible": "eq(-1,%s)" % torrent_options[2] # }, # { # "id": "mct_download_path", # "type": "text", # "label": "MCT - " + config.get_localized_string(30017), # "default": DOWNLOAD_PATH, # "enabled": True, # "visible": "eq(-2,%s)" % torrent_options[2] # }, # { # "id": "bt_buffer", # "type": "text", # "label": "BT - " + config.get_localized_string(70758), # "default": BUFFER_BT, # "enabled": True, # "visible": "eq(-3,%s)" % torrent_options[1] # }, # { # "id": "bt_download_path", # "type": "text", # "label": "BT - " + config.get_localized_string(30017), # "default": DOWNLOAD_PATH_BT, # "enabled": True, # "visible": "eq(-4,%s)" % torrent_options[1] # }, # { # "id": "mct_download_limit", # "type": "text", # "label": config.get_localized_string(70759), # "default": DOWNLOAD_LIMIT, # "enabled": True, # "visible": "eq(-5,%s) | eq(-5,%s)" % (torrent_options[1], torrent_options[2]) # }, # { # "id": "mct_rar_unpack", # "type": "bool", # "label": config.get_localized_string(70760), # "default": RAR, # "enabled": True, # "visible": True # }, # { # "id": "mct_background_download", # "type": "bool", # "label": config.get_localized_string(70761), # "default": BACKGROUND, # "enabled": True, # "visible": True # }, # { # "id": "magnet2torrent", # "type": "bool", # "label": config.get_localized_string(70762), # "default": MAGNET2TORRENT, # "enabled": True, # "visible": True # } # ] # platformtools.show_channel_settings(list_controls=list_controls, callback='save_setting_torrent', item=item, # caption=config.get_localized_string(70257), custom_button={'visible': False}) # def save_setting_torrent(item, dict_data_saved): # if dict_data_saved and "list_torrent" in dict_data_saved: # config.set_setting("torrent_client", dict_data_saved["list_torrent"], server="torrent") # if dict_data_saved and "mct_buffer" in dict_data_saved: # config.set_setting("mct_buffer", dict_data_saved["mct_buffer"], server="torrent") # if dict_data_saved and "mct_download_path" in dict_data_saved: # config.set_setting("mct_download_path", dict_data_saved["mct_download_path"], server="torrent") # if dict_data_saved and "mct_background_download" in dict_data_saved: # config.set_setting("mct_background_download", dict_data_saved["mct_background_download"], server="torrent") # if dict_data_saved and "mct_rar_unpack" in dict_data_saved: # config.set_setting("mct_rar_unpack", dict_data_saved["mct_rar_unpack"], server="torrent") # if dict_data_saved and "mct_download_limit" in dict_data_saved: # config.set_setting("mct_download_limit", dict_data_saved["mct_download_limit"], server="torrent") # if dict_data_saved and "bt_buffer" in dict_data_saved: # config.set_setting("bt_buffer", dict_data_saved["bt_buffer"], server="torrent") # if dict_data_saved and "bt_download_path" in dict_data_saved: # config.set_setting("bt_download_path", dict_data_saved["bt_download_path"], server="torrent") # if dict_data_saved and "magnet2torrent" in dict_data_saved: # config.set_setting("magnet2torrent", dict_data_saved["magnet2torrent"], server="torrent") def menu_servers(item): logger.info() itemlist = list() itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60550), action="servers_blacklist", folder=False, thumbnail=get_thumb("setting_0.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60551), action="servers_favorites", folder=False, thumbnail=get_thumb("setting_0.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60552), action="", folder=False, text_bold = True, thumbnail=get_thumb("setting_0.png"))) # Home - Configurable servers server_list = list(servertools.get_debriders_list().keys()) for server in server_list: server_parameters = servertools.get_server_parameters(server) if server_parameters["has_settings"]: itemlist.append( Item(channel=CHANNELNAME, title = ". " + config.get_localized_string(60553) % server_parameters["name"], action="server_debrid_config", config=server, folder=False, thumbnail="")) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60554), action="", folder=False, text_bold = True, thumbnail=get_thumb("setting_0.png"))) server_list = list(servertools.get_servers_list().keys()) for server in sorted(server_list): server_parameters = servertools.get_server_parameters(server) logger.info(server_parameters) if server_parameters["has_settings"] and [x for x in server_parameters["settings"] if x["id"] not in ["black_list", "white_list"]]: itemlist.append( Item(channel=CHANNELNAME, title=". " + config.get_localized_string(60553) % server_parameters["name"], action="server_config", config=server, folder=False, thumbnail="")) # End - Configurable servers return itemlist def server_config(item): return platformtools.show_channel_settings(channelpath=filetools.join(config.get_runtime_path(), "servers", item.config)) def server_debrid_config(item): return platformtools.show_channel_settings(channelpath=filetools.join(config.get_runtime_path(), "servers", "debriders", item.config)) def servers_blacklist(item): server_list = servertools.get_servers_list() blacklisted = [] list_controls = [] list_servers = [] for i, server in enumerate(sorted(server_list.keys())): server_parameters = server_list[server] defaults = servertools.get_server_parameters(server) control = server_parameters["name"] # control.setArt({'thumb:': server_parameters['thumb'] if 'thumb' in server_parameters else config.get_online_server_thumb(server)}) if defaults.get("black_list", False) or config.get_setting("black_list", server=server): blacklisted.append(i) list_controls.append(control) list_servers.append(server) ris = platformtools.dialog_multiselect(config.get_localized_string(60550), list_controls, preselect=blacklisted) if ris is not None: cb_servers_blacklist({list_servers[n]: True if n in ris else False for n, it in enumerate(list_controls)}) # return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, caption=config.get_localized_string(60550), callback="cb_servers_blacklist") def cb_servers_blacklist(dict_values): blaklisted = [] progreso = platformtools.dialog_progress(config.get_localized_string(60557), config.get_localized_string(60558)) n = len(dict_values) i = 1 for k, v in list(dict_values.items()): config.set_setting("black_list", v, server=k) if v: # If the server is blacklisted it cannot be in the favorites list config.set_setting("favorites_servers_list", 0, server=k) blaklisted.append(k) f = True progreso.update(old_div((i * 100), n), config.get_localized_string(60559) % k) i += 1 config.set_setting("black_list", blaklisted, server='servers') progreso.close() def servers_favorites(item): server_list = servertools.get_servers_list() dict_values = {} list_controls = [{'id': 'favorites_servers', 'type': 'bool', 'label': config.get_localized_string(60577), 'default': False, 'enabled': True, 'visible': True}, {'id': 'quality_priority', 'type': 'bool', 'label': config.get_localized_string(30069), 'default': False, 'enabled': 'eq(-1,True)', 'visible': True}] dict_values['favorites_servers'] = config.get_setting('favorites_servers') dict_values['quality_priority'] = config.get_setting('quality_priority') if dict_values['favorites_servers'] == None: dict_values['favorites_servers'] = False server_names = [config.get_localized_string(59992)] for server in sorted(server_list.keys()): if config.get_setting("black_list", server=server): continue server_names.append(server_list[server]['name']) orden = config.get_setting("favorites_servers_list", server=server) if orden > 0: dict_values[orden] = len(server_names) - 1 for x in range(1, 12): control = {'id': x, 'type': 'list', 'label': config.get_localized_string(60597) % x, 'lvalues': server_names, 'default': 0, 'enabled': 'eq(-%s,True)' % str(x + 1), 'visible': True} list_controls.append(control) return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, item=server_names, caption=config.get_localized_string(60551), callback="cb_servers_favorites") def cb_servers_favorites(server_names, dict_values): dict_name = {} dict_favorites = {} progreso = platformtools.dialog_progress(config.get_localized_string(60557), config.get_localized_string(60558)) for i, v in list(dict_values.items()): if i == "favorites_servers": config.set_setting("favorites_servers", v) elif i == "quality_priority": config.set_setting("quality_priority", v) elif int(v) > 0: dict_name[server_names[v]] = int(i) servers_list = list(servertools.get_servers_list().items()) n = len(servers_list) i = 1 for server, server_parameters in servers_list: if server_parameters['name'] in list(dict_name.keys()): dict_favorites[dict_name[server_parameters['name']]] = server config.set_setting("favorites_servers_list", dict_name[server_parameters['name']], server=server) else: config.set_setting("favorites_servers_list", 0, server=server) progreso.update(old_div((i * 100), n), config.get_localized_string(60559) % server_parameters['name']) i += 1 c = 1 logger.info(dict_favorites) favorites_servers_list = [] while c in dict_favorites: favorites_servers_list.append(dict_favorites[c]) c += 1 config.set_setting("favorites_servers_list", favorites_servers_list, server='servers') if not dict_name: # If there is no server in the list, deactivate it config.set_setting("favorites_servers", False) progreso.close() def settings(item): config.open_settings() def submenu_tools(item): logger.info() itemlist = list() # Custom tools import os channel_custom = os.path.join(config.get_runtime_path(), 'channels', 'custom.py') if not filetools.exists(channel_custom): user_custom = os.path.join(config.get_data_path(), 'custom.py') if filetools.exists(user_custom): filetools.copy(user_custom, channel_custom, silent=True) if filetools.exists(channel_custom): itemlist.append(Item(channel='custom', action='mainlist', title='Custom Channel')) itemlist.append(Item(channel=CHANNELNAME, action="check_quickfixes", folder=False, title=config.get_localized_string(30001), plot=config.get_addon_version(with_fix=True))) # itemlist.append(Item(channel=CHANNELNAME, action="update_quasar", folder=False, # title=config.get_localized_string(70569))) itemlist.append(Item(channel=CHANNELNAME, action="", title="", folder=False, thumbnail=get_thumb("setting_0.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60564) + ":", action="", folder=False, text_bold=True, thumbnail=get_thumb("channels.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60565), action="conf_tools", folder=True, extra="lib_check_datajson", thumbnail=get_thumb("channels.png"))) if config.get_videolibrary_support(): itemlist.append(Item(channel=CHANNELNAME, action="", title="", folder=False, thumbnail=get_thumb("setting_0.png"))) itemlist.append(Item(channel=CHANNELNAME, title=config.get_localized_string(60566) + ":", action="", folder=False, text_bold=True, thumbnail=get_thumb("videolibrary.png"))) itemlist.append(Item(channel=CHANNELNAME, action="restore_tools", folder=False, thumbnail=get_thumb("videolibrary.png"), title="- " + config.get_localized_string(60567))) itemlist.append(Item(channel="videolibrary", action="update_videolibrary", folder=False, thumbnail=get_thumb("videolibrary.png"), title="- " + config.get_localized_string(60568))) return itemlist def check_quickfixes(item): logger.info() if not config.dev_mode(): from platformcode import updater if not updater.check()[0]: platformtools.dialog_ok('Kodi on Demand', config.get_localized_string(70667)) else: return False # def update_quasar(item): # logger.info() # from platformcode import custom_code, platformtools # stat = False # stat = custom_code.update_external_addon("quasar") # if stat: # platformtools.dialog_notification("Actualización Quasar", "Realizada con éxito") # else: # platformtools.dialog_notification("Actualización Quasar", "Ha fallado. Consulte el log") def conf_tools(item): logger.info() # Enable or disable channels if item.extra == "channels_onoff": if config.get_platform(True)['num_version'] >= 17.0: # From Kodi 16 you can use multiselect, and from 17 with preselect return channels_onoff(item) import channelselector from core import channeltools channel_list = channelselector.filterchannels("allchannelstatus") excluded_channels = ['url', 'search', 'videolibrary', 'setting', 'news', # 'help', 'downloads'] list_controls = [] try: list_controls.append({'id': "all_channels", 'type': "list", 'label': config.get_localized_string(60594), 'default': 0, 'enabled': True, 'visible': True, 'lvalues': ['', config.get_localized_string(60591), config.get_localized_string(60592), config.get_localized_string(60593)]}) for channel in channel_list: # If the channel is on the exclusion list, we skip it if channel.channel not in excluded_channels: channel_parameters = channeltools.get_channel_parameters(channel.channel) status_control = "" status = config.get_setting("enabled", channel.channel) # if status does not exist, there is NO value in _data.json if status is None: status = channel_parameters["active"] logger.debug("%s | Status (XML): %s" % (channel.channel, status)) if not status: status_control = config.get_localized_string(60595) else: logger.debug("%s | Status: %s" % (channel.channel, status)) control = {'id': channel.channel, 'type': "bool", 'label': channel_parameters["title"] + status_control, 'default': status, 'enabled': True, 'visible': True} list_controls.append(control) else: continue except: import traceback logger.error("Error: %s" % traceback.format_exc()) else: return platformtools.show_channel_settings(list_controls=list_controls, item=item.clone(channel_list=channel_list), caption=config.get_localized_string(60596), callback="channel_status", custom_button={"visible": False}) # Checking channel_data.json files elif item.extra == "lib_check_datajson": itemlist = [] import channelselector from core import channeltools channel_list = channelselector.filterchannels("allchannelstatus") # Having an exclusion list doesn't make much sense because it checks if channel.json has "settings", but just in case it is left excluded_channels = ['url', 'setting', 'help'] try: import os from core import jsontools for channel in channel_list: list_status = None default_settings = None # It is checked if the channel is in the exclusion list if channel.channel not in excluded_channels: # It is checked that it has "settings", otherwise it skips list_controls, dict_settings = channeltools.get_channel_controls_settings(channel.channel) if not list_controls: itemlist.append(Item(channel=CHANNELNAME, title=channel.title + config.get_localized_string(60569), action="", folder=False, thumbnail=channel.thumbnail)) continue # logger.info(channel.channel + " SALTADO!") # The json file settings of the channel are loaded file_settings = os.path.join(config.get_data_path(), "settings_channels", channel.channel + "_data.json") dict_settings = {} dict_file = {} if filetools.exists(file_settings): # logger.info(channel.channel + " Has _data.json file") channeljson_exists = True # We get saved settings from ../settings/channel_data.json try: dict_file = jsontools.load(filetools.read(file_settings)) if isinstance(dict_file, dict) and 'settings' in dict_file: dict_settings = dict_file['settings'] except EnvironmentError: logger.error("ERROR when reading the file: %s" % file_settings) else: # logger.info(channel.channel + " No _data.json file") channeljson_exists = False if channeljson_exists: try: datajson_size = filetools.getsize(file_settings) except: import traceback logger.error(channel.title + config.get_localized_string(60570) % traceback.format_exc()) else: datajson_size = None # If the _data.json is empty or does not exist ... if (len(dict_settings) and datajson_size) == 0 or not channeljson_exists: # We get controls from the file ../channels/channel.json needsfix = True try: # Default settings are loaded list_controls, default_settings = channeltools.get_channel_controls_settings( channel.channel) # logger.info(channel.title + " | Default: %s" % default_settings) except: import traceback logger.error(channel.title + config.get_localized_string(60570) % traceback.format_exc()) # default_settings = {} # If _data.json needs to be repaired or doesn't exist ... if needsfix or not channeljson_exists: if default_settings is not None: # We create the channel_data.json default_settings.update(dict_settings) dict_settings = default_settings dict_file['settings'] = dict_settings # We create the file ../settings/channel_data.json if not filetools.write(file_settings, jsontools.dump(dict_file), silent=True): logger.error("ERROR saving file: %s" % file_settings) list_status = config.get_localized_string(60560) else: if default_settings is None: list_status = config.get_localized_string(60571) else: # logger.info(channel.channel + " - NO correction needed!") needsfix = False # If the channel status has been set it is added to the list if needsfix is not None: if needsfix: if not channeljson_exists: list_status = config.get_localized_string(60588) list_colour = "red" else: list_status = config.get_localized_string(60589) list_colour = "green" else: # If "needsfix" is "false" and "datjson_size" is None, an error will have occurred if datajson_size is None: list_status = config.get_localized_string(60590) list_colour = "red" else: list_status = config.get_localized_string(60589) list_colour = "green" if list_status is not None: itemlist.append(Item(channel=CHANNELNAME, title=channel.title + list_status, action="", folder=False, thumbnail=channel.thumbnail, text_color=list_colour)) else: logger.error("Something is wrong with the channel %s" % channel.channel) # If the channel is on the exclusion list, we skip it else: continue except: import traceback logger.error("Error: %s" % traceback.format_exc()) return itemlist def channels_onoff(item): import channelselector, xbmcgui from core import channeltools # Load list of options # ------------------------ lista = []; ids = [] channels_list = channelselector.filterchannels('allchannelstatus') for channel in channels_list: channel_parameters = channeltools.get_channel_parameters(channel.channel) lbl = '%s' % channel_parameters['language'] # ~ lbl += ' %s' % [config.get_localized_category(categ) for categ in channel_parameters['categories']] lbl += ' %s' % ', '.join(config.get_localized_category(categ) for categ in channel_parameters['categories']) it = xbmcgui.ListItem(channel.title, lbl) it.setArt({ 'thumb': channel.thumbnail, 'fanart': channel.fanart }) lista.append(it) ids.append(channel.channel) # Dialog to pre-select # ---------------------------- preselecciones = [config.get_localized_string(70517), config.get_localized_string(70518), config.get_localized_string(70519)] ret = platformtools.dialog_select(config.get_localized_string(60545), preselecciones) if ret == -1: return False # order cancel if ret == 2: preselect = [] elif ret == 1: preselect = list(range(len(ids))) else: preselect = [] for i, canal in enumerate(ids): channel_status = config.get_setting('enabled', canal) if channel_status is None: channel_status = True if channel_status: preselect.append(i) # Dialog to select # ------------------------ ret = platformtools.dialog_multiselect(config.get_localized_string(60545), lista, preselect=preselect, useDetails=True) if ret == None: return False # order cancel seleccionados = [ids[i] for i in ret] # Save changes to activated channels # ------------------------------------ for canal in ids: channel_status = config.get_setting('enabled', canal) if channel_status is None: channel_status = True if channel_status and canal not in seleccionados: config.set_setting('enabled', False, canal) elif not channel_status and canal in seleccionados: config.set_setting('enabled', True, canal) return False def channel_status(item, dict_values): try: for k in dict_values: if k == "all_channels": logger.info("All channels | Selected state: %s" % dict_values[k]) if dict_values[k] != 0: excluded_channels = ['url', 'search', 'videolibrary', 'setting', 'news', 'help', 'downloads'] for channel in item.channel_list: if channel.channel not in excluded_channels: from core import channeltools channel_parameters = channeltools.get_channel_parameters(channel.channel) new_status_all = None new_status_all_default = channel_parameters["active"] # Option Activate all if dict_values[k] == 1: new_status_all = True # Option Deactivate all if dict_values[k] == 2: new_status_all = False # Retrieve default status option if dict_values[k] == 3: # If you have "enabled" in the _data.json, it is because the state is not that of the channel.json if config.get_setting("enabled", channel.channel): new_status_all = new_status_all_default # If the channel does not have "enabled" in the _data.json it is not saved, it goes to the next else: continue # Channel status is saved if new_status_all is not None: config.set_setting("enabled", new_status_all, channel.channel) break else: continue else: logger.info("Channel: %s | State: %s" % (k, dict_values[k])) config.set_setting("enabled", dict_values[k], k) logger.info("the value is like %s " % config.get_setting("enabled", k)) platformtools.itemlist_update(Item(channel=CHANNELNAME, action="mainlist")) except: import traceback logger.error("Error detail: %s" % traceback.format_exc()) platformtools.dialog_notification(config.get_localized_string(60579), config.get_localized_string(60580)) def restore_tools(item): import service from core import videolibrarytools import os seleccion = platformtools.dialog_yesno(config.get_localized_string(60581), config.get_localized_string(60582) + '\n' + config.get_localized_string(60583)) if seleccion == 1: # tvshows heading = config.get_localized_string(60584) p_dialog = platformtools.dialog_progress_bg(config.get_localized_string(60585), heading) p_dialog.update(0, '') show_list = [] for path, folders, files in filetools.walk(videolibrarytools.TVSHOWS_PATH): show_list.extend([filetools.join(path, f) for f in files if f == "tvshow.nfo"]) if show_list: t = float(100) / len(show_list) for i, tvshow_file in enumerate(show_list): head_nfo, serie = videolibrarytools.read_nfo(tvshow_file) path = filetools.dirname(tvshow_file) if not serie.active: # if the series is not active discard continue # We delete the folder with the series ... if tvshow_file.endswith('.strm') or tvshow_file.endswith('.json') or tvshow_file.endswith('.nfo'): os.remove(os.path.join(path, tvshow_file)) # filetools.rmdirtree(path) # ... and we add it again service.update(path, p_dialog, i, t, serie, 3) p_dialog.close() # movies heading = config.get_localized_string(60586) p_dialog2 = platformtools.dialog_progress_bg(config.get_localized_string(60585), heading) p_dialog2.update(0, '') movies_list = [] for path, folders, files in filetools.walk(videolibrarytools.MOVIES_PATH): movies_list.extend([filetools.join(path, f) for f in files if f.endswith(".json")]) logger.debug("movies_list %s" % movies_list) if movies_list: t = float(100) / len(movies_list) for i, movie_json in enumerate(movies_list): try: from core import jsontools path = filetools.dirname(movie_json) movie = Item().fromjson(filetools.read(movie_json)) # We delete the folder with the movie ... filetools.rmdirtree(path) import math heading = config.get_localized_string(20000) p_dialog2.update(int(math.ceil((i + 1) * t)), heading, config.get_localized_string(60389) % (movie.contentTitle, movie.channel.capitalize())) # ... and we add it again videolibrarytools.save_movie(movie) except Exception as ex: logger.error("Error creating movie again") template = "An exception of type %s occured. Arguments:\n%r" message = template % (type(ex).__name__, ex.args) logger.error(message) p_dialog2.close() def report_menu(item): logger.info('URL: ' + item.url) from channelselector import get_thumb thumb_debug = get_thumb("update.png") thumb_error = get_thumb("error.png") thumb_next = get_thumb("next.png") itemlist = [] paso = 1 # Create a menu of options to allow the user to report an Alpha failure through a "pastebin" server # For the report to be complete, the user must have the option DEBUG = ON # Free pastbin servers have capacity limitations, so the size of the log is important # At the end of the upload operation, the user is passed the log address on the server to report them itemlist.append(Item(channel=item.channel, action="", title=config.get_localized_string(707418), thumbnail=thumb_next, folder=False)) # if not config.get_setting('debug'): itemlist.append(Item(channel=item.channel, action="activate_debug", extra=True, title=config.get_localized_string(707419) % str(paso), thumbnail=thumb_debug, folder=False)) paso += 1 itemlist.append(Item(channel="channelselector", action="getmainlist", title=config.get_localized_string(707420) % str(paso), thumbnail=thumb_debug)) paso += 1 itemlist.append(Item(channel=item.channel, action="report_send", title=config.get_localized_string(707421) % str(paso), thumbnail=thumb_error, folder=False)) paso += 1 # if config.get_setting('debug'): itemlist.append(Item(channel=item.channel, action="activate_debug", extra=False, title=config.get_localized_string(707422) % str(paso), thumbnail=thumb_debug, folder=False)) paso += 1 if item.url: itemlist.append(Item(channel=item.channel, action="", title="", folder=False)) itemlist.append(Item(channel=item.channel, action="", title=config.get_localized_string(707423), thumbnail=thumb_next, folder=False)) if item.one_use: action = '' url = '' else: action = 'call_browser' url = item.url itemlist.append(Item(channel=item.channel, action=action, title="**- LOG: [COLOR gold]%s[/COLOR] -**" % item.url, url=url, thumbnail=thumb_next, unify=False, folder=False)) itemlist.append(Item(channel=item.channel, action="call_browser", title="su Github (raccomandato)", url='https://github.com/kodiondemand/addon/issues', thumbnail=thumb_next, folder=False)) itemlist.append(Item(channel=item.channel, action="call_browser", url='https://t.me/kodiondemand', title="Su telegram", thumbnail=thumb_next, unify=False, folder=False)) if item.one_use: itemlist.append(Item(channel=item.channel, action="", title="[COLOR orange]NO ACCEDA al INFORME: se BORRARÁ[/COLOR]", thumbnail=thumb_next, folder=False)) itemlist.append(Item(channel=item.channel, action="", title="[COLOR orange]ya que es de un solo uso[/COLOR]", thumbnail=thumb_next, folder=False)) return itemlist def activate_debug(item): logger.info(item.extra) from platformcode import platformtools #Enable / disable DEBUB option in settings.xml if isinstance(item.extra, str): return report_menu(item) if item.extra: config.set_setting('debug', True) platformtools.dialog_notification(config.get_localized_string(707430), config.get_localized_string(707431)) else: config.set_setting('debug', False) platformtools.dialog_notification(config.get_localized_string(707430), config.get_localized_string(707432)) def report_send(item, description='', fatal=False): import xbmc import random import traceback if PY3: # from future import standard_library # standard_library.install_aliases() import urllib.parse as urlparse # It is very slow in PY2. In PY3 it is native import urllib.parse as urllib else: import urllib # We use the native of PY2 which is faster import urlparse try: requests_status = True import requests except: requests_status = False logger.error(traceback.format_exc()) from core import jsontools, httptools, scrapertools from platformcode import envtal # This function performs the LOG upload operation. The file size is of great importance because # Free pastebin services have limitations, sometimes very low. # There is an ervice, File.io, that allows direct upload of "binary files" through the "request" function # This dramatically increases the ability to send the log, well above what is needed. # Therefore it is necessary to have a list of "pastebin" services that can perform the upload operation, # either by available capacity or by availability. # In order to use the "pastebin" servers with a common code, a dictionary has been created with the servers # and their characteristics. In each entry the peculiarities of each server are collected, both to form # the request with POST as for the way to receive the upload code in the response (json, header, regex # in data, ...). # Starting this method randomizes the list of "pastebin" servers to prevent all users from doing # uploads against the same server and may cause overloads. # The log file is read and its size is compared with the server capacity (parameter 10 of each entry # (starting from 0), expressed in MB, until a qualified one is found. If the upload fails, it continues trying # with the following servers that have the required capacity. # If no available server is found, the user is asked to try again later, or to upload the log. # directly on the forum. If it is a size problem, you are asked to reset Kodi and redo the fault, to # that the LOG is smaller. pastebin_list = { 'hastebin': ('1', 'https://hastebin.com/', 'documents', 'random', '', '', 'data', 'json', 'key', '', '0.29', '10', True, 'raw/', '', ''), 'dpaste': ('1', 'http://dpaste.com/', 'api/v2/', 'random', 'content=', '&syntax=text&title=%s&poster=alfa&expiry_days=7', 'headers', '', '', 'location', '0.23', '15', True, '', '.txt', ''), 'ghostbin': ('1', 'https://ghostbin.com/', 'paste/new', 'random', 'lang=text&text=', '&expire=2d&password=&title=%s', 'data', 'regex', '(.*?)\s*-\s*Ghostbin<\/title>', '', '0.49', '15', False, 'paste/', '', ''), 'write.as': ('1', 'https://write.as/', 'api/posts', 'random', 'body=', '&title=%s', 'data', 'json', 'data', 'id', '0.018', '15', True, '', '', ''), 'oneclickpaste': ('1', 'http://oneclickpaste.com/', 'index.php', 'random', 'paste_data=', '&title=%s&format=text&paste_expire_date=1W&visibility=0&pass=&submit=Submit', 'data', 'regex', '<a class="btn btn-primary" href="[^"]+\/(\d+\/)">\s*View\s*Paste\s*<\/a>', '', '0.060', '5', True, '', '', ''), 'bpaste': ('1', 'https://bpaste.net/', '', 'random', 'code=', '&lexer=text&expiry=1week', 'data', 'regex', 'View\s*<a\s*href="[^*]+/(.*?)">raw<\/a>', '', '0.79', '15', True, 'raw/', '', ''), 'dumpz': ('0', 'http://dumpz.org/', 'api/dump', 'random', 'code=', '&lexer=text&comment=%s&password=', 'headers', '', '', 'location', '0.99', '15', False, '', '', ''), 'file.io': ('1', 'https://file.io/', '', 'random', '', 'expires=1w', 'requests', 'json', 'key', '', '99.0', '30', False, '', '.log', ''), 'uploadfiles': ('1', 'https://up.uploadfiles.io/upload', '', 'random', '', '', 'requests', 'json', 'url', '', '99.0', '30', False, None, '', '') } pastebin_list_last = ['hastebin', 'ghostbin', 'file.io'] # We leave these services the last pastebin_one_use = ['file.io'] # Single-use servers and deletes pastebin_dir = [] paste_file = {} paste_params = () paste_post = '' status = False msg = config.get_localized_string(707424) # DEBUG = ON is verified, if it is not it is rejected and the user is asked to activate it and reproduce the fault if not config.get_setting('debug'): platformtools.dialog_notification(config.get_localized_string(707425), config.get_localized_string(707426)) return report_menu(item) # From each to the future the user will be allowed to enter a brief description of the fault that will be added to the LOG if description == 'OK': description = platformtools.dialog_input('', 'Introduzca una breve descripción del fallo') # We write in the log some Kodi and Alpha variables that will help us diagnose the failure environment = envtal.list_env() if not environment['log_path']: environment['log_path'] = str(filetools.join(xbmc.translatePath("special://logpath/"), 'kodi.log')) environment['log_size_bytes'] = str(filetools.getsize(environment['log_path'])) environment['log_size'] = str(round(float(environment['log_size_bytes']) / (1024*1024), 3)) # LOG file is read log_path = environment['log_path'] if filetools.exists(log_path): log_size_bytes = int(environment['log_size_bytes']) # File size in Bytes log_size = float(environment['log_size']) # File size in MB log_data = filetools.read(log_path) # File data if not log_data: # Some mistake? platformtools.dialog_notification(config.get_localized_string(707427), '', 2) return report_menu(item) else: # Log no existe or erroneous path? platformtools.dialog_notification(config.get_localized_string(707427), '', 2) return report_menu(item) # If the fault description has been entered, the beginning of the LOG data is inserted # log_title = '***** FAULT DESCRIPTION *****' # if description: # log_data = '%s\n%s\n\n%s' %(log_title, description, log_data) # Server names "patebin" are scrambled for label_a, value_a in list(pastebin_list.items()): if label_a not in pastebin_list_last: pastebin_dir.append(label_a) random.shuffle(pastebin_dir) pastebin_dir.extend(pastebin_list_last) # We leave these services the last #pastebin_dir = ['uploadfiles'] # For testing a service #log_data = 'TEST FOR SERVICE TESTS' # The list of "pastebin" servers is scrolled to locate an active one, with capacity and availability for paste_name in pastebin_dir: if pastebin_list[paste_name][0] != '1': # If the server is not active, we pass continue if pastebin_list[paste_name][6] == 'requests' and not requests_status: # If "requests" is not active, we pass continue paste_host = pastebin_list[paste_name][1] # Server URL "pastebin" paste_sufix = pastebin_list[paste_name][2] # API suffix for POST paste_title = '' if pastebin_list[paste_name][3] == 'random': paste_title = "LOG" + str(random.randrange(1, 999999999)) # LOG title paste_post1 = pastebin_list[paste_name][4] # Initial part of the POST paste_post2 = pastebin_list[paste_name][5] # Secondary part of POST paste_type = pastebin_list[paste_name][6] # Type of downloadpage: DATE HEADERS paste_resp = pastebin_list[paste_name][7] # Response type: JSON or data with REGEX paste_resp_key = pastebin_list[paste_name][8] # If JSON, label `primary with KEY paste_url = pastebin_list[paste_name][9] # Primary label for HEADER and sec. for JSON paste_file_size = float(pastebin_list[paste_name][10]) # Server capacity in MB if paste_file_size > 0: # If it is 0, the capacity is unlimited if log_size > paste_file_size: # Capacity and size verification msg = 'Log file too large. Restart Kodi and retry' continue paste_timeout = int(pastebin_list[paste_name][11]) # Timeout for the server paste_random_headers = pastebin_list[paste_name][12] # Do you use RAMDOM headers to mislead the serv? paste_host_return = pastebin_list[paste_name][13] # Part of url to compose the key for user paste_host_return_tail = pastebin_list[paste_name][14] # Url suffix to compose user key paste_headers = {} if pastebin_list[paste_name][15]: # Headers required by the server paste_headers.update(jsontools.load((pastebin_list[paste_name][15]))) if paste_name in pastebin_one_use: pastebin_one_use_msg = 'DO NOT ACCESS THE REPORT: it will be DELETED' item.one_use = True else: pastebin_one_use_msg = '' try: # POST is created with server options "pastebin" # This is the "requests" format if paste_type == 'requests': paste_file = {'file': (paste_title+'.log', log_data)} if paste_post1: paste_file.update(paste_post1) if paste_post2: if '%s' in paste_post2: paste_params = paste_post2 % (paste_title+'.log', log_size_bytes) else: paste_params = paste_post2 # This is the download format else: # log_data = 'Server Test to see its viability (áéíóúñ¿?)' if paste_name in ['hastebin']: # There are some services that do not need "quote" paste_post = log_data else: paste_post = urllib.quote_plus(log_data) # A "quote" is made from the LOG data if paste_post1: paste_post = '%s%s' % (paste_post1, paste_post) if paste_post2: if '%s' in paste_post2: paste_post += paste_post2 % paste_title else: paste_post += paste_post2 # Request is made on downloadpage with HEADERS or DATA, with server parameters if paste_type == 'headers': data = httptools.downloadpage(paste_host+paste_sufix, post=paste_post, timeout=paste_timeout, random_headers=paste_random_headers, headers=paste_headers).headers elif paste_type == 'data': data = httptools.downloadpage(paste_host+paste_sufix, post=paste_post, timeout=paste_timeout, random_headers=paste_random_headers, headers=paste_headers).data # If the request is in REQUESTS format, it is made here elif paste_type == 'requests': #data = requests.post(paste_host, params=paste_params, files=paste_file, # timeout=paste_timeout) data = httptools.downloadpage(paste_host, params=paste_params, file=log_data, file_name=paste_title+'.log', timeout=paste_timeout, random_headers=paste_random_headers, headers=paste_headers) except: msg = 'Inténtelo más tarde' logger.error('Failed to save report. ' + msg) logger.error(traceback.format_exc()) continue # The server response is analyzed and the upload key is located to form the url to pass to the user if data: paste_host_resp = paste_host if paste_host_return == None: # If you return the full url, it is not composed paste_host_resp = '' paste_host_return = '' # Responses to REQUESTS requests if paste_type == 'requests': # Response of request type "requests"? if paste_resp == 'json': # Answer in JSON format? if paste_resp_key in data.data: if not paste_url: key = jsontools.load(data.data)[paste_resp_key] # with a label else: key = jsontools.load(data.data)[paste_resp_key][paste_url] # with two nested tags item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, paste_host_return_tail) else: logger.error('ERROR in data return format. data.data=' + str(data.data)) continue # Responses to DOWNLOADPAGE requests elif paste_resp == 'json': # Answer in JSON format? if paste_resp_key in data: if not paste_url: key = jsontools.load(data)[paste_resp_key] # with a label else: key = jsontools.load(data)[paste_resp_key][paste_url] # con two nested tags item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, paste_host_return_tail) else: logger.error('ERROR in data return format. data=' + str(data)) continue elif paste_resp == 'regex': # Answer in DATA, to search with a REGEX? key = scrapertools.find_single_match(data, paste_resp_key) if key: item.url = "%s%s%s" % (paste_host_resp+paste_host_return, key, paste_host_return_tail) else: logger.error('ERROR in data return format. data=' + str(data)) continue elif paste_type == 'headers': # Answer in HEADERS, to search in "location"? if paste_url in data: item.url = data[paste_url] # Key return label item.url = urlparse.urljoin(paste_host_resp + paste_host_return, item.url + paste_host_return_tail) else: logger.error('ERROR in data return format. response.headers=' + str(data)) continue else: logger.error('ERROR in data return format. paste_type=' + str(paste_type) + ' / DATA: ' + data) continue status = True # Upload operation completed successfully logger.info('Report created: ' + str(item.url)) # The URL of the user report is saved # if fatal: # For future use, for logger.crash # platformtools.dialog_ok('KoD CREATED ERROR report', 'Report it in the forum by adding FATAL ERROR and this URL: ', '[COLOR gold]%s[/COLOR]' % item.url, pastebin_one_use_msg) # else: # Report URL passed to user # platformtools.dialog_ok('KoD Crash Report CREATED', 'Report it on the forum by adding a bug description and this URL: ', '[COLOR gold]%s[/COLOR]' % item.url, pastebin_one_use_msg) break # Operation finished, we don't keep looking if not status and not fatal: # Operation failed ... platformtools.dialog_notification(config.get_localized_string(707428), msg) #... cause is reported logger.error(config.get_localized_string(707428) + msg) # Control is returned with updated item.url, so the report URL will appear in the menu item.action = 'report_menu' platformtools.itemlist_update(item, True) # return report_menu(item) def call_browser(item): import webbrowser if not webbrowser.open(item.url): import xbmc if xbmc.getCondVisibility('system.platform.linux') and xbmc.getCondVisibility( 'system.platform.android'): # android xbmc.executebuiltin('StartAndroidActivity("", "android.intent.action.VIEW", "", "%s")' % (item.url)) else: try: import urllib.request as urllib except ImportError: import urllib short = urllib.urlopen( 'https://u.nu/api.php?action=shorturl&format=simple&url=' + item.url).read() platformtools.dialog_ok(config.get_localized_string(20000), config.get_localized_string(70740) % short)