Files
ETPlot/generate_gallery.py
T
Kylian Schmidt 976d90f26b Refactor gallery generator with smart file handling and improved UI
Key improvements:
- Add intelligent file update checking with 30-second buffer to avoid unnecessary operations
- Implement efficient PDF to PNG conversion (only when source is newer)
- Add smart file copying (skip if target is up-to-date)
- Create proper folder structure using plot_root/source_name pattern
- Improve template with better text handling for long plot names
- Add text wrapping, truncation, and hover tooltips for plot names
- Remove unnecessary directory cleaning for true incremental updates
- Fix title display issue (was showing '.' instead of 'Gallery')
- Add comprehensive logging showing what's processed vs skipped

Performance benefits:
- Subsequent runs are significantly faster (only processes changed files)
- Reduces ImageMagick conversions and file I/O operations
- Maintains file system timing robustness with buffer delays

UI improvements:
- Better handling of long filenames with word wrapping
- Constrained text areas prevent overlap between thumbnails
- Hover tooltips show full names when truncated
- Responsive grid layout maintained
2025-07-07 13:27:25 +02:00

185 lines
6.0 KiB
Python

import subprocess
import shutil
from pathlib import Path
from jinja2 import Environment, FileSystemLoader
from config import Config
CONFIG = Config.from_yaml("config.yaml")
env = Environment(loader=FileSystemLoader("."))
template = env.get_template("template.html")
def convert_pdf_to_png(pdf_path: Path):
png_path = pdf_path.with_suffix(".png")
# Check if PNG exists and is newer than PDF (with 30 second buffer)
if png_path.exists():
pdf_mtime = pdf_path.stat().st_mtime
png_mtime = png_path.stat().st_mtime
if png_mtime >= (pdf_mtime + 30): # 30 second buffer
# PNG is up to date, no need to convert
return
else:
print(f"PDF {pdf_path.name} is newer than PNG, reconverting...")
print(f"Converting {pdf_path}{png_path}")
subprocess.run([
"convert",
"-density", str(CONFIG.png_dpi),
str(pdf_path),
"-quality", "95",
str(png_path)
], check=True)
def needs_update(source_file: Path, target_file: Path) -> bool:
"""Check if target file needs to be updated based on source file modification time"""
if not target_file.exists():
return True
source_mtime = source_file.stat().st_mtime
target_mtime = target_file.stat().st_mtime
# Return True if source is newer than target (with 30 second buffer)
return source_mtime > (target_mtime + 30)
def build_gallery(source_dir: Path, web_dir: Path, relative_path: Path = None):
"""
Build gallery for a directory, copying files to web directory and generating index.html
Args:
source_dir: Source directory containing PDFs
web_dir: Target web directory
relative_path: Relative path for navigation (None for root)
"""
if relative_path is None:
relative_path = Path(".")
# Ensure web directory exists
target_dir = web_dir / relative_path
target_dir.mkdir(parents=True, exist_ok=True)
# Find all PDFs in source directory
pdfs = sorted(source_dir.glob("*.pdf"))
items = []
for pdf in pdfs:
# Convert PDF to PNG in source directory
convert_pdf_to_png(pdf)
png_path = pdf.with_suffix(".png")
# Copy both PDF and PNG to target directory only if needed
target_pdf = target_dir / pdf.name
target_png = target_dir / png_path.name
if needs_update(pdf, target_pdf):
print(f"Copying {pdf.name} to {target_pdf}")
shutil.copy2(pdf, target_pdf)
else:
print(f"Skipping {pdf.name} (up to date)")
if png_path.exists() and needs_update(png_path, target_png):
print(f"Copying {png_path.name} to {target_png}")
shutil.copy2(png_path, target_png)
elif png_path.exists():
print(f"Skipping {png_path.name} (up to date)")
items.append({
"name": pdf.name,
"pdf_href": pdf.name,
"png_href": png_path.name
})
# Find subdirectories in source
subdirs = sorted([d.name for d in source_dir.iterdir() if d.is_dir()])
# Generate index.html in target directory
output_html = target_dir / "index.html"
# Create a meaningful title
if str(relative_path) == ".":
title = "Gallery"
else:
title = f"Gallery: {relative_path}"
with output_html.open("w") as f:
f.write(template.render(
title=title,
items=items,
subdirs=subdirs,
relpath=str(relative_path)
))
print(f"Generated {output_html}")
# Recursively process subdirectories
for subdir in subdirs:
source_subdir = source_dir / subdir
new_relative_path = relative_path / subdir
build_gallery(source_subdir, web_dir, new_relative_path)
def loop_over_sources():
web_root = Path(CONFIG.web_folder)
for source in CONFIG.sources:
print(f"Processing {source.name}: {source.path}")
if source.path.is_dir():
# Create a subdirectory based on plot_root and source name
source_web_dir = web_root / CONFIG.plot_root / source.name
build_gallery(source.path, source_web_dir)
print(f"Processed {source.name}: {source.path}")
elif source.path.suffix == ".pdf":
# For single PDF files, copy to web root with plot_root and source name as directory
source_web_dir = web_root / CONFIG.plot_root / source.name
convert_pdf_to_png(source.path)
png_path = source.path.with_suffix(".png")
# Ensure web directory exists
source_web_dir.mkdir(parents=True, exist_ok=True)
# Copy PDF and PNG to web directory only if needed
target_pdf = source_web_dir / source.path.name
target_png = source_web_dir / png_path.name
if needs_update(source.path, target_pdf):
print(f"Copying {source.path.name} to {target_pdf}")
shutil.copy2(source.path, target_pdf)
else:
print(f"Skipping {source.path.name} (up to date)")
if png_path.exists() and needs_update(png_path, target_png):
print(f"Copying {png_path.name} to {target_png}")
shutil.copy2(png_path, target_png)
elif png_path.exists():
print(f"Skipping {png_path.name} (up to date)")
# Create items list for template
items = [{
"name": source.path.name,
"pdf_href": source.path.name,
"png_href": png_path.name
}]
# Generate index.html for single PDF
output_html = source_web_dir / "index.html"
with output_html.open("w") as f:
f.write(template.render(
title=source.name,
items=items,
subdirs=[],
relpath=source.name
))
print(f"Generated {output_html}")
print(f"Processed {source.name}: {source.path}")
if __name__ == "__main__":
loop_over_sources()
print("Done")