1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
#!/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()
|