-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(editor): Cap recording import for timeline stitching #1719
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
5207fbb
34cd3f4
c2ecf24
386ed05
092c0cc
c462553
05b46dd
37c7059
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -36,7 +36,7 @@ | |||||
| }, | ||||||
| "bundle": { | ||||||
| "active": true, | ||||||
| "createUpdaterArtifacts": true, | ||||||
| "createUpdaterArtifacts": false, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This change disables generation of auto-update artifacts (
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src-tauri/tauri.conf.json
Line: 39
Comment:
**Likely unintentional: `createUpdaterArtifacts` set to `false`**
This change disables generation of auto-update artifacts (`.sig` / `.tar.gz` / `.nsis.zip`) at bundle time. If the Cap desktop app ships auto-update, this will silently break it for any release built from this commit — users on older versions won't receive the update. The previous value was `true`. Please verify this was intentional; if not, revert.
```suggestion
"createUpdaterArtifacts": true,
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||
| "targets": "all", | ||||||
| "icon": [ | ||||||
| "icons/32x32.png", | ||||||
|
|
@@ -64,8 +64,7 @@ | |||||
| "x": 480, | ||||||
| "y": 140 | ||||||
| } | ||||||
| }, | ||||||
| "frameworks": ["../../../target/native-deps/Spacedrive.framework"] | ||||||
| } | ||||||
| }, | ||||||
| "windows": { | ||||||
| "nsis": { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ import { createElementBounds } from "@solid-primitives/bounds"; | |
| import { createEventListener } from "@solid-primitives/event-listener"; | ||
| import { LogicalPosition } from "@tauri-apps/api/dpi"; | ||
| import { Menu, MenuItem } from "@tauri-apps/api/menu"; | ||
| import { open as openDialog } from "@tauri-apps/plugin-dialog"; | ||
| import { platform } from "@tauri-apps/plugin-os"; | ||
| import { cx } from "cva"; | ||
| import { | ||
|
|
@@ -25,7 +26,7 @@ import "./styles.css"; | |
| import Tooltip from "~/components/Tooltip"; | ||
| import { defaultCaptionSettings } from "~/store/captions"; | ||
| import { defaultKeyboardSettings } from "~/store/keyboard"; | ||
| import { commands } from "~/utils/tauri"; | ||
| import { commands, events } from "~/utils/tauri"; | ||
| import { | ||
| applyCaptionResultToProject, | ||
| getCaptionGenerationErrorMessage, | ||
|
|
@@ -722,6 +723,35 @@ export function Timeline(props: { | |
| } | ||
| }; | ||
|
|
||
| const [isImporting, setIsImporting] = createSignal(false); | ||
|
|
||
| const handleImportCapRecording = async () => { | ||
| const selected = await openDialog({ | ||
| directory: true, | ||
| title: "Select a Cap Recording to Import", | ||
| filters: [{ name: "Cap Recording", extensions: ["cap"] }], | ||
| }); | ||
|
Comment on lines
+728
to
+732
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: apps/desktop/src/routes/editor/Timeline/index.tsx
Line: 728-733
Comment:
**`filters` has no effect for directory pickers on most platforms**
`openDialog` is called with `directory: true`, which opens a folder-selection dialog. The `filters: [{ name: "Cap Recording", extensions: ["cap"] }]` option is designed for file pickers and is ignored by OS-level folder dialogs on macOS and Windows. The `.endsWith(".cap")` check below correctly guards against bad input, but the filter gives a false impression of UI-side enforcement.
How can I resolve this? If you propose a fix, please make it concise. |
||
| if (!selected || typeof selected !== "string") return; | ||
| if (!selected.endsWith(".cap")) { | ||
| toast.error("Please select a .cap recording folder"); | ||
| return; | ||
| } | ||
| setIsImporting(true); | ||
| try { | ||
| await commands.importCapRecording(selected); | ||
| } catch (e) { | ||
| toast.error(String(e)); | ||
| setIsImporting(false); | ||
| } | ||
| }; | ||
|
|
||
| const importedListenerPromise = events.capRecordingImported.listen(() => { | ||
| window.location.reload(); | ||
| }); | ||
| onCleanup(() => { | ||
| importedListenerPromise.then((unlisten) => unlisten()); | ||
| }); | ||
|
|
||
| const split = () => editorState.timeline.interactMode === "split"; | ||
|
|
||
| const maskImage = () => { | ||
|
|
@@ -840,14 +870,29 @@ export function Timeline(props: { | |
| <div class="absolute inset-0 flex items-end"> | ||
| <TimelineMarkings /> | ||
| </div> | ||
| <div class="absolute bottom-0 z-30"> | ||
| <div class="absolute bottom-0 z-30 flex items-center gap-2"> | ||
| <Tooltip content="Add track"> | ||
| <TrackManager | ||
| options={trackOptions()} | ||
| onToggle={handleToggleTrack} | ||
| onAdd={handleAddTrack} | ||
| /> | ||
| </Tooltip> | ||
| <Tooltip content="Import Cap recording"> | ||
| <button | ||
| class="flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-blue-500 text-white text-xs font-medium disabled:opacity-50" | ||
| onClick={handleImportCapRecording} | ||
| disabled={isImporting()} | ||
| > | ||
| <Show | ||
| when={isImporting()} | ||
| fallback={<IconLucidePlus class="size-3" />} | ||
| > | ||
| <IconLucideLoader2 class="size-3 animate-spin" /> | ||
| </Show> | ||
| Import recording | ||
| </button> | ||
| </Tooltip> | ||
| </div> | ||
| </div> | ||
| <Show when={!editorState.playing && editorState.previewTime}> | ||
|
|
@@ -998,6 +1043,19 @@ export function Timeline(props: { | |
| /> | ||
| </TrackRow> | ||
| </Show> | ||
| <button | ||
| class="flex items-center gap-1.5 px-3 py-2 mt-1 rounded-xl bg-blue-500 text-white text-xs font-medium disabled:opacity-50 self-start" | ||
| onClick={handleImportCapRecording} | ||
| disabled={isImporting()} | ||
| > | ||
| <Show | ||
| when={isImporting()} | ||
| fallback={<IconLucidePlus class="size-3" />} | ||
| > | ||
| <IconLucideLoader2 class="size-3 animate-spin" /> | ||
| </Show> | ||
|
||
| Import recording | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
@@ -1011,6 +1069,8 @@ function TrackRow(props: { | |
| children: JSX.Element; | ||
| onDelete?: () => void; | ||
| onContextMenu?: (e: MouseEvent) => void; | ||
| onImport?: () => void; | ||
| importing?: boolean; | ||
| }) { | ||
| return ( | ||
| <div | ||
|
|
@@ -1039,6 +1099,25 @@ function TrackRow(props: { | |
| <IconCapTrash class="size-4" /> | ||
| </button> | ||
| </Show> | ||
| <Show when={props.onImport}> | ||
| <button | ||
| class="absolute inset-0 z-20 flex items-center justify-center rounded-xl border border-blue-400/70 bg-blue-500/90 text-white disabled:opacity-50" | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| props.onImport?.(); | ||
| }} | ||
| onMouseDown={(e) => e.stopPropagation()} | ||
| disabled={props.importing} | ||
| title="Import Cap recording" | ||
| > | ||
| <Show | ||
| when={props.importing} | ||
| fallback={<IconLucidePlus class="size-4" />} | ||
| > | ||
| <IconLucideLoader2 class="size-4 animate-spin" /> | ||
| </Show> | ||
| </button> | ||
| </Show> | ||
| </div> | ||
| <div class="flex-1 relative overflow-hidden min-w-0"> | ||
| {props.children} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unwrap_or(0)corrupts clip indices when a prior external recording is missingclip_index_offsetis computed by summing the segment count of every previously-imported external recording. If any of those paths is missing or has been moved,load_for_projectfails and the closure returns0for that recording. As a result the new recording'srecording_clipindices are computed too low, silently colliding with the indices of an existing external recording inProjectRecordingsMeta::segments. The timeline would then map multipleTimelineSegments to the wrong clips.Consider propagating the error instead of swallowing it:
Prompt To Fix With AI