Skip to content

Videos Module — #VideoVisit (+ AudioPost)

Location: apps/api/src/modules/videos/ and apps/api/src/modules/audio/

Aggregate roots: VideoVisit, VideoReaction, VideoRepost, VideoComment; AudioPost

Short video cards per interest (Wave 6 §3.5.3). The file itself goes through the media pipeline (R2) — this context only persists the URL + metadata. Cross-context refs (authorId, interestId) are UUID-only.


VideoVisit

FieldNotes
mediaUrl / thumbnailUrlR2 public URLs from the media presign flow
caption≤ 280 chars
durationSecclamped to ≤ 60 s (MAX_VIDEO_SECONDS)
interestIdoptional interest anchor (UUID, no FK)
removedAtadmin soft-remove flag

Every card is enriched with author, interest, likeCount / repostCount / commentCount, and viewer flags (viewerHasLiked, viewerHasReposted) via batched groupBy aggregations.


HTTP Endpoints

All under /api/v1/videos, JwtAuthGuard.

MethodPathDescription
GET/videos?interestId=&limit=&cursor=Feed, newest first (offset cursor)
POST/videosCreate a clip (≤ 60 s, throttled 5/min, 30/h)
GET/videos/by-author/:authorIdAuthor's clips (latest 30)
GET/videos/:idSingle card
DELETE/videos/:idAuthor hard-deletes
POST/videos/:id/likeLike (idempotent upsert)
DELETE/videos/:id/likeUnlike
POST/videos/:id/repostRepost (idempotent upsert)
DELETE/videos/:id/repostUn-repost
GET/videos/:id/commentsComments, oldest first (≤ 200)
POST/videos/:id/commentsAdd a comment (≤ 2000 chars)
DELETE/videos/comments/:commentIdAuthor soft-removes (removedAt)

Likes/reposts are unique per (videoId, userId); engagement mutations return the refreshed card.


Discovery Integration

The unified discovery stream (/modules/discovery) merges videos into the feed and attaches an engagement block (engagement: { ... }) for type === 'video' items only — likes/reposts/comments counts ride along with the stream card.


AudioPost

Sibling context in apps/api/src/modules/audio/ — short voice notes per interest, same shape (mediaUrl, caption ≤ 280, durationSec clamped to ≤ 300 s, removedAt). Routes mirror the video CRUD: GET /audio, POST /audio, GET /audio/by-author/:authorId, GET /audio/:id, DELETE /audio/:id.

Note: AudioPost has no engagement layer yet — no likes, reposts, or comments. Adding parity with VideoVisit is a follow-up.


Events

Neither context emits or consumes domain events today — engagement is read-model only.


Admin

Both kinds are moderated through the unified content endpoint: GET /admin/content?kind=videos|audio, POST /admin/content/:kind/:id/remove|restore (sets/clears removedAt). See /modules/content-contexts for the full kind list.

Regulus — invite-only social-knowledge platform