Use html5tagger for AutoIndex
This commit is contained in:
parent
fed2ef3527
commit
36e3cc9df7
|
@ -8,9 +8,11 @@ from os import path
|
||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
from stat import S_ISDIR
|
from stat import S_ISDIR
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Any, AnyStr, Callable, Dict, Optional, Tuple, Union
|
from typing import Any, AnyStr, Callable, Dict, Iterable, Optional, Union
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
|
from html5tagger import Document, E
|
||||||
|
|
||||||
from sanic.compat import Header, open_async, stat_async
|
from sanic.compat import Header, open_async, stat_async
|
||||||
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
|
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
|
||||||
from sanic.helpers import Default, _default
|
from sanic.helpers import Default, _default
|
||||||
|
@ -347,32 +349,20 @@ async def file_stream(
|
||||||
|
|
||||||
|
|
||||||
class AutoIndex:
|
class AutoIndex:
|
||||||
INDEX_STYLE = """
|
STYLE = """
|
||||||
html { font-family: sans-serif }
|
html { font-family: sans-serif; }
|
||||||
ul { padding: 0; list-style: none; }
|
main { padding: 1rem; }
|
||||||
li {
|
table { width: 100%; max-width: 1200px; }
|
||||||
display: flex; justify-content: space-between;
|
td { font-family: monospace; }
|
||||||
font-family: monospace;
|
td:last-child { text-align: right; }
|
||||||
|
span.icon { margin-right: 1rem; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html { background: #111; color: #ccc; }
|
||||||
|
a { color: #ccc; }
|
||||||
|
a:visited { color: #777; }
|
||||||
}
|
}
|
||||||
li > span { padding: 0.1rem 0.6rem; }
|
|
||||||
li > span:first-child { flex: 4; }
|
|
||||||
li > span:last-child { flex: 1; }
|
|
||||||
"""
|
"""
|
||||||
OUTPUT_HTML = (
|
TITLE = "📁 File browser"
|
||||||
"<!DOCTYPE html><html lang=en>"
|
|
||||||
"<meta charset=UTF-8><title>{title}</title>\n"
|
|
||||||
"<style>{style}</style>\n"
|
|
||||||
"<h1>{title}</h1>\n"
|
|
||||||
"{body}"
|
|
||||||
)
|
|
||||||
FILE_WRAPPER_HTML = "<ul>{first_line}{files}</ul>"
|
|
||||||
FILE_LINE_HTML = (
|
|
||||||
"<li>"
|
|
||||||
"<span>{icon} <a href={file_name}>{file_name}</a></span>"
|
|
||||||
"<span>{file_access}</span>"
|
|
||||||
"<span>{file_size}</span>"
|
|
||||||
"</li>"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, directory: Path, autoindex: bool, index_name: str
|
self, directory: Path, autoindex: bool, index_name: str
|
||||||
|
@ -390,25 +380,43 @@ class AutoIndex:
|
||||||
return await file(index_file)
|
return await file(index_file)
|
||||||
|
|
||||||
async def index(self):
|
async def index(self):
|
||||||
return html(
|
return html(self.render())
|
||||||
self.OUTPUT_HTML.format(
|
|
||||||
title="📁 File browser",
|
def render(self) -> str:
|
||||||
style=self.INDEX_STYLE,
|
doc = Document(title=self.TITLE, lang="en")
|
||||||
body=self._list_files(),
|
doc.style(self.STYLE)
|
||||||
)
|
with doc.main:
|
||||||
|
self._headline(doc)
|
||||||
|
self._file_table(doc)
|
||||||
|
return str(doc)
|
||||||
|
|
||||||
|
def _headline(self, doc: Document):
|
||||||
|
doc.h1(self.TITLE)
|
||||||
|
|
||||||
|
def _file_table(self, doc: Document):
|
||||||
|
with doc.table:
|
||||||
|
self._parent(doc)
|
||||||
|
for f in self._iter_files():
|
||||||
|
del f["priority"]
|
||||||
|
self._file_cell(doc, **f)
|
||||||
|
|
||||||
|
def _parent(self, doc: Document):
|
||||||
|
self._file_cell(doc, "📁", "..", "", "")
|
||||||
|
|
||||||
|
def _file_cell(
|
||||||
|
self,
|
||||||
|
doc: Document,
|
||||||
|
icon: str,
|
||||||
|
file_name: str,
|
||||||
|
file_access: str,
|
||||||
|
file_size: str,
|
||||||
|
):
|
||||||
|
first = E.span(icon, class_="icon").a(file_name, href=file_name)
|
||||||
|
doc.tr.td(first, width="65%").td(file_access).td(
|
||||||
|
file_size, width="15%"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _list_files(self) -> str:
|
def _prepare_file(self, path: Path) -> Dict[str, Union[int, str]]:
|
||||||
prepared = [self._prepare_file(f) for f in self.directory.iterdir()]
|
|
||||||
files = "".join(itemgetter(2)(p) for p in sorted(prepared))
|
|
||||||
return self.FILE_WRAPPER_HTML.format(
|
|
||||||
files=files,
|
|
||||||
first_line=self.FILE_LINE_HTML.format(
|
|
||||||
icon="📁", file_name="..", file_access="", file_size=""
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _prepare_file(self, path: Path) -> Tuple[int, str, str]:
|
|
||||||
stat = path.stat()
|
stat = path.stat()
|
||||||
modified = datetime.fromtimestamp(stat.st_mtime)
|
modified = datetime.fromtimestamp(stat.st_mtime)
|
||||||
is_dir = S_ISDIR(stat.st_mode)
|
is_dir = S_ISDIR(stat.st_mode)
|
||||||
|
@ -416,10 +424,14 @@ class AutoIndex:
|
||||||
file_name = path.name
|
file_name = path.name
|
||||||
if is_dir:
|
if is_dir:
|
||||||
file_name += "/"
|
file_name += "/"
|
||||||
display = self.FILE_LINE_HTML.format(
|
return {
|
||||||
icon=icon,
|
"priority": is_dir * -1,
|
||||||
file_name=file_name,
|
"file_name": file_name,
|
||||||
file_access=modified.isoformat(),
|
"icon": icon,
|
||||||
file_size=stat.st_size,
|
"file_access": modified.isoformat(),
|
||||||
)
|
"file_size": stat.st_size,
|
||||||
return is_dir * -1, file_name, display
|
}
|
||||||
|
|
||||||
|
def _iter_files(self) -> Iterable[Dict[str, Any]]:
|
||||||
|
prepared = [self._prepare_file(f) for f in self.directory.iterdir()]
|
||||||
|
return sorted(prepared, key=itemgetter("priority"))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user