Plan asset library implementation

This commit is contained in:
seanbetts committed 2026-04-26 11:41:12 +01:00
1 parent 340cb8be20
commit df87ba4ac4
2 files changed
+252

No files matched your search

+1
View File
@@ -3,6 +3,7 @@ __pycache__/
*.pyc
.venv/
node_modules/
.worktrees/
# Local agent/browser state
.local/
@@ -0,0 +1,251 @@
# Asset Library Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Generate a media-type-first asset library under each run folder so humans can browse videos, images, logos, poster frames, thumbnails, and variants without navigating source-mirrored URLs.
**Architecture:** Extend `scripts/save_visual_assets.py` after the existing raw download path. The raw `assets/discovered/` archive remains the canonical downloaded copy, and a new library layer copies retrieved files into `assets/library/` with readable filenames plus JSON/TSV manifests under `assets/manifests/`.
**Tech Stack:** Python standard library, `unittest`, existing shell/Python watch scripts.
---
### Task 1: Metadata Extraction Helpers
**Files:**
- Modify: `scripts/save_visual_assets.py`
- Test: `tests/test_save_visual_assets.py`
- [ ] **Step 1: Write failing tests**
Add tests that build a fake run with WordPress media JSON containing:
```python
{
"id": 433313,
"slug": "3-in-1-2",
"mime_type": "video/mp4",
"post": 433326,
"source_url": "https://komodostation.com/wp-content/uploads/2026/04/3-in-1.mp4",
"title": {"rendered": "3 in 1"},
"media_details": {
"width": 1920,
"height": 850,
"filesize": 12242748,
"sizes": {},
},
}
```
Expected helper output:
```python
{
"media_id": "433313",
"parent_id": "433326",
"title": "3 in 1",
"width": 1920,
"height": 850,
"filesize": 12242748,
}
```
- [ ] **Step 2: Run tests and confirm failure**
Run:
```bash
python3 -m unittest tests/test_save_visual_assets.py
```
Expected: failure because metadata helper functions do not exist.
- [ ] **Step 3: Implement metadata helpers**
Add small helpers in `scripts/save_visual_assets.py`:
```python
def extract_media_metadata(obj: dict, source_path: str) -> dict:
details = obj.get("media_details") if isinstance(obj.get("media_details"), dict) else {}
title = obj.get("title") if isinstance(obj.get("title"), dict) else {}
return {
"media_id": str(obj.get("id") or ""),
"parent_id": str(obj.get("post") or ""),
"title": title.get("rendered") or obj.get("slug") or "",
"slug": obj.get("slug") or "",
"mime": obj.get("mime_type") or "",
"width": details.get("width") or "",
"height": details.get("height") or "",
"filesize": details.get("filesize") or "",
"source_json": source_path,
}
```
Update collection to store metadata alongside URL and MIME.
- [ ] **Step 4: Run tests and confirm pass**
Run:
```bash
python3 -m unittest tests/test_save_visual_assets.py
```
Expected: all tests pass.
### Task 2: Library Classification And Filenames
**Files:**
- Modify: `scripts/save_visual_assets.py`
- Test: `tests/test_save_visual_assets.py`
- [ ] **Step 1: Write failing tests**
Add tests for these classification outcomes:
```python
("video/mp4", "3-in-1.mp4", "3 in 1", 1920, 850) -> "videos"
("image/svg+xml", "controllerLogo.svg", "controllerLogo", "", "") -> "logos"
("image/png", "videoframe_789.png", "videoframe_789", 1920, 1080) -> "poster-frames"
("image/avif", "d760...-150x150.avif", "spec image", 150, 150) -> "thumbnails"
("image/avif", "d760...-2048x988.avif", "spec image", 2048, 988) -> "variants"
("image/avif", "d760....avif", "spec image", 2048, 988) -> "images"
```
Add a filename test:
```python
build_library_filename(metadata, url, "steam-controller") == "steam-controller_433313_3-in-1_1920x850.mp4"
```
- [ ] **Step 2: Run tests and confirm failure**
Run:
```bash
python3 -m unittest tests/test_save_visual_assets.py
```
Expected: failure because classification and filename helpers do not exist.
- [ ] **Step 3: Implement helpers**
Add helpers:
- `slugify(value: str) -> str`
- `has_size_suffix(url: str) -> bool`
- `is_thumbnail_size(width, height, url) -> bool`
- `infer_hardware(metadata, url, sources) -> str`
- `classify_library_category(metadata, url) -> str`
- `build_library_filename(metadata, url, hardware) -> str`
Use `steam-controller`, `steam-machine`, `steam-frame`, and `unknown` hardware slugs.
- [ ] **Step 4: Run tests and confirm pass**
Run:
```bash
python3 -m unittest tests/test_save_visual_assets.py
```
Expected: all tests pass.
### Task 3: Generate Library Files And Manifests
**Files:**
- Modify: `scripts/save_visual_assets.py`
- Test: `tests/test_save_visual_assets.py`
- [ ] **Step 1: Write failing integration test**
Create a test fake run containing a video, logo, poster frame, thumbnail, and variant. Mock downloads with valid bytes. Run `save_visual_assets.main()` and assert:
```text
assets/library/videos/steam-controller_433313_3-in-1_1920x850.mp4
assets/library/logos/steam-controller_413728-controller-logo.svg
assets/library/poster-frames/steam-controller_433298_videoframe-789_1920x1080.png
assets/library/thumbnails/steam-controller_433298_videoframe-789_150x150.png
assets/library/variants/steam-controller_433477_spec-image_2048x988.avif
assets/manifests/asset-library.json
assets/manifests/asset-library.tsv
```
Assert manifest rows include original URL, raw path, library path, category, hardware, media ID, parent ID, dimensions, MIME, detected format, and source JSON.
- [ ] **Step 2: Run tests and confirm failure**
Run:
```bash
python3 -m unittest tests/test_save_visual_assets.py
```
Expected: failure because library generation does not exist.
- [ ] **Step 3: Implement library generation**
After a download is retrieved or found cached:
1. Build metadata.
2. Choose category.
3. Build a readable filename.
4. Copy the raw downloaded file into `assets/library/<category>/`.
5. Resolve filename collisions by appending `-2`, `-3`, etc.
6. Write JSON and TSV manifests under `assets/manifests/`.
- [ ] **Step 4: Run tests and confirm pass**
Run:
```bash
python3 -m unittest tests/test_save_visual_assets.py
```
Expected: all tests pass.
### Task 4: Regenerate Current Run And Verify
**Files:**
- Runtime artifacts: `/Users/sean/steam_hardware_watch/2026-04-26/assets/library`
- Runtime artifacts: `/Users/sean/steam_hardware_watch/2026-04-26/assets/manifests`
- [ ] **Step 1: Regenerate assets**
Run:
```bash
python3 scripts/save_visual_assets.py --run-dir /Users/sean/steam_hardware_watch/2026-04-26 --base-dir /Users/sean/steam_hardware_watch
```
- [ ] **Step 2: Verify media-type folders**
Run:
```bash
find /Users/sean/steam_hardware_watch/2026-04-26/assets/library -maxdepth 2 -type f | sed 's#.*/assets/library/##' | sort | head -n 80
```
Expected: files appear under `videos/`, `images/`, `logos/`, `poster-frames/`, `thumbnails/`, and `variants/`.
- [ ] **Step 3: Verify manifests**
Run:
```bash
python3 -m json.tool /Users/sean/steam_hardware_watch/2026-04-26/assets/manifests/asset-library.json >/dev/null
test -s /Users/sean/steam_hardware_watch/2026-04-26/assets/manifests/asset-library.tsv
```
Expected: exit 0.
- [ ] **Step 4: Run final tests**
Run:
```bash
python3 -m py_compile scripts/save_visual_assets.py
python3 -m unittest tests/test_save_visual_assets.py
```
Expected: all tests pass.