Skip to content

fix(web): make uploaded videos play in the chat#1343

Open
wbxl2000 wants to merge 7 commits into
mainfrom
feat/video-in
Open

fix(web): make uploaded videos play in the chat#1343
wbxl2000 wants to merge 7 commits into
mainfrom
feat/video-in

Conversation

@wbxl2000

@wbxl2000 wbxl2000 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue. This continues the video-upload work on feat/video-in (the server-side materialize of video uploads already landed earlier on this branch).

Problem

After the server stopped inlining uploaded videos as base64, the web UI could not actually show a sent video:

  • The user bubble rendered the raw <video path="…"></video> tag as text, and the message showed up twice.
  • The <video> element that did render was stuck at 0:00 — GET /api/v1/files/{id} did not support byte-range requests, which browsers require to play and seek video.
  • With range added, the <video src> still failed with 401: the daemon requires a Bearer token, but browsers load <video> natively and never attach it.

What changed

  • Render the <video path="…"></video> text tag in a user message as a real attachment, and reconcile the daemon echo into the optimistic message so the raw tag and duplicate bubble are gone.
  • Add byte-range (206 Partial Content) support to GET /api/v1/files/{id} so video can stream and seek.
  • Fetch the video bytes through the authenticated API client and play from a blob URL, since the browser cannot authorize a <video> src on its own.
  • Let users click an uploaded image in the chat to open it in the preview panel.

Tests: added coverage for the video-tag rendering, the echo reconciliation, and the file-download range responses.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

sailist and others added 2 commits July 2, 2026 16:06
- copy TUI video placeholders into the shared cache instead of
  inlining the original source path
- emit <video path="..."> tags so ReadMediaFile / the provider's
  VideoUploader owns upload behavior
- apply the same cache materialization to server prompt video
  submissions, matching the TUI flow
- update TUI unit tests and server e2e test to assert cache-path
  behavior
Render the server's <video path> tag as a real video and reconcile the echoed user message so the bubble no longer shows raw markup or a duplicate. Serve file downloads with byte-range support and fetch video bytes with the bearer credential into a blob URL, since browsers cannot authorize a <video> src on their own. Also let users click an uploaded image to open it in the preview panel.
@changeset-bot

changeset-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 03a0590

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0cbd45e2d7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/kimi-web/src/components/chat/ChatPane.vue Outdated
# Conflicts:
#	apps/kimi-code/src/tui/utils/image-placeholder.ts
#	packages/server/src/routes/prompts.ts
#	packages/server/test/prompt.e2e.test.ts
@pkg-pr-new

pkg-pr-new Bot commented Jul 3, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@03a0590
npx https://pkg.pr.new/@moonshot-ai/kimi-code@03a0590

commit: 03a0590

openMediaPreview stored the raw getFileUrl as sourceUrl, and FilePreview renders it with a native <img> that sends no Authorization header, so the enlarge action 401'd for uploaded images. When the media carries a fileId, fetch the bytes through the authenticated API client and preview a blob URL instead, revoking it when the preview is replaced or closed.
@wbxl2000

wbxl2000 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f1f15b7535

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/kimi-web/src/components/chat/AuthMedia.vue
AuthMedia fetches the file bytes asynchronously; when the component is reused with a new fileId before a prior fetch resolves (e.g. queued thumbnails keyed by index), the older response could still create a blob URL and show the previous file. Add a per-request sequence guard (and an unmount guard) so a stale response is discarded and its blob URL revoked instead of being applied.
@wbxl2000

wbxl2000 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4371b747b7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/kimi-web/src/composables/messagesToTurns.ts
Comment thread apps/kimi-web/src/composables/useFilePreview.ts
wbxl2000 added 2 commits July 3, 2026 19:19
Treating any standalone <video path="..."> text as an uploaded daemon file and stripping the basename into getFileUrl is only valid for server cache files named after the file-store id (f_…). TUI/ReadMediaFile tags use arbitrary cache names like <uuid>-<label>, and older transcripts may point at paths like /tmp/foo.mp4; those produced a broken /files/<basename> request. Only extract a fileId when the basename matches the file-store id shape, otherwise leave the raw tag as text.
Closing an uploaded-image preview before getFileBlob() resolved left previewRequestSeq untouched, so the fetch callback still passed its seq check, created a blob URL, then skipped attaching it because previewFile was already null — leaking up to the file size until another preview opened. Bump previewRequestSeq on close so the in-flight callback bails before creating the blob URL.
@wbxl2000

wbxl2000 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03a0590493

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return;
}
try {
const blob = await getKimiWebApi().getFileBlob(props.fileId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid eager full downloads for mounted media

When a rendered chat turn or queued item has a fileId, this call runs immediately on component mount and downloads the entire image/video into a Blob before assigning src. That bypasses the native loading="lazy" and preload="metadata" behavior below, so opening a session with several historical large video uploads can start many full downloads and hold all of those blobs in memory even if the user never scrolls to or plays them. Defer the authenticated fetch until the media is visible/played, or use an authorized streaming URL.

Useful? React with 👍 / 👎.

previewFile.value = base;
void getKimiWebApi().getFileBlob(media.fileId).then((blob) => {
if (seq !== previewRequestSeq) return;
mediaObjectUrl = URL.createObjectURL(blob);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Revoke preview blobs when leaving the file panel

If an uploaded image preview is opened and the user switches to another detail panel, useDetailPanel only changes detailTarget; it does not call closeFilePreview. This promise can still create mediaObjectUrl after the file panel is hidden, and an already-shown blob URL is likewise kept until another file preview or a session switch, leaking potentially large uploaded-image blobs. Check that the file panel is still active before creating the object URL and revoke/close when detailTarget leaves file.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants