#!/usr/bin/env python3
# Generate index.html files for
# all subdirectories in a directory tree.
# By default only the current folder is processed.
# Use -r or --recursive to process nested folders.
import argparse
import datetime
import os
import sys
from pathlib import Path
index_file_name = 'index.html'
CSS = """
"""
def process_dir(top_dir, opts):
glob_patt = opts.filter or '*'
path_top_dir: Path
path_top_dir = Path(top_dir)
index_file = None
index_path = Path(path_top_dir, index_file_name)
if opts.verbose:
print(f'Traversing dir {path_top_dir.absolute()}')
try:
index_file = open(index_path, "w")
except Exception as e:
print('cannot create file %s %s' % (index_path, e))
return
index_file.write(f'''
''')
# sort dirs first
sorted_entries = sorted(path_top_dir.glob(glob_patt), key= lambda p: (p.is_file(), p.name))
entry: Path
for entry in sorted_entries:
# don't include index.html in the file listing
if entry.name.lower() == index_file_name.lower():
continue
if entry.is_dir() and opts.recursive:
process_dir(entry, opts)
if not os.access(str(entry), os.W_OK):
print(f"***ERROR*** folder {entry.absolute()} is not writable! SKIPPING!")
continue
if opts.verbose:
print(f'{entry.absolute()}')
size_bytes = -1 ## is a folder
size_pretty = '—'
try:
if entry.is_file():
size_bytes = entry.stat().st_size
size_pretty = pretty_size(size_bytes)
last_modified = datetime.datetime.fromtimestamp(entry.stat().st_mtime).replace(microsecond=0)
last_modified_iso = last_modified.isoformat()
last_modified_human_readable = last_modified.strftime("%c")
except Exception as e:
print('ERROR accessing file name:', e, entry)
continue
if entry.is_dir() and not entry.is_symlink():
entry_type = 'folder'
elif entry.is_dir() and entry.is_symlink():
entry_type = 'folder-shortcut'
elif entry.is_file() and entry.is_symlink():
entry_type = 'file-shortcut'
else:
entry_type = 'file'
index_file.write(f"""
""")
if index_file:
index_file.close()
# bytes pretty-printing
UNITS_MAPPING = [
(1024 ** 5, ' PB'),
(1024 ** 4, ' TB'),
(1024 ** 3, ' GB'),
(1024 ** 2, ' MB'),
(1024 ** 1, ' KB'),
(1024 ** 0, (' byte', ' bytes')),
]
def pretty_size(bytes, units=UNITS_MAPPING):
"""Human-readable file sizes.
ripped from https://pypi.python.org/pypi/hurry.filesize/
"""
for factor, suffix in units:
if bytes >= factor:
break
amount = int(bytes / factor)
if isinstance(suffix, tuple):
singular, multiple = suffix
if amount == 1:
suffix = singular
else:
suffix = multiple
return str(amount) + suffix
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='''DESCRIPTION:
Generate directory index files (recursive is OFF by default).
Start from current dir or from folder passed as first positional argument.
Optionally filter by file types with --filter "*.py". ''')
parser.add_argument('top_dir',
nargs='?',
action='store',
help='top folder from which to start generating indexes, '
'use current folder if not specified',
default=os.getcwd())
parser.add_argument('--filter', '-f',
help='only include files matching glob',
required=False)
parser.add_argument('--recursive', '-r',
action='store_true',
help="recursively process nested dirs (FALSE by default)",
required=False)
parser.add_argument('--verbose', '-v',
action='store_true',
help='***WARNING: this can take a very long time with complex file tree structures***'
' verbosely list every processed file',
required=False)
config = parser.parse_args(sys.argv[1:])
process_dir(config.top_dir, config)