from dataclasses import dataclass import os import re from flask import Blueprint, current_app, escape, flash, render_template from flask import jsonify from .. import util VIDEO_EXTENSIONS = ['.avi', '.m4v', '.mkv', '.mp4', '.mpg'] MAX_RECURSE_DEPTH = 5 bp = Blueprint('file_listing', __name__, url_prefix='/file_listing') @dataclass class path_entry: base_name = '' rel_path = '' # NOTE: fs path relative to config['MOVIE_DIR'] file_extension = '' is_dir = False depth = 0 contents = [] @bp.route('/folder_contents/') def getBaseFolderContents(): full_path = current_app.config['MOVIE_DIR'] paths = [] # validate input if not os.path.exists(full_path): flash('path in "MOVIE_DIR" is invalid') else: for entry in os.scandir(full_path): paths.append(getPathInfo(entry, MAX_RECURSE_DEPTH)) return render_template( 'file_listing/folder_contents.html', paths=paths, debug_str=util.dumpObject(paths) ) # NOTE: return a listing of files and directories in a sub_directory as JSON # @param subpath a path relative to config['MOVIE_DIR'] @bp.route('/folder_contents/') def getFolderJSON(subpath = ''): video_path = current_app.config['MOVIE_DIR'] full_path = os.path.join(video_path, subpath) errors = [] paths = [] current_app.logger.debug('video_path: {}'.format(video_path)) current_app.logger.debug('subpath: {}'.format(subpath)) current_app.logger.debug('full_path: {}'.format(full_path)) # validate input if re.search('\.\.', subpath) or not os.path.exists(full_path): errors.append('invalid path: {}'.format(full_path)) elif not os.path.isdir(full_path): errors.append('path is not a directory: {}'.format(full_path)) else: for entry in os.scandir(full_path): # TODO: remove 'getPathInfo()' when finished with async file listing # make sure to continue to test for '.' and symlinks # TODO: add errors to json, 'flash()' won't trigger on this function since it's async pe = getPathInfo(entry, MAX_RECURSE_DEPTH) if pe: paths.append({ 'base_name': pe.base_name, 'rel_path': pe.rel_path, 'file_extension': pe.file_extension, 'is_dir': pe.is_dir, }) return jsonify(errors=errors, paths=paths) def getPathInfo(entry, depth=0): if not entry.name.startswith('.') and not entry.is_symlink(): video_path = current_app.config['MOVIE_DIR'] pe = path_entry() pe.base_name = entry.name pe.rel_path = re.sub(video_path, '', entry.path) pe.is_dir = entry.is_dir() pe.depth = depth if entry.is_file(): ext = os.path.splitext(entry)[1] if ext in VIDEO_EXTENSIONS: pe.file_extension = ext else: return None elif entry.is_dir() and depth < MAX_RECURSE_DEPTH: for e in os.scandir(entry): p = getPathInfo(e, depth + 1) if p: pe.contents.append(p) return pe return None