def run(self): okno = None try: okno = Window(xbmcgui.getCurrentWindowId()) okno.setProperty('RSS.count', '0') except: pass unread_c = 0 for set in self.sets: self.reader.ReadSet(set, True) for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False log('All sets read from cache') if ui != None and ui.isReady: ui.updateChannelList() changes = False next_update = -1 for set in self.sets: for source in set.sources: last_checking = time.time() - source.lastupdate interval = int(source.updateinterval) * 60 if last_checking > interval: for src in set.sources: for channel in src.channels: unread_c -= channel.unread_count self.reader.ReadSource(source, False) changes = True for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False if next_update == -1 or interval < next_update: next_update = interval elif next_update == -1 or interval - last_checking < next_update: next_update = interval - last_checking if changes: log('Sets updated from URL') if ui != None and ui.isReady: ui.updateChannelList()
def run(self): okno = None try: okno = Window(xbmcgui.getCurrentWindowId()) okno.setProperty('RSS.count', '0'); except: pass unread_c = 0 for set in self.sets: self.reader.ReadSet(set, True) for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False log('All sets read from cache') if ui != None and ui.isReady: ui.updateChannelList() changes = False next_update = -1 for set in self.sets: for source in set.sources: last_checking = time.time() - source.lastupdate interval = int(source.updateinterval) * 60 if last_checking > interval: for src in set.sources: for channel in src.channels: unread_c -= channel.unread_count self.reader.ReadSource(source, False) changes = True for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False if next_update == -1 or interval < next_update: next_update = interval elif next_update == -1 or interval - last_checking < next_update: next_update = interval - last_checking if changes: log('Sets updated from URL') if ui != None and ui.isReady: ui.updateChannelList()
def shutdown(self): """ Stop the background services """ from xbmcgui import Window window_cls = Window(10000) window_cls.setProperty('nf_service_status', 'stopped') for server in self.SERVERS: server['instance'].server_close() server['instance'].shutdown() server['instance'] = None server['thread'].join() server['thread'] = None info('Stopped MSL Service')
def main(): """ Grabs kodi_id and kodi_type and sends a request to our main python instance that context menu needs to be displayed """ window = Window(10000) kodi_id = listitem.getVideoInfoTag().getDbId() if kodi_id == -1: # There is no getDbId() method for getMusicInfoTag # YET TO BE IMPLEMENTED - lookup ID using path kodi_id = listitem.getMusicInfoTag().getURL() kodi_type = _get_kodi_type() args = {'kodi_id': kodi_id, 'kodi_type': kodi_type} while window.getProperty('plex_command'): sleep(20) window.setProperty('plex_command', 'CONTEXT_menu?%s' % urlencode(args))
def run(self): """Main loop. Runs until xbmc.Monitor requests abort""" # pylint: disable=broad-except try: self.start_services() except Exception as exc: from xbmcgui import Window window_cls = Window(10000) window_cls.setProperty('nf_service_status', 'stopped') import traceback from resources.lib.kodi.ui import show_addon_error_info error(traceback.format_exc()) show_addon_error_info(exc) return while not self.controller.abortRequested(): if self._tick_and_wait_for_abort(): break self.shutdown()
def start_services(self): """ Start the background services """ from resources.lib.services.playback.controller import PlaybackController from resources.lib.services.library_updater import LibraryUpdateService from resources.lib.services.settings_monitor import SettingsMonitor for server in self.SERVERS: server['instance'].server_activate() server['instance'].timeout = 1 server['thread'].start() info('[{}] Thread started'.format(server['name'])) self.controller = PlaybackController() self.library_updater = LibraryUpdateService() self.settings_monitor = SettingsMonitor() # Mark the service as active from xbmcgui import Window window_cls = Window(10000) window_cls.setProperty('nf_service_status', 'running') if not g.ADDON.getSettingBool('disable_startup_notification'): from resources.lib.kodi.ui import show_notification show_notification(get_local_string(30110))
class SimpleCache(): ''' Cache version log: 1: Toonmania2 0.4.0 2: Toonmania2 0.4.1 I realized '/GetNew(...)' and '/GetPopular(...)' routes just need the IDs, not the whole JSON data. If something is in a '/GetPopular(...)' it will definitely be in the corresponding '/GetAll(...)'. So we keep only the IDs and retrieve the full entry from the 'All' routes. This change helps use less disk-space and memory. 3: Toonmania2 0.4.2 In this cache version we're using separate cache files for each route the user visited. This way uses we less memory because we only need to load the routes the user wants to go to, not one huge file that has everything, with what the user wants or doesn't want. ''' # Cache version, for future extension. Used with properties saved to disk. CACHE_VERSION = 3 LIFETIME_THREE_DAYS = 72 # 3 days, in hours. LIFETIME_FIVE_DAYS = 120 # 5 days. LIFETIME_ONE_WEEK = 168 # 7 days. LIFETIME_FOREVER = 0 # Never expires. # Path to .../kodi/userdata/addons_data/plugin.video.toonmania2/cache/ -> where the JSON cache files will be. CACHE_PATH_DIR = xbmc.translatePath(xbmcaddon.Addon().getAddonInfo('profile')).decode('utf-8') + 'cache' + osSeparator # Property name pointing to a Python 'set()' of property names. # This is used to quickly tell if a property exists or not by checking its name in the set, # rather than retrieving a property that could be a huge JSON blob just to see that it exists. PROPERTY_DISK_NAMES_SET = 'scache.prop.names' # Property name pointing to a comma-separated list of dirty disk-enabled properties that need saving. # This list is converted to a set when read and used for quick testing. PROPERTY_DIRTY_NAMES_SET = 'scache.prop.dirty' def __init__(self): ''' Initialised at every directory change in Kodi <= 17.6. ''' self.window = Window(getCurrentWindowId()) self.diskNamesSet = None self.dirtyNamesSet = None def setCacheProperty(self, propName, data, saveToDisk, lifetime=72): ''' Creates a persistent XBMC window memory property. :param propName: Name/Identifier the property should have, used to retrieve it later. Needs to be file-system friendly, and cannot have commas in the name. :param data: Data to store in the property, needs to be JSON-serializable. :param saveToDisk: Boolean if this property should be saved to a JSON cache file on disk to be loaded on later sessions. Best used for big collections of web-requested data. :param lifetime: When saving to disk, 'lifetime' specifies how many hours since its creation that the property should have on disk, before being erased. Defaults to 72 hours (3 days). Setting it as '0' (zero) will make it last forever. ''' if saveToDisk: # Create the window memory property. self._storeCacheProperty(propName, data, lifetime, self._getEpochHours()) # Store the name of this disk-enabled property in a set to quickly tell # that it's already in memory and doesn't need to be loaded from disk. self._ensureDiskNamesSet() self.diskNamesSet.add(propName) self._storeMemorySet(self.PROPERTY_DISK_NAMES_SET, self.diskNamesSet) # Add the name of this new property to a set used by saveCacheIfDirty() # to tell which disk-enabled properties need saving to disk. self._ensureDirtyNamesSet() self.dirtyNamesSet.add(propName) self._storeMemorySet(self.PROPERTY_DIRTY_NAMES_SET, self.dirtyNamesSet) else: # A memory-only property. Other fields (lifetime, epoch etc.) are not needed. self.window.setProperty(propName, json.dumps(data)) def setCacheProperties(self, properties, saveToDisk): ''' Convenience function to create several properties at once. :param properties: For disk-enabled properties (saveToDisk=True), it's an iterable where each entry should be a tuple, list or other indexable object of this format: ((str)PROPERTY_NAME, (anything)PROPERTY_DATA, (int)LIFETIME_HOURS) Otherwise (with saveToDisk=False), 'properties' is an iterable of (name, data) pairs. The 'PROPERTY_DATA' or 'data' fields should be JSON-serializable. ''' if saveToDisk: self._ensureDiskNamesSet() self._ensureDirtyNamesSet() for pEntry in properties: propName, data, lifetime = pEntry self._storeCacheProperty(propName, data, lifetime, self._getEpochHours()) self.diskNamesSet.add(propName) self.dirtyNamesSet.add(propName) # These properties are being created \ updated and need saving. self._storeMemorySet(self.PROPERTY_DISK_NAMES_SET, self.diskNamesSet) self._storeMemorySet(self.PROPERTY_DIRTY_NAMES_SET, self.dirtyNamesSet) else: for propName, data in properties: self.window.setProperty(propName, json.dumps(data)) # Memory-only properties. def getCacheProperty(self, propName, readFromDisk): ''' Tries to return the data from a window memory property. For the pure property string use the setRaw(...)\getRaw(...) functions instead. :param propName: Name of the property to retrieve. :param readFromDisk: Used with properties that might be saved on disk. It tries to load from memory first though, then if it's not in memory we load from its file, if it exists. :returns: The property data, if it exists, or None. ''' if readFromDisk: # A disk-enabled property. # If it's already in the disk names set then it was either added manually or # already loaded from disk into memory. self._ensureDiskNamesSet() if propName in self.diskNamesSet: propRaw = self.window.getProperty(propName) return json.loads(propRaw)[0] if propRaw else None # Data is always the first JSON field. else: # Disk-enabled property isn't in memory yet, try to read it from its file. fileProp = self._tryLoadCacheProperty(propName) if fileProp: propName, data, lifetime, epoch = fileProp self._storeCacheProperty(propName, data, lifetime, epoch) self.diskNamesSet.add(propName) self._storeMemorySet(self.PROPERTY_DISK_NAMES_SET, self.diskNamesSet) return data else: return None else: # A memory-only property, points directly to data. propRaw = self.window.getProperty(propName) return json.loads(propRaw) if propRaw else None def getCacheProperties(self, propNames, readFromDisk): ''' Retrieves a **generator** to more than one property data at once. The data is guaranteed to come in the same order as the provided names. ''' if readFromDisk: self._ensureDiskNamesSet() anyLoadedFromFile = False for propName in propNames: if propName in self.diskNamesSet: propRaw = self.window.getProperty(propName) yield json.loads(propRaw)[0] if propRaw else None else: fileProp = self._tryLoadCacheProperty(propName) if fileProp: propName, data, lifetime, epoch = fileProp self._storeCacheProperty(propName, data, lifetime, epoch) self.diskNamesSet.add(propName) anyLoadedFromFile = True yield data if anyLoadedFromFile: self._storeMemorySet(self.PROPERTY_DISK_NAMES_SET, self.diskNamesSet) else: for propName in propNames: propRaw = self.window.getProperty(propName) yield json.loads(propRaw) if propRaw else None # Memory-only property, points directly to data. def clearCacheProperty(self, propName, readFromDisk): ''' Removes a property from memory. The next time the cache is saved, this property won't be included and therefore forgotten. ''' self.window.clearProperty(propName) if readFromDisk: # Direct way to remove the property name from the comma-separated property name list. diskNamesRaw = self.window.getProperty(self.PROPERTY_DISK_NAMES_SET) self.window.setProperty( self.PROPERTY_DISK_NAMES_SET, diskNamesRaw.replace(propName, '').replace(',,', ',').strip(', ') ) dirtyNamesRaw = self.window.getProperty(self.PROPERTY_DIRTY_NAMES_SET) self.window.setProperty( self.PROPERTY_DIRTY_NAMES_SET, dirtyNamesRaw.replace(propName, '').replace(',,', ',').strip(', ') ) def setRawProperty(self, propName, data): ''' Convenience function to set a window memory property that doesn't need JSON serialization or saving to disk. Used for unimportant memory-only properties that should persist between add-on directories. :param propName: The name of the property used to identify the data, later used to retrieve it. :param rawData: String data, stored as it is. ''' self.window.setProperty(propName, data) def getRawProperty(self, propName): ''' Retrieves a direct window property by name. ''' return self.window.getProperty(propName) def clearRawProperty(self, propName): ''' Clears a direct window property by name. To clear a property that was created with setCacheProperty() use clearCacheProperty() instead. ''' return self.window.clearProperty(propName) def saveCacheIfDirty(self): # Optimised way to check if anything needs saving. Most of the time # 'dirtyNamesRaw' will be an empty string, easy to check for truthness. dirtyNamesRaw = self.window.getProperty(self.PROPERTY_DIRTY_NAMES_SET) if dirtyNamesRaw: for propName in dirtyNamesRaw.split(','): self._saveCacheProperty(propName) # Reset the dirty names set (and its window property). self.dirtyNamesSet = set() self.window.setProperty(self.PROPERTY_DIRTY_NAMES_SET, '') def clearCacheFiles(self): dirPaths, filePaths = xbmcvfs.listdir(self.CACHE_PATH_DIR) for filePath in filePaths: self._writeBlankCacheFile(self.CACHE_PATH_DIR + filePath) # Clear the disk names set. All disk-enabled properties will be forgotten. self.window.setProperty(self.PROPERTY_DISK_NAMES_SET, '') self.diskNamesSet = set() # 'True' if one or more cache files were cleared / reset. return len(filePaths) > 0 def _writeBlankCacheFile(self, fullPath): '''Assumes the directory to the file exists.''' file = xbmcvfs.File(fullPath, 'w') file.write('null') # JSON equivalent to None. file.close() def _ensureDiskNamesSet(self): ''' For Kodi <= 17.6, used to initialise this class member in case it's invalid. But this function is only called when this member is needed. ''' if self.diskNamesSet == None: self.diskNamesSet = self._stringToSet(self.window.getProperty(self.PROPERTY_DISK_NAMES_SET)) def _ensureDirtyNamesSet(self): if self.dirtyNamesSet == None: self.dirtyNamesSet = self._stringToSet(self.window.getProperty(self.PROPERTY_DIRTY_NAMES_SET)) def _storeMemorySet(self, setPropName, setObject): self.window.setProperty(setPropName, self._setToString(setObject)) def _tryLoadCacheProperty(self, propName): ''' Tries to load the cache file for the named property. If a cache file doesn't exist for a property, a blank cache file is created. :returns: A tuple of property entries, each entry is a tuple of fields (propName, data, lifetime, epoch). ''' currentEpoch = self._getEpochHours() fullPath = self.CACHE_PATH_DIR + propName + '.json' try: if xbmcvfs.exists(fullPath): file = xbmcvfs.File(fullPath) data = file.read() file.close() if data and data != 'null': fileProp = json.loads(data) # Version restriction. version = fileProp['version'] if version >= self.CACHE_VERSION: lifetime = fileProp['lifetime'] epoch = fileProp['epoch'] # Lifetime restriction. See if the property lasts forever or if # the elapsed time since its creation epoch is bigger than its lifetime. if lifetime == 0 or lifetime >= abs(currentEpoch - epoch): return (fileProp['propName'], fileProp['data'], lifetime, epoch) else: # Initialize a blank cache file. xbmcvfs.mkdir(self.CACHE_PATH_DIR) self._writeBlankCacheFile(fullPath) except: pass return None # Fall-through. def _storeCacheProperty(self, propName, data, lifetime, epoch): ''' Internal. Stores data in a persistent XBMC window memory property. ''' self.window.setProperty(propName, json.dumps((data, lifetime, epoch))) def _saveCacheProperty(self, propName): ''' Internal. Saves a specific dirty, disk-enabled property to disk. Assumes the destination folder already exists. ''' propRaw = self.window.getProperty(propName) if propRaw: # Base structure as in _storeCacheProperty(), with the cache version added. data, lifetime, epoch = json.loads(propRaw) file = xbmcvfs.File(self.CACHE_PATH_DIR + propName + '.json', 'w') file.write( json.dumps( { 'version': self.CACHE_VERSION, 'propName': propName, 'lifetime': lifetime, 'epoch': epoch, 'data': data } ) ) file.close() def _setToString(self, setObject): return ','.join(element for element in setObject) def _stringToSet(self, text): return set(text.split(',')) if text else set() # The IF is to get a truly empty set, else it'd hold an empty string. def _getEpochHours(self): ''' Internal. Gets the current UNIX epoch time in hours. ''' return int(time() // 3600.0)
class NetflixService(object): """ Netflix addon service """ SERVERS = [] HOST_ADDRESS = '127.0.0.1' def __init__(self): self.window_cls = Window(10000) # Kodi home window # If you use multiple Kodi profiles you need to distinguish the property of current profile self.prop_nf_service_status = g.py2_encode( 'nf_service_status_' + get_current_kodi_profile_name()) self.controller = None self.library_updater = None self.settings_monitor = None def init_servers(self): """Initialize the http servers""" try: # Import modules here to intercept possible missing libraries on linux systems from resources.lib.services.msl.http_server import MSLTCPServer from resources.lib.services.nfsession.http_server import NetflixTCPServer from resources.lib.services.cache.http_server import CacheTCPServer # Do not change the init order of the servers, # MSLTCPServer must always be initialized first to get the DRM info self.SERVERS = [{ 'name': 'MSL', 'class': MSLTCPServer, 'instance': None, 'thread': None }, { 'name': 'NS', 'class': NetflixTCPServer, 'instance': None, 'thread': None }, { 'name': 'CACHE', 'class': CacheTCPServer, 'instance': None, 'thread': None }] for server in self.SERVERS: self._init_server(server) return True except Exception as exc: # pylint: disable=broad-except error( 'Background services do not start due to the following error') import traceback error(g.py2_decode(traceback.format_exc(), 'latin-1')) if isinstance(exc, gaierror): message = ( 'Something is wrong in your network localhost configuration.\r\n' 'It is possible that the hostname {} can not be resolved.' ).format(self.HOST_ADDRESS) elif ImportError(exc, ImportError): message = ( 'In your system is missing some required library to run Netflix.\r\n' 'Read how to install the add-on in the GitHub Readme.\r\n' 'Error details: {}'.format(exc)) else: message = unicode(exc) self._set_service_status('error', message) return False def _init_server(self, server): server['class'].allow_reuse_address = True server['instance'] = server['class']( (self.HOST_ADDRESS, select_port(server['name']))) server['thread'] = threading.Thread( target=server['instance'].serve_forever) def start_services(self): """ Start the background services """ from resources.lib.services.playback.action_controller import ActionController from resources.lib.services.library_updater import LibraryUpdateService from resources.lib.services.settings_monitor import SettingsMonitor for server in self.SERVERS: server['instance'].server_activate() server['instance'].timeout = 1 server['thread'].start() info('[{}] Thread started'.format(server['name'])) self.controller = ActionController() self.library_updater = LibraryUpdateService() self.settings_monitor = SettingsMonitor() # Mark the service as active self._set_service_status('running') if not g.ADDON.getSettingBool('disable_startup_notification'): from resources.lib.kodi.ui import show_notification show_notification(get_local_string(30110)) def shutdown(self): """ Stop the background services """ self._set_service_status('stopped') for server in self.SERVERS: server['instance'].shutdown() server['instance'].server_close() server['instance'] = None server['thread'].join() server['thread'] = None info('Stopped MSL Service') def run(self): """Main loop. Runs until xbmc.Monitor requests abort""" try: self.start_services() except Exception as exc: # pylint: disable=broad-except self._set_service_status('stopped') import traceback from resources.lib.kodi.ui import show_addon_error_info error(g.py2_decode(traceback.format_exc(), 'latin-1')) show_addon_error_info(exc) return while not self.controller.abortRequested(): if self._tick_and_wait_for_abort(): break self.shutdown() def _tick_and_wait_for_abort(self): try: self.controller.on_service_tick() self.library_updater.on_service_tick() g.CACHE_MANAGEMENT.on_service_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification error(g.py2_decode(traceback.format_exc(), 'latin-1')) show_notification(': '.join( (exc.__class__.__name__, unicode(exc)))) return self.controller.waitForAbort(1) def _set_service_status(self, status, message=None): """Save the service status to a Kodi property""" from json import dumps status = {'status': status, 'message': message} self.window_cls.setProperty(self.prop_nf_service_status, dumps(status))
ImgBoxDisk = None ImgBoxDisk = None filename = xbmc.translatePath("special://profile/addon_data/service.diskfree/diskfree.png") #filename = 'special://home/henri/.xbmc/scripts/test.png' ImgBoxDiskId = None ImgBoxDiskId = None #start_time = time.time() start_time = 0 re_added_control = False #Recupère les arguments envoyés par le skin qui a lancé le script for arg in sys.argv: param = str(arg).lower() debug("param = %s " % param) if 'disk=' in param: disk = param.replace('disk=', '') #On récupère l'ID de la fenêtre de skin qui à lancer le script okno = Window(xbmcgui.getCurrentWindowId()) #On recupere les parametres des disques #time = time.time() freespace = freespace_disk('/home/') totalspace = totalspace_disk('/home') diskfree = ('%iGB') % (((freespace / 1024) / 1024) / 1024) freespace = (((freespace / 1024) / 1024) / 1024) totalspace = (((totalspace / 1024) / 1024) / 1024) percent = totalspace and (totalspace - freespace) * 1.0 / totalspace or 0.0 debug( "f = %s, t = %s Pou = %s " % (freespace,totalspace,percent)) okno.setProperty('DISK' , '%s Go' % freespace )
class NetflixService(object): """ Netflix addon service """ from resources.lib.services.msl.http_server import MSLTCPServer from resources.lib.services.nfsession.http_server import NetflixTCPServer SERVERS = [ { 'name': 'MSL', 'class': MSLTCPServer, 'instance': None, 'thread': None }, { 'name': 'NS', 'class': NetflixTCPServer, 'instance': None, 'thread': None }, ] def __init__(self): self.window_cls = Window(10000) # Kodi home window # If you use multiple Kodi profiles you need to distinguish the property of current profile self.prop_nf_service_status = g.py2_encode( 'nf_service_status_' + get_current_kodi_profile_name()) for server in self.SERVERS: self.init_server(server) self.controller = None self.library_updater = None self.settings_monitor = None def init_server(self, server): server['class'].allow_reuse_address = True server['instance'] = server['class']( ('127.0.0.1', select_port(server['name']))) server['thread'] = threading.Thread( target=server['instance'].serve_forever) def start_services(self): """ Start the background services """ from resources.lib.services.playback.controller import PlaybackController from resources.lib.services.library_updater import LibraryUpdateService from resources.lib.services.settings_monitor import SettingsMonitor for server in self.SERVERS: server['instance'].server_activate() server['instance'].timeout = 1 server['thread'].start() info('[{}] Thread started'.format(server['name'])) self.controller = PlaybackController() self.library_updater = LibraryUpdateService() self.settings_monitor = SettingsMonitor() # Mark the service as active self.window_cls.setProperty(self.prop_nf_service_status, 'running') if not g.ADDON.getSettingBool('disable_startup_notification'): from resources.lib.kodi.ui import show_notification show_notification(get_local_string(30110)) def shutdown(self): """ Stop the background services """ self.window_cls.setProperty(self.prop_nf_service_status, 'stopped') for server in self.SERVERS: server['instance'].shutdown() server['instance'].server_close() server['instance'] = None server['thread'].join() server['thread'] = None info('Stopped MSL Service') def run(self): """Main loop. Runs until xbmc.Monitor requests abort""" # pylint: disable=broad-except try: self.start_services() except Exception as exc: self.window_cls.setProperty(self.prop_nf_service_status, 'stopped') import traceback from resources.lib.kodi.ui import show_addon_error_info error(traceback.format_exc()) show_addon_error_info(exc) return while not self.controller.abortRequested(): if self._tick_and_wait_for_abort(): break self.shutdown() def _tick_and_wait_for_abort(self): try: self.controller.on_playback_tick() self.library_updater.on_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification error(traceback.format_exc()) show_notification(': '.join( (exc.__class__.__name__, unicode(exc)))) return self.controller.waitForAbort(1)
#sql_tvshowlinkepisode = "select idshow from tvshowlinkepisode where idEpisode=%u" % ( fields[ 13 ], ) # format our records start and end xbmc.executehttpapi( "SetResponseFormat(OpenRecord,%s)" % ( "<record>", ) ) xbmc.executehttpapi( "SetResponseFormat(CloseRecord,%s)" % ( "</record>", ) ) # query the database movies_xml = xbmc.executehttpapi( "QueryVideoDatabase(%s)" % quote_plus( sql_movies ), ) episodes_xml = xbmc.executehttpapi( "QueryVideoDatabase(%s)" % quote_plus( sql_episodes ), ) # separate the records movies = re.findall( "<record>(.+?)</record>", movies_xml, re.DOTALL ) episodes = re.findall( "<record>(.+?)</record>", episodes_xml, re.DOTALL ) # enumerate thru our records and set our properties for count, movie in enumerate( movies ): # separate individual fields fields = re.findall( "<field>(.*?)</field>", movie, re.DOTALL ) # set title WINDOW.setProperty( "LatestMovie.%d.Label" % ( count + 1, ), fields[ 1 ] ) # set year WINDOW.setProperty( "LatestMovie.%d.Label2" % ( count + 1, ), fields[ 8 ] ) # set running time (uncomment below if you want running time for label2) #WINDOW.setProperty( "LatestMovie.%d.Label2" % ( count + 1, ), fields[ 12 ] ) # set path WINDOW.setProperty( "LatestMovie.%d.OnClick" % ( count + 1, ), fields[ 24 ] + fields[ 23 ] ) # set thumbnail thumbnail = xbmc.getCacheThumbName( fields[ 24 ] + fields[ 23 ] ) WINDOW.setProperty( "LatestMovie.%d.Thumb" % ( count + 1, ), "U:/userdata/Thumbnails/Video/%s/%s" % ( thumbnail[ 0 ], thumbnail, ) ) # enumerate thru our records and set our properties for count, episode in enumerate( episodes ): # separate individual fields fields = re.findall( "<field>(.*?)</field>", episode, re.DOTALL ) # set title WINDOW.setProperty( "LatestEpisode.%d.Label" % ( count + 1, ), fields[ 1 ] )
from xbmc import translatePath, sleep, log, LOGERROR from xbmcgui import Window _ADDON = Addon(id='plugin.video.plexkodiconnect') try: _ADDON_PATH = _ADDON.getAddonInfo('path').decode('utf-8') except TypeError: _ADDON_PATH = _ADDON.getAddonInfo('path').decode() try: _BASE_RESOURCE = translatePath( os_path.join(_ADDON_PATH, 'resources', 'lib')).decode('utf-8') except TypeError: _BASE_RESOURCE = translatePath( os_path.join(_ADDON_PATH, 'resources', 'lib')).decode() sys_path.append(_BASE_RESOURCE) from pickler import unpickle_me, pickl_window ############################################################################### if __name__ == "__main__": WINDOW = Window(10000) while WINDOW.getProperty('plex_command'): sleep(20) WINDOW.setProperty('plex_command', 'CONTEXT_menu') while not pickl_window('plex_result'): sleep(50) RESULT = unpickle_me() if RESULT is None: log('PLEX.%s: Error encountered, aborting' % __name__, level=LOGERROR)
try: _addon_path = _addon.getAddonInfo('path').decode('utf-8') except TypeError: _addon_path = _addon.getAddonInfo('path').decode() try: _base_resource = translatePath(os_path.join( _addon_path, 'resources', 'lib')).decode('utf-8') except TypeError: _base_resource = translatePath(os_path.join( _addon_path, 'resources', 'lib')).decode() sys_path.append(_base_resource) from pickler import unpickle_me, pickl_window ############################################################################### if __name__ == "__main__": win = Window(10000) while win.getProperty('plex_command'): sleep(20) win.setProperty('plex_command', 'CONTEXT_menu') while not pickl_window('plex_result'): sleep(50) result = unpickle_me() if result is None: log('PLEX.%s: Error encountered, aborting' % __name__, level=LOGERROR)
#On récupère l'ID de la fenêtre de skin qui à lancer le script okno = Window(xbmcgui.getCurrentWindowId()) #Nb de news dans le flux NbNews = len(headlines) #Si il est > à la limite demandée, on ne récupére que limit if limit > NbNews: limit = NbNews for i in range(0, limit): #On défini les Properties d'apres headlines #0 => title, 1 => date, 2=> description, #3 => content_type, 4 =>img_name, #5 => link_video, 6 => NoNews #7 => ImageCount, 8 => SlideShowable debug('%sRSS.%s.Title' % (prefix,i)) okno.setProperty('%sRSS.%s.Title' % (prefix,i) , headlines[i][0] ) okno.setProperty('%sRSS.%s.Date' % (prefix,i) , headlines[i][1]) description = re.sub('(<[bB][rR][ /]>)|(<[/ ]*[pP]>)', '[CR]', headlines[i][2], re.DOTALL) #On nettoie le code HTML html = cleanText(description) okno.setProperty('%sRSS.%s.Desc' % (prefix, i) , html) okno.setProperty('%sRSS.%s.Image' % (prefix, i) , headlines[i][4]) ##Traitement des liens YT link_video = headlines[i][5] if 'youtube.com/v' in link_video: vid_ids = re.findall('http://www.youtube.com/v/(.{11})\??', link_video, re.DOTALL ) for id in vid_ids: link_video = 'plugin://plugin.video.youtube/?action=play_video&videoid=%s' % id debug("VIDEO YT = %s " % link_video)
#On récupère l'ID de la fenêtre de skin qui à lancer le script okno = Window(xbmcgui.getCurrentWindowId()) #Nb de news dans le flux NbNews = len(headlines) #Si il est > à la limite demandée, on ne récupére que limit if limit > NbNews: limit = NbNews for i in range(0, limit): #On défini les Properties d'apres headlines #0 => title, 1 => date, 2=> description, #3 => content_type, 4 =>img_name, #5 => link_video, 6 => NoNews #7 => ImageCount, 8 => SlideShowable debug('%sRSS.%s.Title' % (prefix, i)) okno.setProperty('%sRSS.%s.Title' % (prefix, i), headlines[i][0]) okno.setProperty('%sRSS.%s.Date' % (prefix, i), headlines[i][1]) description = re.sub('(<[bB][rR][ /]>)|(<[/ ]*[pP]>)', '[CR]', headlines[i][2], re.DOTALL) #On nettoie le code HTML html = cleanText(description) okno.setProperty('%sRSS.%s.Desc' % (prefix, i), html) okno.setProperty('%sRSS.%s.Image' % (prefix, i), headlines[i][4]) ##Traitement des liens YT link_video = headlines[i][5] if 'youtube.com/v' in link_video: vid_ids = re.findall('http://www.youtube.com/v/(.{11})\??', link_video, re.DOTALL) for id in vid_ids: link_video = 'plugin://plugin.video.youtube/?action=play_video&videoid=%s' % id debug("VIDEO YT = %s " % link_video)
class ProgressManager(PlaybackActionManager): """Detect the progress of the played video and send the data to the netflix service""" def __init__(self): # pylint: disable=super-on-old-class super(ProgressManager, self).__init__() self.event_data = {} self.is_event_start_sent = False self.last_tick_count = 0 self.tick_elapsed = 0 self.last_player_state = {} self.is_player_in_pause = False self.lock_events = False self.window_cls = Window(10000) # Kodi home window def _initialize(self, data): if not data['event_data']: common.warn('ProgressManager: disabled due to no event data') self.enabled = False return self.event_data = data['event_data'] def _on_tick(self, player_state): if self.lock_events: return if self.is_player_in_pause and (self.tick_elapsed - self.last_tick_count) >= 1800: # When the player is paused for more than 30 minutes we interrupt the sending of events (1800secs=30m) _send_event(EVENT_ENGAGE, self.event_data, self.last_player_state) _send_event(EVENT_STOP, self.event_data, self.last_player_state) self.is_event_start_sent = False self.lock_events = True else: if not self.is_event_start_sent: # We do not use _on_playback_started() to send EVENT_START, because StreamContinuityManager # and ResumeManager may cause inconsistencies with the content of player_state data # When the playback starts for the first time, for correctness should send elapsed_seconds value to 0 if self.tick_elapsed < 5 and self.event_data[ 'resume_position'] is None: player_state['elapsed_seconds'] = 0 _send_event(EVENT_START, self.event_data, player_state) self.is_event_start_sent = True self.tick_elapsed = 0 else: # Generate events to send to Netflix service every 1 minute (60secs=1m) if (self.tick_elapsed - self.last_tick_count) >= 60: _send_event(EVENT_KEEP_ALIVE, self.event_data, player_state) self.last_tick_count = self.tick_elapsed # On Kodi we can save every second instead every minute, but only after the first minute if self.last_tick_count: self._save_resume_time(player_state['elapsed_seconds']) self.last_player_state = player_state self.tick_elapsed += 1 # One tick almost always represents one second def on_playback_pause(self, player_state): if not self.is_event_start_sent: return self.tick_elapsed = 0 self.is_player_in_pause = True _send_event(EVENT_ENGAGE, self.event_data, player_state) def on_playback_resume(self, player_state): self.is_player_in_pause = False self.lock_events = False def on_playback_seek(self, player_state): if not self.is_event_start_sent or self.lock_events: # This might happen when ResumeManager skip is performed return self.tick_elapsed = 0 _send_event(EVENT_ENGAGE, self.event_data, player_state) def _on_playback_stopped(self): if not self.is_event_start_sent or self.lock_events: return self.tick_elapsed = 0 _send_event(EVENT_ENGAGE, self.event_data, self.last_player_state) _send_event(EVENT_STOP, self.event_data, self.last_player_state) def _save_resume_time(self, resume_time): """Save resume time in order to modify the frontend cache""" # Why this, the video lists are requests to the web service only once and then will be cached in order to # quickly get the data and speed up a lot the GUI response. # Watched status of a (video) list item is based on resume time, and the resume time is saved in the cache data. # To avoid slowing down the GUI by invalidating the cache to get new data from website service, one solution is # modify the cache data. # Altering here the cache on the fly is not possible because it is currently not shared between service-frontend # therefore we save the value in a Kodi property and we will modify the cache from addon frontend. # The choice to save the value in a Kodi property is to not continuously lock with mutex the database. # The callback _on_playback_stopped can not be used, because the loading of frontend happen before. self.window_cls.setProperty('nf_playback_resume_time', str(resume_time))
def Read(sets, reader): okno = None try: if isWindow: if ID == -1: okno = Window(xbmcgui.getCurrentWindowId()) print ' >> WINDOW ID = %i' % xbmcgui.getCurrentWindowId() else: okno = Window(ID) else: if ID == -1: okno = WindowDialog(xbmcgui.getCurrentWindowDialogId()) else: okno = WindowDialog(ID) okno.setProperty('RSS.count', '0'); except: pass unread_c = 0 for set in sets: reader.ReadSet(set, True) for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False log('All sets read from cache') changes = False next_update = -1 for set in sets: for source in set.sources: last_checking = time.time() - source.lastupdate interval = int(source.updateinterval) * 60 if last_checking > interval: for src in set.sources: for channel in src.channels: unread_c -= channel.unread_count reader.ReadSource(source, False) changes = True for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False if next_update == -1 or interval < next_update: next_update = interval elif next_update == -1 or interval - last_checking < next_update: next_update = interval - last_checking if changes: log('Sets updated from URL') #items = sorted(self.items, cmp=DateCompare, reverse = self.sortdesc ) try: okno.setProperty( "unread_rss", str(unread_c) ) setting_script = xbmc.translatePath('special://home/addons/script.rssclient/set_properties.py') okno.setProperty('SettingScript', setting_script) itemList = [] for set in sets: for source in set.sources: for channel in source.channels: for item in channel.items: itemList.append(item) c = 1 items = sorted(itemList, cmp=DateCompare, reverse = False ) for item in items: if c <= limit: okno.setProperty('%sRSS.%d.Title' % (prefix, c), item.title) okno.setProperty('%sRSS.%d.Desc' % (prefix, c), cleanText(item.description)) if len(item.image) > 0: okno.setProperty('%sRSS.%d.Image' % (prefix, c), item.image[0]) else: okno.setProperty('%sRSS.%d.Image' % (prefix, c), '') okno.setProperty('%sRSS.%d.ImageCount' % (prefix, c), str(len(item.image)) ) okno.setProperty('%sRSS.%d.SlideShowable' % (prefix, c), (( (imageCachingEnabled and len(item.image) > 1) and ['true'] or ['false'])[0])) okno.setProperty('%sRSS.%d.MultiImagePath' % (prefix, c), item.multiimagepath) i = 1 sep_images = '' for image in item.image: okno.setProperty('%sRSS.%d.Image.%d' % (prefix, c,i), image) if len(sep_images) > 0: sep_images = sep_images + ';' sep_images = sep_images + image i = i + 1 okno.setProperty('%sRSS.%d.MultiImages' % (prefix, c), sep_images) if len(item.video) > 1: okno.setProperty('%sRSS.%d.Media' % (prefix, c), item.video) else: okno.setProperty('%sRSS.%d.Media' % (prefix, c), '') okno.setProperty('%sRSS.%d.Date' % (prefix, c), item.date.replace(',', '.')) if item.channel != None: okno.setProperty('%sRSS.%d.Channel' % (prefix, c), item.channel.title) c = c + 1; okno.setProperty('%sRSS.count' % prefix, str(c)); except: pass if alarmEnabled and next_update > -1: alarmhash = xbmc.getCacheThumbName(args + str(time.localtime())).replace('.tbn', '') napis = 'AlarmClock(RSS_CHECK_%s,XBMC.RunScript(script.rssclient%s),%d,True)' % (alarmhash, args, ((next_update+60) / 60.0)) log('Refresh in %d minutes' % ((next_update+60) / 60.0)) xbmc.executebuiltin(napis)
from urllib import urlencode from xbmc import getCondVisibility, sleep from xbmcgui import Window ############################################################################### def _get_kodi_type(): kodi_type = listitem.getVideoInfoTag().getMediaType().decode('utf-8') if not kodi_type: if getCondVisibility('Container.Content(albums)'): kodi_type = "album" elif getCondVisibility('Container.Content(artists)'): kodi_type = "artist" elif getCondVisibility('Container.Content(songs)'): kodi_type = "song" elif getCondVisibility('Container.Content(pictures)'): kodi_type = "picture" return kodi_type if __name__ == "__main__": WINDOW = Window(10000) KODI_ID = listitem.getVideoInfoTag().getDbId() KODI_TYPE = _get_kodi_type() ARGS = {'kodi_id': KODI_ID, 'kodi_type': KODI_TYPE} while WINDOW.getProperty('plex_command'): sleep(20) WINDOW.setProperty('plex_command', 'CONTEXT_menu?%s' % urlencode(ARGS))
prefix = '' for arg in sys.argv: param = arg.lower() if 'prefix=' in param: prefix = param.replace('prefix=', '') if not prefix.endswith('.'): prefix = prefix + '.' elif 'id=' in param: RSSid = param.replace('id=', '') if RSSid > -1: okno = Window(xbmcgui.getCurrentWindowId()) okno.setProperty('RSS.Title', okno.getProperty('%sRSS.%s.Title' % (prefix, RSSid) )) okno.setProperty('RSS.Desc', okno.getProperty('%sRSS.%s.Desc' % (prefix, RSSid) )) okno.setProperty('RSS.Image', okno.getProperty('%sRSS.%s.Image' % (prefix, RSSid) )) okno.setProperty('RSS.Date', okno.getProperty('%sRSS.%s.Date' % (prefix, RSSid) )) okno.setProperty('RSS.Channel', okno.getProperty('%sRSS.%s.Channel' % (prefix, RSSid))) okno.setProperty('RSS.Media', okno.getProperty('%sRSS.%s.Media' % (prefix, RSSid))) okno.setProperty('RSS.MultiImagePath', okno.getProperty('%sRSS.%s.MultiImagePath' % (prefix, RSSid))) okno.setProperty('RSS.SlideShowable', okno.getProperty('%sRSS.%s.SlideShowable' % (prefix, RSSid))) okno.setProperty('RSS.ID', RSSid) i_count = okno.getProperty('%sRSS.%s.ImageCount' % (prefix, RSSid)) okno.setProperty('RSS.ImageCount', i_count) for i in range(1, int(i_count)+1): okno.setProperty('RSS.Image.%d' % i, okno.getProperty('%sRSS.%s.Image.%d' % (prefix, RSSid, i)))
def Read(sets, reader): okno = None try: if isWindow: if ID == -1: okno = Window(xbmcgui.getCurrentWindowId()) print ' >> WINDOW ID = %i' % xbmcgui.getCurrentWindowId() else: okno = Window(ID) else: if ID == -1: okno = WindowDialog(xbmcgui.getCurrentWindowDialogId()) else: okno = WindowDialog(ID) okno.setProperty('RSS.count', '0') except: pass unread_c = 0 for set in sets: reader.ReadSet(set, True) for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False log('All sets read from cache') changes = False next_update = -1 for set in sets: for source in set.sources: last_checking = time.time() - source.lastupdate interval = int(source.updateinterval) * 60 if last_checking > interval: for src in set.sources: for channel in src.channels: unread_c -= channel.unread_count reader.ReadSource(source, False) changes = True for src in set.sources: for channel in src.channels: unread_c += channel.unread_count for item in channel.items: if already_read.count(item.link) > 0: item.read = True else: channel.unread_count += 1 channel.read = False if next_update == -1 or interval < next_update: next_update = interval elif next_update == -1 or interval - last_checking < next_update: next_update = interval - last_checking if changes: log('Sets updated from URL') #items = sorted(self.items, cmp=DateCompare, reverse = self.sortdesc ) try: okno.setProperty("unread_rss", str(unread_c)) setting_script = xbmc.translatePath( 'special://home/addons/script.rssclient/set_properties.py') okno.setProperty('SettingScript', setting_script) itemList = [] for set in sets: for source in set.sources: for channel in source.channels: for item in channel.items: itemList.append(item) c = 1 items = sorted(itemList, cmp=DateCompare, reverse=False) for item in items: if c <= limit: okno.setProperty('%sRSS.%d.Title' % (prefix, c), item.title) okno.setProperty('%sRSS.%d.Desc' % (prefix, c), cleanText(item.description)) if len(item.image) > 0: okno.setProperty('%sRSS.%d.Image' % (prefix, c), item.image[0]) else: okno.setProperty('%sRSS.%d.Image' % (prefix, c), '') okno.setProperty('%sRSS.%d.ImageCount' % (prefix, c), str(len(item.image))) okno.setProperty( '%sRSS.%d.SlideShowable' % (prefix, c), (((imageCachingEnabled and len(item.image) > 1) and ['true'] or ['false'])[0])) okno.setProperty('%sRSS.%d.MultiImagePath' % (prefix, c), item.multiimagepath) i = 1 sep_images = '' for image in item.image: okno.setProperty('%sRSS.%d.Image.%d' % (prefix, c, i), image) if len(sep_images) > 0: sep_images = sep_images + ';' sep_images = sep_images + image i = i + 1 okno.setProperty('%sRSS.%d.MultiImages' % (prefix, c), sep_images) if len(item.video) > 1: okno.setProperty('%sRSS.%d.Media' % (prefix, c), item.video) else: okno.setProperty('%sRSS.%d.Media' % (prefix, c), '') okno.setProperty('%sRSS.%d.Date' % (prefix, c), item.date.replace(',', '.')) if item.channel != None: okno.setProperty('%sRSS.%d.Channel' % (prefix, c), item.channel.title) c = c + 1 okno.setProperty('%sRSS.count' % prefix, str(c)) except: pass if alarmEnabled and next_update > -1: alarmhash = xbmc.getCacheThumbName(args + str(time.localtime())).replace( '.tbn', '') napis = 'AlarmClock(RSS_CHECK_%s,XBMC.RunScript(script.rssclient%s),%d,True)' % ( alarmhash, args, ((next_update + 60) / 60.0)) log('Refresh in %d minutes' % ((next_update + 60) / 60.0)) xbmc.executebuiltin(napis)