def play_external(self, **kwargs): kodi_log(['lib.container.router - Attempting to play item\n', kwargs], 1) if not kwargs.get('tmdb_id'): kwargs['tmdb_id'] = self.tmdb_api.get_tmdb_id(**kwargs) Players(**kwargs).play( handle=self.handle if self.handle != -1 else None)
def convert_timestamp(time_str, time_fmt="%Y-%m-%dT%H:%M:%S", time_lim=19, utc_convert=False): if not time_str: return time_str = time_str[:time_lim] if time_lim else time_str utc_offset = 0 if utc_convert: utc_offset = -time.timezone // 3600 utc_offset += 1 if time.localtime().tm_isdst > 0 else 0 try: time_obj = datetime.datetime.strptime(time_str, time_fmt) time_obj = time_obj + datetime.timedelta(hours=utc_offset) return time_obj except TypeError: try: time_obj = datetime.datetime( *(time.strptime(time_str, time_fmt)[0:6])) time_obj = time_obj + datetime.timedelta(hours=utc_offset) return time_obj except Exception as exc: kodi_log(exc, 1) return except Exception as exc: kodi_log(exc, 1) return
def fivehundred_error(self, request): self.req_500_err[request] = set_timestamp() get_property(self.req_500_err_prop, dumps(self.req_500_err)) kodi_log(u'ConnectionError: {}\nSuppressing retries for 1 minute'.format(dumps(self.req_500_err)), 1) xbmcgui.Dialog().notification( ADDON.getLocalizedString(32308).format(self.req_api_name), ADDON.getLocalizedString(32307))
def refresh_token(self): # Check we haven't attempted too many refresh attempts refresh_attempts = try_int(get_property('TraktRefreshAttempts')) + 1 if refresh_attempts > 5: kodi_log( 'Trakt Unauthorised!\nExceeded refresh_token attempt limit\nSuppressing retries for 10 minutes', 1) get_property('TraktRefreshTimeStamp', set_timestamp(600)) get_property('TraktRefreshAttempts', 0) # Reset refresh attempts return get_property('TraktRefreshAttempts', refresh_attempts) kodi_log('Attempting to refresh Trakt token', 2) if not self.authorization or not self.authorization.get( 'refresh_token'): kodi_log('Trakt refresh token not found!', 1) return postdata = { 'refresh_token': self.authorization.get('refresh_token'), 'client_id': self.client_id, 'client_secret': self.client_secret, 'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', 'grant_type': 'refresh_token' } self.authorization = self.get_api_request_json( 'https://api.trakt.tv/oauth/token', postdata=postdata) if not self.authorization or not self.authorization.get( 'access_token'): kodi_log('Failed to refresh Trakt token!', 2) return self.on_authenticated(auth_dialog=False) kodi_log('Trakt token refreshed', 1) return self.authorization
def connection_error(self, err): self.req_connect_err = set_timestamp() get_property(self.req_connect_err_prop, self.req_connect_err) kodi_log(u'ConnectionError: {}\nSuppressing retries for 1 minute'.format(err), 1) xbmcgui.Dialog().notification( ADDON.getLocalizedString(32308).format(self.req_api_name), ADDON.getLocalizedString(32307))
def on_authenticated(self, auth_dialog=True): """Triggered when device authentication has been completed""" kodi_log(u'Trakt authenticated successfully!', 1) ADDON.setSettingString('trakt_token', dumps(self.authorization)) self.headers['Authorization'] = u'Bearer {0}'.format(self.authorization.get('access_token')) if auth_dialog: self.auth_dialog.close()
def _get_database(self): '''get reference to our sqllite _database - performs basic integrity check''' try: connection = sqlite3.connect(self._db_file, timeout=30, isolation_level=None) connection.execute('SELECT * FROM simplecache LIMIT 1') return connection except Exception: # our _database is corrupt or doesn't exist yet, we simply try to recreate it if xbmcvfs.exists(self._db_file): xbmcvfs.delete(self._db_file) try: connection = sqlite3.connect(self._db_file, timeout=30, isolation_level=None) connection.execute("""CREATE TABLE IF NOT EXISTS simplecache( id TEXT UNIQUE, expires INTEGER, data TEXT, checksum INTEGER)""" ) return connection except Exception as error: kodi_log( "CACHE: Exception while initializing _database: {}".format( error), 1) self.close() return None
def _execute_sql(self, query, data=None): '''little wrapper around execute and executemany to just retry a db command if db is locked''' retries = 0 result = None error = None # always use new db object because we need to be sure that data is available for other simplecache instances with self._get_database() as _database: while not retries == 10 and not self._monitor.abortRequested(): if self._exit: return None try: if isinstance(data, list): result = _database.executemany(query, data) elif data: result = _database.execute(query, data) else: result = _database.execute(query) return result except sqlite3.OperationalError as error: if "_database is locked" in error: kodi_log("CACHE: retrying DB commit...") retries += 1 self._monitor.waitForAbort(0.5) else: break except Exception: break kodi_log("CACHE: _database ERROR ! -- {}".format(error), 1) return None
def create_file(content, filename, *args, **kwargs): """ Create the file and folder structure: filename=.strm file, content= content of file. *args = folders to create. """ # Validify and build path path = kwargs.get('basedir', '').replace('\\', '/') # Convert MS-DOS style paths to UNIX style if not path: # Make sure we actually have a basedir return for folder in args: folder = validify_filename(folder) path = '{}{}/'.format(path, folder) # Validify content of file if kwargs.get('clean_url', True): content = clean_content(content) if not content: return if not filename: return # Check that we can actually make the path if not make_path(path, warn_dialog=True): return # Write out our file filepath = '{}{}.{}'.format(path, validify_filename(filename), kwargs.get('file_ext', 'strm')) write_to_file(filepath, content) kodi_log(['ADD LIBRARY -- Successfully added:\n', filepath, '\n', content], 2) return filepath
def library_autoupdate(list_slugs=None, user_slugs=None, busy_spinner=False, force=False): kodi_log(u'UPDATING TV SHOWS LIBRARY', 1) xbmcgui.Dialog().notification( 'TMDbHelper', u'{}...'.format(ADDON.getLocalizedString(32167))) # Update library from Trakt lists library_adder = None user_lists = _get_monitor_userlists(list_slugs, user_slugs) for list_slug, user_slug in user_lists: library_adder = add_to_library(info='trakt', user_slug=user_slug, list_slug=list_slug, confirm=False, allow_update=False, busy_spinner=busy_spinner, force=force, library_adder=library_adder, finished=False) # Update library from nfos add_to_library(info='update', busy_spinner=busy_spinner, library_adder=library_adder, finished=True, force=force)
def get_stored_token(self): try: token = loads(ADDON.getSettingString('trakt_token')) or {} except Exception as exc: token = {} kodi_log(exc, 1) return token
def get_simple_api_request(self, request=None, postdata=None, headers=None, method=None): try: if method == 'delete': return requests.delete(request, headers=headers, timeout=self.timeout) if method == 'put': return requests.put(request, data=postdata, headers=headers, timeout=self.timeout) if postdata or method == 'post': # If pass postdata assume we want to post return requests.post(request, data=postdata, headers=headers, timeout=self.timeout) return requests.get(request, headers=headers, timeout=self.timeout) except requests.exceptions.ConnectionError as errc: self.connection_error(errc) except requests.exceptions.Timeout as errt: self.timeout_error(errt) except Exception as err: kodi_log(u'RequestError: {}'.format(err), 1)
def colors(self, source): filename = '{}.png'.format(md5hash(source)) destination = self.save_path + filename try: if xbmcvfs.exists(destination): os.utime(destination, None) img = Image.open(xbmc.translatePath(destination)) else: img = _openimage(source, self.save_path, filename) img.thumbnail((256, 256)) img = img.convert('RGB') img.save(destination) maincolor_rgb = self.get_maincolor(img) maincolor_hex = self.rgb_to_hex(*self.get_color_lumsat( *maincolor_rgb)) compcolor_rgb = self.get_compcolor(*maincolor_rgb) compcolor_hex = self.rgb_to_hex(*self.get_color_lumsat( *compcolor_rgb)) maincolor_propname = self.save_prop + '.Main' maincolor_propchek = self.save_prop + '.MainCheck' maincolor_propvalu = get_property(maincolor_propname) if not maincolor_propvalu: get_property(maincolor_propname, set_property=maincolor_hex) else: get_property(maincolor_propchek, set_property=maincolor_propvalu) thread_maincolor = Thread(target=self.set_prop_colorgradient, args=[ maincolor_propname, maincolor_propvalu, maincolor_hex, maincolor_propchek ]) thread_maincolor.start() compcolor_propname = self.save_prop + '.Comp' compcolor_propchek = self.save_prop + '.CompCheck' compcolor_propvalu = get_property(compcolor_propname) if not compcolor_propvalu: get_property(compcolor_propname, set_property=compcolor_hex) else: get_property(compcolor_propchek, set_property=compcolor_propvalu) thread_compcolor = Thread(target=self.set_prop_colorgradient, args=[ compcolor_propname, compcolor_propvalu, compcolor_hex, compcolor_propchek ]) thread_compcolor.start() img.close() return maincolor_hex except Exception as exc: kodi_log(exc, 1) return ''
def wrapper(self, *args, **kwargs): """ Syntactic sugar to log output of function """ response = func(self, *args, **kwargs) log_text = '{}.{}.'.format(self.__class__.__name__, func_name) log_text = format_name(log_text, *args, **kwargs) kodi_log(log_text, 1) kodi_log(response, 1) return response
def _create(self, title='', message='', total=100): self._pd = xbmcgui.DialogProgressBG() self._pd.create(title, message) self._count = 0 self._total = total self._title = title kodi_log([self._title, ' - 00 ', message], self.logging) return self._pd
def run_plugin(**kwargs): with busy_dialog(): kodi_log([ 'lib.script.router - attempting to play\n', kwargs.get('run_plugin') ], 1) xbmc.executebuiltin( try_encode(u'RunPlugin({})'.format(kwargs.get('run_plugin'))))
def play_media(**kwargs): with busy_dialog(): kodi_log([ 'lib.script.router - attempting to play\n', kwargs.get('play_media') ], 1) xbmc.executebuiltin( try_encode(u'PlayMedia({})'.format(kwargs.get('play_media'))))
def close(self): '''tell any tasks to stop immediately (as we can be called multithreaded) and cleanup objects''' self._exit = True # wait for all tasks to complete while self._busy_tasks and not self._monitor.abortRequested(): xbmc.sleep(25) del self._win del self._monitor kodi_log("CACHE: Closed")
def timer_func(timer_name): timer_a = timer() try: yield finally: timer_z = timer() total_time = timer_z - timer_a if total_time > 0.05: kodi_log(u'{}\n{:.3f} sec'.format(timer_name, total_time), 1)
def set_property(self, key, value): key = '{}.{}'.format(self.property_prefix, key) try: if value is None: get_property(key, clear_property=True) else: get_property(key, set_property=u'{0}'.format(value)) except Exception as exc: kodi_log(u'set_property: {}{}'.format(key, exc), 1)
def connection_error(self, err, wait_time=30, msg_affix=''): self.req_connect_err = set_timestamp(wait_time) get_property(self.req_connect_err_prop, self.req_connect_err) kodi_log( u'ConnectionError: {} {}\nSuppressing retries for 30 seconds'. format(msg_affix, err), 1) xbmcgui.Dialog().notification( ADDON.getLocalizedString(32308).format(' '.join( [self.req_api_name, msg_affix])), ADDON.getLocalizedString(32307).format('30'))
def timeout_error(self, err): """ Log timeout error If two timeouts occur in x3 the timeout limit then set connection error e.g. if timeout limit is 10s then two timeouts within 30s trigger connection error """ kodi_log(u'ConnectionTimeOut: {}'.format(err), 1) if get_timestamp(self.req_timeout_err): self.connection_error(err, msg_affix='timeout') self.req_timeout_err = set_timestamp(self.timeout * 3) get_property(self.req_timeout_err_prop, self.req_timeout_err)
def set_list_properties(self, items, key, prop): if not isinstance(items, list): return try: joinlist = [i[key] for i in items[:10] if i.get(key)] joinlist = ' / '.join(joinlist) self.properties.add(prop) self.set_property(prop, joinlist) except Exception as exc: kodi_log(u'Func: set_list_properties\n{0}'.format(exc), 1)
def clear_dir(self, folder): for filename in os.listdir(folder): file_path = os.path.join(folder, filename) try: if os.path.isfile(file_path): os.unlink(file_path) elif os.path.isdir(file_path): self.recursive_delete_dir(file_path) except Exception as e: kodi_log(u'Could not delete file {0}: {1}'.format(file_path, str(e)))
def get_database(self, dbtype, tvshowid=None, attempt_reconnect=False, logging=True): retries = 5 if attempt_reconnect else 1 while not xbmc.Monitor().abortRequested() and retries > 0: database = self._get_kodi_db(dbtype, tvshowid) if database: return database xbmc.Monitor().waitForAbort(1) retries -= 1 if logging: kodi_log(u'Getting KodiDB {} FAILED!'.format(dbtype), 1)
def __init__(self, folder=None, filename=None, mem_only=False): '''Initialize our caching class''' folder = folder or 'database' filename = filename or 'defaultcache.db' self._win = xbmcgui.Window(10000) self._monitor = xbmc.Monitor() self._db_file = get_file_path(folder, filename) self._sc_name = '{}_{}_simplecache'.format(folder, filename) self._mem_only = mem_only self.check_cleanup() kodi_log("CACHE: Initialized")
def _openimage(image, targetpath, filename): """ Open image helper with thanks to sualfred """ # some paths require unquoting to get a valid cached thumb hash cached_image_path = urllib.unquote(image.replace('image://', '')) if cached_image_path.endswith('/'): cached_image_path = cached_image_path[:-1] cached_files = [] for path in [xbmc.getCacheThumbName(cached_image_path), xbmc.getCacheThumbName(image)]: cached_files.append(os.path.join('special://profile/Thumbnails/', path[0], path[:-4] + '.jpg')) cached_files.append(os.path.join('special://profile/Thumbnails/', path[0], path[:-4] + '.png')) cached_files.append(os.path.join('special://profile/Thumbnails/Video/', path[0], path)) for i in range(1, 4): try: ''' Try to get cached image at first ''' for cache in cached_files: if xbmcvfs.exists(cache): try: img = _imageopen(xbmcvfs.translatePath(cache)) return img except Exception as error: kodi_log('Image error: Could not open cached image --> %s' % error, 2) ''' Skin images will be tried to be accessed directly. For all other ones the source will be copied to the addon_data folder to get access. ''' if xbmc.skinHasImage(image): if not image.startswith('special://skin'): image = os.path.join('special://skin/media/', image) try: # in case image is packed in textures.xbt img = _imageopen(xbmcvfs.translatePath(image)) return img except Exception: return '' else: targetfile = os.path.join(targetpath, filename) if not xbmcvfs.exists(targetfile): xbmcvfs.copy(image, targetfile) img = _imageopen(targetfile) return img except Exception as error: kodi_log('Image error: Could not get image for %s (try %d) -> %s' % (image, i, error), 2) xbmc.sleep(500) pass return ''
def is_unaired(self, format_label=u'[COLOR=ffcc0000][I]{}[/I][/COLOR]', check_hide_settings=True): try: if not is_future_timestamp(self.infolabels.get('premiered'), "%Y-%m-%d", 10): return if format_label: self.label = format_label.format(self.label) except Exception as exc: kodi_log(u'Error: {}'.format(exc), 1) if not check_hide_settings: return True return self.unaired_bool()
def wrapper(self, *args, **kwargs): """ Syntactic sugar to time a class function """ timer_a = timer() response = func(self, *args, **kwargs) timer_z = timer() total_time = timer_z - timer_a if total_time > 0.001: timer_name = '{}.{}.'.format(self.__class__.__name__, func_name) timer_name = format_name(timer_name, *args, **kwargs) kodi_log('{}\n{:.3f} sec'.format(timer_name, total_time), 1) return response
def set_params_to_container(self, **kwargs): for k, v in viewitems(kwargs): if not k or not v: continue try: xbmcplugin.setProperty( self.handle, u'Param.{}'.format(k), u'{}'.format(v)) # Set params to container properties except Exception as exc: kodi_log( u'Error: {}\nUnable to set Param.{} to {}'.format( exc, k, v), 1)