Turning folders into web pages with Python: Difference between revisions

From XPUB & Lens-Based wiki
No edit summary
 
(11 intermediate revisions by 2 users not shown)
Line 3: Line 3:
==Turning a folder of pictures into a photo gallery==
==Turning a folder of pictures into a photo gallery==


''using Python + Jinja''
''generating HTML + CSS with Python + Jinja''


This is a way to generate web pages based on a collection of files stored on your computer or a shared server.  
This is a prototype to '''generate web pages''' based on '''a collection of files''' stored on your computer or a shared server.  


It basically is a way to make your own custom '''static site generator'''.
The following code will generate '''this photo gallery''': https://hub.xpub.nl/sergio/SI29/file-sharing/gallery/


The following code will generate this '''photo gallery''': https://hub.xpub.nl/sergio/SI29/file-sharing/gallery/
==Context==
 
This prototype is basically showing you how you can make your own custom '''static site generator'''.
 
A '''static site generator''' ('''SSG''') is a tools/script that helps you to make a website that is based on a bunch of static web pages, that don't use php, flask, node or other web application frameworks. Instead, a SSG is often loved for the fact that it creates a folder of static HTML/CSS/JS files that can be uploaded to a simple web host and require less maintenance, as they don't require a specific more complex technical setup.
 
'''Well-known SSG projects''' include:
 
* [https://getpelican.com/ Pelican]
* [https://gohugo.io/ Hugo]
* [https://www.11ty.dev/ Eleventy]
 
'''Examples''' of artistic projects/scripts/practices that generate static web pages:
 
* [https://git.vvvvvvaria.org/varia/bots/src/branch/master/LogBot varia's logbot] + [https://vvvvvvaria.org/logs/ generated logs]
* [https://draw-it-with-others.org/small-loops/ small-loops], by DIWO [credentials: <span style="background: dodgerblue; color: dodgerblue">tofu : esseresalaminioggi</span>
* [https://git.vvvvvvaria.org/varia/distribusi distribusi], examples where you can see it in use: 
** https://archive.simonbrowne.page/
** https://ururu.cloud/ (initiative by https://dianaband.in/ )
** https://vvvvvvaria.org/archive/
** https://networksofonesown.vvvvvvaria.org/
* [https://simonrepp.com/faircamp/ Faircamp], a SSG for audio producers, by Simon Repp


==How to make this?==
==How to make this?==
Line 32: Line 53:


Just copy and paste!
Just copy and paste!
or use scp:
   
   
  /var/www/html/SI29/file-sharing/gallery/assets/
  $ scp myimagefolder/* /var/www/html/SI29/file-sharing/gallery/assets/


===5. Work on main.py===
===5. Work on main.py===
Line 75: Line 98:


===7. Run the script===
===7. Run the script===
First install jinja2 if you don't have it installed:
$ uv pip install jinja2
Then run the Python script:


  $ uv run main.py
  $ uv run main.py
Line 225: Line 254:


Hopefully this triggers ideas for more extension points.
Hopefully this triggers ideas for more extension points.
[[Category:Workbooks]]

Latest revision as of 16:47, 21 April 2026

For the prototyping class on 10 February 2026 in the context of SI29.

Turning a folder of pictures into a photo gallery

generating HTML + CSS with Python + Jinja

This is a prototype to generate web pages based on a collection of files stored on your computer or a shared server.

The following code will generate this photo gallery: https://hub.xpub.nl/sergio/SI29/file-sharing/gallery/

Context

This prototype is basically showing you how you can make your own custom static site generator.

A static site generator (SSG) is a tools/script that helps you to make a website that is based on a bunch of static web pages, that don't use php, flask, node or other web application frameworks. Instead, a SSG is often loved for the fact that it creates a folder of static HTML/CSS/JS files that can be uploaded to a simple web host and require less maintenance, as they don't require a specific more complex technical setup.

Well-known SSG projects include:

Examples of artistic projects/scripts/practices that generate static web pages:

How to make this?

1. Install uv

(if you don't have uv already)

https://docs.astral.sh/uv/getting-started/installation/

$ curl -LsSf https://astral.sh/uv/install.sh | sh

2. Initialize project

$ uv init gallery

3. Prepare an assets folder

$ mkdir assets

4. Put some files in the assets folder

Just copy and paste!

or use scp:

$ scp myimagefolder/* /var/www/html/SI29/file-sharing/gallery/assets/

5. Work on main.py

from glob import glob
from jinja2 import Template

def main():
    assets = glob('assets/*')

    with open('template.html','r') as f:
        html_template = f.read()

    jinja_template = Template(html_template)
    output = jinja_template.render(assets=assets)

    with open('index.html', 'w') as f:
        f.write(output)

if __name__ == "__main__":
    main()

6. Work on your template.html

<style>
img {
  max-width: 200px;
  object-fit: contain;
}
</style>

<ol>
{% for asset in assets %} 
<li><img src="{{asset}}"/></li>
{% endfor %}
</ol>

7. Run the script

First install jinja2 if you don't have it installed:

$ uv pip install jinja2

Then run the Python script:

$ uv run main.py

8. See the result

Open the index.html file in your browser.

Possible extension points

Timestamps

# Sort by timestamp
assets = sorted(assets, key=os.path.getmtime)

# You can reverse the sort by passing reverse=True to the sorted function
assets = sorted(assets, key=os.path.getmtime, reverse=True)

Media type

File size

# Sort by size
assets = sorted(assets, key=os.path.getsize)

Anecdotes

How about setting up a small gossip system for our gallery?

Choose a file to annotate, and create a file with the extension .anecdote

$ touch photo_1_2026-02-02_11-56-09.jpg.anecdote

Open the anecdote with a text editor and write your gossips there!

$ nano photo_1_2026-02-02_11-56-09.jpg.anecdote

Then in our python code we check for anecdotes:

[...]

assets_or_anecdotes = []
for asset in assets: 

    # Every asset has a file...
    file_maybe_anecdote = {
        "file": asset
    }

    # But some also have an anecdote
    if asset.endswith('.anecdote'):
        # If it is we read its content into the file_maybe_anecdote dictionary
        with open(asset, 'r') as a:
            file_maybe_anecdote['anecdote'] = a.read()
    
    # Append it to the list of assets
    assets_or_anecdotes.append(file_maybe_anecdote)
    
[...]

output = jinja_template.render(assets=assets_or_anecdotes)

In the template, we use conditionals to check if an asset is an anecdote or not, and we render it accordingly.

<ol>
{% for asset in assets %} 
	{% if asset.anecdote %}
	<p>{{ asset.anecdote }}</p>
	{% else %}
	<li><img src="{{asset.file}}"/></li>
	{% endif %}
{% endfor %}
</ol>

(to try out: how to make more anecdotes for the same file?)

Alt text

How to repurpose the anecdotes system to write alt captions for the images?

Hyperlinks

Structured Metadata

# Add some metadata to the assets list

import os
import pwd
from datetime import datetime

#[...]

assets_with_meta = []
for asset in assets:
    # Load and parse stats about a file
    # https://docs.python.org/3/library/stat.html
    stat = os.stat(asset)
    
    size = stat.st_size

    # Work with datetime
    # https://www.programiz.com/python-programming/datetime/strftime
    last_modified = datetime
        .fromtimestamp(stat.st_mtime)
        .strftime("%m/%d/%Y, %H:%M:%S")

    # Get username from user id 
    # https://stackoverflow.com/questions/842059#comment3162593_2899055
    user = pwd.getpwuid(stat.st_uid).pw_name

    # Create a dictionary with all the info
    asset_with_meta = { 
            "file": asset,
            "user": user,
            "last_modified": last_modified,
            "size": size
        }

    # Append it to the list of assets
    assets_with_meta.append(asset_with_meta)

Then you can pass your assets_with_meta list to the template...

output = jinja_template.render(assets=assets_with_meta)

And play around with it in the template.html file

<ol>
{% for asset in assets %} 
<li>
	<img src="{{asset.file}}"/>
	{{asset.user}} - {{asset.last_modified}} - {{asset.size}}
</li>
{% endfor %}
</ol>

etc!!!

Hopefully this triggers ideas for more extension points.