Skip to content

Conversation

oscartbeaumont
Copy link
Member

@oscartbeaumont oscartbeaumont commented Sep 28, 2025

What this PR does:

  • Cap web upload status
    • Fix a bunch of bugs reporting the progress to the Cap web backend
    • Redesigned CapCard which allows interaction while uploading
    • Disable progress reporting for the UploadCapButton. Will renable it in Effectify upload progress system in web #1032.
  • Adjust .cap files to be eagerly created before the recording completes and also to track the upload progress internally.
  • Improve upload system in desktop:
    • Show upload progress in the previous recordings panel
    • Automatically restart uploads on restart (note this doesn't resume just restarts uploads, that could be implemented later on)

Remaining:

  • Ensure recordings.tsx renders progress correctly -> uuid vs cap id
  • Store uploading complete and errors into UploadState within the metadata
  • Render upload errors into the previous recording UI
  • Feature flag the old upload system back in
  • Why is it so slow to start yielding chunks from a new instant mode recording...
  • Progress updates need to be sent more often to the frontend or it needs to be able to refetch them.
  • The uploader takes forever to get started. Is this something with the S3 presigning process or the FS watcher???

TODO:

  • RecordingMeta restructure.
    • We need a way to track upload progress and know whether to resume uploads. That should probs be done via RecordingMeta? By constructing RecordingMeta earlier we can also copy the eager behaviour of Cap web.
    • Construct RecordingMeta on recording start instead of end.
    • Overhaul upload.rs
      • Rebuild file into composible streams
      • from_file implementation for upload
      • New stream implementation buggin:
        • Progress updated stalled on completion
        • Progress updates are too quick
        • Does try_collect abort early or still run the whole stream??? - Is does abort early. Why doesn't my code?
      • Fix todo!()'s
      • HTTP Retries brought back
      • Flatten UploadProgressUpdater into progress
      • Make upload progress abstraction usable with non-mutlipart upload
      • Make UploadProgressEvent reliable
        • Faster event emitting
        • Properly keyed on an object
      • Cleanup imports in upload.rs
      • Cleaning logging in api.rs. It's excessive.
      • Get rid of Channel<UploadProgress> and refactor frontend around new system
    • Instant mode:
      • Ensure early creation of RecordingMeta
      • Report recording errors into RecordingMeta
      • Report upload status into RecordingMeta from multipart upload
      • Can we merge InstantMultipartUpload and the regular single part updater???
      • Can the InstantMultipartUpload be owned and managed by the actor instead of polling the filesystem?
      • Report upload status into RecordingMeta from fallback uploader
      • Setting UploadState correctly???
      • Do we need to mark if this file is instant mode for the resumable upload stuff???
      • In-memory realtime upload progress tracking
      • UploadProgressEvent is being emitted too infrequently
      • UploadProgressEvent cleanup uploads from the SolidJS store once they are complete
    • Studio mode
      • Ensure early creation of RecordingMeta
      • Setting UploadState correctly???
      • Hook up UploadProgressEvent
    • Fix all todo!()'s on the new RecordingMetaInner variant.
    • We need to report failures during the upload process into the RecordingMeta on disk.
    • On startup:
      • Set all recordings which are in the recoding state to failed with crash as error.
      • Restart all in-progress uploads
    • Start a reupload should store it into the project meta so we can resume the reupload
    • Frontend
      • Render recording error state
      • Render uploading error state
      • Render uploading state w/ progress
      • Disable reupload if one is already running
      • Allow hovering on the ProgressCircle for the actual value
      • Fix all todo's
      • Progressive upload failed: ... is not reported to the frontend. This must be reported and renders in the position of the ProgressCircle
  • Reporting failed uploads to the DB backend???

Summary by CodeRabbit

  • New Features

    • Experimental "New uploader" toggle in Desktop settings.
    • Streaming multipart uploader with real-time progress and legacy fallback.
    • Automatic resume of interrupted uploads on Desktop startup.
  • Enhancements

    • Per-recording upload progress overlays, status badges, and disabled actions during active uploads.
    • Unified thumbnail loading state shared with parent components.
    • Progress events surfaced to UI for desktop/web consistency.
  • Bug Fixes

    • Prevented event callbacks after component unmount to avoid stale updates.
  • Chores

    • Backend deletion now also removes related upload records.

Copy link
Contributor

coderabbitai bot commented Sep 28, 2025

Warning

Rate limit exceeded

@oscartbeaumont has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 39 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 30d4726 and 13c71cf.

📒 Files selected for processing (1)
  • apps/desktop/src-tauri/src/upload.rs (5 hunks)

Walkthrough

Adds a streaming multipart uploader plus a legacy uploader, desktop async API helpers for presign/complete/upload workflows, integrates upload state/progress into recording metadata and UI (feature-flagged), introduces an unpublished cap-api crate, and propagates related type/signature changes across desktop, web, and backend layers.

Changes

Cohort / File(s) Summary
Desktop: API & Upload
apps/desktop/src-tauri/src/api.rs, apps/desktop/src-tauri/src/upload.rs, apps/desktop/src-tauri/src/upload_legacy.rs, apps/desktop/src-tauri/src/web_api.rs, apps/desktop/src-tauri/src/lib.rs, apps/desktop/src-tauri/src/recording.rs, apps/desktop/src-tauri/src/general_settings.rs, apps/desktop/src-tauri/src/main.rs, apps/desktop/src-tauri/Cargo.toml
Adds async API helpers for multipart/signed uploads, streaming multipart and single-part uploaders plus a legacy path, presign/complete endpoints, new upload-related public types/events, RecordingMeta.upload/state handling, startup resume_uploads wiring, feature-flag for new uploader, and new Cargo workspace deps (thiserror.workspace, bytes, async-stream).
Desktop UI & Utils
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx, apps/desktop/src/routes/(window-chrome)/settings/general.tsx, apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx, apps/desktop/src-tauri/src/utils/createEventListener.rs, apps/desktop/src-tauri/src/utils/tauri.ts
Adds "New uploader" toggle and GeneralSettingsStore field; switches public types to RecordingMetaWithMetadata; adds uploadProgress store and tauri event listener handling; exposes UploadProgressEvent/UploadMeta/StudioRecordingStatus and updates get/list signatures.
Web Frontend: Thumbnails & Cards
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx, apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx, apps/web/components/VideoThumbnail.tsx
Moves imageStatus out of VideoThumbnail, adds ImageLoadingStatus type and props, threads status through VideoCard/CapCard, shows upload progress/error overlays, and disables actions when upload pending.
Web Actions & Backend
apps/web/actions/video/upload.ts, packages/web-backend/src/Videos/VideosRepo.ts, packages/web-backend/src/Videos/index.ts
Adds optional supportsUploadProgress flag to createVideoAndGetUploadUrl (gates videoUploads writes); changes video delete to run two deletions concurrently inside a DB transaction; removes an unused import.
Project & Recording Crates
crates/project/src/meta.rs, crates/recording/src/studio_recording.rs, crates/recording/src/instant_recording.rs
Introduces UploadMeta/S3UploadMeta/VideoUploadInfo and RecordingMeta.upload; converts InstantRecordingMeta to enum variants; adds StudioRecordingStatus and status propagation; marks MultipleSegments status on stop.
New API Crate
crates/api/Cargo.toml, crates/api/src/lib.rs
Adds new unpublished cap-api crate manifest and placeholder lib.rs with reqwest dependency and workspace lint config.
UI Declarations & Types
packages/ui-solid/src/auto-imports.d.ts, apps/web/components/VideoThumbnail.tsx
Adds global icon typings and exported ImageLoadingStatus type; updates VideoThumbnail props to accept external status setter.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as Desktop UI
  participant App as Desktop App
  participant API as Desktop API module
  participant S3 as Storage

  UI->>App: upload(video_id, file_path, meta, screenshot)
  App->>API: upload_multipart_initiate(video_id)
  API-->>App: upload_id
  loop per chunk
    App->>API: upload_multipart_presign_part(video_id, upload_id, part#, md5)
    API-->>App: presigned_url
    App->>S3: PUT part (presigned_url)
    App-->>UI: emit UploadProgressEvent
  end
  App->>API: upload_multipart_complete(video_id, upload_id, parts, meta)
  API-->>App: optional location
  App->>API: upload_signed(screenshot_presign)
  API-->>App: ok
  App-->>UI: upload result (id, link)
Loading
sequenceDiagram
  autonumber
  participant Startup as App (startup)
  participant Meta as Recording Meta Store
  participant Up as Uploader

  Startup->>Meta: list_recordings()
  Meta-->>Startup: recordings with upload/status
  alt any InProgress or Failed
    Startup->>Up: resume_uploads(items)
    Up-->>Startup: progress events (tauri)
  else none
    Startup-->>Startup: no pending uploads
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Suggested reviewers

  • Brendonovich

Poem

I hop through bytes and signed URLs bright,
I stitch each chunk from dawn to night.
Progress pings like carrot cheer,
Resume, retry, the link appears.
— a rabbit with a tiny USB ear 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Local upload tracking” accurately encapsulates the primary focus of the pull request, which is implementing comprehensive local upload progress tracking across web and desktop applications, making it clear to reviewers and other team members what the major change is about.

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.

@oscartbeaumont

This comment was marked as off-topic.

@oscartbeaumont oscartbeaumont mentioned this pull request Sep 29, 2025
9 tasks
@oscartbeaumont
Copy link
Member Author

Follow-up PRs:

  • Unfeature flag upload progress in web on a PR ontop of the existing stuff for testing
  • Bulk create presigned URL's in chunks (due to expiry on them). Will greatly improve upload speed and Vercel function invocations.
  • Using multipart upload for studio mode
  • Allow uploading multiple chunks at the same time
  • Allow opening corrupted studio recordings with more than one part. Warn the user before opening.
  • Resume multipart uploads on startup instead of just restarting
  • Upload status on the thumbnail cause it's done out of band of the main uploader? -> I don't know if this is worth solving???

Copy link
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: 6

Caution

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

⚠️ Outside diff range comments (1)
apps/web/app/api/desktop/[...route]/video.ts (1)

82-96: Restore braces to fix syntax error.

if (video) now lacks a matching block opener, but the closing } remains. That leaves an unmatched brace and will fail to compile/parse.

Apply this diff to restore the block:

-			if (video)
-				return c.json({
+			if (video) {
+				return c.json({
 						id: video.id,
 						// All deprecated
 						user_id: user.id,
 						aws_region: "n/a",
 						aws_bucket: "n/a",
 					});
+			}
🧹 Nitpick comments (2)
apps/web/actions/video/upload.ts (1)

158-282: Consider decomposing this function for improved maintainability.

The createVideoAndGetUploadUrl function handles multiple concerns: authorization, bucket configuration, video record creation, upload tracking initialization, presigned URL generation, third-party integrations (Dub), and cache revalidation.

Extracting helper functions (e.g., initializeVideoRecord, setupUploadTracking, generatePresignedUrl) would improve testability and readability.

apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx (1)

79-80: Remove inline comment to follow repo conventions

Per the project guidelines for TS/TSX files, we avoid inline comments. Please drop the newly added comment above refetchInterval to stay compliant.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ad9096 and a5e332e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (26)
  • .github/workflows/ci.yml (1 hunks)
  • apps/desktop/src-tauri/Cargo.toml (1 hunks)
  • apps/desktop/src-tauri/src/api.rs (1 hunks)
  • apps/desktop/src-tauri/src/general_settings.rs (3 hunks)
  • apps/desktop/src-tauri/src/lib.rs (16 hunks)
  • apps/desktop/src-tauri/src/main.rs (0 hunks)
  • apps/desktop/src-tauri/src/recording.rs (23 hunks)
  • apps/desktop/src-tauri/src/upload.rs (5 hunks)
  • apps/desktop/src-tauri/src/upload_legacy.rs (1 hunks)
  • apps/desktop/src-tauri/src/web_api.rs (1 hunks)
  • apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx (2 hunks)
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx (1 hunks)
  • apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx (7 hunks)
  • apps/desktop/src/utils/createEventListener.ts (1 hunks)
  • apps/desktop/src/utils/tauri.ts (8 hunks)
  • apps/web/actions/video/upload.ts (3 hunks)
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (5 hunks)
  • apps/web/app/api/desktop/[...route]/video.ts (1 hunks)
  • crates/api/Cargo.toml (1 hunks)
  • crates/api/src/lib.rs (1 hunks)
  • crates/project/src/meta.rs (5 hunks)
  • crates/recording/src/instant_recording.rs (1 hunks)
  • crates/recording/src/studio_recording.rs (1 hunks)
  • packages/ui-solid/src/auto-imports.d.ts (1 hunks)
  • packages/web-backend/src/Videos/VideosRepo.ts (1 hunks)
  • packages/web-backend/src/Videos/index.ts (0 hunks)
💤 Files with no reviewable changes (2)
  • packages/web-backend/src/Videos/index.ts
  • apps/desktop/src-tauri/src/main.rs
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • packages/web-backend/src/Videos/VideosRepo.ts
  • crates/recording/src/instant_recording.rs
  • apps/web/actions/video/upload.ts
  • apps/desktop/src-tauri/src/general_settings.rs
  • packages/ui-solid/src/auto-imports.d.ts
  • crates/recording/src/studio_recording.rs
  • apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx
  • apps/desktop/src/utils/createEventListener.ts
  • crates/project/src/meta.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src-tauri/src/web_api.rs
  • apps/desktop/src-tauri/src/lib.rs
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/desktop/src-tauri/src/upload.rs
  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/desktop/src/utils/tauri.ts
  • crates/api/src/lib.rs
  • apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx
  • apps/desktop/src-tauri/src/upload_legacy.rs
  • apps/desktop/src-tauri/src/api.rs
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • packages/web-backend/src/Videos/VideosRepo.ts
  • apps/web/actions/video/upload.ts
  • packages/ui-solid/src/auto-imports.d.ts
  • apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx
  • apps/desktop/src/utils/createEventListener.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • packages/web-backend/src/Videos/VideosRepo.ts
  • apps/web/actions/video/upload.ts
  • packages/ui-solid/src/auto-imports.d.ts
  • apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx
  • apps/desktop/src/utils/createEventListener.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/recording/src/instant_recording.rs
  • crates/recording/src/studio_recording.rs
  • crates/project/src/meta.rs
  • crates/api/src/lib.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/general_settings.rs
  • crates/recording/src/studio_recording.rs
  • crates/project/src/meta.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src-tauri/src/web_api.rs
  • apps/desktop/src-tauri/src/lib.rs
  • apps/desktop/src-tauri/src/upload.rs
  • crates/api/src/lib.rs
  • apps/desktop/src-tauri/src/upload_legacy.rs
  • apps/desktop/src-tauri/src/api.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/recording/src/instant_recording.rs
  • crates/recording/src/studio_recording.rs
  • crates/project/src/meta.rs
  • crates/api/src/lib.rs
apps/web/actions/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All Groq/OpenAI calls must be implemented in Next.js Server Actions under apps/web/actions; do not place AI calls elsewhere

Files:

  • apps/web/actions/video/upload.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/actions/video/upload.ts
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/api/desktop/[...route]/video.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/actions/video/upload.ts
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/api/desktop/[...route]/video.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/**/*.{ts,tsx}: Do not manually import icons in the desktop app; rely on auto-imported icons
In the desktop app, use @tanstack/solid-query for server state management

Files:

  • apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx
  • apps/desktop/src/utils/createEventListener.ts
  • apps/desktop/src/routes/(window-chrome)/settings/general.tsx
  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/api/desktop/[...route]/video.ts
**/tauri.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Never edit auto-generated IPC bindings file tauri.ts

Do not edit auto-generated files named tauri.ts.

Files:

  • apps/desktop/src/utils/tauri.ts
🧬 Code graph analysis (14)
packages/web-backend/src/Videos/VideosRepo.ts (1)
packages/database/index.ts (1)
  • db (29-34)
crates/recording/src/instant_recording.rs (1)
apps/desktop/src/utils/tauri.ts (1)
  • InstantRecordingMeta (408-408)
apps/web/actions/video/upload.ts (3)
packages/utils/src/constants/plans.ts (1)
  • userIsPro (21-45)
packages/database/index.ts (1)
  • db (29-34)
packages/database/schema.ts (1)
  • videoUploads (656-662)
crates/recording/src/studio_recording.rs (2)
crates/rendering/src/project_recordings.rs (1)
  • Some (197-204)
apps/desktop/src/utils/tauri.ts (1)
  • StudioRecordingStatus (462-462)
apps/desktop/src/routes/(window-chrome)/settings/experimental.tsx (1)
apps/desktop/src/routes/(window-chrome)/settings/Setting.tsx (1)
  • ToggleSetting (24-40)
crates/project/src/meta.rs (1)
apps/desktop/src/utils/tauri.ts (9)
  • UploadMeta (466-466)
  • S3UploadMeta (449-449)
  • VideoUploadInfo (478-478)
  • InstantRecordingMeta (408-408)
  • RecordingMeta (436-436)
  • StudioRecordingStatus (462-462)
  • StudioRecordingMeta (461-461)
  • SingleSegment (458-458)
  • MultipleSegments (417-417)
apps/desktop/src-tauri/src/recording.rs (3)
apps/desktop/src/utils/tauri.ts (9)
  • InstantRecordingMeta (408-408)
  • MultipleSegments (417-417)
  • Platform (427-427)
  • RecordingMeta (436-436)
  • SharingMeta (456-456)
  • StudioRecordingMeta (461-461)
  • StudioRecordingStatus (462-462)
  • UploadMeta (466-466)
  • VideoUploadInfo (478-478)
apps/desktop/src-tauri/src/upload.rs (5)
  • build_video_meta (268-291)
  • compress_image (293-321)
  • create_or_get_video (199-266)
  • spawn (330-346)
  • singlepart_uploader (646-687)
crates/project/src/meta.rs (4)
  • default (54-60)
  • default (313-315)
  • load_for_project (147-153)
  • output_path (187-192)
apps/desktop/src-tauri/src/lib.rs (4)
apps/desktop/src/utils/tauri.ts (11)
  • InstantRecordingMeta (408-408)
  • RecordingMeta (436-436)
  • SharingMeta (456-456)
  • StudioRecordingMeta (461-461)
  • StudioRecordingStatus (462-462)
  • UploadMeta (466-466)
  • VideoUploadInfo (478-478)
  • SingleSegment (458-458)
  • MultipleSegments (417-417)
  • UploadProgress (468-468)
  • GeneralSettingsStore (391-391)
apps/desktop/src-tauri/src/upload.rs (5)
  • create_or_get_video (199-266)
  • upload_image (155-197)
  • upload_video (58-139)
  • build_video_meta (268-291)
  • spawn (330-346)
apps/desktop/src-tauri/src/upload_legacy.rs (4)
  • create_or_get_video (384-446)
  • upload_image (328-382)
  • upload_video (205-326)
  • build_video_meta (497-520)
crates/project/src/meta.rs (4)
  • status (218-226)
  • path (143-145)
  • path (329-331)
  • path (380-382)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (2)
packages/ui-solid/src/ProgressCircle.tsx (1)
  • ProgressCircle (55-112)
apps/web/components/VideoThumbnail.tsx (1)
  • VideoThumbnail (56-125)
apps/desktop/src-tauri/src/upload.rs (5)
apps/desktop/src/utils/tauri.ts (6)
  • UploadProgress (468-468)
  • VideoUploadInfo (478-478)
  • RecordingMeta (436-436)
  • S3UploadMeta (449-449)
  • UploadMeta (466-466)
  • UploadProgressEvent (469-469)
crates/project/src/meta.rs (5)
  • path (143-145)
  • path (329-331)
  • path (380-382)
  • load_for_project (147-153)
  • status (218-226)
apps/desktop/src-tauri/src/upload_legacy.rs (4)
  • upload_video (205-326)
  • compress_image (576-603)
  • new (123-129)
  • create_or_get_video (384-446)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get (183-194)
apps/desktop/src-tauri/src/api.rs (9)
  • upload_multipart_initiate (10-44)
  • upload_multipart_complete (111-162)
  • upload_multipart_presign_part (46-88)
  • resp (40-40)
  • resp (84-84)
  • resp (158-158)
  • resp (210-210)
  • upload_signed (182-214)
  • desktop_video_progress (216-244)
apps/desktop/src/utils/tauri.ts (1)
packages/web-domain/src/Video.ts (1)
  • UploadProgress (61-68)
apps/desktop/src/routes/(window-chrome)/settings/recordings.tsx (4)
apps/desktop/src/utils/tauri.ts (3)
  • RecordingMetaWithMetadata (437-437)
  • events (283-327)
  • UploadProgress (468-468)
apps/desktop/src/utils/createEventListener.ts (1)
  • createTauriEventListener (30-44)
packages/ui-solid/src/ProgressCircle.tsx (1)
  • ProgressCircle (55-112)
apps/desktop/src-tauri/src/upload.rs (2)
  • progress (393-403)
  • progress (728-828)
apps/desktop/src-tauri/src/upload_legacy.rs (2)
apps/desktop/src/utils/tauri.ts (4)
  • UploadProgress (468-468)
  • VideoUploadInfo (478-478)
  • S3UploadMeta (449-449)
  • Video (471-471)
apps/desktop/src-tauri/src/upload.rs (18)
  • serde_json (261-261)
  • total (691-691)
  • total (698-700)
  • total (708-710)
  • total (718-720)
  • spawn (330-346)
  • response (242-242)
  • upload_video (58-139)
  • create_or_get_video (199-266)
  • build_video_meta (268-291)
  • progress (393-403)
  • progress (728-828)
  • compress_image (293-321)
  • run (348-424)
  • size (694-694)
  • size (702-704)
  • size (712-714)
  • size (722-724)
apps/desktop/src-tauri/src/api.rs (1)
apps/desktop/src-tauri/src/upload.rs (4)
  • total (691-691)
  • total (698-700)
  • total (708-710)
  • total (718-720)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Clippy
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (9)
.github/workflows/ci.yml (1)

96-101: Clippy component pin looks good.

Explicitly installing the clippy component avoids missing-toolchain issues on fresh runners. Thanks for tightening this up.

packages/ui-solid/src/auto-imports.d.ts (1)

94-95: LGTM! Icon declarations align with upload tracking features.

The addition of IconPhRecordFill and IconPhWarningBold supports the PR's goal of displaying recording and error states in the UI. The declarations follow the existing pattern and are auto-generated, ensuring consistency.

apps/web/actions/video/upload.ts (1)

167-167: LGTM! Feature flag approach for upload progress tracking.

The optional parameter with a conservative default (false) ensures backward compatibility while allowing opt-in for the new upload progress feature. The TODO comment appropriately signals the experimental nature of this capability.

Also applies to: 177-178

apps/desktop/src-tauri/src/web_api.rs (1)

28-30: Header rename aligns with backend check.

Server-side progress gating already reads X-Cap-Desktop-Version, so this update restores parity. Looks good.

apps/desktop/src/utils/createEventListener.ts (1)

34-43: Abort guard prevents stale callbacks.

Setting the aborted flag before awaiting unlisten stops late-fire events from touching torn-down components. Nice catch.

crates/api/src/lib.rs (1)

1-4: Lib scaffolding looks good.

Crate doc/TODO setup is fine for the initial scaffold.

apps/desktop/src-tauri/src/general_settings.rs (1)

100-165: New uploader flag wired correctly.

Default helper + serde settings match existing pattern, so persisted configs behave as expected.

crates/api/Cargo.toml (1)

1-11: Cargo manifest scaffold looks fine.

Package metadata and reqwest dependency are consistent with the new crate setup.

apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (1)

295-552: CapCard progress handling looks solid.

The z-index tweak plus disabled states for active uploads achieve the intended UX without regressions.

Copy link
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: 5

Caution

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

⚠️ Outside diff range comments (1)
apps/web/components/VideoThumbnail.tsx (1)

76-80: Add missing effect dependency.

setImageStatus comes from props, so the hooks linter flags this empty dependency array. Include it to keep lint/tests clean.

-		useEffect(() => {
+		useEffect(() => {
 			if (imageRef.current?.complete && imageRef.current.naturalWidth !== 0) {
 				setImageStatus("success");
 			}
-		}, []);
+		}, [setImageStatus]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5e332e and 35ebc8d.

📒 Files selected for processing (7)
  • apps/desktop/src-tauri/src/lib.rs (16 hunks)
  • apps/desktop/src-tauri/src/recording.rs (23 hunks)
  • apps/desktop/src-tauri/src/upload.rs (5 hunks)
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (8 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx (3 hunks)
  • apps/web/components/VideoThumbnail.tsx (2 hunks)
  • packages/web-backend/src/Videos/VideosRepo.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • packages/web-backend/src/Videos/VideosRepo.ts
  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
  • apps/desktop/src-tauri/src/upload.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src-tauri/src/lib.rs
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • packages/web-backend/src/Videos/VideosRepo.ts
  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • packages/web-backend/src/Videos/VideosRepo.ts
  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • apps/desktop/src-tauri/src/upload.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src-tauri/src/lib.rs
🧬 Code graph analysis (6)
packages/web-backend/src/Videos/VideosRepo.ts (1)
packages/database/index.ts (1)
  • db (29-34)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (1)
apps/web/components/VideoThumbnail.tsx (1)
  • ImageLoadingStatus (8-8)
apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx (1)
apps/web/components/VideoThumbnail.tsx (1)
  • ImageLoadingStatus (8-8)
apps/desktop/src-tauri/src/upload.rs (6)
apps/desktop/src/utils/tauri.ts (7)
  • UploadProgress (468-468)
  • VideoUploadInfo (478-478)
  • GeneralSettingsStore (391-391)
  • RecordingMeta (436-436)
  • S3UploadMeta (449-449)
  • UploadMeta (466-466)
  • UploadProgressEvent (469-469)
apps/desktop/src-tauri/src/lib.rs (13)
  • std (1486-1504)
  • std (1528-1550)
  • tauri_specta (1894-2008)
  • app (1146-1147)
  • app (2307-2307)
  • app (2344-2344)
  • app (2350-2350)
  • app (2550-2551)
  • None (2687-2687)
  • new (344-346)
  • new (763-767)
  • new (1426-1456)
  • clone (338-340)
crates/project/src/meta.rs (4)
  • path (143-145)
  • path (329-331)
  • path (380-382)
  • load_for_project (147-153)
apps/desktop/src-tauri/src/upload_legacy.rs (4)
  • upload_video (205-326)
  • compress_image (576-603)
  • new (123-129)
  • upload_image (328-382)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get (183-194)
apps/desktop/src-tauri/src/api.rs (9)
  • upload_multipart_initiate (10-44)
  • upload_multipart_complete (111-162)
  • upload_multipart_presign_part (46-88)
  • resp (40-40)
  • resp (84-84)
  • resp (158-158)
  • resp (210-210)
  • upload_signed (182-214)
  • desktop_video_progress (216-244)
apps/desktop/src-tauri/src/recording.rs (3)
apps/desktop/src/utils/tauri.ts (10)
  • InstantRecordingMeta (408-408)
  • MultipleSegments (417-417)
  • Platform (427-427)
  • RecordingMeta (436-436)
  • SharingMeta (456-456)
  • StudioRecordingMeta (461-461)
  • StudioRecordingStatus (462-462)
  • UploadMeta (466-466)
  • GeneralSettingsStore (391-391)
  • VideoUploadInfo (478-478)
apps/desktop/src-tauri/src/upload.rs (5)
  • build_video_meta (268-291)
  • compress_image (293-321)
  • upload_video (58-139)
  • spawn (330-346)
  • singlepart_uploader (647-688)
crates/project/src/meta.rs (3)
  • default (54-60)
  • default (313-315)
  • load_for_project (147-153)
apps/desktop/src-tauri/src/lib.rs (3)
apps/desktop/src/utils/tauri.ts (11)
  • InstantRecordingMeta (408-408)
  • RecordingMeta (436-436)
  • SharingMeta (456-456)
  • StudioRecordingMeta (461-461)
  • StudioRecordingStatus (462-462)
  • UploadMeta (466-466)
  • VideoUploadInfo (478-478)
  • SingleSegment (458-458)
  • MultipleSegments (417-417)
  • UploadProgress (468-468)
  • GeneralSettingsStore (391-391)
apps/desktop/src-tauri/src/upload.rs (4)
  • create_or_get_video (199-266)
  • upload_video (58-139)
  • build_video_meta (268-291)
  • spawn (330-346)
crates/project/src/meta.rs (5)
  • status (218-226)
  • path (143-145)
  • path (329-331)
  • path (380-382)
  • load_for_project (147-153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)

Copy link
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

♻️ Duplicate comments (1)
apps/web/actions/video/upload.ts (1)

243-246: Transaction atomicity concern persists when supportsUploadProgress is enabled.

The past review flagged that the videos insert (line 241) and videoUploads insert (now lines 244-246) lack transaction atomicity. While gating the videoUploads insert behind supportsUploadProgress reduces the frequency of this issue, the underlying risk remains: when the flag is true, if presigned URL generation fails (lines 251-257), both database records may be orphaned without cleanup.

Consider wrapping lines 241-246 in db().transaction(...) when supportsUploadProgress is enabled, or implement compensating cleanup in the error path.

-		await db().insert(videos).values(videoData);
-
-		if (supportsUploadProgress)
-			await db().insert(videoUploads).values({
-				videoId: idToUse,
-			});
+		if (supportsUploadProgress) {
+			await db().transaction(async (tx) => {
+				await tx.insert(videos).values(videoData);
+				await tx.insert(videoUploads).values({
+					videoId: idToUse,
+				});
+			});
+		} else {
+			await db().insert(videos).values(videoData);
+		}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35ebc8d and fa894b7.

📒 Files selected for processing (1)
  • apps/web/actions/video/upload.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/actions/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

All Groq/OpenAI calls must be implemented in Next.js Server Actions under apps/web/actions; do not place AI calls elsewhere

Files:

  • apps/web/actions/video/upload.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/actions/video/upload.ts
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/actions/video/upload.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/actions/video/upload.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/actions/video/upload.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/actions/video/upload.ts
🧬 Code graph analysis (1)
apps/web/actions/video/upload.ts (2)
packages/utils/src/constants/plans.ts (1)
  • userIsPro (21-45)
packages/database/schema.ts (1)
  • videoUploads (656-662)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Clippy
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)

Copy link
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

Caution

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

⚠️ Outside diff range comments (1)
apps/web/components/VideoThumbnail.tsx (1)

76-80: Add setImageStatus to the dependency array.

The useEffect calls setImageStatus but omits it from the dependency array, violating React's exhaustive-deps rule and potentially causing stale closures.

Apply this diff:

 useEffect(() => {
   if (imageRef.current?.complete && imageRef.current.naturalWidth !== 0) {
     setImageStatus("success");
   }
-}, []);
+}, [setImageStatus]);
♻️ Duplicate comments (1)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (1)

515-517: Remove inline comment to comply with repo policy.

The inline comment // "block group", violates the coding guidelines that disallow inline comments.

Apply this diff:

 className={clsx(
   "relative",
-  // "block group",
   anyCapSelected && "cursor-pointer pointer-events-none",
 )}

As per coding guidelines

🧹 Nitpick comments (1)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (1)

524-553: Consider extracting the upload overlay into a dedicated component.

The conditional upload progress/error overlay spans 30 lines and duplicates structural elements (aspect-video container, rounded corners, absolute positioning). Extracting it into a separate component (e.g., CapCardUploadOverlay) would reduce nesting, improve readability, and make the overlay logic reusable.

For example, create a new component:

interface CapCardUploadOverlayProps {
  uploadProgress: { status: string; progress: number } | null;
}

function CapCardUploadOverlay({ uploadProgress }: CapCardUploadOverlayProps) {
  if (!uploadProgress) return null;

  return (
    <div className="relative inset-0 w-full h-full z-20">
      <div className="overflow-hidden relative mx-auto w-full h-full rounded-t-xl border-b border-gray-3 aspect-video bg-black z-5">
        <div className="flex absolute inset-0 justify-center items-center rounded-t-xl">
          {uploadProgress.status === "failed" ? (
            <div className="flex flex-col items-center">
              <div className="flex justify-center items-center mb-2 w-8 h-8 bg-red-500 rounded-full">
                <FontAwesomeIcon icon={faVideo} className="text-white size-3" />
              </div>
              <p className="text-[13px] text-center text-white">Upload failed</p>
            </div>
          ) : (
            <div className="relative size-20 md:size-16">
              <ProgressCircle
                progressTextClassName="md:!text-[11px]"
                subTextClassName="!mt-0 md:!text-[7px] !text-[10px] mb-1"
                className="md:scale-[1.5] scale-[1.2]"
                progress={uploadProgress.progress}
              />
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Then replace lines 524-553 with:

-{imageStatus !== "success" && uploadProgress ? (
-  <div className="relative inset-0 w-full h-full z-20">
-    <div className="overflow-hidden relative mx-auto w-full h-full rounded-t-xl border-b border-gray-3 aspect-video bg-black z-5">
-      <div className="flex absolute inset-0 justify-center items-center rounded-t-xl">
-        {uploadProgress.status === "failed" ? (
-          <div className="flex flex-col items-center">
-            <div className="flex justify-center items-center mb-2 w-8 h-8 bg-red-500 rounded-full">
-              <FontAwesomeIcon
-                icon={faVideo}
-                className="text-white size-3"
-              />
-            </div>
-            <p className="text-[13px] text-center text-white">
-              Upload failed
-            </p>
-          </div>
-        ) : (
-          <div className="relative size-20 md:size-16">
-            <ProgressCircle
-              progressTextClassName="md:!text-[11px]"
-              subTextClassName="!mt-0 md:!text-[7px] !text-[10px] mb-1"
-              className="md:scale-[1.5] scale-[1.2]"
-              progress={uploadProgress.progress}
-            />
-          </div>
-        )}
-      </div>
-    </div>
-  </div>
-) : null}
+{imageStatus !== "success" && <CapCardUploadOverlay uploadProgress={uploadProgress} />}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa894b7 and df98f25.

📒 Files selected for processing (3)
  • apps/desktop/src-tauri/src/recording.rs (23 hunks)
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (8 hunks)
  • apps/web/components/VideoThumbnail.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src-tauri/src/recording.rs
  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • apps/desktop/src-tauri/src/recording.rs
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/components/VideoThumbnail.tsx
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
🧬 Code graph analysis (2)
apps/desktop/src-tauri/src/recording.rs (3)
apps/desktop/src/utils/tauri.ts (17)
  • InstantRecordingMeta (408-408)
  • MultipleSegments (417-417)
  • Platform (427-427)
  • ProjectConfiguration (432-432)
  • RecordingMeta (436-436)
  • SharingMeta (456-456)
  • StudioRecordingMeta (461-461)
  • StudioRecordingStatus (462-462)
  • TimelineConfiguration (464-464)
  • TimelineSegment (465-465)
  • UploadMeta (466-466)
  • ZoomMode (482-482)
  • ZoomSegment (483-483)
  • CaptureWindowWithThumbnail (367-367)
  • VideoUploadInfo (478-478)
  • RecordingMode (438-438)
  • RecordingEvent (435-435)
apps/desktop/src-tauri/src/upload.rs (6)
  • build_video_meta (268-291)
  • compress_image (293-321)
  • create_or_get_video (199-266)
  • upload_video (58-139)
  • spawn (330-346)
  • singlepart_uploader (647-688)
crates/project/src/meta.rs (2)
  • load_for_project (147-153)
  • output_path (187-192)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (3)
apps/web/app/Layout/features.ts (1)
  • useFeatureFlag (32-34)
apps/web/components/VideoThumbnail.tsx (1)
  • ImageLoadingStatus (8-8)
packages/ui-solid/src/ProgressCircle.tsx (1)
  • ProgressCircle (55-112)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (15)
apps/web/components/VideoThumbnail.tsx (2)

6-6: LGTM!

The removal of the unused useState import resolves the issue flagged in the previous review.


8-8: LGTM!

The new ImageLoadingStatus type and props correctly externalize the image loading state management, allowing parent components to coordinate upload overlay and thumbnail visibility logic.

Also applies to: 17-18

apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (3)

35-38: LGTM!

The integration of ImageLoadingStatus and external state management for the thumbnail aligns well with the coordinated upload overlay UI introduced in this PR.

Also applies to: 46-46, 179-180


372-375: LGTM!

Disabling Download and Duplicate actions during active uploads when the feature flag is enabled prevents user actions that could conflict with ongoing uploads.

Also applies to: 433-436


565-568: LGTM!

The use of the valid Tailwind utility hidden instead of display-none correctly hides the thumbnail when the upload overlay should be visible, and the props passed to VideoThumbnail enable external state coordination.

Also applies to: 571-572

apps/desktop/src-tauri/src/recording.rs (10)

2-2: LGTM!

The new imports support upload tracking, progressive upload spawning, and thumbnail compression. All appear necessary and are used in the changed code segments.

Also applies to: 5-8, 19-19, 37-37, 45-45


55-55: LGTM!

Removing Option<> wrapper from progressive_upload simplifies the API and eliminates potential None cases, as the uploader is now always created upfront for instant recordings.

Also applies to: 148-148


1082-1147: LGTM!

The implementation correctly pre-creates the video upload, constructs the shareable link, initializes RecordingMeta with the new upload field (set to None at start), and saves metadata eagerly before recording begins. Error handling is proper throughout, and the Windows-safe timestamp formatting prevents path issues.


1519-1554: Past review concerns addressed: no unwrap() on error paths.

The error handling now correctly uses if let Ok(mut project_meta) = RecordingMeta::load_for_project(...) and .save_for_project().map_err(...).ok() to handle failures gracefully without panicking. This resolves the critical issue flagged in previous reviews where unwrap() could crash the desktop app when already handling an upload error.


1646-1718: LGTM!

The progressive upload error handling and fallback logic are well-structured. The code checks if progressive upload succeeded, falls back to upload_video if needed, and updates RecordingMeta with failure status using proper error handling (no unwrap() calls). Thumbnail upload is handled separately with appropriate error logging.


1732-1739: LGTM!

Metadata loading and saving after recording finish uses proper error handling with if let Ok(mut meta) = ... and .map_err(...)? to propagate errors. No unwrap() calls that could panic.


1211-1211: LGTM!

Progressive upload is correctly spawned upfront for instant recordings, with necessary app handle and recording directory cloned before moving into the spawn closure. The uploader receives the finish signal channel to coordinate recording completion.

Also applies to: 1216-1216, 1275-1281


1912-1917: LGTM!

The refactored let-chain pattern improves readability and is idiomatic Rust. The logic correctly merges overlapping or near-overlapping intervals by extending the last merged interval when appropriate.


932-932: LGTM!

Passing function references directly to spawn_blocking is cleaner and equivalent to wrapping in a closure. The refactor improves code clarity.

Also applies to: 940-940


271-271: LGTM!

The Cursor import is necessary for PNG encoding on macOS. The clamp_channel function correctly clamps float values to the 0-255 range for u8 conversion. Extracting recording_dir before calling handle_recording_end enables error path metadata updates.

Also applies to: 523-523, 1430-1430, 1432-1432

@oscartbeaumont
Copy link
Member Author

oscartbeaumont commented Oct 2, 2025

Known issues:

  • Video player controls missing after upload
  • Don't skip the first chunk, reupload it! We will need to dedup the parts array.
  • Instant mode seems to not be yielded chunks for a long time (until ended maybe). This could be the new logic on chunk size?
  • Windows issues (might be a problem from main)
  • Retry policy on S3 request isn't working well. I've changed the config but it needs testing.

Copy link
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 (1)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (1)

515-517: Remove inline comment to comply with repo policy.

The inline comment on line 516 violates the coding guidelines that disallow comments in this repository.

Apply this diff:

 						className={clsx(
 							"relative",
-							// "block group",
 							anyCapSelected && "cursor-pointer pointer-events-none",
 						)}

As per coding guidelines

🧹 Nitpick comments (2)
apps/desktop/src-tauri/src/upload.rs (2)

584-644: Suggest explicit retry limit for consistency.

The multipart uploader correctly computes MD5 checksums, presigns parts, and uploads with retry on server errors. However, unlike singlepart_uploader (line 667), this function does not explicitly set max_retries_per_request, relying on the default instead.

Apply this diff to add an explicit retry limit consistent with the singlepart uploader:

             let url = Uri::from_str(&presigned_url).map_err(|err| format!("uploader/part/{part_number}/invalid_url: {err:?}"))?;
             let resp = reqwest::Client::builder()
                 .retry(reqwest::retry::for_host(url.host().unwrap_or("<unknown>").to_string()).classify_fn(|req_rep| {
                     if req_rep.status().is_some_and(|s| s.is_server_error()) {
                         req_rep.retryable()
                     } else {
                         req_rep.success()
                     }
-                }))
+                })
+                .max_retries_per_request(5)
+                .max_extra_load(5.0))
                 .build()

729-830: Consider logging progress API errors for debugging.

The progress tracking function correctly manages delayed API updates, continuous frontend reemits, and cleanup on stream completion. Task cancellation is properly handled with abort().

However, API call failures at line 765, 773, and 793 are silently ignored with .ok(). While best-effort progress reporting may be acceptable, logging these errors could aid debugging of progress reporting issues mentioned in the PR objectives.

Consider adding error logging for progress API calls:

                         api::desktop_video_progress(&app_clone, &video_id_clone, uploaded, total).await.ok();
+                            .map_err(|e| tracing::warn!("Progress update failed: {}", e))
+                            .ok();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df98f25 and 46b9f16.

📒 Files selected for processing (3)
  • apps/desktop/src-tauri/src/upload.rs (5 hunks)
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (8 hunks)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(org)/dashboard/spaces/[spaceId]/components/VideoCard.tsx
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/desktop/src-tauri/src/upload.rs
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • apps/desktop/src-tauri/src/upload.rs
🧬 Code graph analysis (2)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (2)
apps/web/app/Layout/features.ts (1)
  • useFeatureFlag (32-34)
apps/web/components/VideoThumbnail.tsx (1)
  • ImageLoadingStatus (8-8)
apps/desktop/src-tauri/src/upload.rs (3)
apps/desktop/src/utils/tauri.ts (7)
  • UploadProgress (468-468)
  • VideoUploadInfo (478-478)
  • GeneralSettingsStore (391-391)
  • RecordingMeta (436-436)
  • S3UploadMeta (449-449)
  • UploadMeta (466-466)
  • UploadProgressEvent (469-469)
apps/desktop/src-tauri/src/upload_legacy.rs (6)
  • upload_video (205-326)
  • compress_image (576-603)
  • new (123-129)
  • create_or_get_video (384-446)
  • run (622-805)
  • build_video_meta (497-520)
apps/desktop/src-tauri/src/api.rs (9)
  • upload_multipart_initiate (10-44)
  • upload_multipart_complete (111-162)
  • upload_multipart_presign_part (46-88)
  • resp (40-40)
  • resp (84-84)
  • resp (158-158)
  • resp (210-210)
  • upload_signed (182-214)
  • desktop_video_progress (216-244)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (21)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (8)

32-32: LGTM!

The imports are correctly structured and align with the feature flag and thumbnail loading status changes introduced in this PR.

Also applies to: 36-39


179-180: LGTM!

The feature flag and image status state are properly initialized with correct types.


301-301: LGTM!

The z-index adjustments establish a proper stacking hierarchy: container (z-10) < upload overlay (z-20) < dropdown (z-[51]).

Also applies to: 323-323


372-375: LGTM!

The disabled condition correctly prevents downloads during active mutations and when uploads are in progress (behind the feature flag).


433-436: LGTM!

The disabled condition correctly prevents duplication during active mutations and when uploads are in progress (behind the feature flag).


519-521: LGTM!

The simplified isDeleting check correctly prevents navigation during deletion.


524-553: LGTM!

The upload progress overlay correctly handles both error and progress states with appropriate z-index layering and responsive sizing.


565-568: LGTM!

The containerClass correctly uses the valid Tailwind hidden utility to toggle thumbnail visibility, and the imageStatus props are properly passed to enable external control of loading state.

Also applies to: 571-572

apps/desktop/src-tauri/src/upload.rs (13)

3-56: LGTM!

The imports, struct definitions, and constants are well-organized. The use of String for uploaded and total in UploadProgressEvent is appropriate for JSON serialization across the Tauri IPC boundary.


58-139: LGTM!

The function correctly handles feature-flag routing, streams file data efficiently, and runs video and thumbnail uploads concurrently. Error propagation is appropriate.


141-153: LGTM!

The helper function correctly opens a file and constructs a streaming reader with proper error handling.


155-197: LGTM!

The function follows the same feature-flag pattern as upload_video and correctly handles image uploads.


199-266: LGTM!

The function properly constructs API requests, handles authentication errors, and deserializes responses with appropriate error handling.


268-291: LGTM!

The function correctly extracts video metadata using ffmpeg with appropriate error handling.


293-321: LGTM!

The function correctly offloads CPU-intensive image processing to a blocking task and produces a compressed thumbnail with appropriate quality settings.


323-426: LGTM! Past review concern addressed.

The InstantMultipartUpload::run method correctly implements the multipart upload flow with feature-flag routing. Line 405 now sorts the collected parts by part_number in ascending order before calling upload_multipart_complete, which addresses the previous review comment about AWS requiring parts in correct sequence.

The recording metadata is properly persisted at upload initiation and completion.


428-459: LGTM!

The Chunk struct and from_file_to_chunks function provide a clean abstraction for reading files in chunks. The #[allow(unused)] attribute suggests this is reserved for future use.


461-582: LGTM! Intentional design for header rewriting.

The function correctly implements a streaming chunker for growing files, with the clever approach of deferring part 1 to allow for MP4 header updates after recording completes. The re-read of the first chunk at the end (lines 552-575) ensures any header rewrites are captured in the uploaded file.

The realtime completion signaling and file size polling logic are appropriate for the use case.


646-689: LGTM!

The single-part uploader correctly handles presigned URL uploads with appropriate retry configuration, timeouts, and error handling.


691-727: LGTM!

The UploadedChunk trait provides a clean abstraction for progress tracking, with correct implementations for multiple types.


832-855: LGTM!

The Tauri channel progress tracker correctly computes and sends progress ratios to the frontend.

Copy link
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: 2

Caution

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

⚠️ Outside diff range comments (1)
apps/desktop/src-tauri/src/recording.rs (1)

652-660: Fix clippy: avoid ? on explicit return Err(...)

return Err(...)?; triggers clippy’s needless_question_mark. Return the Err directly.

Apply this diff:

-        return Err("Recording not in progress".to_string())?;
+        return Err("Recording not in progress".to_string());
♻️ Duplicate comments (3)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (1)

515-517: Remove inline comment to satisfy repo policy.

Inline comments are disallowed in this repo; please drop the // "block group", entry.
As per coding guidelines

 						className={clsx(
 							"relative",
-							// "block group",
 							anyCapSelected && "cursor-pointer pointer-events-none",
apps/desktop/src-tauri/src/recording.rs (1)

753-781: Failure path now persists status to meta

On errors, Studio MultipleSegments.status -> Failed { error }, Instant -> Failed { error }, and saved without panicking. This resolves prior unwrap risks.

apps/desktop/src-tauri/src/lib.rs (1)

2470-2584: Robust resume logic for uploads and crashed recordings

  • Marks Studio/Instant InProgress as Failed on startup.
  • Resumes Multipart via InstantMultipartUpload::spawn.
  • Resumes SinglePart by rebuilding meta and uploading; persists Failed on any error, Complete + Sharing on success.
    This addresses “stuck uploading” issues flagged previously.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 46b9f16 and f26d5c5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • apps/desktop/src-tauri/Cargo.toml (1 hunks)
  • apps/desktop/src-tauri/src/lib.rs (16 hunks)
  • apps/desktop/src-tauri/src/recording.rs (20 hunks)
  • apps/desktop/src/utils/tauri.ts (8 hunks)
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (8 hunks)
  • crates/recording/src/studio_recording.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src-tauri/Cargo.toml
🧰 Additional context used
📓 Path-based instructions (11)
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/recording/src/studio_recording.rs
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • crates/recording/src/studio_recording.rs
  • apps/desktop/src/utils/tauri.ts
  • apps/desktop/src-tauri/src/recording.rs
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
  • apps/desktop/src-tauri/src/lib.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • crates/recording/src/studio_recording.rs
  • apps/desktop/src-tauri/src/recording.rs
  • apps/desktop/src-tauri/src/lib.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/recording/src/studio_recording.rs
**/tauri.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Never edit auto-generated IPC bindings file tauri.ts

Do not edit auto-generated files named tauri.ts.

Files:

  • apps/desktop/src/utils/tauri.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/**/*.{ts,tsx}: Do not manually import icons in the desktop app; rely on auto-imported icons
In the desktop app, use @tanstack/solid-query for server state management

Files:

  • apps/desktop/src/utils/tauri.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/desktop/src/utils/tauri.ts
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/desktop/src/utils/tauri.ts
  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx
🧬 Code graph analysis (5)
crates/recording/src/studio_recording.rs (1)
apps/desktop/src/utils/tauri.ts (1)
  • StudioRecordingStatus (465-465)
apps/desktop/src/utils/tauri.ts (1)
packages/web-domain/src/Video.ts (1)
  • UploadProgress (61-68)
apps/desktop/src-tauri/src/recording.rs (3)
apps/desktop/src/utils/tauri.ts (10)
  • InstantRecordingMeta (410-410)
  • MultipleSegments (419-419)
  • Platform (429-429)
  • RecordingMeta (438-438)
  • SharingMeta (459-459)
  • StudioRecordingMeta (464-464)
  • StudioRecordingStatus (465-465)
  • TimelineSegment (468-468)
  • UploadMeta (469-469)
  • VideoUploadInfo (481-481)
apps/desktop/src-tauri/src/upload.rs (5)
  • compress_image (293-321)
  • create_or_get_video (199-266)
  • upload_video (58-139)
  • spawn (330-346)
  • singlepart_uploader (647-689)
crates/project/src/meta.rs (4)
  • default (54-60)
  • default (313-315)
  • load_for_project (147-153)
  • output_path (187-192)
apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx (2)
apps/web/app/Layout/features.ts (1)
  • useFeatureFlag (32-34)
apps/web/components/VideoThumbnail.tsx (1)
  • ImageLoadingStatus (8-8)
apps/desktop/src-tauri/src/lib.rs (4)
apps/desktop/src/utils/tauri.ts (11)
  • InstantRecordingMeta (410-410)
  • RecordingMeta (438-438)
  • SharingMeta (459-459)
  • StudioRecordingMeta (464-464)
  • StudioRecordingStatus (465-465)
  • UploadMeta (469-469)
  • VideoUploadInfo (481-481)
  • SingleSegment (461-461)
  • MultipleSegments (419-419)
  • UploadProgress (471-471)
  • GeneralSettingsStore (393-393)
apps/desktop/src-tauri/src/upload.rs (5)
  • create_or_get_video (199-266)
  • upload_image (155-197)
  • upload_video (58-139)
  • build_video_meta (268-291)
  • spawn (330-346)
apps/desktop/src-tauri/src/upload_legacy.rs (5)
  • create_or_get_video (384-446)
  • upload_image (328-382)
  • upload_video (205-326)
  • build_video_meta (497-520)
  • new (123-129)
crates/project/src/meta.rs (4)
  • status (218-226)
  • path (143-145)
  • path (329-331)
  • path (380-382)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (17)
crates/recording/src/studio_recording.rs (1)

552-553: Good addition: persist completion status in metadata

Setting status to Complete on finish is correct and matches the new status model.

apps/desktop/src-tauri/src/recording.rs (7)

332-337: Windows-safe timestamp formatting

Using dots on Windows is correct to avoid invalid filename characters.


339-359: Initialize meta with explicit recording status

Setting Studio to InProgress and Instant to InProgress; upload: None. This enables reliable crash/finish handling.


498-505: Progressive upload spawn order: verify file readiness

Spawning InstantMultipartUpload before actor build is fine if it waits on finish_upload_rx before touching the file. Please confirm the actor defers file reads until that signal.


550-572: Graceful error handling on actor spawn failure

Emits UI error, persists failure via handle_recording_end, and returns early. Good defensive path.


876-945: Instant finish: correct progressive-upload-first strategy with thumbnail update and fallback

  • Waits for progressive upload; on success uploads thumbnail only.
  • On progressive failure, builds meta and runs full single-part upload; persists UploadMeta::Failed on errors.
    This matches PR goals and avoids silent stalls.

959-967: Persist final meta and sharing on success

Loads, updates, and saves meta; errors are surfaced. Looks good.


1139-1144: Segment merge uses let-chains; simpler and efficient

The merge logic is correct and clearer with if let Some(last) … && ….

apps/desktop/src/utils/tauri.ts (3)

128-136: Type updates align with backend (RecordingMetaWithMetadata)

New return types for getRecordingMeta/listRecordings match lib.rs changes.

Also applies to: 134-136


305-329: New upload progress event exported

uploadProgressEvent wiring matches tauri_specta event added in Rust. Good.


393-393: Approve: GeneralSettingsStore extended & no stale references
GeneralSettingsStore.enableNewUploader, StudioRecordingStatus, UploadMeta, and UploadProgressEvent support the new uploader; no remaining references to RecordingMetaWithMode or legacy wrappers found.

apps/desktop/src-tauri/src/lib.rs (6)

2008-2009: Expose upload progress event

Event is registered for TS autogen; consistent with tauri.ts.


1418-1459: RecordingMetaWithMetadata: stable wrapper for UI

Mode/status derivation is correct for Studio/Instant. Good use of flatten to preserve original shape.


1470-1477: get_recording_meta now returns wrapper

Matches TS binding changes; simplifies UI consumption.


1481-1523: list_recordings returns path + wrapper; sorted by created time

Looks good. Silent skip on load errors keeps UI resilient.


894-917: Block metadata reads for non-finalized studio recordings

Returning errors for Failed/InProgress prevents bogus duration estimation. Good guardrail.


1072-1080: Upload tracking and persistence on exported video

  • Uses meta.output_path() to locate render.
  • Persists UploadMeta::SinglePartUpload before upload, then Complete/Failed after.
  • Clipboard update + notification on success. Solid flow.

Also applies to: 1115-1124, 1139-1147, 1162-1166

Copy link
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/desktop/src-tauri/src/recording.rs (2)

898-920: Verify thumbnail upload error handling doesn't block the upload flow.

The thumbnail upload for successful progressive uploads is done after screenshot_task.await and uses ok() to ignore errors (line 919). This means:

  1. Thumbnail upload failures won't fail the overall upload (good)
  2. Thumbnail upload is attempted even if screenshot creation failed (acceptable)

However, consider whether users should be notified of thumbnail upload failures, as this affects the video preview in the UI.


938-943: Save errors are silently ignored on upload failure path.

When upload_video fails, the code loads and updates RecordingMeta to persist the failure state (lines 938-943). However, save_for_project() failures are silently ignored via ok() (line 942). While this prevents cascading failures, users won't know if the failure state wasn't persisted.

Consider logging the save error or using a pattern like:

.map_err(|e| error!("Failed to persist upload error state: {e}"))
.ok();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5b7e39 and ef47a9d.

📒 Files selected for processing (2)
  • apps/desktop/src-tauri/src/recording.rs (20 hunks)
  • crates/recording/src/instant_recording.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/**/src/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

For desktop IPC, use tauri_specta derive/macros on Rust types/events; do not hand-roll bindings

Files:

  • crates/recording/src/instant_recording.rs
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/recording.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • crates/recording/src/instant_recording.rs
  • apps/desktop/src-tauri/src/recording.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/recording/src/instant_recording.rs
🧬 Code graph analysis (2)
crates/recording/src/instant_recording.rs (1)
apps/desktop/src/utils/tauri.ts (1)
  • InstantRecordingMeta (410-410)
apps/desktop/src-tauri/src/recording.rs (3)
apps/desktop/src/utils/tauri.ts (11)
  • InstantRecordingMeta (410-410)
  • MultipleSegments (419-419)
  • Platform (429-429)
  • ProjectConfiguration (434-434)
  • RecordingMeta (438-438)
  • SharingMeta (459-459)
  • StudioRecordingMeta (464-464)
  • StudioRecordingStatus (465-465)
  • TimelineConfiguration (467-467)
  • UploadMeta (469-469)
  • VideoUploadInfo (481-481)
apps/desktop/src-tauri/src/upload.rs (4)
  • compress_image (293-321)
  • create_or_get_video (199-266)
  • spawn (330-346)
  • singlepart_uploader (647-689)
crates/project/src/meta.rs (4)
  • default (54-60)
  • default (313-315)
  • load_for_project (147-153)
  • output_path (187-192)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Clippy
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (10)
crates/recording/src/instant_recording.rs (1)

111-114: LGTM! API refactor correctly applied.

The change from struct-like syntax to the Complete variant is correct and aligns with the enum-based InstantRecordingMeta API introduced in cap_project. The field names and values are preserved as expected.

apps/desktop/src-tauri/src/recording.rs (9)

293-331: Verify video pre-creation doesn't leave orphaned metadata on failure.

The video is pre-created at lines 298-312, but if this fails, the function returns early with an error. However, since RecordingMeta is saved at line 362 (after this point), there's no orphaned metadata risk here. The flow is correct.


333-364: LGTM! Early metadata persistence enables crash recovery.

The early creation and persistence of RecordingMeta (before recording starts) is a good design choice. It ensures that:

  1. Windows path compatibility is handled with safe timestamp formatting
  2. The recording directory and metadata exist before any recording operations
  3. Failed or crashed recordings can be detected and handled on restart

499-505: LGTM! Progressive upload spawning is correct.

The unconditional spawning of InstantMultipartUpload for instant recordings aligns with the API change making progressive_upload a required field. The placement within the actor spawn task ensures proper error propagation.


754-782: Error handling improved but save failures are still silent.

The previous review flagged unwrap() calls on error paths, and this has been addressed by using ok() instead (line 778). However, while this prevents panics, save failures are still silently ignored. The error is logged (line 776), which is acceptable for a failure-handling path where we're already in an error state and want to avoid cascading failures.

Based on past review comments, the unwrap issue has been resolved.


877-893: LGTM! Progressive upload error handling is correct.

The logic correctly handles both success and failure of the progressive upload:

  • Success: logs and skips fallback upload
  • Failure: logs error and proceeds to fallback upload_video

960-967: LGTM! Final metadata persistence is correct.

On successful recording completion, the metadata is loaded, updated with the final state, and saved with proper error propagation (line 966). This ensures that recording metadata is persisted before proceeding.


1139-1147: LGTM! Let-chain pattern improves readability.

The refactor to use Rust's let-chain syntax (if let Some(last) = merged.last_mut() && condition) is more idiomatic and improves readability by reducing nesting.


1173-1181: LGTM! Test helper updated to match struct changes.

The temporary RecordingMeta construction in the test helper correctly includes the new upload: None field to match the updated struct definition.


60-60: Instant/CompletedRecording::Instant variants include required progressive_upload All construction sites supply the mandatory field.

Copy link
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 (7)
apps/desktop/src-tauri/src/upload.rs (7)

42-47: Remove commented-out code.

The commented config field should either be removed or uncommented if it's needed for future work. Commented code clutters the codebase and should be cleaned up.

Apply this diff:

 pub struct UploadedItem {
     pub link: String,
     pub id: String,
-    // #[allow(unused)]
-    // pub config: S3UploadMeta,
 }

49-54: Consider using numeric types for uploaded and total.

The uploaded and total fields are represented as String, which requires parsing on the receiving end and can lead to errors. Consider using u64 directly, as Serde and Tauri handle numeric serialization well.


59-140: Consider extracting repeated feature flag checking.

The feature flag checking pattern (lines 67-88) is repeated in upload_image (lines 157-170) and InstantMultipartUpload::run (lines 356-373). Consider extracting this into a helper function to reduce duplication and improve maintainability.

Example refactor:

fn is_new_uploader_enabled(app: &AppHandle) -> bool {
    GeneralSettingsStore::get(app)
        .map_err(|err| error!("Error checking status of new uploader flow from settings: {err}"))
        .ok()
        .and_then(|v| v.map(|v| v.enable_new_uploader))
        .unwrap_or(false)
}

294-322: Consider image resize filter quality.

The implementation uses FilterType::Nearest for resizing (line 304), which is fast but produces lower quality results. For thumbnail generation, consider using FilterType::Lanczos3 or FilterType::CatmullRom for better visual quality, unless performance is critical here.


446-467: Remove or utilize unused function.

The from_file_to_chunks function is marked with #[allow(unused)]. If this function is not needed, remove it. If it's planned for future use, document the intention or create a TODO tracking item.


609-609: Verify MD5 computation for large chunks.

Computing MD5 synchronously (line 609) for chunks up to 5MB could block the async runtime. Consider whether this is acceptable or if MD5 computation should be moved to a blocking task for better async performance.


664-676: Inconsistent retry configuration between single and multipart uploaders.

singlepart_uploader has more aggressive retry settings (max 5 retries, max_extra_load 5.0) compared to multipart_uploader which uses the default retry limits. Consider whether multipart uploads should have similar retry resilience, or document why they differ.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef47a9d and 8d6cb13.

📒 Files selected for processing (1)
  • apps/desktop/src-tauri/src/upload.rs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src-tauri/src/upload.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • apps/desktop/src-tauri/src/upload.rs
🧬 Code graph analysis (1)
apps/desktop/src-tauri/src/upload.rs (3)
apps/desktop/src-tauri/src/upload_legacy.rs (2)
  • upload_video (205-326)
  • run (622-805)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get (183-194)
apps/desktop/src-tauri/src/api.rs (9)
  • upload_multipart_initiate (10-44)
  • upload_multipart_complete (111-162)
  • upload_multipart_presign_part (46-88)
  • resp (40-40)
  • resp (84-84)
  • resp (158-158)
  • resp (210-210)
  • upload_signed (182-214)
  • desktop_video_progress (216-244)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (14)
apps/desktop/src-tauri/src/upload.rs (14)

3-40: LGTM!

The imports are well-organized and appropriate for the streaming upload implementation.


67-72: Verify error handling for settings retrieval.

When GeneralSettingsStore::get fails, the error is logged but the function continues with a default value of false. Ensure this is the intended behavior, as settings retrieval failures might indicate a more serious problem that should prevent upload.


142-154: LGTM!

The file reader stream implementation is clean and handles errors appropriately.


269-292: LGTM!

The video metadata extraction using ffmpeg is implemented correctly.


407-413: Verify deduplication strategy for multipart uploads.

The deduplication keeps the last occurrence of each part number (lines 407-413). Ensure this is the intended behavior, as typically you'd want to keep the most recent upload of a part if it was reuploaded. The comment in the PR objectives mentions "First chunk may be skipped and needs reupload; requires deduplication of parts array", so this appears intentional.

Based on the PR context, this deduplication strategy aligns with the known issue about chunk reuploads.


378-390: Consider error handling consistency for RecordingMeta saves.

The save_for_project() call at line 387-390 logs errors with .ok() but continues execution. Later at lines 422-428, the save returns an error. Ensure this inconsistency is intentional. If saving metadata after initiating the upload isn't critical, document this behavior.


436-444: LGTM!

The Chunk struct is well-defined with clear documentation.


469-589: Complex state machine requires careful testing.

This function manages multiple state variables and has intricate logic for handling growing files during recording. Key behaviors to verify:

  1. Part numbering: starts at 1, first chunk is part 1 but yielded last
  2. Polling interval: 500ms between checks (lines 507, 585)
  3. First chunk handling: stored size at line 545, re-read at lines 558-582

Ensure thorough testing covers:

  • Normal recording completion
  • Recording failures/crashes
  • Edge cases where file size changes between checks
  • Behavior when realtime_upload_done signal is never sent

The implementation handles the complex requirements described in the PR objectives.


532-538: Verify partial read handling.

The inner read loop (lines 532-538) accumulates bytes until chunk_size is reached. If file.read() returns 0 (EOF) before reading the full chunk, the function breaks and yields a partial chunk. Verify this is the intended behavior, especially for the first chunk which might be incomplete if the file is still being written.


617-625: Review retry configuration for multipart uploads.

The retry policy (lines 617-625) only retries on server errors (5xx). However, network issues, timeouts, and certain 4xx errors might also benefit from retries. The PR objectives mention "S3 retry policy on requests requires further testing". Consider expanding the retry conditions or documenting why only server errors are retried.

Based on PR objectives noting that "S3 retry policy on requests requires further testing; current retry behavior may be insufficient."


698-734: LGTM!

The UploadedChunk trait provides a clean abstraction for different chunk types, enabling generic progress tracking.


736-837: Complex progress tracking with multiple concurrent tasks.

The progress function manages two types of background tasks:

  1. pending_task: delayed API update after 2 seconds (line 779)
  2. reemit_task: continuous frontend events every 700ms (line 789)

Key observations:

  • Tasks are properly cancelled with .abort() when new chunks arrive
  • The cleanup event (lines 826-830) with 0/0 values signals completion
  • The reemit interval (700ms) and API delay (2 seconds) are hardcoded

Consider:

  • Making intervals configurable or documenting why these values were chosen
  • Ensuring .abort() doesn't leave partial state (Tokio handles this safely, but worth verifying)

The PR objectives mention "event emission frequency and frontend refetch tuning" as an open item.


788-802: Potential task leak if stream errors before completion.

The reemit_task spawned at line 788 runs in an infinite loop. If the stream errors or is dropped before the cleanup logic (lines 819-821), this task may not be cancelled. Verify that the stream wrapper properly handles cleanup on error paths.


839-862: LGTM!

The tauri_progress function provides a clean channel-based progress tracking mechanism.

Copy link
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

♻️ Duplicate comments (1)
apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx (1)

467-472: Duplicated upload progress logic.

This code duplicates the pattern in HLSVideoPlayer.tsx (lines 283-288). See the review comment on that file for the recommended refactor to extract this into a shared hook.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d6cb13 and f0435a4.

📒 Files selected for processing (2)
  • apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx (1 hunks)
  • apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and data fetching in the web app
Web mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData rather than broad invalidations
Client code should use useEffectQuery/useEffectMutation and useRpcClient from apps/web/lib/EffectRuntime.ts; do not create ManagedRuntime inside components

Files:

  • apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx
  • apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx
apps/web/app/**/*.{tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer Server Components for initial data in the Next.js App Router and pass initialData to client components

Files:

  • apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx
  • apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx
  • apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; leverage shared types from packages

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Files:

  • apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx
  • apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx
  • apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx
  • apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx
🧬 Code graph analysis (2)
apps/web/app/s/[videoId]/_components/HLSVideoPlayer.tsx (1)
apps/web/app/s/[videoId]/_components/ProgressCircle.tsx (1)
  • useUploadProgress (26-66)
apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx (1)
apps/web/app/s/[videoId]/_components/ProgressCircle.tsx (1)
  • useUploadProgress (26-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)

Copy link
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: 3

Caution

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

⚠️ Outside diff range comments (1)
apps/desktop/src-tauri/src/upload.rs (1)

294-322: Extract duplicated image compression logic.

This function is duplicated in upload_legacy.rs (lines 575-602). Consider extracting it to a shared utility module to avoid code duplication.

Create a new module (e.g., src/image_utils.rs or add to an existing utils module):

pub async fn compress_image(path: PathBuf) -> Result<Vec<u8>, String> {
    // ... existing implementation ...
}

Then import and use it in both upload.rs and upload_legacy.rs.

🧹 Nitpick comments (3)
apps/desktop/src-tauri/src/upload.rs (3)

504-506: Consider tuning the polling interval based on recording characteristics.

The 100ms polling interval is used in two places: when file metadata access fails (line 504) and when waiting for new data (line 574). Depending on the recording bitrate and chunk size, this interval might be:

  • Too aggressive (wasting CPU on frequent checks for slow recordings)
  • Too slow (delaying chunk availability for fast recordings)

Consider making the polling interval configurable or adaptive based on observed file growth rates.

Also applies to: 574-574


813-824: Cleanup signal using "0, 0" values is non-obvious.

The stream completion emits an UploadProgressEvent with uploaded: "0" and total: "0" to signal the frontend to remove the event from its store. This convention is not self-documenting and could be misinterpreted as an error state or upload cancellation.

Consider one of these alternatives:

  1. Add a boolean completed field to UploadProgressEvent to explicitly signal completion
  2. Use a separate event type for completion notifications
  3. Document this behavior prominently in the UploadProgressEvent struct definition

Example with a completion flag:

 #[derive(Clone, Serialize, Type, tauri_specta::Event)]
 pub struct UploadProgressEvent {
     video_id: String,
     uploaded: String,
     total: String,
+    completed: bool,
 }

772-791: Consider documenting the reemit interval choice.

The continuous reemit task runs every 700ms to provide smooth progress updates to the frontend. While this interval seems reasonable, it would be helpful to document why this specific value was chosen (e.g., balancing frontend responsiveness vs. event frequency).

Consider extracting this as a named constant:

const PROGRESS_REEMIT_INTERVAL_MS: u64 = 700;

Then use it in the interval creation:

-                        let mut interval = time::interval(Duration::from_millis(700));
+                        let mut interval = time::interval(Duration::from_millis(PROGRESS_REEMIT_INTERVAL_MS));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0435a4 and 58dede1.

📒 Files selected for processing (1)
  • apps/desktop/src-tauri/src/upload.rs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src-tauri/src/upload.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • apps/desktop/src-tauri/src/upload.rs
🧬 Code graph analysis (1)
apps/desktop/src-tauri/src/upload.rs (4)
crates/utils/src/lib.rs (1)
  • spawn_actor (37-44)
crates/project/src/meta.rs (5)
  • path (143-145)
  • path (329-331)
  • path (380-382)
  • load_for_project (147-153)
  • status (218-226)
apps/desktop/src-tauri/src/upload_legacy.rs (6)
  • upload_video (205-326)
  • compress_image (576-603)
  • new (123-129)
  • upload_image (328-382)
  • create_or_get_video (384-446)
  • build_video_meta (497-520)
apps/desktop/src-tauri/src/api.rs (9)
  • upload_multipart_initiate (10-44)
  • upload_multipart_complete (111-162)
  • upload_multipart_presign_part (46-88)
  • resp (40-40)
  • resp (84-84)
  • resp (158-158)
  • resp (210-210)
  • upload_signed (182-214)
  • desktop_video_progress (216-244)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (4)
apps/desktop/src-tauri/src/upload.rs (4)

52-53: Consider numeric types for progress values.

The uploaded and total fields are declared as String, which requires conversion for numeric operations and increases the risk of parsing errors in consumers. Unless there's a specific serialization requirement for the frontend, using u64 would be more type-safe.

If String types are required for JSON serialization to the frontend, please confirm this is intentional. Otherwise, apply this diff:

 pub struct UploadProgressEvent {
     video_id: String,
-    uploaded: String,
-    total: String,
+    uploaded: u64,
+    total: u64,
 }

Note: This change would require updating all .to_string() calls when constructing these events throughout the file.


407-413: Deduplication strategy keeps last occurrence of each part.

The deduplication logic uses HashMap to keep only the last occurrence of each part number. This is likely intentional since from_pending_file_to_chunks re-emits part 1 with the corrected MP4 header. However, this behavior should be verified as correct.

Please confirm that keeping the last occurrence of duplicate part numbers is the intended behavior, especially for part 1 which is re-uploaded with the corrected MP4 header after recording completes.


598-598: base64::encode is still supported
base64 v0.22.1 retains the encode convenience function; no migration required.


387-390: Swallowed initial metadata save errors are intentional. The codebase consistently logs and ignores failures from save_for_project() during upload initiation—only the final save error is propagated.

Likely an incorrect or invalid review comment.

Copy link
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: 15

Caution

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

⚠️ Outside diff range comments (1)
apps/desktop/src-tauri/src/upload.rs (1)

329-330: Remove implementation comment.

Per coding guidelines, comments are not permitted.

Apply this diff:

 impl InstantMultipartUpload {
-    /// starts a progressive (multipart) upload that runs until recording stops
-    /// and the file has stabilized (no additional data is being written).
     pub fn spawn(
♻️ Duplicate comments (1)
apps/desktop/src-tauri/src/upload.rs (1)

45-46: Remove commented-out code.

The commented-out lines violate the coding guidelines that prohibit commented code.

Apply this diff:

 pub struct UploadedItem {
     pub link: String,
     pub id: String,
-    // #[allow(unused)]
-    // pub config: S3UploadMeta,
 }
🧹 Nitpick comments (1)
apps/desktop/src-tauri/src/upload.rs (1)

446-467: Remove unused function or integrate into codebase.

The from_file_to_chunks function is marked #[allow(unused)] indicating it's dead code. Either integrate it into the codebase or remove it.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58dede1 and 30d4726.

📒 Files selected for processing (1)
  • apps/desktop/src-tauri/src/upload.rs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,rs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add inline, block, or docstring comments in any language; code must be self-explanatory

Files:

  • apps/desktop/src-tauri/src/upload.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • apps/desktop/src-tauri/src/upload.rs
🧬 Code graph analysis (1)
apps/desktop/src-tauri/src/upload.rs (5)
apps/desktop/src/utils/tauri.ts (5)
  • UploadProgress (471-471)
  • VideoUploadInfo (481-481)
  • S3UploadMeta (452-452)
  • UploadMeta (469-469)
  • UploadProgressEvent (472-472)
crates/project/src/meta.rs (5)
  • path (143-145)
  • path (329-331)
  • path (380-382)
  • load_for_project (147-153)
  • status (218-226)
apps/desktop/src-tauri/src/upload_legacy.rs (6)
  • upload_video (205-326)
  • compress_image (576-603)
  • new (123-129)
  • upload_image (328-382)
  • create_or_get_video (384-446)
  • build_video_meta (497-520)
apps/desktop/src-tauri/src/general_settings.rs (1)
  • get (183-194)
apps/desktop/src-tauri/src/api.rs (9)
  • upload_multipart_initiate (10-44)
  • upload_multipart_complete (111-162)
  • upload_multipart_presign_part (46-88)
  • resp (40-40)
  • resp (84-84)
  • resp (158-158)
  • resp (210-210)
  • upload_signed (182-214)
  • desktop_video_progress (216-244)
🪛 GitHub Check: Clippy
apps/desktop/src-tauri/src/upload.rs

[failure] 579-579: borrowed data escapes outside of function
error[E0521]: borrowed data escapes outside of function
--> apps/desktop/src-tauri/src/upload.rs:579:9
|
577 | fn retryable_client(host: &str) -> reqwest::ClientBuilder {
| ---- - let's call the lifetime of this reference '1
| |
| host is a reference that is only valid in the function body
578 | reqwest::Client::builder().retry(
579 | reqwest::retry::for_host(host)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| host escapes the function body here
| argument requires that '1 must outlive 'static

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (2)
apps/desktop/src-tauri/src/upload.rs (2)

407-413: Deduplication and sorting logic looks correct.

The parts deduplication using HashMap and subsequent sorting properly addresses the previous review concern about part ordering for AWS CompleteMultipartUpload.


605-605: Remove debug logging comment.

Per coding guidelines, comments are not permitted.

Apply this diff:

-    debug!("Initializing multipart uploader for video {video_id:?}");
-
     try_stream! {

Likely an incorrect or invalid review comment.

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.

1 participant