def __init__(self): log("Setting up fileops") self.settings = settings() self.settings._get_general() self._exists = lambda path: xbmcvfs.exists(path) self._rmdir = lambda path: xbmcvfs.rmdir(path) self._mkdir = lambda path: xbmcvfs.mkdir(path) self._delete = lambda path: xbmcvfs.delete(path) self.downloadcount = 0 self.tempdir = os.path.join(utils.__addonprofile__, 'temp') if not self._exists(self.tempdir): if not self._exists(utils.__addonprofile__): if not self._mkdir(utils.__addonprofile__): raise CreateDirectoryError(utils.__addonprofile__) if not self._mkdir(self.tempdir): raise CreateDirectoryError(self.tempdir)
def initial_vars(self): providers = provider.get_providers() self.settings = settings() self.filters = apply_filters() self.movie_providers = providers['movie_providers'] self.tv_providers = providers['tv_providers'] self.musicvideo_providers = providers['musicvideo_providers'] self.download_counter = {} self.download_counter['Total Artwork'] = 0 self.reportdata = '[B]Artwork Downloader:[/B]' self.mediatype = '' self.medianame = '' self.mediapath = '' self.dbid = '' self.mode = '' self.silent = '' self.gui_selected_type = '' self.failed_items = [] self.download_list = [] self.download_art_succes = False
def run(): addon = constants.addon settings_ = settings.settings(addon) user_agent = settings_.getSetting('user_agent') PLUGIN_URL = constants.PLUGIN_NAME PLUGIN_NAME = constants.PLUGIN_NAME port = int(settings_.getSettingInt('stream_port', 8011) ) server = streamer.MyHTTPServer( ('', port), streamer.myStreamer) server.setDetails(plugin_handle, PLUGIN_NAME, PLUGIN_URL, addon, user_agent, settings_) monitor = xbmc.Monitor() thread = threading.Thread(None, server.run) thread.start() while not monitor.abortRequested(): if monitor.waitForAbort(1): break server.socket.close() server.shutdown() thread.join() # class KodiServer: # def __init__(self): # addon = constants.addon # settings_ = settings.settings(addon) # user_agent = settings_.getSetting('user_agent') # PLUGIN_URL = constants.PLUGIN_NAME # PLUGIN_NAME = constants.PLUGIN_NAME # port = int(settings_.getSettingInt('stream_port', 8011) ) # server = streamer.MyHTTPServer( ('', port), streamer.myStreamer) # server.setDetails(plugin_handle, PLUGIN_NAME, PLUGIN_URL, addon, user_agent, settings_) # def terminate(self): # self.server.socket.close() # self.server.shutdown()
def autostart(): xbmcaddon.Addon().setSetting(id="files_overwrite", value="false") setting = settings() setting._get_general() tempdir = os.path.join(__addonprofile__, "temp") service_runtime = str(setting.service_runtime + ":00") log("## Service - Run at startup: %s" % setting.service_startup, xbmc.LOGNOTICE) log("## Service - Delayed startup: %s minutes" % setting.service_startupdelay, xbmc.LOGNOTICE) log("## Service - Run as service: %s" % setting.service_enable, xbmc.LOGNOTICE) log("## Service - Time: %s" % service_runtime, xbmc.LOGNOTICE) log("##########........................") # Check if tempdir exists and remove it if xbmcvfs.exists(tempdir): xbmcvfs.rmdir(tempdir) log("Removing temp folder from previous aborted run.") xbmc.sleep(5000) # Run script when enabled and check on existence of tempdir. # This because it is possible that script was running even when we previously deleted it. # Could happen when switching profiles and service gets triggered again if setting.service_startup and not xbmcvfs.exists(tempdir): xbmc.executebuiltin( "XBMC.AlarmClock(ArtworkDownloader,XBMC.RunScript(script.artwork.downloader,silent=true),00:%s:15,silent)" % setting.service_startupdelay ) if setting.service_enable: while not xbmc.abortRequested: xbmc.sleep(5000) if not (time.strftime("%H:%M") == service_runtime): pass else: if not xbmcvfs.exists(tempdir): log("Time is %s:%s, Scheduled run starting" % (time.strftime("%H"), time.strftime("%M"))) xbmc.executebuiltin("XBMC.RunScript(script.artwork.downloader,silent=true)") # Because we now use the commoncache module the script is run so fast it is possible it is started twice # within the one minute window. So keep looping until it goes out of that window while not xbmc.abortRequested and time.strftime("%H:%M") == service_runtime: xbmc.sleep(5000) else: log("Addon already running, scheduled run aborted", xbmc.LOGNOTICE)
import time import xbmc from resources.lib import settings import constants addon = constants.addon settingsModule = settings.settings(addon) class gPlayer(xbmc.Player): def __init__(self, *args, **kwargs): xbmc.Player.__init__(self) self.dbID = kwargs["dbID"] self.dbType = kwargs["dbType"] self.movieWatchTime = settingsModule.movieWatchTime self.tvWatchTime = settingsModule.tvWatchTime self.isExit = False self.videoDuration = None if self.dbType == "movie": self.isMovie = True self.markedWatched = self.movieWatchTime else: self.isMovie = False self.markedWatched = self.tvWatchTime def onPlayBackStarted(self): while not self.videoDuration: try:
#global variables PLUGIN_URL = sys.argv[0] plugin_handle = None plugin_queries = None try: plugin_handle = int(sys.argv[1]) plugin_queries = settings.parse_query(sys.argv[2][1:]) except: pass addon_dir = xbmc.translatePath(addon.getAddonInfo('path')) kodi_common.debugger() # cloudservice - create settings module settings = settings.settings(addon) # retrieve settings user_agent = settings.getSetting('user_agent') #obsolete, replace, revents audio from streaming #if user_agent == 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)': # addon.setSetting('user_agent', 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0') instanceName = addon_parameters.PLUGIN_NAME + str( settings.getSetting('account_default', 1)) service = cloudservice2(PLUGIN_URL, addon, instanceName, user_agent, settings) # must load after all other (becomes blocking) # streamer if service is not None and service.settings.streamer: from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
def __init__(self): xbmc.Monitor.__init__(self) self.settingsModule = settings.settings(constants.addon) self.getSettings()
def get_image_list(self,media_item): self.settings = settings() self.settings._get_general() # Get settings from settings.xml self.settings._get_artwork() # Get settings from settings.xml self.settings._get_limit() # Get settings from settings.xml self.settings._vars() # Get some settings vars self.settings._artype_list() # Fill out the GUI and Arttype lists with enabled options image_list = [] target_extrafanartdirs = [] target_extrathumbsdirs = [] target_artworkdir = [] for item in media_item['path']: target_artworkdir = os.path.join(item + '/').replace('BDMV','').replace('VIDEO_TS','') target_extrafanartdirs = os.path.join(item + 'extrafanart' + '/') target_extrathumbsdirs = os.path.join(item + 'extrathumbs' + '/') break file_list = xbmcvfs.listdir(target_artworkdir)[1] ### Processes the bulk mode downloading of files i = 0 j = 0 for item in self.settings.available_arttypes: if item['bulk_enabled'] and media_item['mediatype'] == item['media_type']: #log('finding: %s, arttype counter: %s'%(item['art_type'], j)) j += 1 # File checking if item['art_type'] == 'extrafanart': i += 1 extrafanart_file_list = '' if xbmcvfs.exists(target_extrafanartdirs): extrafanart_file_list = xbmcvfs.listdir(extrafanart_dir)[1] #log('list of extrafanart files: %s'%file_list) #log('extrafanart found: %s'%len(file_list)) if len(extrafanart_file_list) <= self.settings.limit_extrafanart_max: i += 1 elif item['art_type'] == 'extrathumbs': i += 1 extrathumbs_file_list = '' if xbmcvfs.exists(target_extrathumbsdirs): extrathumbs_file_list = xbmcvfs.listdir(extrathumbs_dir)[1] #log('list of extrathumbs files: %s'%file_list) #log('extrathumbs found: %s'%len(file_list)) if len(extrathumbs_file_list) <= self.settings.limit_extrathumbs_max: i += 1 elif item['art_type'] in ['seasonposter']: for season in media_item['seasons']: if season == '0': filename = "season-specials-poster.jpg" elif season == 'all': filename = "season-all-poster.jpg" else: filename = (item['filename'] % int(season)) if filename in file_list: url = os.path.join(target_artworkdir, filename).encode('utf-8') i += 1 generalinfo = '%s: %s | ' %( __localize__(32141), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32144), season) generalinfo += '%s: %s | ' %( __localize__(32143), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32145), 'n/a') # Fill list #log ('found: %s'%url) image_list.append({'url': url, 'preview': url, 'id': filename, 'type': [item['art_type']], 'size': '0', 'season': season, 'language': 'EN', 'votes': '0', 'generalinfo': generalinfo}) else: pass elif item['art_type'] in ['seasonbanner']: for season in media_item['seasons']: if season == '0': filename = "season-specials-banner.jpg" elif season == 'all': filename = "season-all-banner.jpg" else: filename = (item['filename'] % int(season)) if filename in file_list: url = os.path.join(target_artworkdir, filename).encode('utf-8') i += 1 generalinfo = '%s: %s | ' %( __localize__(32141), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32144), season) generalinfo += '%s: %s | ' %( __localize__(32143), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32145), 'n/a') # Fill list #log ('found: %s'%url) image_list.append({'url': url, 'preview': url, 'id': filename, 'type': [item['art_type']], 'size': '0', 'season': season, 'language': 'EN', 'votes': '0', 'generalinfo': generalinfo}) else: pass elif item['art_type'] in ['seasonlandscape']: for season in media_item['seasons']: if season == 'all' or season == '': filename = "season-all-landscape.jpg" else: filename = (item['filename'] % int(season)) if filename in file_list: url = os.path.join(target_artworkdir, filename).encode('utf-8') i += 1 generalinfo = '%s: %s | ' %( __localize__(32141), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32144), season) generalinfo += '%s: %s | ' %( __localize__(32143), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32145), 'n/a') # Fill list log ('found: %s'%url) image_list.append({'url': url, 'preview': url, 'id': filename, 'type': [item['art_type']], 'size': '0', 'season': season, 'language': 'EN', 'votes': '0', 'generalinfo': generalinfo}) else: pass else: filename = item['filename'] if filename in file_list: url = os.path.join(target_artworkdir, filename).encode('utf-8') i += 1 generalinfo = '%s: %s | ' %( __localize__(32141), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32143), 'n/a') generalinfo += '%s: %s | ' %( __localize__(32145), 'n/a') # Fill list #log ('found: %s'%url) image_list.append({'url': url, 'preview': url, 'id': filename, 'type': [item['art_type']], 'size': '0', 'season': 'n/a', 'language': 'EN', 'votes': '0', 'generalinfo': generalinfo}) log('total local files needed: %s'%j) log('total local files found: %s'%i) if j > i: #log('scan providers for more') scan_more = True else: #log('don''t scan for more') scan_more = False if image_list == []: return image_list, scan_more else: # Sort the list before return. Last sort method is primary image_list = sorted(image_list, key=itemgetter('votes'), reverse=True) image_list = sorted(image_list, key=itemgetter('size'), reverse=False) image_list = sorted(image_list, key=itemgetter('language')) return image_list, scan_more
addon = xbmcaddon.Addon(id='plugin.video.acd-testing') #global variables PLUGIN_URL = sys.argv[0] plugin_handle = int(sys.argv[1]) plugin_queries = settings.parse_query(sys.argv[2][1:]) addon_dir = xbmc.translatePath( addon.getAddonInfo('path') ) kodi_common.debugger() # cloudservice - create settings module settings = settings.settings(addon) # retrieve settings user_agent = settings.getSetting('user_agent') #obsolete, replace, revents audio from streaming #if user_agent == 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)': # addon.setSetting('user_agent', 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0') mode = settings.getParameter('mode','main') # make mode case-insensitive mode = mode.lower()
def __init__(self): self.settings = settings() self.settings._get_limit()
def run(self, dbID=None, dbType=None, filePath=None, writer=None, query=None, addon=None, host=None): addon = constants.addon self.addon = addon self.PLUGIN_URL = constants.PLUGIN_NAME self.PLUGIN_NAME = constants.PLUGIN_NAME self.cloudservice2 = constants.cloudservice2 #global variables self.PLUGIN_URL = sys.argv[0] self.plugin_handle = int(sys.argv[1]) plugin_queries = settings.parse_query(sys.argv[2][1:]) # cloudservice - create settings module self.settingsModule = settings.settings(addon) self.user_agent = self.settingsModule.getSetting('user_agent') self.accountAmount = addon.getSettingInt('account_amount') mode = self.settingsModule.getParameter('mode', 'main') mode = mode.lower() try: instanceName = (plugin_queries['instance']).lower() except: instanceName = None if not instanceName and mode == 'main': self.addMenu(self.PLUGIN_URL + '?mode=enroll', '[B]1. %s[/B]' % addon.getLocalizedString(30207), instanceName=True) self.addMenu(self.PLUGIN_URL + '?mode=fallback', '[B]2. %s[/B]' % addon.getLocalizedString(30220), instanceName=True) self.addMenu(self.PLUGIN_URL + '?mode=validate', '[B]3. %s[/B]' % addon.getLocalizedString(30021), instanceName=True) self.addMenu(self.PLUGIN_URL + '?mode=delete', '[B]4. %s[/B]' % addon.getLocalizedString(30022), instanceName=True) defaultAccount = addon.getSetting('default_account') fallBackAccounts = addon.getSetting('fallback_accounts').split(',') for count in range (1, self.accountAmount + 1): instanceName = self.PLUGIN_NAME + str(count) username = self.addon.getSetting(instanceName + '_username') if username: countStr = str(count) if countStr == defaultAccount: username = '******' % username elif countStr in fallBackAccounts: username = '******' % username self.addMenu('%s?mode=main&instance=%s' % (self.PLUGIN_URL, instanceName), username, instanceName=instanceName) xbmcplugin.setContent(self.plugin_handle, 'files') xbmcplugin.addSortMethod(self.plugin_handle, xbmcplugin.SORT_METHOD_LABEL) elif instanceName and mode == 'main': fallbackAccounts = addon.getSetting('fallback_accounts').split(',') options = [self.addon.getLocalizedString(30219), self.addon.getLocalizedString(30002), addon.getLocalizedString(30023), self.addon.getLocalizedString(30159) ] account = re.sub('[^\d]', '', instanceName) fallbackExists = False if account in fallbackAccounts: fallbackExists = True options.insert(0, self.addon.getLocalizedString(30212) ) else: options.insert(0, self.addon.getLocalizedString(30213) ) selection = xbmcgui.Dialog().contextmenu(options) if selection == 0: if fallbackExists: mode = 'deletefallback' else: mode = 'addfallback' elif selection == 1: mode = 'makedefault' elif selection == 2: mode = 'rename' elif selection == 3: mode = 'validate' elif selection == 4: mode = 'delete' selection = xbmcgui.Dialog().yesno(self.addon.getLocalizedString(30000), '%s %s?' % (self.addon.getLocalizedString(30121), addon.getSetting(instanceName + '_username') ) ) if not selection: return else: return self.accountActions(addon, mode, instanceName) elif mode == 'enroll' or mode == 'makedefault': self.accountActions(addon, mode, instanceName) elif mode == 'settings_default': self.getAccounts() selection = xbmcgui.Dialog().select(addon.getLocalizedString(30120), self.accountNames) if selection == -1: return addon.setSetting('default_account', self.accountNumbers[selection] ) addon.setSetting('default_account_ui', self.accountNames[selection] ) elif mode == 'fallback': self.getAccounts() fallbackAccounts = addon.getSetting('fallback_accounts') fallbackAccountNames = addon.getSetting('fallback_accounts_ui') if fallbackAccounts: fallbackAccounts = [self.accountNumbers.index(x) for x in fallbackAccounts.split(',') if x in self.accountNumbers] selection = xbmcgui.Dialog().multiselect(addon.getLocalizedString(30120), self.accountNames, preselect=fallbackAccounts) else: selection = xbmcgui.Dialog().multiselect(addon.getLocalizedString(30120), self.accountNames) if selection is None: return addon.setSetting('fallback_accounts', ','.join(self.accountNumbers[x] for x in selection) ) addon.setSetting('fallback_accounts_ui', ', '.join(self.accountNames[x] for x in selection) ) addon.setSetting('fallback', 'true') xbmc.executebuiltin('Container.Refresh') elif mode == 'validate': self.getAccounts() selection = xbmcgui.Dialog().multiselect(addon.getLocalizedString(30024), self.accountNames) if selection is None: return for index_ in selection: instanceName = self.accountInstances[index_] validation = self.cloudservice2(self.plugin_handle, self.PLUGIN_URL, addon, instanceName, self.user_agent, self.settingsModule) validation.refreshToken() if validation.failed: accountName = self.accountNames[index_] selection = xbmcgui.Dialog().yesno(addon.getLocalizedString(30000), '%s %s' % (accountName, addon.getLocalizedString(30019) ) ) if selection: self.accountActions(addon, 'delete', instanceName) xbmcgui.Dialog().ok(addon.getLocalizedString(30000), addon.getLocalizedString(30020) ) elif mode == 'settings_delete' or mode == 'delete': self.getAccounts() selection = xbmcgui.Dialog().multiselect(addon.getLocalizedString(30158), self.accountNames) if selection is None: return self.accountActions(addon, 'delete', [self.accountInstances[x] for x in selection] ) if mode == 'settings_delete' and selection: xbmcgui.Dialog().ok(addon.getLocalizedString(30000), addon.getLocalizedString(30160) ) elif mode == 'video': if not dbType and not dbID and not filePath: return instanceName = constants.PLUGIN_NAME + str(self.settingsModule.getSetting('default_account', 1) ) service = self.cloudservice2(self.plugin_handle, self.PLUGIN_URL, addon, instanceName, self.user_agent, self.settingsModule) if service.failed: xbmcgui.Dialog().ok(addon.getLocalizedString(30000), addon.getLocalizedString(30005) ) return if not self.settingsModule.cryptoPassword or not self.settingsModule.cryptoSalt: xbmcgui.Dialog().ok(addon.getLocalizedString(30000), addon.getLocalizedString(30208) ) return driveID = self.settingsModule.getParameter('filename') #file ID try: service except NameError: xbmcgui.Dialog().ok(addon.getLocalizedString(30000), addon.getLocalizedString(30051) + ' ' + addon.getLocalizedString(30052) ) xbmc.log(addon.getLocalizedString(30051) + constants.PLUGIN_NAME + '-login', xbmc.LOGERROR) xbmcplugin.endOfDirectory(self.plugin_handle) return resumeOption = False if dbID: if dbType == 'movie': jsonQuery = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "id": "1", "method": "VideoLibrary.GetMovieDetails", "params": { "movieid":' + str(dbID) + ', "properties": ["resume"] } }') jsonKey = 'moviedetails' else: jsonQuery = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "id": "1", "method": "VideoLibrary.GetEpisodeDetails", "params": { "episodeid":' + str(dbID) + ', "properties": ["resume"] } }') jsonKey = 'episodedetails' import json jsonQuery = jsonQuery.encode('utf-8', errors='ignore') jsonResponse = json.loads(jsonQuery) resumeData = jsonResponse['result'][jsonKey]['resume'] resumePosition = resumeData['position'] videoLength = resumeData['total'] else: from sqlite3 import dbapi2 as sqlite dbPath = xbmc.translatePath(self.settingsModule.getSetting('video_db') ) db = sqlite.connect(dbPath) dirPath = os.path.dirname(filePath) + os.sep fileName = os.path.basename(filePath) resumePosition = list(db.execute('SELECT timeInSeconds FROM bookmark WHERE idFile=(SELECT idFile FROM files WHERE idPath=(SELECT idPath FROM path WHERE strPath=?) AND strFilename=?)', (dirPath, fileName) ) ) if resumePosition: resumePosition = resumePosition[0][0] videoLength = list(db.execute('SELECT totalTimeInSeconds FROM bookmark WHERE idFile=(SELECT idFile FROM files WHERE idPath=(SELECT idPath FROM path WHERE strPath=?) AND strFilename=?)', (dirPath, fileName) ) )[0][0] else: resumePosition = 0 # import pickle # resumeDBPath = xbmc.translatePath(self.settingsModule.resumeDBPath) # resumeDB = os.path.join(resumeDBPath, 'kodi_resumeDB.p') # try: # with open(resumeDB, 'rb') as dic: # videoData = pickle.load(dic) # except: # videoData = {} # try: # resumePosition = videoData[filename] # except: # videoData[filename] = 0 # resumePosition = 0 # strmName = self.settingsModule.getParameter('title') + ".strm" # cursor = list(db.execute('SELECT timeInSeconds FROM bookmark WHERE idFile=(SELECT idFile FROM files WHERE strFilename="%s")' % strmName) ) # if cursor: # resumePosition = cursor[0][0] # else: # resumePosition = 0 if resumePosition > 0: import time options = [] options.append('Resume from ' + str(time.strftime('%H:%M:%S', time.gmtime(resumePosition) ) ) ) options.append('Play from beginning') selection = xbmcgui.Dialog().contextmenu(options) if selection == 0: # resumePosition = resumePosition / total * 100 resumeOption = True # elif selection == 1: # resumePosition = '0' # videoData[filename] = 0 elif selection == -1: return driveURL = 'https://www.googleapis.com/drive/v2/files/%s?includeTeamDriveItems=true&supportsTeamDrives=true&alt=media' % driveID url = 'http://localhost:' + str(service.settings.streamPort) + '/crypto_playurl' data = 'instance=' + str(service.instanceName) + '&url=' + driveURL req = urllib.request.Request(url, data.encode('utf-8') ) try: response = urllib.request.urlopen(req) response.close() except urllib.error.URLError as e: xbmc.log(self.addon.getAddonInfo('name') + ': ' + str(e), xbmc.LOGERROR) return item = xbmcgui.ListItem(path='http://localhost:' + str(service.settings.streamPort) + '/play') # item.setProperty('StartPercent', str(position) ) # item.setProperty('startoffset', '60') if resumeOption: # item.setProperty('totaltime', '1') item.setProperty('totaltime', str(videoLength) ) item.setProperty('resumetime', str(resumePosition) ) xbmcplugin.setResolvedUrl(self.plugin_handle, True, item) if dbID: from resources.lib import gplayer player = gplayer.gPlayer(dbID=dbID, dbType=dbType) # with open(resumeDB, 'wb+') as dic: # pickle.dump(videoData, dic) # del videoData xbmc.sleep(100) monitor = xbmc.Monitor() while not monitor.abortRequested() and not player.isExit: player.sleep() player.saveTime() # with open(resumeDB, 'rb') as dic: # videoData = pickle.load(dic) # if player.videoWatched: # del videoData[filename] # else: # videoData[filename] = player.time # with open(resumeDB, 'wb+') as dic: # pickle.dump(videoData, dic) # if dbType == 'movie': # xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.RefreshMovie", "params": {"movieid":' + str(dbID) + ', "ignorenfo": true}, "id": "1"}') # elif dbType == 'episode': # xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.RefreshEpisode", "params": {"episodeid":' + str(dbID) + ', "ignorenfo": true}, "id": "1"}') # request = {"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "filter": {"field": "playcount", "operator": "greaterthan", "value": "0"}, "limits": { "start": 0 }, "properties": ["playcount"], "sort": { "order": "ascending", "method": "label" } }, "id": "libMovies"} # request = {"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "filter": {"field": "playcount", "operator": "greaterthan", "value": "0"}, "limits": { "start": 0 }, "properties": ["playcount"], "sort": { "order": "ascending", "method": "label" } }, "id": "libMovies"} xbmcplugin.endOfDirectory(self.plugin_handle) return