{input['title']}
{input['date']}
backpy{{{{ ' | ' if minimal else ' ' }}}}raw{format_tags(input['tags'])}
{html}
from pathlib import Path
from argparse import ArgumentParser
import os
import re
import sys
import json
def link(src, dst, is_dir=False):
dst = Path(dst)
if dst.exists():
return
dst.parent.mkdir(exist_ok=True, parents=True)
dst.symlink_to(Path(src).absolute(), is_dir)
def glob_all_to(src, dst):
src = Path(src)
dst = Path(dst)
for page in Path(src).glob('**/*'):
if page.name.startswith('.'):
continue
if page.is_dir():
Path(f'{dst}/{page.relative_to(src)}').mkdir(exist_ok=True, parents=True)
else:
yield page, Path(f'{dst}/{page.relative_to(src)}')
def minify(html):
from bs4 import BeautifulSoup
import minify_html
soup = BeautifulSoup(html, 'html.parser')
for div in soup.find_all('div'):
div.unwrap()
tab = False
for code in soup.css.select('pre code'):
tab_width = min((x for x in (len(x) - len(x.lstrip(' ')) for x in code.get_text().split('\n')) if x > 1), default=4)
new = re.sub(f'^({" " * tab_width})+', lambda x: '\t' * (len(x.group(0)) // tab_width), code.get_text(), flags=re.M)
if new != code.string:
code.string = new
tab = True
code.unwrap()
sty = soup.new_tag('style')
sty.string = '*{tab-size:4;max-width:1000px}' if tab else '*{max-width:1000px}'
soup.insert(1, sty)
for tag in soup.find_all(True):
tag.attrs = {k: v for k, v in tag.attrs.items() if k != 'class'}
return minify_html.minify(str(soup))
def path_write(path, text):
path.parent.mkdir(exist_ok=True, parents=True)
if minimal and path.suffix == '.html':
path.write_text(minify(text))
else:
path.write_text(text)
base = Path('templates/base.html')
outdir = Path('/tmp/2h')
minimal = False
def make_page(page, dest):
import html
import frontmatter
global base
assert dest.is_relative_to(outdir)
if isinstance(base, Path):
base = base.read_text()
post = frontmatter.loads(page)
post['title'] = post['title']
post.content = re.sub(r"py{{ (.+?) }}", lambda x: str(eval(x.group(1))), post.content)
out = base.replace('{{ content }}', post.content).replace('{{ title }}', post['title'] + f' - {"μ.twoha.cc" if minimal else "u.twoha.cc"}')
if not minimal:
dump = json.dumps(post.metadata)
assert '-->' not in dump
content = f'\n{post.content}'
sheets = (f'' for x in ['/static/style.css', *post.get('sheets', [])])
out = out.replace('{{ sheets }}', '\n'.join(sheets))
path_write(outdir / '_partial' / dest.relative_to(outdir), content)
path_write(dest, out)
else:
path_write(dest, out)
def main(_):
outdir.mkdir(exist_ok=True, parents=True)
link('static', outdir / 'static', True)
link('music', outdir / 'music', True)
for s in Path('rootstatic').glob('*'):
link(f'rootstatic/{s.name}', outdir / s.name)
for s, d in glob_all_to('pages', outdir):
make_page(s.read_text(), d)
def songlist(_):
from mutagen.id3 import ID3
p = Path('static/songlist.json')
old = set()
songs = []
if p.exists():
for song in json.loads(p.read_text()):
old.add(song['href'])
songs.append(song)
for mp3 in Path('music').glob('*.mp3'):
i = ID3(mp3)
if f'/{mp3}' not in old:
songs.append({
'name': i.get('TIT2')[0],
'artist': (i.get('TCOM') or i.get('TPE1') or [''])[0],
'href': f'/{mp3}',
'src': ''
})
songs.sort(key=lambda x: (x['src'], x['artist'], x['name']))
with open('static/songlist.json', 'w') as f:
json.dump(songs, f, indent=2, ensure_ascii=False)
def format_tags(tags):
tags = (f'{t}' for t in tags)
return " ".join(tags)
def md_to_html(path):
import markdown
from markdown.extensions.codehilite import CodeHiliteExtension
import frontmatter
input = frontmatter.load(path)
import markdown_katex.extension
import markdown_katex.wrapper
ext = ['fenced_code', 'toc', 'tables', 'markdown_katex']
markdown_katex.extension.KATEX_STYLES = ''
sheets = ['/static/highlight.css']
if minimal:
import latex2mathml.converter
def convert(t, _):
return latex2mathml.converter.convert(t)
markdown_katex.wrapper.tex2html = convert
else:
real = markdown_katex.wrapper.tex2html
def convert(*args):
sheets.append('/static/katex.min.css')
markdown_katex.wrapper.tex2html = real
return real(*args)
markdown_katex.wrapper.tex2html = convert
ext.append(CodeHiliteExtension(css_class='hl'))
if 'toc' in input:
input.content = '[TOC]\n' + input.content
html = markdown.markdown(input.content, extensions=ext)
return f'''---
title: {repr(input['title'])}
sheets: {repr(sheets)}
---
{input['date']}
backpy{{{{ ' | ' if minimal else ' ' }}}}raw{format_tags(input['tags'])}