From faf8ee98b0c06cd98457ec77190853171f351688 Mon Sep 17 00:00:00 2001 From: neodk2004 Date: Sun, 16 Aug 2026 13:07:31 +0200 Subject: [PATCH] fix(videos): fan out posts that self-heal to READY via polling getByPostId's self-heal path (client polls while PROCESSING, checks Mux directly) set status/muxPlaybackId on READY but never called fanOutPost, unlike the mux webhook handler's video.asset.ready case. Videos that self-healed this way played fine but never entered any pet's feed. Found via a live upload test after the replica rollout. --- src/trpc/routers/videos.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/trpc/routers/videos.ts b/src/trpc/routers/videos.ts index 8e1d445..e9890aa 100644 --- a/src/trpc/routers/videos.ts +++ b/src/trpc/routers/videos.ts @@ -4,6 +4,8 @@ import { TRPCError } from "@trpc/server"; import { router, protectedProcedure } from "@/trpc/init"; import { mux } from "@/lib/mux"; import { assertPetOwnership } from "@/lib/assert-pet-ownership"; +import { redis } from "@/lib/redis"; +import { fanOutPost } from "@/lib/feed-helpers"; const MAX_VIDEO_DURATION_SECS = 180; // 3 min hard cap — Mux rejects longer via passthrough check @@ -80,6 +82,7 @@ export const videosRouter = router({ .query(async ({ ctx, input }) => { const videoPost = await ctx.prisma.videoPost.findUnique({ where: { postId: input.postId }, + include: { post: { select: { petId: true, createdAt: true } } }, }); if (!videoPost) throw new TRPCError({ code: "NOT_FOUND" }); @@ -95,7 +98,7 @@ export const videosRouter = router({ if (assetId) { const asset = await mux.video.assets.retrieve(assetId); if (asset.status === "ready" && asset.playback_ids?.[0]) { - return await ctx.prisma.videoPost.update({ + const updated = await ctx.prisma.videoPost.update({ where: { postId: input.postId }, data: { status: "READY", @@ -105,6 +108,19 @@ export const videosRouter = router({ aspectRatio: asset.aspect_ratio ?? null, }, }); + // Mirrors the mux/route.ts webhook handler's fan-out on + // video.asset.ready — without this, a video that self-heals + // here (webhook delayed or unreachable) never enters any + // pet's feed even though it plays fine. zadd is idempotent, + // so this is safe to run again if the real webhook also lands. + await fanOutPost( + redis, + input.postId, + videoPost.post.petId, + videoPost.post.createdAt, + ctx.prisma + ).catch((err) => console.error("[videos.getByPostId] fanOutPost failed", err)); + return updated; } else if (asset.status === "errored") { return await ctx.prisma.videoPost.update({ where: { postId: input.postId },