192 lines
5.8 KiB
Python
192 lines
5.8 KiB
Python
from doit import create_after
|
|
from glob import glob
|
|
from itertools import chain
|
|
|
|
|
|
def reltouch(source_filename, dest_filename):
|
|
from os import stat, utime
|
|
stat_res = stat(source_filename)
|
|
utime(dest_filename, ns=(stat_res.st_atime_ns, stat_res.st_mtime_ns))
|
|
|
|
|
|
def resize_image(basename, width, image_format):
|
|
from PIL import Image
|
|
with Image.open('assets/{}.png'.format(basename)) as im:
|
|
if 'A' in im.getbands() and image_format != 'jpeg':
|
|
im = im.convert('RGBA')
|
|
else:
|
|
im = im.convert('RGB')
|
|
height = im.height * width // im.width
|
|
new = im.resize((width, height), resample=Image.LANCZOS)
|
|
if image_format == 'jpeg':
|
|
kwargs = dict(
|
|
optimize=True,
|
|
progressive=True,
|
|
quality=80,
|
|
)
|
|
elif image_format == 'webp':
|
|
kwargs = dict(
|
|
quality=79,
|
|
)
|
|
elif image_format == 'png':
|
|
kwargs = dict(
|
|
optimize=True,
|
|
)
|
|
new.save('static/{}-{}.{}'.format(basename, width, image_format),
|
|
**kwargs)
|
|
reltouch('assets/{}.png'.format(basename),
|
|
'static/{}-{}.{}'.format(basename, width, image_format))
|
|
|
|
|
|
def task_logotype():
|
|
"""resize and convert logotype"""
|
|
widths = (200, 400, 600, 800)
|
|
image_formats = ('jpeg', 'webp')
|
|
for width in widths:
|
|
for image_format in image_formats:
|
|
yield dict(
|
|
name='{}.{}'.format(width, image_format),
|
|
actions=[(resize_image,
|
|
('logotype', width, image_format))],
|
|
targets=[f'static/logotype-{width}.{image_format}'],
|
|
file_dep=['assets/logotype.png'],
|
|
clean=True,
|
|
)
|
|
|
|
|
|
def task_service_icon():
|
|
"""resize and convert service icons"""
|
|
widths = (20, 40, 80)
|
|
formats = ('webp', 'png')
|
|
for width in widths:
|
|
for image_format in formats:
|
|
for basename in ('twitter', 'mastodon', 'misskey'):
|
|
yield dict(
|
|
name='{}-{}.{}'.format(basename, width, image_format),
|
|
actions=[(resize_image, (basename, width, image_format))],
|
|
targets=[
|
|
'static/{}-{}.{}'.format(basename, width,
|
|
image_format)],
|
|
file_dep=['assets/{}.png'.format(basename)],
|
|
clean=True,
|
|
)
|
|
|
|
|
|
def task_copy():
|
|
"copy assets verbatim"
|
|
|
|
assets = ('icon.png', 'logotype.png')
|
|
|
|
def do_the_thing(src, dst):
|
|
from shutil import copy
|
|
copy(src, dst)
|
|
reltouch(src, dst)
|
|
|
|
for asset in assets:
|
|
src = 'assets/{}'.format(asset)
|
|
dst = 'static/{}'.format(asset)
|
|
yield dict(
|
|
name=asset,
|
|
actions=[(do_the_thing, (src, dst))],
|
|
targets=[dst],
|
|
file_dep=[src],
|
|
clean=True,
|
|
)
|
|
|
|
|
|
def task_minify_css():
|
|
"""minify css file with csscompressor"""
|
|
|
|
from csscompressor import compress
|
|
|
|
def minify():
|
|
with open('assets/styles.css') as in_:
|
|
with open('static/styles.css', 'w') as out:
|
|
out.write(compress(in_.read()))
|
|
reltouch('assets/styles.css', 'static/styles.css')
|
|
|
|
return dict(
|
|
actions=[minify],
|
|
targets=['static/styles.css'],
|
|
file_dep=['assets/styles.css'],
|
|
clean=True,
|
|
)
|
|
|
|
|
|
def task_rollup():
|
|
"""rollup javascript bundle"""
|
|
|
|
filenames = ['settings.js', 'instance_buttons.js']
|
|
for filename in filenames:
|
|
src = 'assets/{}'.format(filename)
|
|
dst = 'static/{}'.format(filename)
|
|
name = filename.split('.')[0]
|
|
yield dict(
|
|
name=filename,
|
|
file_dep=list(chain(
|
|
# fuck it
|
|
glob('assets/*.js'),
|
|
glob('components/*.html'))) + ['rollup.config.js'],
|
|
targets=[dst],
|
|
clean=True,
|
|
actions=[
|
|
['node_modules/.bin/rollup', '-c',
|
|
'-i', src, '-o', dst, '-n', name, '-f', 'iife'],
|
|
],
|
|
)
|
|
|
|
|
|
@create_after('logotype')
|
|
@create_after('service_icon')
|
|
@create_after('copy')
|
|
@create_after('minify_css')
|
|
@create_after('rollup')
|
|
def task_compress():
|
|
"""
|
|
make gzip and brotli compressed versions of each
|
|
static file for the server to lazily serve
|
|
"""
|
|
|
|
files = chain(
|
|
glob('static/*.css'),
|
|
glob('static/*.js'),
|
|
glob('static/*.jpeg'),
|
|
glob('static/*.png'),
|
|
glob('static/*.webp'),
|
|
)
|
|
|
|
def compress_brotli(filename):
|
|
import brotli
|
|
with open(filename, 'rb') as in_:
|
|
with open(filename + '.br', 'wb') as out:
|
|
out.write(brotli.compress(in_.read()))
|
|
reltouch(filename, filename+'.br')
|
|
|
|
def compress_gzip(filename):
|
|
import gzip
|
|
with open(filename, 'rb') as in_:
|
|
with gzip.open(filename + '.gz', 'wb') as out:
|
|
out.write(in_.read())
|
|
reltouch(filename, filename+'.gz')
|
|
|
|
for filename in files:
|
|
yield dict(
|
|
file_dep=(filename,),
|
|
targets=(filename+'.br',),
|
|
name=filename+'.br',
|
|
actions=[(compress_brotli, (filename,))],
|
|
clean=True,
|
|
)
|
|
yield dict(
|
|
file_dep=(filename,),
|
|
targets=(filename+'.gz',),
|
|
name=filename+'.gz',
|
|
actions=[(compress_gzip, (filename,))],
|
|
clean=True,
|
|
)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import doit
|
|
doit.run(globals())
|