• Extends the 跳舞直播间 (dance room) system to one independent live room
    per character, with an operator playback-management/blacklist layer
    (T39), a "我的作品" page for paid users (T38), a standalone 人衣图生成
    admin tool, and a new admin-dance-library.html page for curating the
    per-character dance clip pool with AI QC.
    
    On top of that, dance clips now have an explicit published (上架/下架)
    status: passing AI QC (or a manual admin override to "correct") auto-
    publishes a clip, failing it auto-unpublishes it, and only published
    clips are eligible for a room's random-fill pool — unreviewed/rejected
    clips stay in the library but no longer autoplay for viewers. Existing
    already-QC'd "correct" clips are backfilled to published on first boot
    so the room doesn't go quiet.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01MvkJsRcQ2qEuaFea31rBY8
    yibo authored
     
    Browse File »

  • consumeCharacterSegment(clip), called alongside markPlayed() at both
    dance-specific scheduling call sites (T34's priorityInbox insert and
    the normal fill loop) — deliberately not folded into the shared
    markPlayed() itself, since live.html's text-channel clips never have a
    user_paid character behind them and architecture.md 2.1 keeps the two
    channels' scheduling logic independent. Decrements
    characters.remainingSegments each time a user_paid character's clip
    gets scheduled (its first play via T34's priority insert, and every
    later replay through the normal random-fill pool); hits 0 ->
    status:"expired".
    
    pickNextDanceClip's random-fill pool now excludes clips belonging to
    an expired character (需求 3.3 第 7 点: excluded from future selection,
    but the character record and its generated videos are never deleted —
    GET /api/admin/characters and GET /api/library still return them).
    
    dance-mode3.test.js: buying exactly 1 segment expires the character
    right after its single (priority-boosted) play; totalSegments stays 1
    as history while remainingSegments goes to 0; the character and its
    clip remain queryable via the admin/library APIs afterward.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_011DUiYs6UjxDMkfvnjtsKFo
    yibo authored
     
    Browse File »
  • T34: priorityInbox (M.8) — paid_boost clips get inserted immediately
    after whatever's currently playing (danceSchedule[0]), pushing every
    later entry's timing back, rather than waiting for the fill-loop's
    normal "top up to 30s ahead" trigger. That distinction mattered:
    draining priorityInbox only from inside the fill loop (first attempt)
    technically satisfied "never loses to a vote-ranked pick" but could
    leave a ready paid_boost clip waiting a long time if the schedule
    already had ~30s queued — dance-mode3.test.js's race test caught this
    (timed out) before the eager-insert fix. ensureDanceSchedule's existing
    "generating -> done" reconciliation loop diverts a job into
    priorityInbox instead of danceQueue's "ready" state when
    job.priority === "paid_boost", spliced out of danceQueue after the
    loop (not mid-iteration).
    
    T35: POST /api/dance/upload-character (需登录+emailVerified, same rule
    as T27). Face photo -> T2 upload+EXIF strip -> T32 moderateFaceBlacklist
    -> T25-style describeCharacterImage (new pluggable fn, shares
    VISION_API_KEY) -> characters.create(source:"user_paid", isDefault:
    false, totalSegments/remainingSegments computed from pricing_config's
    new spend_character_per_segment×segmentsPerUnit) -> creditLedger.spend
    (relatedAssetId=character.id, rolled back on failure) ->
    submitGeneration(..., priority:"paid_boost") -> lands in priorityInbox
    (cache hit) or danceQueue pending T34's reconciliation (fresh
    generation). Garment is optional (need求文档 3.3 第 4 点): given
    garmentId is validated active-or-400, otherwise a random active
    garment is picked — a fully empty wardrobe is a 400 before any spend.
    
    dance-mode3.test.js covers both: auth/balance/no-active-garments gates,
    the happy path (cost scales with requested segments, character record
    shape, clip carries priority/requestedByUserId), and T34's actual
    guarantee — a paid_boost character is scheduled at or ahead of an
    already-ready free pick for the same garment, distinguished by
    characterId since both target the same garment on purpose.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_011DUiYs6UjxDMkfvnjtsKFo
    yibo authored
     
    Browse File »