aboutsummaryrefslogtreecommitdiff
path: root/generate-gallery.py
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2026-01-28 16:03:16 +0100
committerFlorian Fischer <florian.fischer@muhq.space>2026-01-28 16:03:16 +0100
commit96373c6d93f0c310f8b777494c5a40c27fd78a0b (patch)
treebfb9825ac80cd2331ede1adc21f84230813a1f22 /generate-gallery.py
parenta584e47488212c61defe9e266d23eb036c0148e4 (diff)
downloadscratch-gallery-96373c6d93f0c310f8b777494c5a40c27fd78a0b.tar.gz
scratch-gallery-96373c6d93f0c310f8b777494c5a40c27fd78a0b.zip
initial commit
Diffstat (limited to 'generate-gallery.py')
-rwxr-xr-xgenerate-gallery.py154
1 files changed, 154 insertions, 0 deletions
diff --git a/generate-gallery.py b/generate-gallery.py
new file mode 100755
index 0000000..454d093
--- /dev/null
+++ b/generate-gallery.py
@@ -0,0 +1,154 @@
+#!/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, _, files in os.walk(root):
+ parent = pathlib.Path(_parent)
+ scope = nested_get(projects, parent.parts[1:])
+ if scope is None:
+ 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)
+
+ 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()