#!/usr/bin/env python3
"""Skript um eine HTML-Gallery fuer mehrere Scratch-Spiele zu erstellen"""
import argparse
import os
import pathlib
import tomllib
import shutil
import subprocess
import sys
from jinja2 import Environment, FileSystemLoader, select_autoescape
def nested_get(data_dict, key_sequence):
"""
Retrieves a value from a nested dictionary using an iterative approach.
Args:
data_dict (dict): The dictionary to traverse.
key_sequence (list): A list of keys representing the path.
Returns:
Any: The value at the specified path.
"""
current_level = data_dict
for key in key_sequence:
if key not in current_level:
return None
current_level = current_level[key]
return current_level
def nested_set(data_dict, key_sequence, value):
"""
Sets a value in a nested dictionary, creating intermediate dictionaries if needed.
Args:
data_dict (dict): The dictionary to modify.
key_sequence (list): A list of keys representing the path.
value (Any): The value to set.
"""
for key in key_sequence[:-1]:
data_dict = data_dict.setdefault(key, {})
data_dict[key_sequence[-1]] = value
def load_projects(root: str) -> dict:
"""Load all game meta data from the games directory"""
projects = {}
for _parent, dirs, files in os.walk(root):
parent = pathlib.Path(_parent)
scope = nested_get(projects, parent.parts[1:])
if scope is None and len(dirs):
scope = {}
nested_set(projects, parent.parts[1:], scope)
if not 'meta.toml' in files:
continue
with open(parent / 'meta.toml', 'rb') as meta_file:
game = tomllib.load(meta_file)
if 'name' not in game:
game['name'] = 'Project'
if 'authors' not in game:
game['authors'] = ['anonym']
else:
game['authors'] = ', '.join(game['authors'])
if 'path' not in game:
game['path'] = parent.relative_to(root)
if 'license' not in game:
game['license'] = 'Alle Rechte vorbehalten'
nested_set(projects, parent.parts[1:], game)
if len(projects) > 1:
del projects['test']
return projects
def prepare_outputdir(_outdir: str, all_projects: dict):
"""Prepare the output dir recreating the project structure"""
outdir = pathlib.Path(_outdir)
for section, subsections in all_projects.items():
sectiondir = outdir / section
for subsection, projects in subsections.items():
subsectiondir = sectiondir / subsection
for title in projects:
projectdir = subsectiondir / title
os.makedirs(projectdir, exist_ok=True)
def package_projects(_indir: str, _outdir: str, all_projects: dict):
"""Package all scratch projects to html"""
# Ensure that the local turbowarp-packer-cli is available through npm
subprocess.run('npm install'.split(), cwd='turbowarp-packager-cli', check=True)
_cmd = 'npx turbowarp-packager-cli -s ../turbowarp-packager-settings.json -i "{infile}" -o "{outdir}"'
indir = pathlib.Path(_indir)
outdir = pathlib.Path(_outdir)
for section, subsections in all_projects.items():
for subsection, projects in subsections.items():
for title, project in projects.items():
print(f'package {title}')
projectindir = indir / section / subsection / title
projectoutdir = outdir / section / subsection / title
infile = projectindir / project['entry']
print(projectindir / 'meta.toml', projectoutdir / 'meta.toml')
shutil.copyfile(projectindir / 'meta.toml', projectoutdir / 'meta.toml')
# The project is not a scratch file
if not infile.suffix == '.sb3':
print(infile, projectoutdir / project['entry'])
shutil.copyfile(infile, projectoutdir / project['entry'])
continue
cmd = _cmd.format(infile='..' / infile, outdir='..' / projectoutdir)
print(cmd)
subprocess.run(cmd, cwd='turbowarp-packager-cli', check=True, shell=True)
project['entry'] = 'demo_output.html'
def main():
"""Main entry point to generate a HTML Gallery"""
parser = argparse.ArgumentParser()
parser.add_argument('--author', default='Florian Fischer')
parser.add_argument('--title', default='Scratch Galerie')
parser.add_argument('--no-license', action='store_true')
parser.add_argument('--projects-dir', default='projects', help='location of the projects')
parser.add_argument('--output-dir',
default='build',
help='output location of the generated HTML code')
args = parser.parse_args()
env = Environment(loader=FileSystemLoader("templates"), autoescape=select_autoescape())
projects = load_projects(args.projects_dir)
print(projects, file=sys.stderr)
prepare_outputdir(args.output_dir, projects)
package_projects(args.projects_dir, args.output_dir, projects)
template = env.get_template("index.html")
with open(pathlib.Path(args.output_dir) / 'index.html', 'w', encoding='utf-8') as outfile:
print(template.render(projects=projects, **args.__dict__), file=outfile)
if __name__ == '__main__':
main()