#!/usr/bin/env python3 """ Build a JSON index for the NIU skylapse archive. Run this from the /skylapses directory on the server, for example: cd /var/www/html/skylapses python3 build_video_index.py It writes ./video_index.json, which skylapse_search.html uses. """ import json import re from collections import Counter from datetime import datetime, timezone from pathlib import Path ROOT = Path('.') OUTPUT = ROOT / 'video_index.json' VIDEO_EXTS = {'.mov', '.mp4', '.m4v', '.webm'} DATE_RE = re.compile(r'(? str: stem = Path(filename).stem parts = stem.split('_') if len(parts) <= 1: return 'video' return ' '.join(parts[1:]).replace('-', ' ').strip() or 'video' def main() -> None: rows = [] for path in ROOT.rglob('*'): if not path.is_file() or path.name == OUTPUT.name: continue if path.suffix.lower() not in VIDEO_EXTS: continue match = DATE_RE.search(path.name) if not match: continue rel = path.relative_to(ROOT).as_posix() archive = rel.split('/')[0] if '/' in rel else 'root' rows.append( { 'date': match.group(1), 'filename': path.name, 'path': rel, 'archive': archive, 'camera': camera_from_name(path.name), 'ext': path.suffix.lower().lstrip('.'), 'url': rel, } ) rows.sort(key=lambda x: (x['date'], x['archive'], x['filename'])) date_values = [row['date'] for row in rows] payload = { 'meta': { 'generated_at': datetime.now(timezone.utc).isoformat(), 'record_count': len(rows), 'archive_count': len({row['archive'] for row in rows}), 'camera_count': len({row['camera'] for row in rows}), 'min_date': min(date_values) if date_values else None, 'max_date': max(date_values) if date_values else None, 'archives': dict(sorted(Counter(row['archive'] for row in rows).items())), }, 'records': rows, } OUTPUT.write_text(json.dumps(payload, indent=2), encoding='utf-8') print(f'Wrote {len(rows)} records to {OUTPUT}') if __name__ == '__main__': main()