28d936d0

By: Michael Lynch <git@mtlynch.io>

Move media publishing out of the upload handlers

The image and video upload handlers each built a MediaItem inline, ran
the processor, and inserted the database row. That sequence was
duplicated across both paths and tied HTTP request handling directly to
media processing and storage, making the publish workflow awkward to
test without driving the full server.

Introduce a media.Service that owns the publish workflow end to end: it
constructs the MediaItem, processes the upload into its file variants,
then records the media row. Handlers now depend on a narrow
MediaPublisher interface and hand off the parsed request via
PublishImage/PublishAnimatedImage/PublishVideo, which share one
PublishParams type; the media type is implied by the method, so callers
cannot request an unsupported combination.

The service also owns the error contract: it translates processing
errors into media package sentinel errors, so handlers map
media.Err* to HTTP responses without reaching into the uploads package.

This keeps the write-files-before-insert ordering (which leaves orphaned
files rather than a gallery entry pointing at missing files on a failed
insert) in one place and lets the publishing logic be tested directly,
without the HTTP layer.

Also rename littlemoments.NewFileID to GenerateNewFileID and introduce
the GenerateNewFileIDFunc type for injecting it as a dependency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 2s 31s 31s 34s 2s
Eval 30s 1m06s 1m09s 1m40s 32s
Build - - - - -
Suite 2s 1m38s 1m37s 1m40s 35s

Timeline

0s10s20s30s40s50s1m1m10s1m20s1m30s1m40s