Skip to content

fix(video): Allow seeking in video player progress bar#37197

Open
NAME-ASHWANIYADAV wants to merge 5 commits into
RocketChat:developfrom
NAME-ASHWANIYADAV:fix/video-seeking-bug
Open

fix(video): Allow seeking in video player progress bar#37197
NAME-ASHWANIYADAV wants to merge 5 commits into
RocketChat:developfrom
NAME-ASHWANIYADAV:fix/video-seeking-bug

Conversation

@NAME-ASHWANIYADAV
Copy link
Copy Markdown
Contributor

@NAME-ASHWANIYADAV NAME-ASHWANIYADAV commented Oct 10, 2025

Closes #22308

Proposed changes

This pull request resolves a long-standing bug that prevented users from seeking through uploaded video files. The core of the issue was twofold, involving both server-side headers and a client-side race condition.

1. The Root Cause: Lack of Server Support for Range Requests

Modern web browsers rely on HTTP Range Requests (sending a Range header) to stream and seek through video content. When a user clicks on a video progress bar, the browser sends a request to the server asking for only a specific "byte range" of the video file, rather than re-downloading the entire file.

For a server to honor these requests, it must first advertise its capability by sending the Accept-Ranges: bytes header in its initial response. The Rocket.Chat server was not sending this header for file uploads, causing most browsers to fall back to downloading the entire file from the beginning, thus making seeking impossible.

2. The Client-Side Issue: False Error State During Seeking

Simultaneously, a client-side hook, useReloadOnError, was incorrectly interpreting the browser's attempt to seek as a playback error. When a user tried to seek, the video's seeking property would become true. This state was not being handled, causing the error-handling logic to trigger a reload of the video source, which forced the playback position to jump back to where it was.

The Solution

This PR implements a comprehensive fix addressing both issues:

  • Server-Side Fix (FileSystem.ts & GridFS.ts):
    The Accept-Ranges: bytes header has been added to the response for file uploads. This correctly signals to the browser that our server supports HTTP 206 Partial Content responses, enabling native browser seeking.

  • Client-Side Fix (useReloadOnError.tsx):
    The useReloadOnError hook has been updated to check if the video element is currently in a seeking state (event.target.seeking). If it is, the reload logic is skipped. This prevents the hook from interfering with the browser's native seeking behavior.

Together, these changes ensure a smooth and functional seeking experience across all modern browsers.

Steps to test or reproduce

  1. Upload any .mp4 video file to a channel.
  2. Play the video.
  3. Click on any point in the video's progress bar.
  4. Expected behavior: The video playback should immediately and correctly jump to the selected time.

Further comments

This is my first contribution to Rocket.Chat. I'm happy to make any changes or improvements based on your feedback.

Summary by CodeRabbit

  • New Features

    • Enabled HTTP range requests on file/media endpoints for smoother streaming and precise seeking.
  • Bug Fixes

    • Fixed video seeking and playback recovery (progress-bar seeking now works reliably).
    • Improved handling of partial requests to avoid incorrect content-length and out-of-range responses.
  • Style

    • Simplified video rendering for more consistent in-player sizing and behavior.

@NAME-ASHWANIYADAV NAME-ASHWANIYADAV requested review from a team as code owners October 10, 2025 08:22
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Oct 10, 2025

🦋 Changeset detected

Latest commit: 60dbead

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

This PR includes changesets to release 41 packages
Name Type
@rocket.chat/meteor Patch
@rocket.chat/core-typings Patch
@rocket.chat/rest-typings Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/api-client Patch
@rocket.chat/apps Patch
@rocket.chat/core-services Patch
@rocket.chat/cron Patch
@rocket.chat/ddp-client Patch
@rocket.chat/fuselage-ui-kit Patch
@rocket.chat/gazzodown Patch
@rocket.chat/http-router Patch
@rocket.chat/livechat Patch
@rocket.chat/model-typings Patch
@rocket.chat/ui-avatar Patch
@rocket.chat/ui-client Patch
@rocket.chat/ui-contexts Patch
@rocket.chat/ui-voip Patch
@rocket.chat/web-ui-registration Patch
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/abac Patch
@rocket.chat/federation-matrix Patch
@rocket.chat/license Patch
@rocket.chat/media-calls Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/models Patch
@rocket.chat/network-broker Patch
@rocket.chat/omni-core-ee Patch
@rocket.chat/mock-providers Patch
@rocket.chat/ui-video-conf Patch
@rocket.chat/instance-status Patch
@rocket.chat/omni-core Patch
@rocket.chat/server-fetch 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

@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented Oct 10, 2025

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Oct 10, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 10, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds HTTP Range support and Accept-Ranges headers to file-serving endpoints, updates frontend video rendering to use a native

Changes

Cohort / File(s) Summary
Backend — FileSystem range handling
apps/meteor/app/file-upload/server/config/FileSystem.ts
Adds Accept-Ranges: bytes; parses incoming Range header with getFileRange; applies setRangeHeaders; early-returns on out-of-range; sets Content-Length only if not already present; streams with start/end when ranged.
Backend — GridFS headers
apps/meteor/app/file-upload/server/config/GridFS.ts
Adds Accept-Ranges: bytes header to GET handlers for uploads and user data files; no signature or error-path changes.
Frontend — Video attachment rendering
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx
Replaces fuselage Box wrapper with native <video> inside MessageGenericPreview; removes video_type prop and userAgentMIMETypeFallback; moves max-width/width styling onto the <video> element; preserves captions/description.
Frontend — Media error recovery
apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx
Stops listening for play events (now only ['error','stalled']); returns early from recovery when the media element is seeking, avoiding reloads during user seeks.
Release metadata
.changeset/dull-laws-push.md
Adds a changeset declaring a patch release for @rocket.chat/meteor documenting the video seek fix.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as Browser / Client
    participant Video as VideoElement
    participant Server as File Server

    rect rgba(245,250,255,0.5)
    Note over User,Client: User initiates seek via progress bar
    User->>Client: click/drag progress bar
    Client->>Video: set currentTime (seeking)
    end

    rect rgba(240,255,245,0.5)
    Note over Video,Server: Range request and guarded recovery
    Video->>Server: GET /file (Range: bytes=...)
    Server->>Video: 206 Partial Content + `Accept-Ranges: bytes` + range headers
    Video->>Client: resume playback at target time
    Note over Client: useReloadOnError detects `seeking` and returns early (no reload)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 I hopped through bytes to mend the play,

Now seeking works when you slide away,
Partial replies and headers kind,
The progress bar and I are aligned. 🎬🐇

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the PR: fixing video seeking in the player's progress bar, which aligns with the core changes across server and client code.
Linked Issues check ✅ Passed All code changes directly address the requirements from issue #22308: server-side range header support in FileSystem.ts and GridFS.ts enables HTTP 206 responses; useReloadOnError.tsx prevents seeking from triggering error recovery, restoring proper seeking behavior.
Out of Scope Changes check ✅ Passed The VideoAttachment.tsx changes (Box removal, video element replacement, styling updates) are directly related to improving video playback support. All modifications align with the PR's objective of enabling video seeking functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into develop

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (1)

79-84: Remove unreachable code.

Since 'play' was removed from the events array (line 5), event.type will never equal 'play' and this entire conditional block is now dead code.

Apply this diff to remove the unreachable code:

-	} else if (event.type === 'play') {
-		// The user has initiated a playback for the first time, probably we should wait for the stalled or error event
-		// the url may still be valid since we dont know the expiration time yet
-		isRecovering.current = false;
-		return;
-	}
+	}
🧹 Nitpick comments (1)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)

4-4: Remove unused import.

useRef is imported but never used in the component. The mediaRef comes from the useReloadOnError hook, not from useRef.

Apply this diff to remove the unused import:

-import { useMemo, useRef } from 'react';
+import { useMemo } from 'react';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6b9b1d1 and 18b5b14.

📒 Files selected for processing (4)
  • apps/meteor/app/file-upload/server/config/FileSystem.ts (2 hunks)
  • apps/meteor/app/file-upload/server/config/GridFS.ts (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)
apps/meteor/server/ufs/ufs-store.ts (1)
  • getURL (304-309)
🔇 Additional comments (6)
apps/meteor/app/file-upload/server/config/GridFS.ts (2)

165-165: LGTM! Range request support correctly advertised.

The Accept-Ranges: bytes header enables browsers to request partial content for video seeking. The underlying readFromGridFS function (lines 89-100) already handles range requests, so this header completes the implementation.


185-185: LGTM! Consistent range support for user data files.

The header addition matches the implementation in GridFS:Uploads and is backed by the same readFromGridFS range handling logic.

apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (2)

5-5: LGTM! Simplified event handling.

Removing the 'play' event from the listener array is appropriate since the first-recovery logic (lines 79-84) is now unreachable and unnecessary for the fix.


57-59: LGTM! Correctly prevents recovery during seeking.

The early return when seeking is true prevents the URL recovery mechanism from interfering with user-initiated seek operations, which was the root cause of the progress bar bug.

apps/meteor/app/file-upload/server/config/FileSystem.ts (1)

31-31: LGTM! Range support correctly implemented.

The Accept-Ranges: bytes header is backed by proper range request handling (lines 36-47), enabling video seeking for filesystem uploads.

apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)

32-32: LGTM! Cleaner video element structure.

The refactor from a Box wrapper with nested <source> to a direct <video> element with src attribute simplifies the component while maintaining the same functionality. The mediaRef from useReloadOnError is correctly attached, preserving the error recovery behavior.

Comment thread apps/meteor/app/file-upload/server/config/FileSystem.ts
@NAME-ASHWANIYADAV NAME-ASHWANIYADAV force-pushed the fix/video-seeking-bug branch 3 times, most recently from de55f01 to 05bc50e Compare October 12, 2025 04:28
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (4)

32-32: Use proper video element syntax with fallback content.

The <video> element is not a void element in HTML5 and should use opening and closing tags rather than self-closing syntax. While React tolerates self-closing non-void elements, it's non-standard and may cause issues in SSR contexts or future React versions. Additionally, include fallback content for better UX when video playback is unsupported.

Apply this diff:

-					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} type={userAgentMIMETypeFallback(type)} style={{ maxWidth: 368, width: '100%' }} />
+					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} style={{ maxWidth: 368, width: '100%' }}>
+						Your browser does not support video playback.
+					</video>

32-32: Remove non-functional type attribute from video element.

The type attribute is valid only on <source> elements, not on the <video> element itself. When using src directly on the video element, browsers determine the MIME type from HTTP response headers or the file extension. This attribute is non-functional and can be removed.

Apply this diff:

-					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} type={userAgentMIMETypeFallback(type)} style={{ maxWidth: 368, width: '100%' }}>
+					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} style={{ maxWidth: 368, width: '100%' }}>
 						Your browser does not support video playback.
 					</video>

31-32: Remove duplicate style properties.

The same maxWidth and width styles are applied to both the MessageGenericPreview wrapper (line 31) and the video element (line 32). This is redundant; apply the styles only to the video element.

Apply this diff:

-				<MessageGenericPreview style={{ maxWidth: 368, width: '100%' }}>
+				<MessageGenericPreview>
 					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} style={{ maxWidth: 368, width: '100%' }}>
 						Your browser does not support video playback.
 					</video>

32-32: Add accessibility label to video element.

The video element lacks an accessible label for screen readers. Use the title prop to provide an aria-label attribute for better accessibility.

Apply this diff:

-					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} style={{ maxWidth: 368, width: '100%' }}>
+					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} aria-label={title} style={{ maxWidth: 368, width: '100%' }}>
 						Your browser does not support video playback.
 					</video>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between de55f01 and 05bc50e.

📒 Files selected for processing (4)
  • .changeset/dull-laws-push.md (1 hunks)
  • apps/meteor/app/file-upload/server/config/FileSystem.ts (3 hunks)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx
  • .changeset/dull-laws-push.md
  • apps/meteor/app/file-upload/server/config/FileSystem.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)
apps/meteor/server/ufs/ufs-store.ts (1)
  • getURL (304-309)
🔇 Additional comments (1)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)

32-32: Core video element implementation successfully enables seeking.

The transition to a native HTML video element with direct src attribute aligns well with the server-side Range request support added in this PR. This approach correctly enables browser-native seeking behavior via the progress bar, addressing the root issue. The suggested refactors above focus on HTML best practices and accessibility, but the core fix is sound.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/meteor/app/file-upload/server/config/FileSystem.ts (1)

80-103: Consider adding range support to FileSystemAvatars for consistency.

FileSystemAvatars lacks the Accept-Ranges header and range request handling present in the other two handlers. While avatars are typically small images that don't require range support, adding it would ensure consistency across all file handlers and future-proof the code if larger avatar formats (e.g., animated content) are supported.

If desired, apply this diff to add range support:

 	async get(file, _req, res) {
 		if (!this.store) {
 			return;
 		}
 		const filePath = await this.store.getFilePath(file._id, file);
+		const options: { start?: number; end?: number } = {};
 
 		try {
 			const stat = await fsp.stat(filePath);
 
 			if (stat?.isFile()) {
 				file = FileUpload.addExtensionTo(file);
+				res.setHeader('Accept-Ranges', 'bytes');
+
+				if (_req.headers.range) {
+					const range = getFileRange(file, _req);
+
+					if (range) {
+						setRangeHeaders(range, file, res);
+						if (range.outOfRange) {
+							return;
+						}
+						options.start = range.start;
+						options.end = range.stop;
+					}
+				}
+
+				if (!res.getHeader('Content-Length')) {
+					res.setHeader('Content-Length', file.size || 0);
+				}
 
-				(await this.store.getReadStream(file._id, file)).pipe(res);
+				(await this.store.getReadStream(file._id, file, options)).pipe(res);
 			}
 		} catch (e) {
 			res.writeHead(404);
 			res.end();
 		}
 	},
apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (1)

57-59: Consider adding a debug log when skipping recovery due to seeking.

For consistency with other early-return paths (lines 62, 75, 84), a debug log would help troubleshoot scenarios where seeking and recovery logic interact.

Apply this diff:

 if ((event.target as HTMLMediaElement)?.seeking) {
+	console.debug('Media element is seeking, skipping recovery for event:', event.type);
 	return;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 05bc50e and ad88db0.

📒 Files selected for processing (4)
  • .changeset/dull-laws-push.md (1 hunks)
  • apps/meteor/app/file-upload/server/config/FileSystem.ts (3 hunks)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/dull-laws-push.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx
🔇 Additional comments (4)
apps/meteor/app/file-upload/server/config/FileSystem.ts (2)

31-31: LGTM! Accept-Ranges header correctly advertises range support.

The header is properly set before the range handling logic (lines 36-47), enabling browsers to request partial content for video seeking.


119-139: LGTM! Range support correctly implemented for user data files.

This implementation addresses the past review comment and correctly handles HTTP Range requests:

  • Advertises range support via Accept-Ranges: bytes header
  • Detects range requests and computes the requested byte range
  • Sets appropriate response headers (status 206, Content-Range, Content-Length)
  • Streams only the requested portion of the file
  • Falls back to full file with 200 OK when no range is requested

The logic mirrors FileSystemUploads (lines 36-47) and enables video seeking for user data files.

apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (2)

57-59: LGTM! Seeking guard correctly prevents reload during user-initiated seeks.

This check directly addresses the PR objective by preventing the recovery logic from interfering when the user seeks through the video progress bar. The optional chaining (?.) safely handles cases where event.target might be null, and checking the seeking property before the isRecovering guard ensures seek operations bypass recovery entirely.


5-5: Removal of ‘play’ event is safe Remaining recovery on ‘error’ and ‘stalled’ covers all URL refresh cases; no code or tests bind to ‘play’, so URL recovery remains intact.

@reetp
Copy link
Copy Markdown
Collaborator

reetp commented Oct 16, 2025

I will ask if someone will look at this.

Please keep your code up to date with latest code.

Note - there is a very long road to getting code merged and it can take several months/releases.

@casalsgh
Copy link
Copy Markdown
Contributor

Pinged Engineering asking to check it out

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d20ae6e and 2798752.

📒 Files selected for processing (6)
  • .changeset/dull-laws-push.md (1 hunks)
  • apps/meteor/app/file-upload/server/config/FileSystem.ts (3 hunks)
  • apps/meteor/app/file-upload/server/config/GridFS.ts (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (3 hunks)
  • error.txt (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/dull-laws-push.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx
  • apps/meteor/app/file-upload/server/config/GridFS.ts
  • apps/meteor/app/file-upload/server/config/FileSystem.ts
🔇 Additional comments (3)
apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (3)

5-5: Good simplification of event handling.

Removing the 'play' event from the recovery trigger list is correct. Recovery should only be triggered by actual error conditions (error, stalled), not by normal playback events.


57-59: Excellent fix for the seeking bug!

This early return is the core solution to the reported issue. By checking seeking state, the hook correctly avoids triggering URL recovery when the user is actively seeking through the video via the progress bar. This prevents the video source from being reloaded mid-seek, which was causing the playback position to jump back.

The use of optional chaining ensures safe access to the seeking property.


73-80: Logic flow is correct after removing play event handling.

The conditional block correctly handles the first recovery attempt check, and the removal of the previous event.type === 'play' branch simplifies the logic appropriately since 'play' is no longer in the events list.

Comment thread error.txt Outdated
@NAME-ASHWANIYADAV NAME-ASHWANIYADAV force-pushed the fix/video-seeking-bug branch 2 times, most recently from 9a7bb36 to 1871cbd Compare October 18, 2025 04:14
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)

22-22: Use the memoized src in the video element.

Line 22 creates a memoized src value, but line 30 calls getURL(url) again instead of using it. This creates an inconsistency: the useReloadOnError hook on line 23 monitors one URL (src), while the video element may load a different one (though likely the same result). Use the memoized src for consistency and to avoid redundant computation.

Apply this diff:

-					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} style={{ maxWidth: 368, width: '100%' }}>
+					<video controls preload='metadata' ref={mediaRef} src={src} style={{ maxWidth: 368, width: '100%' }}>

Also applies to: 30-30

apps/meteor/app/file-upload/server/config/GridFS.ts (1)

199-211: Add Accept-Ranges: bytes header to GridFS:Avatars handler for consistency.

The GridFS:Avatars handler omits the Accept-Ranges: bytes header, unlike GridFS:Uploads (line 165) and GridFS:UserDataFiles (line 185). All three handlers use the same readFromGridFS() function, which includes full range request handling (via getFileRange() and setRangeHeaders()). The missing header should be added at line 202 to maintain consistency and enable seeking behavior if avatars are served as video or animated content.

async get(file, req, res) {
	file = FileUpload.addExtensionTo(file);

	res.setHeader('Accept-Ranges', 'bytes');
	await readFromGridFS(file.store, file._id, file, req, res);
},
♻️ Duplicate comments (2)
apps/meteor/app/file-upload/server/config/GridFS.ts (1)

185-189: Same Content-Length ordering concern applies here.

This handler has the same header ordering pattern as GridFS:Uploads (line 165-169). Ensure setRangeHeaders properly overwrites Content-Length for range responses.

apps/meteor/app/file-upload/server/config/FileSystem.ts (1)

80-103: Avatars endpoint also lacks range request support.

Similar to GridFS:Avatars, the FileSystemAvatars handler does not include range request support. This is likely intentional if avatars are exclusively static images, but should be verified if any video or animated content is served via this endpoint.

🧹 Nitpick comments (2)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)

29-30: Consider removing duplicate styles.

The style { maxWidth: 368, width: '100%' } is applied to both the MessageGenericPreview container and the video element. Since the video already has these constraints, applying them to the parent is redundant.

Apply this diff to remove the duplicate style from the parent:

-				<MessageGenericPreview style={{ maxWidth: 368, width: '100%' }}>
+				<MessageGenericPreview>
 					<video controls preload='metadata' ref={mediaRef} src={src} style={{ maxWidth: 368, width: '100%' }}>
apps/meteor/app/file-upload/server/config/GridFS.ts (1)

165-169: Inconsistency in Content-Length header handling pattern, but no functional risk.

The concern about incorrect content-length reaching browsers is invalid. The setRangeHeaders function explicitly removes the Content-Length header (line 55 in ranges.ts) before setting it to the correct partial content length (line 56), ensuring 206 responses have accurate metadata.

However, there is a real inconsistency in approach: FileSystem.ts uses a defensive pattern with conditional checks (if (!res.getHeader('Content-Length')) at lines 50 and 136), while GridFS.ts sets the header unconditionally (line 169) and relies on setRangeHeaders to override it. The FileSystem pattern is cleaner since it avoids unnecessary header removal/replacement cycles.

For consistency and clarity, consider adopting the FileSystem pattern in GridFS.ts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2798752 and 9a7bb36.

📒 Files selected for processing (5)
  • .changeset/dull-laws-push.md (1 hunks)
  • apps/meteor/app/file-upload/server/config/FileSystem.ts (3 hunks)
  • apps/meteor/app/file-upload/server/config/GridFS.ts (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .changeset/dull-laws-push.md
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)
apps/meteor/server/ufs/ufs-store.ts (1)
  • getURL (304-309)
🔇 Additional comments (2)
apps/meteor/app/file-upload/server/config/FileSystem.ts (2)

31-54: LGTM! Range request handling is correctly implemented.

The implementation properly:

  • Advertises range support via Accept-Ranges: bytes
  • Parses and validates range requests using getFileRange
  • Sets appropriate response headers via setRangeHeaders
  • Handles out-of-bounds ranges with early return
  • Conditionally sets Content-Length only if not already set (avoiding overwrite of partial content length)
  • Passes start/end options to the read stream for efficient partial reads

This follows the correct pattern for HTTP 206 Partial Content responses.


119-139: LGTM! Range request handling correctly mirrors FileSystemUploads implementation.

The implementation correctly addresses the previous review feedback (past comment on line 119). The range handling logic is now complete and consistent with FileSystemUploads, properly supporting HTTP 206 Partial Content responses for user data files.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)

31-31: Remove the empty track element.

The track element specifies kind='captions' but lacks a src attribute. Without a source, this element serves no purpose and should be removed.

Apply this diff:

 					<video controls preload='metadata' ref={mediaRef} src={src}>
-						<track kind='captions' />
 					</video>
apps/meteor/app/file-upload/server/config/FileSystem.ts (1)

119-139: I need to gather more information to verify the concerns. Let me inspect the implementation files directly.

Now let me inspect the ranges.ts file and the FileSystem.ts file more closely:

Let me inspect the ranges.ts file and FileSystem.ts directly:

Based on my verification, I have found that the review comment identifies legitimate code quality and consistency issues. The code under review does not properly align with the same implementation pattern in the Uploads configuration (earlier in the same file) and doesn't follow best practices for HTTP range request handling. The issues are actionable and correct.

Rename _req to req and update Content-Type default for consistency and clarity.

The file FileSystem.ts contains two separate handlers: one for Uploads (lines ~34–36) already applies the suggested improvements, while the UserDataFiles handler (lines 122–139) has not. fs.createReadStream's end parameter is inclusive, which aligns with range.stop semantics in HTTP range requests. The inconsistency suggests this was an oversight during implementation.

Apply these fixes:

-	async get(file, _req, res) {
+	async get(file, req, res) {
@@
-				res.setHeader('Content-Type', file.type || '');
+				res.setHeader('Content-Type', file.type || 'application/octet-stream');
@@
-				if (_req.headers.range) {
-					const range = getFileRange(file, _req);
+				if (req.headers.range) {
+					const range = getFileRange(file, req);
🧹 Nitpick comments (3)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2)

30-30: Use the memoized src variable instead of calling getURL(url) again.

Line 22 already memoizes src via useMemo(() => getURL(url), [getURL, url]), but line 30 calls getURL(url) again. This bypasses the memoization and can trigger unnecessary recalculations.

Apply this diff:

-					<video controls preload='metadata' ref={mediaRef} src={getURL(url)} style={{ maxWidth: 368, width: '100%' }}>
+					<video controls preload='metadata' ref={mediaRef} src={src} style={{ maxWidth: 368, width: '100%' }}>

29-30: Remove duplicate inline styles.

Both MessageGenericPreview (line 29) and the video element (line 30) specify identical inline styles {{ maxWidth: 368, width: '100%' }}. The parent container's styles should be sufficient.

Apply this diff to remove the duplicate:

 				<MessageGenericPreview style={{ maxWidth: 368, width: '100%' }}>
-					<video controls preload='metadata' ref={mediaRef} src={src} style={{ maxWidth: 368, width: '100%' }}>
+					<video controls preload='metadata' ref={mediaRef} src={src}>
apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (1)

57-59: Correct fix: Suppress recovery during media seek operations.

The seeking check at lines 57–59 is properly implemented. The HTMLMediaElement.seeking property is standard across all browsers and correctly identifies when a user is actively seeking. The optional chaining safely handles non-media elements, and the early return prevents false error recovery during normal seek operations.

However, the test suite lacks an explicit test for this seeking scenario. While the fix is sound, add a test case to verify that seek events don't trigger unnecessary recovery:

it('does not recover on seek (seeking property true)', async () => {
	const original = '/sampleurl?token=abc';
	const { result } = renderHook(() => useReloadOnError(original, 'audio'));

	const media = makeMediaEl();

	act(() => {
		result.current.mediaRef(media);
	});

	// Mock seeking as true
	Object.defineProperty(media, 'seeking', { value: true, configurable: true });

	await act(async () => {
		media._emit('stalled'); // Would normally trigger recovery
	});

	// No fetch should occur while seeking is true
	expect(global.fetch).not.toHaveBeenCalled();
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9a7bb36 and 1871cbd.

📒 Files selected for processing (5)
  • .changeset/dull-laws-push.md (1 hunks)
  • apps/meteor/app/file-upload/server/config/FileSystem.ts (3 hunks)
  • apps/meteor/app/file-upload/server/config/GridFS.ts (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/dull-laws-push.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/meteor/app/file-upload/server/config/GridFS.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)
apps/meteor/server/ufs/ufs-store.ts (1)
  • getURL (304-309)
🔇 Additional comments (4)
apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (1)

2-2: LGTM: Import cleanup is correct.

Removing the unused Box import is appropriate since the video element is now rendered directly without a Box wrapper.

apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (1)

5-5: LGTM: Correctly removed 'play' from error events.

The 'play' event is not an error condition and shouldn't trigger recovery logic. This change makes the event list semantically correct by limiting it to actual error states.

apps/meteor/app/file-upload/server/config/FileSystem.ts (2)

31-31: Good addition: advertise byte ranges on uploads.

Setting Accept-Ranges: bytes enables native seeking; placement is correct post file existence check.


159-159: No functional change.

Looks like formatting/closing brace movement only; nothing to action.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/meteor/app/file-upload/server/config/FileSystem.ts (1)

80-103: Consider range support for consistency.

FileSystemAvatars does not implement range request handling, unlike the other handlers. While avatars are typically small and don't require seeking, adding range support would provide consistency across all file handlers and could benefit clients with slow connections requesting large avatar images.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1871cbd and d48fc43.

📒 Files selected for processing (5)
  • .changeset/dull-laws-push.md (1 hunks)
  • apps/meteor/app/file-upload/server/config/FileSystem.ts (3 hunks)
  • apps/meteor/app/file-upload/server/config/GridFS.ts (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2 hunks)
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/meteor/app/file-upload/server/config/GridFS.ts
  • apps/meteor/client/components/message/content/attachments/file/hooks/useReloadOnError.tsx
🔇 Additional comments (5)
.changeset/dull-laws-push.md (1)

1-5: LGTM!

The changeset correctly documents this patch release for video seeking support.

apps/meteor/app/file-upload/server/config/FileSystem.ts (2)

31-54: LGTM!

The HTTP range request implementation is correct and complete. The handler properly advertises range support, parses range headers, sets 206 status with appropriate headers via setRangeHeaders, and streams only the requested byte range.


119-139: LGTM! Past review concern addressed.

The range request implementation is now complete and matches the FileSystemUploads handler. This correctly resolves the critical issue flagged in the previous review where Accept-Ranges was advertised without implementation.

apps/meteor/client/components/message/content/attachments/file/VideoAttachment.tsx (2)

11-20: LGTM!

Removing video_type from the props is correct since the video element now uses a direct src attribute instead of a nested <source type="..."> element. Modern browsers will determine the MIME type automatically.


29-30: LGTM! Cleaner video rendering.

The simplified approach using a native <video> element with a direct src attribute is more maintainable and aligns with modern HTML5 video practices. The removal of the Box wrapper and nested source element reduces unnecessary complexity.

@NAME-ASHWANIYADAV
Copy link
Copy Markdown
Contributor Author

Hello mentors,

Thank you for the feedback. I have now addressed all the suggestions from the code rabbit ai bot and have resolved the open conversations on the pull request.

I believe all the required actions from my side are now complete. As this is my first contribution and I'm new to the open-source process, please let me know if I've missed anything or if there are any further changes needed.

I would appreciate it if you could take another look. I'm looking forward to your feedback.

Thank you!

@reetp
Copy link
Copy Markdown
Collaborator

reetp commented Oct 18, 2025

Hello mentors,

Please note no one is mentoring you. This is not school.

Your PR will get reviewed in due course but nothing moves fast here and it may take weeks or even months.

@NAME-ASHWANIYADAV
Copy link
Copy Markdown
Contributor Author

Ok , I will wait and I just said mentors in respect to fellow members or contributes who are more experienced then me , I apologise if it offended anyone

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.60%. Comparing base (0bec138) to head (60dbead).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #37197      +/-   ##
===========================================
+ Coverage    70.56%   70.60%   +0.04%     
===========================================
  Files         3182     3182              
  Lines       112405   112396       -9     
  Branches     20412    20387      -25     
===========================================
+ Hits         79316    79358      +42     
+ Misses       31033    30987      -46     
+ Partials      2056     2051       -5     
Flag Coverage Δ
unit 71.54% <60.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

Video not seekable via progressbar

4 participants