Files
embycovers/AGENT.MD
T
ponzischeme89 e0b51397e7 Initial commit
2026-04-15 09:27:29 +12:00

396 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Emby Cover App Maintenance Guide
## Purpose
This app is a FastAPI-based internal tool for generating and applying custom Emby artwork.
It currently supports:
- Searching Emby movies and series
- Generating a wide `Thumb` image
- Optionally generating a matching tall `Primary` poster
- Using Emby-known logos and backdrops already attached to the item
- Applying generated artwork back to Emby
- Adding optional studio badges and a red `NEW EPISODES` series tag
The codebase is intentionally small and centralized:
- Backend and image pipeline: `app.py`
- Frontend UI, CSS, and browser logic: `templates/index.html`
- Static studio logos: `static/studios/`
- Generated cache files: `cache/`
## Runtime Model
The app is mostly a single-server process with one HTML page.
- FastAPI serves the UI and JSON/image endpoints
- `httpx.AsyncClient` is reused for Emby API calls
- Pillow is used for all image composition
- The browser page is a single template with inline CSS and JS
There is no frontend build system and no database.
## Configuration
Environment variables:
- `EMBY_URL`
- `EMBY_API_KEY`
Defaults are currently hard-coded in `app.py`. That is convenient for local usage but should be treated carefully if this app is ever shared more broadly.
## Key Files
### `app.py`
This file contains:
- App startup/shutdown
- Emby API helpers
- Image loading and upload helpers
- Font selection helpers
- The entire artwork render pipeline
- Search/image/apply API routes
This is the core of the system. Most behavior changes happen here.
### `templates/index.html`
This file contains:
- Full UI markup
- Full CSS
- Full browser-side state management
- Search/pagination logic
- Asset picker logic for Emby-known logos and backdrops
- Generate/apply button behavior
There is no JS framework. Everything is plain DOM code.
## How The App Works
### 1. Search flow
Browser:
- User types into the search box
- JS debounces and cancels stale requests
- `GET /api/search` is called with `q`, `start`, and `limit`
- Results are rendered in the left sidebar with lightweight poster images
Server:
- `/api/search` queries Emby `/Items`
- Returns paginated search results with metadata needed by the UI
- Search result posters are served via `/api/poster/{item_id}` using reduced dimensions for speed
### 2. Item image discovery flow
When an item is selected:
- The browser immediately starts a preview generate call
- In parallel, it calls `GET /api/images/{item_id}`
That endpoint returns the Emby-known images already attached to the item:
- `logos`
- `backdrops`
- `primaries`
The UI uses that to populate:
- Logo asset picker
- Backdrop asset picker
- Accurate backdrop counts
Important:
- This is Phase 1 of image browsing only
- It only exposes images Emby already knows for the item
- It does not yet perform remote/provider image lookup
### 3. Generate flow
Browser:
- `POST /api/generate`
- Sends the selected options:
- background mode
- selected backdrop index
- selected logo index
- logo position
- logo size
- darkness
- studio badge
- series tag
- whether to also generate a matching primary poster
Server:
- Pulls `Primary`, selected `Logo`, and selected `Backdrop` from Emby
- Builds the wide thumb with `generate_thumbnail(...)`
- Writes the thumb to cache
- Optionally builds and caches a tall primary via `generate_primary_cover(...)`
- Returns the thumb image stream to the browser
### 4. Apply flow
Browser:
- `POST /api/apply`
- Sends the same effective rendering settings as generate
Server:
- Rebuilds the same cache key
- Reads the cached thumb if present
- If missing, regenerates on demand to avoid cache-miss 400 failures
- Uploads the thumb to Emby as `Thumb`
- If `generate_primary` is enabled, uploads the cached or regenerated tall poster as `Primary`
Important:
- `Apply` must remain tolerant of cache misses
- Do not reintroduce a hard dependency on a pre-existing cache file only
## Rendering Pipeline
The central renderer is `generate_thumbnail(...)`.
It is used for:
- Wide thumbs
- Tall primary covers, through `generate_primary_cover(...)`
Pipeline stages:
1. Resolve background
2. Crop/fit to target aspect ratio
3. Apply darkening and vignette
4. Resolve logo or fallback title text
5. Position logo/text based on selected alignment
6. Add studio logo overlay
7. Add optional `NEW EPISODES` tag
8. Encode as PNG
### Layout assumptions
The renderer now contains separate behavior for tall layouts.
This matters because:
- Wide thumbnails and tall posters cannot share the same padding rules
- Tall primary posters need different bottom spacing and logo height caps
When changing artwork layout, always test both:
- `800x450` thumb
- `1000x1500` primary
## Caching
Cache files live in `cache/`.
Current cache helpers:
- Thumb cache: `<cache_key>.png`
- Primary cache: `<cache_key>.primary.png`
The cache key is derived from rendering inputs such as:
- item id
- backdrop index
- logo index
- colors
- logo position
- scale
- darkness
- studio settings
- series tag state
Important:
- If you add any new visual option, add it to `build_cache_key(...)`
- If you forget, the app will reuse stale cached artwork from a different configuration
## Image Selection Rules
### Logo images
Logo selection is index-based.
Notes:
- The app should always treat invalid or non-image logo responses as optional
- `emby_get_image_optional(...)` already guards against non-image content-types
- `generate_thumbnail(...)` also guards against invalid logo bytes and falls back to text
Do not make logo loading a hard-fail path.
### Backdrops
Backdrop selection is index-based and uses the asset picker.
If a backdrop is unavailable:
- The render should still succeed
- The fallback is blurred poster or solid background
## Upload Rules
Uploading back to Emby is done through `emby_upload_image(...)`.
Current behavior:
- Upload source image is normalized to RGB JPEG
- `Thumb` and `Primary` uploads both use that path
Be careful when changing upload behavior:
- Emby may accept source formats differently than display formats
- The preview proxy can preserve PNG content-type
- The upload path currently standardizes to JPEG intentionally
## Frontend State
The browser keeps a single `state` object in `templates/index.html`.
Key fields:
- `item`
- `backdropIndex`
- `logoIndex`
- `imageInfo`
- `searchQuery`
- `searchStart`
- `searchLimit`
- `searchTotal`
- `generated`
When adding a new visual option:
1. Add UI control
2. Add local JS state if needed
3. Include it in `POST /api/generate`
4. Include it in `POST /api/apply`
5. Add it to `build_cache_key(...)`
6. Apply it in the render pipeline
If any of those steps are skipped, behavior will drift.
## Laptop UX Constraints
The app has custom responsive rules for laptop-sized screens.
Areas that have already needed tuning:
- Preview area height
- Controls bar spacing
- Sidebar width
- Results panel layout
When changing layout:
- Test desktop wide view
- Test laptop-height view
- Test narrow desktop widths before mobile breakpoints
The controls container is particularly sensitive because the preview and controls compete vertically.
## Known Fragile Areas
### 1. Cache key drift
Symptoms:
- `Apply` uploads unexpected artwork
- Generate/apply feel out of sync
Cause:
- A new option was added to rendering but not to `build_cache_key(...)`
### 2. Emby returns non-image content for optional assets
Symptoms:
- PIL `UnidentifiedImageError`
Cause:
- Emby returned HTML, JSON, or another unexpected response for an optional asset
Current guardrails already exist. Preserve them.
### 3. Tall poster layout quality
Symptoms:
- Tall primary poster logo sits too low
- Title/logo scale looks like a stretched thumb layout
Cause:
- Using wide-layout spacing assumptions on primary posters
Always treat primary layout as distinct.
### 4. Search feels slow
Symptoms:
- Listing poster thumbnails load slowly
- Search UI feels laggy
Current mitigations:
- Paginated search
- Smaller poster fetches
- Shared HTTP client
- Stale request cancellation
Avoid switching result posters back to full-size originals.
## Safe Maintenance Workflow
When changing this app:
1. Read both `app.py` and the relevant section of `templates/index.html`
2. Keep generate/apply payloads aligned
3. Keep cache key aligned with render options
4. Test thumb and primary rendering
5. Run:
```powershell
python -m py_compile app.py
```
6. Manually verify:
- search
- item select
- generate
- apply
- optional primary generation
- logo/backdrop asset switching
## Recommended Future Enhancements
Likely next improvements:
- Separate primary preview in the UI
- Remote/provider image search from Emby metadata sources
- Better poster-specific typography/layout rules
- Better user-visible error reporting for partial apply failures
- Cache cleanup strategy
## Rules For Future Agents
- Do not split backend logic into multiple files unless there is a strong reason. This app is currently maintainable specifically because it is centralized.
- Do not introduce a frontend framework casually. The current plain JS approach is appropriate for the apps size.
- Do not remove the cache-miss regeneration fallback from `apply`.
- Do not assume Emby optional image endpoints always return valid images.
- Do not change wide-thumb behavior without checking tall-primary behavior too.
- If adding a new render option, update both generate and apply paths and the cache key in the same change.