This repository has no description

blob: generic random-access blob store with FileStore + S3Store master

New pkg/blob defines a content-agnostic storage interface: type Store interface { URL(key string) string Open(ctx, key string) (Reader, error) NewWriter(ctx, key, contentType string) (Writer, error) Move(ctx, srcKey, dstKey string) error Delete(ctx, key string) error ParseLocation(location string) (key string, ok bool) } Reader is io.ReaderAt + Size + Close (random access is assumed; both the gstreamer demuxers and HTTP playback need it). Writer is io.Writer + Complete/Abort/Close, with Complete giving an atomic publish to the configured key (rename on disk, CopyObject+Delete on S3). Two implementations: - FileStore: rooted at a directory, staging via temp files under <root>/.staging/<uuid>, Complete = fsync+rename, Move = os.Rename. Includes a ParseLocation that accepts absolute paths, file:// URLs, and pre-relative keys (with .. escape sanitization). - S3Store: thin wrapper around the existing s3.ReaderAt / s3.MultipartWriter helpers, with the same staging-then-Move Complete contract. Move = CopyObject + DeleteObject. VOD processor (pkg/vod/process.go) is now Store-agnostic: - ProcessVOD takes a blob.Store instead of *config.CLI + S3-only plumbing - openSource translates the Upload row's backend-specific Location via store.ParseLocation, then store.Open - staging + content-addressed promotion go through Store.NewWriter + store.Move + Close-as-Abort Bootstrap (pkg/cmd) picks FileStore when S3 isn't configured (rooted at DataDir so it can see both uploads/ and vod/), S3Store otherwise. This unblocks single-node deployments and sets us up for future composed stores (memcache -> disk cache -> S3 archive). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>