def reset_artwork(): ''' Remove all existing texture. ''' thumbnails = xbmc.translatePath('special://thumbnails/') if xbmcvfs.exists(thumbnails): dirs, ignore = xbmcvfs.listdir(thumbnails) for directory in dirs: ignore, thumbs = xbmcvfs.listdir( os.path.join(thumbnails, directory)) for thumb in thumbs: LOG.debug("DELETE thumbnail %s", thumb) xbmcvfs.delete(os.path.join(thumbnails, directory, thumb)) with Database('texture') as texdb: texdb.cursor.execute( "SELECT tbl_name FROM sqlite_master WHERE type='table'") for table in texdb.cursor.fetchall(): name = table[0] if name != 'version': texdb.cursor.execute("DELETE FROM " + name) LOG.info("[ reset artwork ]")
def unpack(file_path): """ Get the file list from archive file. Params: file_path The path to the archive file. Return: tuple(whole_path:str, subfiles:list) whole_path The quoted path to the subfiles. subfiles The list of subtitle files. Raise: TypeError The file type is unsupported. Some of the files is theoretically supported, such as 7z/tar. However, some encoding (Chinese chars for example) in file names may cause failure, even crash, so raise TypeError to avoid the worst condition. """ exts = ( ".srt", ".sub", ".smi", ".ssa", ".ass", ".sup" ) supported_archive_exts = ( ".zip", ".7z", ".tar", ".bz2", ".rar", ".gz", ".xz", ".iso", ".tgz", ".tbz2", ".cbr" ) self_archive_exts = ( ".zip", ".rar" ) if not file_path.endswith(supported_archive_exts): log(sys._getframe().f_code.co_name, "Unknown file ext: %s" % (os.path.basename(file_path)), level=xbmc.LOGERROR) return '', [] file_path = file_path.rstrip('/') if file_path.endswith(self_archive_exts): archive_file = urllib.parse.quote_plus(xbmc.translatePath(file_path)) ext = file_path[file_path.rfind('.') + 1:] archive_path = '%(protocol)s://%(archive_file)s' % {'protocol':ext, 'archive_file': archive_file} log(sys._getframe().f_code.co_name, "Get %s archive: %s" % (ext, archive_path), level=xbmc.LOGDEBUG) dirs, files = xbmcvfs.listdir(archive_path) if ('__MACOSX') in dirs: dirs.remove('__MACOSX') if len(dirs) > 0: archive_path = os.path.join(archive_path, dirs[0], '').replace('\\','/') dirs, files = xbmcvfs.listdir(archive_path) subtitle_list = [] for subfile in files: if subfile.endswith(exts): subtitle_list.append(subfile) elif file_path.endswith('.7z'): archive_path, subtitle_list = unpack_7z(file_path) else: subtitle_list = [] log(sys._getframe().f_code.co_name, "Skip: Danger file ext: %s" % (archive_path), level=xbmc.LOGERROR) return archive_path, subtitle_list
def run_plugin(path, wait=False): if wait: dirs, files = xbmcvfs.listdir(path) return dirs, files else: xbmc.executebuiltin('RunPlugin({})'.format(path)) return [], []
def delete_node_by_id(self, view_id): ''' Remove node and children files based on view_id. ''' path = xbmc.translatePath("special://profile/library/video/") dirs, files = xbmcvfs.listdir(path) for directory in dirs: if directory.startswith('jellyfin') and directory.endswith( view_id): _, files = xbmcvfs.listdir(os.path.join(path, directory)) for file in files: self.delete_node(os.path.join(path, directory, file)) xbmcvfs.rmdir(os.path.join(path, directory))
def grab_tv_guide(menu_id, menu): xmltv_url = get_xmltv_url(menu_id) Script.log('xmltv url of {}: {}'.format(menu_id, xmltv_url)) xmltv_fn = os.path.basename(urlparse(xmltv_url).path) Script.log('xmltv filename of {}: {}'.format(menu_id, xmltv_fn)) xmltv_fp = os.path.join(Script.get_info('profile'), xmltv_fn) # Remove old xmltv files of this country dirs, files = xbmcvfs.listdir(Script.get_info('profile')) for fn in files: if xmltv_infos[menu_id]['keyword'] in fn and fn != xmltv_fn: Script.log('Remove old xmltv file: {}'.format(fn)) xbmcvfs.delete(os.path.join(Script.get_info('profile'), fn)) # Check if we need to download a fresh xmltv file if not xbmcvfs.exists(xmltv_fp): Script.log("xmltv file of {} for today does not exist, let's download it".format(menu_id)) r = urlquick.get(xmltv_url) with open(xmltv_fp, 'wb') as f: f.write(r.content) # Grab programmes in xmltv file programmes = read_current_programmes(xmltv_fp) # Use the channel as key tv_guide = {} for programme in programmes: programme = programme_post_treatment(programme) tv_guide[programme['channel']] = programme return tv_guide
def copy_directory_contents_xbmcvfs(directory_from, directory_to): overall_success = True files_out = list() dirs_in_dir, files_in_dir = xbmcvfs.listdir( os.path.join(directory_from, '')) for ff in files_in_dir: if not xbmcvfs.copy( os.path.join(directory_from, ff), os.path.join( directory_to, ff)): #If move does not work, then copy overall_success = False else: xbmc.log( msg= 'Retro BIOS Tool: The file was copied from: %(file_from)s, to: %(file_to)s' % { 'file_from': os.path.join(directory_from, ff), 'file_to': os.path.join(directory_to, ff) }, level=xbmc.LOGDEBUG) files_out.append(os.path.join(directory_to, ff)) for dd in dirs_in_dir: if xbmcvfs.exists(os.path.join(directory_to, dd, '')) or xbmcvfs.mkdir( os.path.join(directory_to, dd)): files_out2, success = copy_directory_contents_xbmcvfs( os.path.join(directory_from, dd), os.path.join(directory_to, dd)) files_out = files_out + files_out2 else: overall_success = False return files_out, overall_success
def download_xmltv_file(country_id, day_delta=0): """Try to download XMLTV file of country_id for today + day_delta. Args: country_id (str) day_delta (int): 0: Today, 1: Tomorrow,... Returns: str: xmltv filepath. """ # Retrieve URL xmltv_url = get_xmltv_url(country_id, day_delta=day_delta) Script.log('xmltv url of {} country with day_delta {}: {}'.format( country_id, day_delta, xmltv_url)) # Compute dst filepath xmltv_fn = os.path.basename(urlparse(xmltv_url).path) Script.log('xmltv filename: {}'.format(xmltv_fn)) xmltv_fp = os.path.join(Script.get_info('profile'), xmltv_fn) # Remove old xmltv files of this country dirs, files = xbmcvfs.listdir(Script.get_info('profile')) today = datetime.date.today() for fn in files: if xmltv_infos[country_id]['keyword'] not in fn: continue try: file_date_s = fn.split( xmltv_infos[country_id]['keyword'])[1].split('.xml')[0] file_date = datetime_strptime(file_date_s, '%Y%m%d').date() if file_date < today: Script.log('Remove old xmltv file: {}'.format(fn)) xbmcvfs.delete(os.path.join(Script.get_info('profile'), fn)) except Exception: pass # Check if we need to download a fresh xmltv file need_to_downlod_xmltv_file = False if not xbmcvfs.exists(xmltv_fp): Script.log( "xmltv file of {} for today does not exist, let's download it". format(country_id)) need_to_downlod_xmltv_file = True else: # Check if we have the last version of the file current_file_md5 = compute_md5(xmltv_fp) remote_file_md5 = get_remote_xmltv_md5(country_id, day_delta=day_delta) print(current_file_md5) print(remote_file_md5) if current_file_md5 != remote_file_md5: Script.log( "A new version of xmltv file of {} for today exists, let's download it" .format(country_id)) need_to_downlod_xmltv_file = True if need_to_downlod_xmltv_file: r = urlquick.get(xmltv_url, max_age=-1) with open(xmltv_fp, 'wb') as f: f.write(r.content) return xmltv_fp
def delete_playlists(self): ''' Remove all jellyfin playlists. ''' path = xbmc.translatePath("special://profile/playlists/video/") _, files = xbmcvfs.listdir(path) for file in files: if file.startswith('jellyfin'): self.delete_playlist(os.path.join(path, file))
def load_external_plugins(): for d in PLUGIN_DIRS: common.logger.log_debug('Adding plugin path: %s' % d) sys.path.insert(0, d) for filename in xbmcvfs.listdir(d)[1]: if not filename.startswith('__') and filename.endswith('.py'): mod_name = filename[:-3] imp = __import__(mod_name, globals(), locals()) sys.modules[mod_name] = imp common.logger.log_debug('Loaded %s as %s from %s' % (imp, mod_name, filename))
def delete_nodes(self): ''' Remove node and children files. ''' path = xbmc.translatePath("special://profile/library/video/") dirs, files = xbmcvfs.listdir(path) for file in files: if file.startswith('jellyfin'): self.delete_node(os.path.join(path, file)) for directory in dirs: if directory.startswith('jellyfin'): _, files = xbmcvfs.listdir(os.path.join(path, directory)) for file in files: self.delete_node(os.path.join(path, directory, file)) xbmcvfs.rmdir(os.path.join(path, directory))
def get_zip_directory(path, folder): dirs, files = xbmcvfs.listdir(path) if folder in dirs: return os.path.join(path, folder) for directory in dirs: result = get_zip_directory(os.path.join(path, directory), folder) if result: return result
def delete_playlist_by_id(self, view_id): ''' Remove playlist based based on view_id. ''' path = xbmc.translatePath("special://profile/playlists/video/") _, files = xbmcvfs.listdir(path) for file in files: file = file if file.startswith('jellyfin') and file.endswith( '%s.xsp' % view_id): self.delete_playlist(os.path.join(path, file))
def delete_recursive(path, dirs): ''' Delete files and dirs recursively. ''' for directory in dirs: dirs2, files = xbmcvfs.listdir(os.path.join(path, directory)) for file in files: xbmcvfs.delete(os.path.join(path, directory, file)) delete_recursive(os.path.join(path, directory), dirs2) xbmcvfs.rmdir(os.path.join(path, directory))
def start(): http = HTTP() monitor = xbmc.Monitor() restart_queued = False boot_merge = settings.getBool('boot_merge', False) set_kodi_string('_iptv_merge_force_run') while not monitor.waitForAbort(1): http.start() if settings.getBool('http_api', False) else http.stop() forced = get_kodi_string('_iptv_merge_force_run') or 0 if forced or boot_merge or (settings.getBool('auto_merge', True) and time.time() - userdata.get('last_run', 0) > settings.getInt('reload_time_hours', 12) * 3600): set_kodi_string('_iptv_merge_force_run', '1') url = router.url_for('run_merge', forced=int(forced)) dirs, files = xbmcvfs.listdir(url) result, msg = int(files[0][0]), unquote_plus(files[0][1:]) if result: restart_queued = True userdata.set('last_run', int(time.time())) set_kodi_string('_iptv_merge_force_run') if restart_queued and settings.getBool('restart_pvr', False): if forced: progress = gui.progressbg(heading='Reloading IPTV Simple Client') if KODI_VERSION > 18: restart_queued = False try: xbmcaddon.Addon(IPTV_SIMPLE_ID).setSetting('m3uPathType', '0') except Exception as e: pass elif forced or (not xbmc.getCondVisibility('Pvr.IsPlayingTv') and not xbmc.getCondVisibility('Pvr.IsPlayingRadio')): restart_queued = False kodi_rpc('Addons.SetAddonEnabled', {'addonid': IPTV_SIMPLE_ID, 'enabled': False}) wait_delay = 4 for i in range(wait_delay): if monitor.waitForAbort(1): break if forced: progress.update((i+1)*int(100/wait_delay)) kodi_rpc('Addons.SetAddonEnabled', {'addonid': IPTV_SIMPLE_ID, 'enabled': True}) if forced: progress.update(100) progress.close() boot_merge = False http.stop()
def find_files(root): dirs, files = xbmcvfs.listdir(root) found_files = [] for dir in dirs: path = os.path.join(xbmc.translatePath(root), dir) found_files = found_files + find_files(path) file_list = [] for file in files: if file.endswith('.' + plugin.get_setting('ffmpeg.ext', str)): file = os.path.join(xbmc.translatePath(root), file) file_list.append(file) return found_files + file_list
def clear_cache(plugin): # Callback function of clear cache setting button # Clear urlquick cache urlquick.cache_cleanup(-1) Script.notify(plugin.localize(30371), '') # Remove all tv guides dirs, files = xbmcvfs.listdir(Script.get_info('profile')) for fn in files: if '.xml' in fn and fn != 'settings.xml': Script.log('Remove xmltv file: {}'.format(fn)) xbmcvfs.delete(os.path.join(Script.get_info('profile'), fn))
def delete_folder(path): ''' Delete objects from kodi cache ''' LOG.debug("--[ delete folder ]") dirs, files = xbmcvfs.listdir(path) delete_recursive(path, dirs) for file in files: xbmcvfs.delete(os.path.join(path, file)) xbmcvfs.delete(path) LOG.info("DELETE %s", path)
def copy_recursive(path, dirs, dest): for directory in dirs: dirs_dir = os.path.join(path, directory) dest_dir = os.path.join(dest, directory) xbmcvfs.mkdir(dest_dir) dirs2, files = xbmcvfs.listdir(dirs_dir) if dirs2: copy_recursive(dirs_dir, dirs2, dest_dir) for file in files: copy_file(os.path.join(dirs_dir, file), os.path.join(dest_dir, file))
def copytree(path, dest): ''' Copy folder content from one to another. ''' dirs, files = xbmcvfs.listdir(path) if not xbmcvfs.exists(dest): xbmcvfs.mkdirs(dest) if dirs: copy_recursive(path, dirs, dest) for file in files: copy_file(os.path.join(path, file), os.path.join(dest, file)) LOG.info("Copied %s", path)
def user_playlist_import(): path = settings.import_export_path if len(path) == 0 or not session.is_logged_in: return files = xbmcvfs.listdir(path)[1] files = [ py2_decode(name) for name in files if py2_decode(name).startswith('playlist_') ] selected = xbmcgui.Dialog().select(path, files) if selected < 0: return name = os.path.join(path, files[selected]) try: session.user.import_playlists(name) except Exception as e: log.logException(e)
def list_all(self, current_archive_file=None, current_directory_out=None): files_out = list() if current_archive_file is None: current_archive_file = self.archive_file if current_directory_out is None: current_directory_out = self.directory_out if current_archive_file is not None: if current_directory_out is None: #Default to the same directory as the current_archive_file directory_to = os.path.join( os.path.split(xbmc.translatePath(current_archive_file))[0], '') else: directory_to = os.path.join( xbmc.translatePath(current_directory_out), '') if 'archive://' in current_archive_file or 'rar://' in current_archive_file: archive_path = current_archive_file else: if self.use_vfs_rar: archive_path = 'rar://%(archive_file)s' % { 'archive_file': url_quote(xbmc.translatePath(current_archive_file)) } else: archive_path = 'archive://%(archive_file)s' % { 'archive_file': url_quote(xbmc.translatePath(current_archive_file)) } dirs_in_archive, files_in_archive = xbmcvfs.listdir(archive_path) for ff in files_in_archive: file_from = os.path.join(archive_path, ff).replace( '\\', '/' ) #Windows unexpectedly requires a forward slash in the path files_out.append( file_from) #Append the file to the list of extracted files for dd in dirs_in_archive: files_out2 = self.list_all( current_archive_file=os.path.join(archive_path, dd, '').replace('\\', '/'), current_directory_out=os.path.join(directory_to, dd)) files_out = files_out + files_out2 #Append the files in the subdir to the list of extracted files return files_out
def scan_folder(self, path): dirs, files = xbmcvfs.listdir(path) images = [ xbmc.validatePath(path + f) for f in files if f.lower()[-3:] in ('jpg', 'png') ] if RECURSIVE: for directory in dirs: if directory.startswith('.'): continue images.extend( self.scan_folder( xbmc.validatePath('/'.join((path, directory, ''))))) return images
def grab_tv_guide(menu_id): try: xmltv_url = get_xmltv_url(menu_id) Script.log('xmltv url of {}: {}'.format(menu_id, xmltv_url)) xmltv_fn = os.path.basename(urlparse(xmltv_url).path) Script.log('xmltv filename of {}: {}'.format(menu_id, xmltv_fn)) xmltv_fp = os.path.join(Script.get_info('profile'), xmltv_fn) # Remove old xmltv files of this country dirs, files = xbmcvfs.listdir(Script.get_info('profile')) for fn in files: if xmltv_infos[menu_id]['keyword'] in fn and fn != xmltv_fn: Script.log('Remove old xmltv file: {}'.format(fn)) xbmcvfs.delete(os.path.join(Script.get_info('profile'), fn)) # Check if we need to download a fresh xmltv file if not xbmcvfs.exists(xmltv_fp): Script.log( "xmltv file of {} for today does not exist, let's download it". format(menu_id)) r = urlquick.get(xmltv_url) with open(xmltv_fp, 'wb') as f: f.write(r.content) # Grab programmes in xmltv file programmes = read_current_programmes(xmltv_fp) # Use the channel as key tv_guide = {} for programme in programmes: programme = programme_post_treatment(programme) tv_guide[programme['channel']] = programme return tv_guide except Exception as e: Script.notify(Script.localize(LABELS['TV guide']), Script.localize( LABELS['An error occurred while getting TV guide']), display_time=7000) Script.log('xmltv module failed with error: {}'.format( e, lvl=Script.ERROR)) return {}
def get_fanart(item_id, path, server_id=None): ''' Get extra fanart for listitems. This is called by skinhelper. Images are stored locally, due to the Kodi caching system. ''' if not item_id and 'plugin.video.jellyfin' in path: item_id = path.split('/')[-2] if not item_id: return LOG.info("[ extra fanart ] %s", item_id) objects = Objects() list_li = [] directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id) server = TheVoid('GetServerAddress', {'ServerId': server_id}).get() if not xbmcvfs.exists(directory): xbmcvfs.mkdirs(directory) item = TheVoid('GetItem', {'ServerId': server_id, 'Id': item_id}).get() obj = objects.map(item, 'Artwork') backdrops = api.API(item, server).get_all_artwork(obj) tags = obj['BackdropTags'] for index, backdrop in enumerate(backdrops): tag = tags[index] fanart = os.path.join(directory, "fanart%s.jpg" % tag) li = xbmcgui.ListItem(tag, path=fanart) xbmcvfs.copy(backdrop, fanart) list_li.append((fanart, li, False)) else: LOG.debug("cached backdrop found") dirs, files = xbmcvfs.listdir(directory) for file in files: fanart = os.path.join(directory, file) li = xbmcgui.ListItem(file, path=fanart) list_li.append((fanart, li, False)) xbmcplugin.addDirectoryItems(int(sys.argv[1]), list_li, len(list_li)) xbmcplugin.endOfDirectory(int(sys.argv[1]))
def unzip(path, dest, folder=None): ''' Unzip file. zipfile module seems to fail on android with badziperror. ''' path = quote_plus(path) root = "zip://" + path + '/' if folder: xbmcvfs.mkdir(os.path.join(dest, folder)) dest = os.path.join(dest, folder) root = get_zip_directory(root, folder) dirs, files = xbmcvfs.listdir(root) if dirs: unzip_recursive(root, dirs, dest) for file in files: unzip_file(os.path.join(root, file), os.path.join(dest, file)) LOG.info("Unzipped %s", path)
def favorites_import(what): path = settings.import_export_path if len(path) == 0 or not session.is_logged_in: return files = xbmcvfs.listdir(path)[1] files = [ py2_decode(name) for name in files if py2_decode(name).startswith('favo_%s' % what) ] selected = xbmcgui.Dialog().select(path, files) if selected < 0: return name = os.path.join(path, files[selected]) ok = False if what == 'playlists': ok = session.user.favorites.import_ids( what=_T('Playlists'), filename=name, action=session.user.favorites.add_playlist) elif what == 'artists': ok = session.user.favorites.import_ids( what=_T('Artists'), filename=name, action=session.user.favorites.add_artist) elif what == 'albums': ok = session.user.favorites.import_ids( what=_T('Albums'), filename=name, action=session.user.favorites.add_album) elif what == 'tracks': ok = session.user.favorites.import_ids( what=_T('Tracks'), filename=name, action=session.user.favorites.add_track) elif what == 'videos': ok = session.user.favorites.import_ids( what=_T('Videos'), filename=name, action=session.user.favorites.add_video) return ok
def _discover_database(self, database): ''' Use UpdateLibrary(video) to update the date modified on the database file used by Kodi. ''' if database == 'video': xbmc.executebuiltin('UpdateLibrary(video)') xbmc.sleep(200) databases = xbmc.translatePath("special://database/") types = { 'video': "MyVideos", 'music': "MyMusic", 'texture': "Textures" } database = types[database] dirs, files = xbmcvfs.listdir(databases) modified = {'db_file': None, 'time': 0} for db_file in reversed(files): if (db_file.startswith(database) and not db_file.endswith('-wal') and not db_file.endswith('-shm') and not db_file.endswith('db-journal')): st = xbmcvfs.Stat(databases + db_file) modified_int = st.st_mtime() LOG.debug("Database detected: %s time: %s", db_file, modified_int) if modified_int > modified['time']: modified['time'] = modified_int modified['db_file'] = db_file LOG.debug("Discovered database: %s", modified) self.discovered_file = modified['db_file'] return xbmc.translatePath("special://database/%s" % modified['db_file'])
def browse(): args = plugin.args if 'path' not in args: # back navigation workaround: just silently fail and we'll # eventually end outside the plugin dir xbmcplugin.endOfDirectory(plugin.handle, succeeded=False) return current_path = args['path'][0] if not current_path.endswith('/'): current_path += '/' dirs = [] files = [] if xbmcvfs.exists(current_path): dirs, files = xbmcvfs.listdir(current_path) for name in dirs: li = ListItem(name) path = os.path.join(current_path, name) params = { b'path': path, b'title': args['title'][0], } if 'fanart' in args: li.setArt({'fanart': args['fanart'][0]}) params.update({b'fanart': args['fanart'][0]}) url = 'plugin://context.item.extras/?' + urlencode(params) xbmcplugin.addDirectoryItem(plugin.handle, url, li, isFolder=True) for name in files: li = ListItem(name) if 'fanart' in args: li.setArt({'fanart': args['fanart'][0]}) url = os.path.join(current_path, py2_decode(name)) xbmcplugin.addDirectoryItem(plugin.handle, url, li, isFolder=False) xbmcplugin.addSortMethod(plugin.handle, xbmcplugin.SORT_METHOD_LABEL) xbmcplugin.endOfDirectory(plugin.handle)
def listDirectory(thesource, thefilter='all'): log_lines = [] log_lines.append('getting contents of folder %s' % thesource) if isXBMC: try: dirs, files = xbmcvfs.listdir(thesource) except OSError: log_lines.append('OSError getting directory list') return [], log_lines except Exception as e: log_lines.append('unexpected error getting directory list') log_lines.append(e) return [], log_lines if thefilter == 'files': log_lines.append('returning files from %s' % thesource) log_lines.append(files) return files, log_lines elif thefilter == 'folders': log_lines.append('returning folders from %s' % thesource) log_lines.append(dirs) return dirs, log_lines else: log_lines.append('returning files and folders from %s' % thesource) log_lines.append(files + dirs) return files + dirs, log_lines else: try: contents = os.listdir(thesource) except OSError: log_lines.append('OSError getting directory list') return [], log_lines except Exception as e: log_lines.append('unexpected error getting directory list') log_lines.append(e) return [], log_lines log_lines.append('returning files and folders from %s' % thesource) return contents, log_lines
def do_GET(self): if self.path.lower() == '/' + PLAYLIST_URL.lower(): path = router.url_for('run_merge', type='playlist', refresh=int( settings.getBool('http_force_merge', True))) content_type = 'application/vnd.apple.mpegurl' elif self.path.lower() == '/' + EPG_URL.lower(): path = router.url_for('run_merge', type='epg', refresh=int( settings.getBool('http_force_merge', True))) content_type = 'text/xml' else: return log.debug('PLUGIN REQUEST: {}'.format(path)) dirs, files = xbmcvfs.listdir(path) result, msg = int(files[0][0]), unquote_plus(files[0][1:]) if not result: raise Exception(msg) if not os.path.exists(msg): raise Exception('File not found: {}'.format(msg)) self.send_response(200) self.send_header('Content-Type', content_type) self.end_headers() with open(msg, 'rb') as f: while True: chunk = f.read(CHUNK_SIZE) if not chunk: break self.wfile.write(chunk)