aboutsummaryrefslogtreecommitdiff
path: root/generate-gallery.py
blob: 773261ff89727f13db7a37dcd79692ac1c566bcd (plain)
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
158
159
160
161
162
163
164
165
166
167
#!/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:
            if 'Datei' in game:
                game['Name'] = pathlib.Path(game['Datei']).stem
            else:
                game['Name'] = 'Project'

        if 'Team' not in game:
            game['Team'] = ['anonym']
        else:
            game['Team'] = ', '.join(game['Team'])

        if 'Pfad' not in game:
            game['Pfad'] = str(parent.relative_to(root))

        if 'Lizenz' not in game:
            game['Lizenz'] = 'Alle Rechte vorbehalten'
        elif game['Lizenz'].startswith('CC') and game['Datei'].endswith('sb3'):
            game['Download'] = game['Datei']

        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}', file=sys.stderr)
                projectindir = indir / section / subsection / title
                projectoutdir = outdir / section / subsection / title
                infile = projectindir / project['Datei']

                shutil.copyfile(projectindir / 'meta.toml', projectoutdir / 'meta.toml')

                if 'Download' in project:
                    shutil.copyfile(projectindir / project['Download'],
                                    projectoutdir / project['Download'])

                # The project is not a scratch file
                if not infile.suffix == '.sb3':
                    shutil.copyfile(infile, projectoutdir / project['Datei'])
                    continue

                cmd = _cmd.format(infile='..' / infile, outdir='..' / projectoutdir)
                subprocess.run(cmd, cwd='turbowarp-packager-cli', check=True, shell=True)
                project['Datei'] = '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('--no-packaging',
                        action='store_true',
                        help='do not package all encountered scratch 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)

    if not args.no_packaging:
        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()