Skip to content

feat(media-animations): OS-level manifest cache + lightweight listing + download UX - #544

Merged
AIEraDev merged 8 commits into
masterfrom
feat/media-animation-export-pipeline
Nov 6, 2026
Merged

AIEraDev merged 8 commits into
masterfrom
feat/media-animation-export-pipeline

Conversation

@AIEraDev

@AIEraDev AIEraDev commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Two-tier catalog: summary listing (~200 bytes/item) + on-demand manifest fetch. MediaAnimationCacheManager stores tracks under AppCache/media-animations/ (mirrors stickerCacheManager). Download button per card. Fixes updateMotion no-op bug (was passing empty tracks to compiler). tsc + i18n check pass.

…ownload UX

- Add MediaAnimationCacheManager (src/features/media-animations/cache/mediaAnimationCache.ts)
  modeled on stickerCache.ts: BaseDirectory.AppCache, index.json, per-slug JSON files,
  isCached / ensureDownloaded / getManifest / clearCache; graceful browser degradation

- Update mediaAnimationCatalog.ts:
  * Import mediaAnimationCacheManager + AnimationManifest
  * Append &fields=summary to category fetch URL (lighter response)
  * normalizeMediaPreset returns tracks:[] and defaultDuration:0.5 (summary has no tracks)
  * Add cachedManifests in-memory map + best-effort initialize() at module load
  * Add downloadManifest() export (ensureDownloaded + populates cachedManifests)
  * getMediaAnimationPreset() merges tracks/defaultDuration from cached manifest when present

- Update PresetCard.tsx:
  * New props: isDownload  * New props: isDownload  * New props: isDownload  * New props: isDownload  * New props: isDownload  * New props: isDownload  * New props: isDownload  * New props: isDownload  * New props: isDo(bo  * New props: isDownload  * New props: isDownload  * New props: isDownload  * New pro:
  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im wn  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im / isDownloading / onDownload to each P  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  * Im  xport into cacheManager.ts

tsc: 0 errors
…ownload UX

Fixes all review findings from .agents/tasks/media-animation-cache/review.json:

1. Fix implicit isDownloaded re-render dependency (review finding 1)
   - Added explicit downloadedIds state Set in MediaAnimationSection
   - handleDownload now updates downloadedIds in finally block alongside
     clearing downloadingIds, making the checkmark update unconditionally

2. Eliminated (preset as any) casts for revisionId/thumbnailUrl/previewUrl (finding 2)
   - Defined MediaAnimationPreset sub-interface in mediaAnimationCache.ts extending
     RemoteTextAnimationPreset with thumbnailUrl?, previewUrl?, revisionId?
   - Updated categorySnapshots, downloadedPresets, normalizeMediaPreset, and
     getMediaAnimationPreset to use MediaAnimationPreset throughout
   - MediaAnimationSection imports MediaAnimationPreset and uses typed access

3. Moved import to top of cacheManage3. Moved import to top of cacheManage3. Moved import to top of cacheManage3. Moved import to top of cacheManage3. Moved import to top of cacheManafol3. Moved import to top of cacheManage3. Moved import to top of cacheManageding 4)3. Moved import to top of cacheManage3. Moved import to top of cachwn3. Moved import to top of cacheManage3. Moved import to top of cu/zh-3. Moved import to top of cacheManage3. Moved import to top of cacheMasta3. Moved import to top of cacheManage3. Moved import to top of cacheManay-tracks on restart fixed (finding 5)
   - MediaAnimationCacheManager.initialize() now calls hydrateManifests() after
     loadIndex(), eagerly reading all previously-downloa     loadIndex(), eagerly reading all previously-downloa     loadIndex(), on     loadIndex(), eagerly reading all previously-downloa     loadIndex(),  ho     loadIndex(), eagerly reading all previously-downloa     loade a     loadIndex(), eagerly reading all previously-downloa     loadIndex(), ean so listeners re-evaluate isCached()

6. SHA-256 hash verification on read (finding 6)
   - Added readAndVerifyManifest() private helper that computes SHA-256 of
     the raw bytes and compares against the stored contentHash
   - ensureDownloaded() stores the hash of the actual written bytes
   - getManifest() and hydrateManifests() go through readAndVerifyManifest();
     corrupted files are evicted from the index and deleted from disk
   - Mirrors the sticker cache pattern

Verification:
- npx tsc --noEmit: 0 errors
- npm run i18n:check: all 9 locales 100% valid
Blocking fixes:
1. updateMotion now passes manifests-merged catalog to compileClipMotionKeyframes
   — catalogSnapshot.presets has tracks:[] (summary); getMediaAnimationPreset()
   merges manifest tracks synchronously before compilation
2. Revert combo→loop phase-key: combo presets use phase 'in' and compile via
   the inPreset    the inPreset    the inPreset    the inPreset    the inPreset    the inPreset    the inPreset    the inPresro   the inPreset    the inPreset    the inPreset    the inPresdr   the inPreset    the inPreset    the inPreset    the ileD   the inPreset    the inPreset    the inPreset    the ilick   the inPreset    the inPreset    the inPreset.t   the inPreset    the inPre     the inPreset    the inPimationTrackProperty, join, appCac   the inPreset    the ind but   the inPreset    the inPreng current action
   (selectAnimation vs downloadAnimation); i18n key added to all 9 locales
7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.om MediaAnimationCacheManager

Verification: tsc --noEmit 0 errors; npm run i18n:check all 9 locales 100%
Remove the status === 'ready' short-circuit in fetchMediaAnimationCategory
so switching tabs always triggers a fresh fetch for that category. The
in-flight dedup (categoryRequests) still prevents concurrent duplicate calls.

This fixes Out/Combo tabs showing stale In data caused by module-level
state persisting across hot-reloads with the old non-filtered fetch format.
…store

Two bugs fixed:
1. Revert over-aggressive re-fetch: restore status==='ready' short-circuit
   so switching tabs doesn't re-fetch already-loaded categories
2. Fix Combo/Out showing In data: combo was mapped to phases['in'] in the
   hook, returning In presets on the Combo tab. Now combo is stored in the
   'loop' phases slot and read back via 'loop' — each of the three tabs
   (in/out/combo) reads a fully independent snapshot slot.
   compileClipMotionKeyframes is unaffected: it uses the flat merged presets
   array where combo presets retain phase='in' for the inPreset branch.
…nent

Extract grid rendering into a CategoryGrid component keyed by activeTab.
Switching tabs unmounts the old grid and mounts a fresh one — each instance
owns its own useMediaAnimationCategory hook, fetch lifecycle, and render
state. Eliminates all cross-tab snapshot bleed regardless of store internals.

Also restores status==='ready' short-circuit in fetchMediaAnimationCategory
so categories only fetch once per session rather than on every tab switch.
… map

Root cause of all tabs showing same data:
useMediaAnimationCategory was reading from snapshot.phases which routes
through the foreign AnimationCatalogSnapshot type — phases['in'] was being
returned for combo, phases['out'] was referencing stale mutable objects, and
React's reference equality check never detected changes.

Fix:
- Add categorySnapshotRefs — a separate Record keyed by MediaAnimationCategory
  that holds immutable copies of each category's snapshot
- emit() rebuilds only the refs that actually changed (status/presets/error)
  so React sees new references exactly when data changes
- useMediaAnimationCategory reads from categorySnapshotRefs[category] directly
  — no phases map, no type casting, no cross-category bleed possible
@AIEraDev
AIEraDev merged commit 2c7d48f into master Nov 6, 2026
7 checks passed
@AIEraDev
AIEraDev deleted the feat/media-animation-export-pipeline branch November 6, 2026 01:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant