• POST /api/admin/upload-video: mp4 magic-byte sniff (checks the ftyp
    box, not offset-0 like images), 50MB cap, no re-encode needed (unlike
    images, no EXIF/GPS concern for video). Reuses the multipart parser
    and uploads/ layout from T2's image path via a shared placeInUploads
    helper.
    yibo authored
     
    Browse Code »
  • mountCrud() is a small generic GET-list/POST-create/PATCH/DELETE
    router shared across the three entities (same shape, see MVCP
    M.3/M.4/M.5) instead of copy-pasting the boilerplate three times.
    Each entity fills in its own admin_seed-appropriate defaults on
    create. characters additionally enforces the single-default-character
    invariant (2 节) across create/update.
    
    HTML admin pages for these come next.
    yibo authored
     
    Browse Code »
  • Single shared password (.env ADMIN_PASSWORD) + in-memory HttpOnly
    session cookie, no accounts/roles — MVCP M.1/M.10's real account
    system supersedes this in period 3. Covers the existing
    /api/admin/library/* routes (previously unauthenticated, see
    architecture.md 1.2's tech-debt note) plus the new upload endpoint.
    
    admin.html gets a fetch() interceptor that redirects to the new
    admin-login.html on any 401 from /api/admin/*, rather than patching
    every call site.
    yibo authored
     
    Browse Code »
  • POST /api/admin/upload: hand-rolled multipart parser (no deps installed
    in this project), real magic-byte sniffing (rejects extension-spoofed
    files), 8MB cap, and EXIF/GPS stripping via a sips bmp round-trip
    (macOS-native, no dependency — sips's direct jpeg->jpeg re-encode alone
    does NOT strip EXIF, only the bmp intermediate does). GET /uploads/*
    serves the result with a path-traversal guard.
    
    forwardToH3Assets() is the T7/T8 hook for pushing a saved upload to H3
    Console's own POST /v1/assets (found in the S1-S4 spike) — not covered
    by the automated suite since it needs a live network call, same
    treatment as the spike scripts.
    yibo authored
     
    Browse Code »
  • S1: person+garment images directly into ref2va with attribute_transfer
    produce correct face/outfit/motion in one call — no separate compose
    step needed. S2: reference_video_urls has a real, strong effect on
    output motion (A/B tested). S4: H3 Console's own POST /v1/assets
    endpoint (undocumented until now) solves asset reachability without
    exposing our own uploads/ publicly.
    
    Evidence and scripts in spike/s1-s2-s4-2026-09-02/. Propagated the
    one-step design into the requirements doc, MVCP design doc, the
    architecture doc, and H3-CONSOLE.md; dev-plan.md's spike section
    marked done and its task table updated accordingly.
    yibo authored
     
    Browse Code »
  • Generic createStore(filePath) shared by characters.json/garments.json/
    dance-refs.json, same full-read/full-write pattern as library.json.
    Not yet wired into server.js routes — that's T3-T5.
    yibo authored
     
    Browse Code »

  • Duration: fixed 10s (within H3's 4-15s hard constraint), frames=240 @ 24fps.
    
    Prompt: follows the internal H3 prompt-writing methodology already sitting
    in the repo (docs/h3skill.md, from a parallel session) — the "all-purpose
    reference mode" six-section structure. Character appearance details are
    auto-captioned from the Stage1 composed image (new composed_images.
    appearanceDescriptor field); dance motion phrasing is admin-authored once
    per reference video (new dance_refs.motionDescriptor field), since the
    format requires concrete descriptors and forbids "as shown in the picture."
    
    Flagged as needing a pass through h3skill.md's own 22-item mechanical
    acceptance checklist before production use, not claimed as fully compliant.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SdtXTsDRz5QWMjvt1HBXLz
    yibo authored
     
    Browse Code »
  • H3_GEN_PARAMS.frames 120 -> 240 (at 24fps, 5s -> 10s), still within H3's
    4-15s range (H2 in docs/h3skill.md). Updated the prompt-optimization system
    prompt's duration reference and widened its word budget (40-70 -> 60-100)
    to match — still one continuous shot, just more room to let the camera move
    and the action unfold. Verified against the real GLM endpoint.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • Previously left as inline "勘误" patch notes; now the baseline doc
    (docs/done) states the current 480x832 default outright, and the
    dance-room docs describe live.html/dance.html as two independently
    configured H3_GEN_PARAMS that happen to agree, rather than flagging an
    erratum.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SdtXTsDRz5QWMjvt1HBXLz
    yibo authored
     
    Browse Code »
  • Data model, front/back-end view inventory, button-level control design, and
    per-user-type process flows for the not-yet-built feature. Also corrects the
    requirements doc's stale "live.html stays landscape" claim now that
    H3_GEN_PARAMS was changed to portrait there too (commit 1dc7084).
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SdtXTsDRz5QWMjvt1HBXLz
    yibo authored
     
    Browse Code »
  • The previous system prompt asked GLM for a free-form "shot-by-shot" expansion,
    which reliably produced 4-5 fake cuts with timestamps running out to 8-10s —
    impossible for a clip fixed at ~5s (H3_GEN_PARAMS.frames / FPS). Rewrote it
    against docs/h3skill.md (MiniMax's own H3 prompt-writing methodology, added
    alongside this commit): every chat prompt has no reference images, so it's
    always T2VA mode, and at ~5s it's one continuous shot, never a montage.
    
    The new prompt asks for H3's actual three-field T2VA structure
    (integrated_multimodal_description / overall_soundscape / non_diegetic_music,
    fixed order and labels), a single dominant camera movement from the official
    vocabulary (type + amplitude + speed), concrete lighting (no hollow buzzwords
    like "8K"/"cinematic" alone), and — previously missing entirely — explicit
    sound design. Verified against the real GLM endpoint on several sample
    prompts: single continuous shot throughout, correct field structure, no more
    impossible multi-shot timestamps.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • Documents the new outfit-swap dance live room (dance.html) requirements
    alongside the existing text-prompt live channel baseline, plus the
    combined system architecture covering both.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SdtXTsDRz5QWMjvt1HBXLz
    yibo authored
     
    Browse Code »
  • Adds a prompt-optimization step ahead of H3 generation: a viewer's short
    chat line ("一只猫跳舞") gets expanded into a detailed shot-by-shot English
    prompt (camera work, lighting, action, setting) via one GLM Coding Plan call
    (glm-5.3-flash, /api/coding/ endpoint — the coding-plan-specific path, not
    the pay-per-token one) before being handed to H3. The expanded prompt becomes
    the clip's fullPrompt; the original short text stays as the display prompt.
    Persisted on the queue entry so a restart mid-generation doesn't re-call GLM.
    Best-effort throughout — no API key, a timeout, or any GLM error just falls
    back to using the original text unchanged, never blocking generation.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • Swaps the H3_GEN_PARAMS default so future chat-triggered generations come out
    mobile-friendly (9:16) instead of 832x480 landscape. Existing library clips
    keep whatever aspect ratio they were generated at — the player already uses
    object-fit: contain, so mixed aspect ratios render fine either way.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • Adds playCount to each library clip, incremented every time it airs
    (markPlayed). The idle-filler pick in pickNextClip now randomizes only among
    the least-played clips in the eligible pool — everyone at 0 plays first, then
    everyone tied at 1, and so on — instead of picking uniformly at random across
    the whole library, which let some clips rack up far more airtime than others
    by chance alone.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • The video autoplays muted underneath a full-screen splash — logo, play icon,
    slogan and a credit line, matching infiniteslop.ai's own splash. Clicking it
    is the user gesture that lets the browser allow unmuted playback, so desktop
    viewers land with sound instead of the silent-until-you-find-the-mute-button
    experience from the previous change. Mobile stays muted after entry, same as
    infiniteslop.ai, since unmuted autoplay-adjacent playback is unreliable there.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • Drops the muted attribute and tries an unmuted autoplay first; if the browser
    blocks unmuted autoplay (no prior user gesture on the page), falls back to
    muted playback and syncs the mute button label to match. Once a viewer
    unmutes once, later clip switches keep playing with sound.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • Reworks live.css/live.html to match infiniteslop.ai's visual language: Tahoma
    bold type, pure-black canvas, hot-pink accent, and hand-drawn white cards with
    a thick black border + hard offset shadow for every button/bubble/prompt card.
    Left queue now groups into 接下来播放/生成中/排队中 sections, the center gets a
    white "now playing" card (time + requester + prompt) plus a pulsing LIVE pill,
    chat bubbles are colored per-nickname via a pastel hash, and the top bar gained
    a chat-close toggle alongside mute and the watching count.
    
    Adds real upvoting to go with the new queue UI: POST /api/queue/vote/:id
    toggles a per-viewer (cookie uid) vote on a queued prompt, and pickNextClip
    now schedules the highest-voted matched/ready entry first instead of strict
    FIFO — lets chat bump a favorite prompt to the front of the line.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_0144MiJ2cLjMd9jWaeEpVfUJ
    yibo authored
     
    Browse Code »
  • - "昵称 X ✎" button (next to the "聊天" title) replaced with a row directly
      above the chat input: hidden until a nickname is set, then shows
      "<nickname>  change name" — matches infiniteslop.ai's own placement/label
    - Nickname prompt now triggers on focusing the chat input itself, not just
      on hitting send — so a first-time visitor sees it the moment they click in
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Code »
  • Previously the nickname modal only appeared implicitly on first chat send,
    with no way to see or change it afterward. Adds an always-visible "昵称 X ✎"
    button next to the chat panel title — same modal, pre-filled with the
    current nickname, keeps the existing uid when editing.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Code »
  • - Cookie-based viewer identity: first chat attempt prompts for a nickname
      (inline modal, not a blocking native prompt()), generates a client-side
      uid, both persisted in cookies for 1 year
    - Right panel is now a live chat feed (username + message + status) with
      an input row below it, replacing the old bare prompt textarea
    - New POST /api/chat: runs a moderation check (banned-word blocklist —
      ponytail: starter list, not a real content policy, swap in the real one)
      before a message is allowed into the existing prompt-matching/generation
      pipeline; rejected messages still show in the chat log, just flagged
    - New GET /api/chat: recent chat history (capped at 300), polled every 3s
    - Left queue simplified to prompt + submitter name only — dropped the
      resolution/duration/date metadata that was there before
    - schedule/promptQueue now carry `requestedBy`/`nickname` through to
      /api/live/state so the left queue and "now playing" bar can show who
      asked for what
    - /api/prompt kept working (now a thin wrapper over the same
      submitPromptToQueue() the chat endpoint uses) for direct API testing
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Code »
  • Inspected infiniteslop.ai's computed styles directly (fonts, colors, button
    shapes) rather than guessing from memory:
    - Pure black background, Tahoma body font (matches their retro-web feel)
    - Shrikhand display font for the logo mark, pink drop-shadow for pop
      (their black stroke doesn't read on a black background, so swapped)
    - Hot-pink accent (#ff90e8, their exact "Say" button color) for active
      tabs, the primary CTA button, and active-row/tab indicators
    - White pill buttons with bold black text for secondary controls (tabs,
      mute toggle) — matches their unmute/chat button treatment
    
    Layout structure (queue-left/player-center/input-right, mobile sub-tabs,
    no native video controls) is unchanged — this is a re-skin, not a layout
    clone, since that structure was built to this project's own requirements.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Code »
  • - library.json: all 303 real entries now have casual zh/en names (prompt/promptEn)
    - ingest.js: fixed a data-loss bug where the H3 console fetch was hardcoded
      to limit=200, silently dropping older jobs (and their edited names) as new
      content pushed them out of the window; sync is now a non-destructive
      merge with a much higher fetch limit
    - ingest.js: replaced the prefix-only test-job regex with a length-gated
      word-boundary check after 5 infra smoke-test clips slipped past it
      ("test verify", "cancel test - ...", "final flip test") — no false
      positives against the 303 real entries
    - deleted the 5 stray test clips (also removed on the H3 console side)
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Code »
  • Static video-gen testing tool (MiniMax native API, then fal.ai) that grew
    into an AI drama live channel backed by an internal H3 Console GPU cluster:
    shared virtual-playhead live playback, prompt submit/match/generate queue,
    admin CRUD for the video library, and bilingual (zh/en) semantic clip names.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse Code »