__sssstatic/gen.py

368 lines
10 KiB
Python
Raw Normal View History

2017-06-28 15:52:35 +02:00
#!/usr/bin/env python
2023-04-29 11:38:51 +02:00
import argparse, json, sys, markdown, shutil, json, re, pathlib, shutil
2023-08-03 11:14:49 +02:00
import utils.imgconv
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
input_dir = None
output_dir = None
style = None
2023-08-03 11:14:49 +02:00
(W, H, FMT) = (None, None, None)
2023-04-29 11:38:51 +02:00
codebase = pathlib.Path(__file__).parent.absolute()
2017-06-28 15:52:35 +02:00
2020-07-13 10:05:10 +02:00
def translate_index_header(txt, dirname):
2023-08-03 11:14:49 +02:00
sections = txt.count('- - -')
2020-07-13 10:05:10 +02:00
if sections == 0:
# not following the normal template -> direct html
2023-08-03 11:14:49 +02:00
txt = txt.replace('data/', dirname + '/') #<--- replace reference to data/
2020-07-13 10:05:10 +02:00
return txt
elif sections == 1:
2023-08-03 11:14:49 +02:00
[text, link] = txt.split('- - -')
2017-06-29 17:25:19 +02:00
2023-08-03 11:14:49 +02:00
text_md = markdown.markdown(text)
link_info = json.loads(link)
2017-06-28 15:52:35 +02:00
2023-08-03 11:14:49 +02:00
dirpath = pathlib.Path(dirname)
img_name = pathlib.Path(link_info['img']).name
img_path = dirpath / img_name
img_resize_path = dirpath / "img-resize" / img_name
2023-04-29 11:38:51 +02:00
2023-08-03 11:14:49 +02:00
picture_el = emit_picture(str(img_path), str(img_resize_path))
2023-04-29 11:38:51 +02:00
table = codebase / "templates/<table>"
with table.open(encoding="utf-8") as fp:
out = fp.read()
out = out.replace('[[text_md]]', text_md);
2023-08-03 11:14:49 +02:00
out = out.replace('[[link_md]]', picture_el);
if link_info['href']:
if link_info['href'] == "data":
href = dirname + "/"
else:
href = link_info['href']
out = "<a href='" + href + "'>" + out + "</a>";
2017-06-29 17:25:19 +02:00
return out
2017-06-28 15:52:35 +02:00
def escape_date(dirname):
return re.sub('^20\d{2}?.', '', dirname)
2023-08-03 11:14:49 +02:00
def emit_picture(src_full, src_resize):
return f'''<picture>
<source media="(max-width: 799px)" srcset="{src_resize}" />
<source media="(min-width: 800px)" srcset="{src_full}" />
<img src="{src_full}" />
</picture>'''
2023-04-29 11:38:51 +02:00
def emit_img(file: pathlib.Path, data_dir: pathlib.Path):
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
## this is where LOD can be applied
2023-08-03 11:14:49 +02:00
resize_dir = data_dir / "img-resize"
resize_dir.mkdir(parents=True, exist_ok=True)
filepath = data_dir / file
resize_file = utils.imgconv.convert(filepath, (W, H, FMT), resize_dir)
2017-06-28 15:52:35 +02:00
2023-08-03 11:14:49 +02:00
return f'<a href="{file}">' + emit_picture(str(file), "img-resize/" + resize_file.name) + '</a>'
2023-04-29 11:38:51 +02:00
def emit_video_mp4(file: pathlib.Path, data_dir: pathlib.Path):
return f'<video controls><source src="{file}" type="video/mp4"</video>'
def emit_audio(file: pathlib.Path, data_dir: pathlib.Path, full=False):
2017-07-02 15:20:44 +02:00
# an '.audio' file is a json file with a list of audio elements
# which need to be bundles as a type of list in a single <li>
2019-07-12 16:44:56 +02:00
2023-04-29 11:38:51 +02:00
file = data_dir / file;
if file.is_file():
with file.open() as fp:
audio = json.loads(fp.read())
if full:
out = '<sound class="full">\n'
2018-12-24 15:10:51 +01:00
else:
2023-04-29 11:38:51 +02:00
out = "<sound>\n"
for a in audio:
out += "<track>\n"
out += "<info>\n"
out += "<name>" + a["name"] + "</name>\n"
#out += "<duration>" + a["length"] + "</duration>\n"
out += "</info>\n"
out += "<audio controls preload>\n"
# if it is a list of files
if isinstance(a["type"], list):
for t in a["type"]:
out += '<source src="' + a["file"] + '.' + t + '" type="audio/' + t + '">\n'
else:
out += '<source src="' + a["file"] + '" type="audio/' + a["type"] + '">\n'
out += "</audio>\n"
out += "</track>\n"
out += "</sound>\n"
return out
return None
2017-07-02 15:20:44 +02:00
2019-07-12 16:44:56 +02:00
def emit_audio_full(file, data_dir):
return emit_audio(file, data_dir, full=True)
2017-07-02 15:20:44 +02:00
def default(file, data_dir):
2017-06-28 15:52:35 +02:00
return None;
content_map = {
'.png': emit_img,
'.jpg': emit_img,
2023-08-03 11:14:49 +02:00
'.jpeg': emit_img,
2017-06-28 15:52:35 +02:00
'.m4v': emit_video_mp4,
'.mov': emit_video_mp4,
2017-07-03 19:16:51 +02:00
'.mp4': emit_video_mp4,
2017-07-02 15:20:44 +02:00
'.audio': emit_audio,
2019-07-12 16:44:56 +02:00
'.audio_full': emit_audio_full,
2017-06-28 15:52:35 +02:00
'.html': default,
'.txt': default
};
2023-04-29 11:38:51 +02:00
def index_content(dir_name: str, data_dir: pathlib.Path, index_txt: pathlib.Path, desc_txt: pathlib.Path, template: str):
2017-06-29 15:00:21 +02:00
2021-04-12 10:48:22 +02:00
print(" indexing content -- " + dir_name);
2017-06-29 15:00:21 +02:00
# desc_txt is a markdown file containing description
# for the project - no layout applied to it, only md
2017-06-29 17:25:19 +02:00
desc_md = None
2023-04-29 11:38:51 +02:00
if desc_txt.is_file():
with desc_txt.open(encoding="utf-8") as fp:
desc_md = markdown.markdown(fp.read());
2017-06-28 15:52:35 +02:00
# index_txt is a json file containing one thing:
# an array of files names or glob patterns
2017-06-29 17:25:19 +02:00
content_index = None
2023-04-29 11:38:51 +02:00
if index_txt.is_file():
with index_txt.open(encoding="utf-8") as fp:
content_index = json.loads(fp.read());
if desc_md is None and content_index is None:
2017-06-28 15:52:35 +02:00
return;
content = ""
2017-06-29 15:00:21 +02:00
if desc_md:
content += "<li>" + "<desc>" + "\n" + desc_md + "</desc>" + "</li>" + "\n";
if content_index:
files = [];
for i in content_index:
2023-04-29 11:38:51 +02:00
f = data_dir / i
if f.is_file():
2023-08-03 11:14:49 +02:00
files.append(data_dir / i);
2017-06-29 15:00:21 +02:00
else:
2023-04-29 11:38:51 +02:00
files += [pathlib.Path(a.name) for a in list(data_dir.glob(i))]
2017-06-29 15:00:21 +02:00
2018-12-24 15:10:51 +01:00
# sort files?
files.sort()
2023-08-03 11:14:49 +02:00
for j in files:
2023-04-29 11:38:51 +02:00
ext = j.suffix.lower()
2017-07-02 15:20:44 +02:00
if ext in content_map:
element = content_map[ext](j, data_dir);
if element:
content += "<li>" + element + "</li>" + "\n";
2017-06-28 15:52:35 +02:00
2023-08-03 11:14:49 +02:00
# check if there is an html file existing already. if so use that one.
2023-04-29 11:38:51 +02:00
html = template.replace('[[content]]', content).replace('[[dir]]', str(dir_name));
2017-06-28 15:52:35 +02:00
try:
2023-04-29 11:38:51 +02:00
out = data_dir / 'index.html'
with out.open('w', encoding="utf-8") as fp:
fp.write(html);
return out
2017-06-28 15:52:35 +02:00
except:
print('error creating content index output file. aborting...');
2023-04-29 11:38:51 +02:00
return None
def check_dir_exists(dirpath, create=False):
d = pathlib.Path(dirpath)
if d.is_dir():
return d
elif create:
print(f"Directory {dirpath} is not a directory... Create it? [y/n]")
x = input()
if x.lower() == "y":
d.mkdir(parents=True)
return d
return None
2017-06-28 15:52:35 +02:00
if __name__ == '__main__':
2023-04-29 11:38:51 +02:00
p = argparse.ArgumentParser(description="SSSSTATIC generates html files from directory contents")
p.add_argument('--input', "-i", help='input directory');
p.add_argument('--output', "-o", help='output directory');
p.add_argument('--style', "-y", default="+++", help='css style, ico, etc.');
p.add_argument("--settings", "-s", default=".static/settings.json", help="Settings file (default: .static/settings.json")
2023-08-03 11:14:49 +02:00
p.add_argument('--format', '-f', default="1000x1000", help='img output formats (ex. 1000x)');
2023-04-29 11:38:51 +02:00
p.add_argument('--clean', help='cleans the output directory');
args = p.parse_args();
if args.settings == ".static/settings.json":
file_settings = codebase / ".static/settings.json"
else:
file_settings = pathlib.Path(args.settings)
if file_settings.exists() and file_settings.is_file():
with file_settings.open(encoding="utf-8") as fp:
settings = json.load(fp)
input_dir = check_dir_exists(settings['inputdir'])
output_dir = check_dir_exists(settings['outputdir'])
style = settings['style']
if args.input:
inputdir = check_dir_exists(args.input)
if inputdir is not None:
input_dir = inputdir;
else:
2023-08-03 11:14:49 +02:00
sys.exit(f"Input directory {args.input} is not a directory... Aborting.")
2023-04-29 11:38:51 +02:00
if args.output:
outputdir = check_dir_exists(args.output, create=True)
if outputdir is not None:
output_dir = outputdir;
else:
2023-08-03 11:14:49 +02:00
sys.exit(f"Output directory {args.output} is not a directory... Aborting.")
if args.format:
(W, H, FMT) = utils.imgconv.parse_format(args.format)
if not FMT:
sys.exit(f"Format {str(args.format)} is not valid. Aborting.")
2023-04-29 11:38:51 +02:00
if style is not None:
style_dir = codebase / f"styles/{style}"
if not style_dir.is_dir():
style = args.style
if input_dir is None:
2023-08-03 11:14:49 +02:00
sys.exit(f"Error with input directory... Aborting.")
2023-04-29 11:38:51 +02:00
if output_dir is None:
print(f"No output directory.")
print(f"Dry-run test? [y/n]")
x = input()
if x.lower() == "n":
sys.exit(1)
else:
if output_dir.exists() and any(output_dir.iterdir()):
print(f"? {output_dir} exists and is not empty. Erase content? [y/n]")
x = input()
if x.lower() == "y":
try:
shutil.rmtree(output_dir)
except:
print(f"error erasing {output_dir}. Continuing.")
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
print('1/3 - Configuring');
2017-06-28 15:52:35 +02:00
# main index template
2023-04-29 11:38:51 +02:00
indx_template = codebase / "templates/index_template.html"
with indx_template.open(encoding="utf-8") as fp:
indx_template_str = fp.read()
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
# images index template
section_indx_template = codebase / "templates/index_apache_template.html"
with section_indx_template.open(encoding="utf-8") as fp:
section_indx_template_str = fp.read()
2017-06-28 15:52:35 +02:00
2021-04-12 10:48:22 +02:00
print('2/3 - Parsing input');
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
dirs = sorted([d for d in input_dir.iterdir() if d.is_dir()])
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
content = ''
for d in reversed(dirs):
indx_txt = d / 'index.txt'
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
if not indx_txt.exists():
print(f"{indx_txt} does not exists. Skipping.")
continue
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
with indx_txt.open(encoding="utf-8") as fp:
txt = fp.read()
dirname = escape_date(d.name)
content += translate_index_header(txt, dirname);
datadir = d / "data"
if datadir.is_dir():
section_indx_txt = datadir / "index.txt"
section_desc_txt = datadir / "desc.txt"
2017-06-28 15:52:35 +02:00
2023-08-03 11:14:49 +02:00
out_index = None
2023-04-29 11:38:51 +02:00
if section_indx_txt.is_file() or section_desc_txt.is_file():
2023-08-03 11:14:49 +02:00
out_index = index_content(dirname, datadir, section_indx_txt, section_desc_txt, section_indx_template_str)
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
if output_dir is not None:
out_datadir = output_dir / dirname
2017-06-29 15:00:21 +02:00
2023-04-29 11:38:51 +02:00
if out_datadir.exists() and any(out_datadir.iterdir()):
print(f" ? {out_datadir} exists and is not empty. Erase content? [y/n]")
x = input()
if x.lower() == "y":
try:
shutil.rmtree(out_datadir)
except:
print(f"error erasing {out_datadir}. Continuing.")
2017-06-29 15:00:21 +02:00
2023-04-29 11:38:51 +02:00
try:
shutil.copytree(datadir, out_datadir, ignore=shutil.ignore_patterns('.DS_Store'), dirs_exist_ok=True)
except:
print(f"error copying {datadir} to {out_datadir}. Continuing.")
continue
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
# copy index after (since index.html might be lurking in content dirs)
2023-08-03 11:14:49 +02:00
if out_index:
try:
shutil.copy2(out_index, out_datadir)
except:
print(f"error copying {out_index} to {out_datadir}. Continuing.")
continue
2017-06-28 15:52:35 +02:00
2021-04-12 10:48:22 +02:00
print('3/3 - Generating ouput');
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
html = indx_template_str.replace('[[content]]', content);
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
if output_dir is not None:
2017-06-28 15:52:35 +02:00
2023-04-29 11:38:51 +02:00
out = output_dir / "index.html"
out.write_text(html, encoding="utf-8")
# style
style_dir = codebase / f"styles/{style}"
if not style_dir.is_dir():
print(f"{style} does not exists (make sure to add a style to styles/)... going vanilla")
else:
try:
out_style_dir = output_dir / style
shutil.copytree(style_dir, out_style_dir, ignore=shutil.ignore_patterns('.DS_Store'), dirs_exist_ok=True)
except:
print(f"error copying {style_dir} to {out_style_dir}. Continuing.")
# robots.txt?
robots = codebase / "templates/robots.txt"
if robots.is_file():
try:
shutil.copy(robots, output_dir)
except:
print(f"error copying {robots} to {output_dir}. Continuing.")
# .htaccess?
ht = codebase / "templates/.htaccess"
if ht.is_file():
try:
shutil.copy(ht, output_dir)
except:
print(f"error copying {ht} to {output_dir}. Continuing.")
2017-06-28 15:52:35 +02:00
2021-04-12 10:48:22 +02:00
print('done.');