Skip to content

fix(projects): relink moved media from registry - #235

Merged
EtienneLescot merged 3 commits into
getopenscreen:mainfrom
arhxam:codex/relink-moved-project-media
Aug 4, 2026
Merged

fix(projects): relink moved media from registry#235
EtienneLescot merged 3 commits into
getopenscreen:mainfrom
arhxam:codex/relink-moved-project-media

Conversation

@arhxam

@arhxam arhxam commented Aug 3, 2026

Copy link
Copy Markdown

Summary

  • relink missing cross-platform asset paths through the existing media-links registry when a project opens
  • restore linked webcam paths together with the screen recording
  • reject missing, changed, and ambiguous registry candidates instead of guessing
  • keep unresolved projects loadable so the renderer can offer recovery

Related issue

Fixes #212

Type of change

  • Bug fix
  • Feature
  • Enhancement
  • Documentation
  • Refactor / maintenance
  • Performance
  • Security

Release impact

  • Patch
  • Minor
  • Major / breaking change
  • No release note needed

Desktop impact

  • Windows
  • macOS
  • Linux
  • Installer / packaging
  • Not platform-specific

Screenshots / video

Not applicable; this changes project loading behavior.

Testing

  • npx vitest --run electron/media/mediaLinksRegistry.test.ts electron/media/projectMediaRelinker.test.ts (12 passed)
  • npx vitest --run --exclude electron/recording/webm-seek-index.test.ts (1,535 passed)
  • npx tsc --noEmit
  • npx tsc -p tsconfig.test.json --noEmit
  • npm run lint (passes with 11 existing warnings)
  • npm run docs:check
  • npm run i18n:check
  • npx vite build

Summary by CodeRabbit

  • New Features

    • Automatically relink moved project media when opening projects.
    • Recover screen, webcam, and cursor recordings when their original locations are no longer available.
    • Preserve unresolved media safely without altering project data.
  • Bug Fixes

    • Project loading now continues when session restoration fails.
    • Prevents incorrect media matches when files are ambiguous or have changed.

@arhxam
arhxam requested a review from EtienneLescot as a code owner August 3, 2026 23:09
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6fddd1bf-c8ea-4a06-a762-0ee78d3ee6b8

📥 Commits

Reviewing files that changed from the base of the PR and between c61cc56 and 27df811.

📒 Files selected for processing (8)
  • electron/ai-edition/chat-service.test.ts
  • electron/ai-edition/document-service.test.ts
  • electron/ai-edition/document-service.ts
  • electron/ipc/handlers.ts
  • electron/media/mediaLinksRegistry.test.ts
  • electron/media/mediaLinksRegistry.ts
  • electron/media/projectMediaRelinker.test.ts
  • electron/media/projectMediaRelinker.ts

📝 Walkthrough

Walkthrough

Project loading now relinks stale screen and webcam media paths through the media-links registry. The lookup validates unique file matches. The relinker preserves unresolved data without mutating the original project.

Changes

Project media relinking

Layer / File(s) Summary
Relocated media registry lookup
electron/media/mediaLinksRegistry.ts, electron/media/mediaLinksRegistry.test.ts
Adds validated relocation lookup by portable basename and recorded size. Tests cover cross-platform paths, ambiguous matches, and changed file contents.
Project asset relinking
electron/media/projectMediaRelinker.ts, electron/media/projectMediaRelinker.test.ts
Relinks verified screen and webcam paths, preserves unresolved assets, and returns a copied project. Tests cover missing fingerprints and input immutability.
Project loading integration
electron/ipc/handlers.ts, electron/ai-edition/document-service.ts, electron/ai-edition/*test.ts
Applies relinking during picker, explicit-path, and document-service loads. The document service receives the recordings directory and validates the relinked project after migration.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: etiennelescot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement issue #212 by relinking screen and webcam media, while rejecting missing or ambiguous registry matches.
Out of Scope Changes check ✅ Passed All production and test changes directly support project media relinking and the requirements in issue #212.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the main change: relinking moved project media through the registry.
Description check ✅ Passed The description completes all template sections and includes scope, issue linkage, release impact, platform impact, and comprehensive testing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/ipc/handlers.ts`:
- Line 3322: Update electron/ipc/handlers.ts at lines 3322 and 3396 in the
loadProjectFile and loadCurrentProjectFile flows: catch
getApprovedProjectSession failures after relinking, then return the relinked
project with a null session instead of aborting. Match the existing
explicit-path flow’s approval-failure handling so the renderer can provide
missing-media recovery.

In `@electron/media/mediaLinksRegistry.test.ts`:
- Around line 88-126: Add a test alongside the existing
findRelocatedMediaByStoredPath cases that registers a screen recording, changes
its file size afterward, and then looks it up using the original fingerprint
size. Assert that the lookup resolves to null, covering the changed-file
rejection branch while preserving the existing matching-path behavior.

In `@electron/media/mediaLinksRegistry.ts`:
- Around line 234-261: Update findRelocatedMediaByStoredPath to use
locale-independent toLowerCase() when normalizing both the stalePath basename
and each entry.lastKnownPath basename; keep the existing basename matching logic
unchanged.

In `@electron/media/projectMediaRelinker.test.ts`:
- Around line 19-52: Add a test alongside the existing relinkProjectMedia test
for an asset with a missing or ambiguous media-registry match. Assert that both
originalPath and cameraTrack.sourcePath remain unchanged in the relinked result
and that the original project is not mutated, while preserving the existing
successful-match coverage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 88186dd4-a09a-46b8-9282-63d58df62e84

📥 Commits

Reviewing files that changed from the base of the PR and between 545043d and c61cc56.

📒 Files selected for processing (5)
  • electron/ipc/handlers.ts
  • electron/media/mediaLinksRegistry.test.ts
  • electron/media/mediaLinksRegistry.ts
  • electron/media/projectMediaRelinker.test.ts
  • electron/media/projectMediaRelinker.ts

Comment thread electron/ipc/handlers.ts
Comment thread electron/media/mediaLinksRegistry.test.ts
Comment thread electron/media/mediaLinksRegistry.ts Outdated
Comment thread electron/media/projectMediaRelinker.test.ts
@EtienneLescot
EtienneLescot force-pushed the main branch 2 times, most recently from 89e07a8 to 545043d Compare August 4, 2026 00:00
@arhxam
arhxam force-pushed the codex/relink-moved-project-media branch 2 times, most recently from 3867d01 to 9831b12 Compare August 4, 2026 00:05

@EtienneLescot EtienneLescot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is #212, and you read it properly: the registry already holds the answer and nothing consults it on load. The strictness in findRelocatedMediaByStoredPath is the right instinct — re-statting the candidate against its recorded fingerprint size, and returning null on matches.length !== 1 rather than taking the first hit, is the shape this needs. Cloning instead of mutating the parsed JSON is right too, and aligning loadProjectFile with the tolerant getApprovedProjectSession try/catch that loadProjectFileFromPath already had is a good drive-by.

Two things block it, and both are about where this runs and what it trusts.

The hook point only covers .openscreen import. Every real project open — including auto-load-last-project on launch — goes through DocumentService.getProject, which is untouched. So a project that is already broken stays broken forever, and media that moves after import is never relinked.

And the size check that makes the match safe is skipped whenever sizeBytes is absent, which is every legacy-migrated document. What is left is a case-insensitive basename match that can repoint a project at unrelated footage, attach that footage's webcam, log nothing, and then get persisted by the renderer. That is a worse failure than the one it fixes.

Details inline. The diagnosis is right; the placement and the fallback need rethinking.

Comment thread electron/ipc/handlers.ts
Comment thread electron/media/mediaLinksRegistry.ts Outdated
Comment thread electron/media/projectMediaRelinker.ts Outdated
Comment thread electron/ipc/handlers.ts Outdated
EtienneLescot
EtienneLescot previously approved these changes Aug 4, 2026

@EtienneLescot EtienneLescot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Threads cleared. The relink now runs where every project open passes, and it refuses rather than guessing when the document recorded no size — which is the case that mattered, since silently repointing a project at unrelated footage is worse than leaving it visibly broken.

Approving. Noting for the record that this does not close #212 on its own: registerMediaLinks early-returns without a webcam or cursor sidecar, so a screen-only recording never enters the registry and the issue's literal repro on a fresh machine still fails. That needs the "locate this media" prompt or fingerprints persisted into the document.

@EtienneLescot EtienneLescot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-approving after the rebase — the previous approval was dismissed by the push. The conflict was imports only: relinkProjectMedia against main widening the Linux capture import to carry LinuxCaptureSourceKind. Both kept, both used.

arhxam and others added 3 commits August 4, 2026 15:33
The relink only ran in the `.openscreen` import handlers, which is the one
moment a project is least likely to need it. Every real open — the project
picker, the agent, the auto-load-last-project effect on launch — goes through
DocumentService.getProject and never touched the stored paths, so a document
already broken by issue getopenscreen#212 was re-read as broken forever, and media moved
after the import was never noticed at all. The hook moves to that choke point,
applied to the upgraded JSON so `documentSchema.parse` still validates what we
hand back. The two import handlers keep their own call: they hand the parsed
project straight to the renderer and use it to approve the recording session,
before anything has been through DocumentService at all.

loadCurrentProjectFile is dropped from the patch rather than moved. It is
plumbed all the way out through preload, the native bridge and projectService,
and nothing under src/ ever calls it, so patching it only made the diff look
like it covered more ground than it did.

An asset with no recorded `sizeBytes` is now refused instead of falling back to
a case-insensitive basename compare. That fallback was not a weaker match, it
was no match: `recording.mp4` is the least distinctive name a screen recorder
can produce, and hitting it repointed the project at unrelated footage AND
attached that footage's webcam, silently, with the renderer persisting the
result on the next save. `sizeBytes` is optional in the schema and only
DocumentService.addAsset ever writes it — migrateProjectDataToAxcutDocument
does not — so every document migrated from v1.7 took that path. Media the user
can see is missing is recoverable; media that is quietly someone else's is not.
The size is now a required argument of findRelocatedMediaByStoredPath, so a
caller with nothing to match on cannot ask for a guess.

Every rewrite is logged, and so is the refusal: this changes paths the renderer
then writes back to disk, which should not happen without a trace.

Two stats, not a fingerprint, now decide whether there is anything to repair at
all. With this running on every open (and on addAsset/removeAsset through
getProject), the previous unconditional registry lookup would open and read
128KB of every asset in the project each time just to confirm that the paths
already resolve.

Known gap, unchanged by this: registerMediaLinks early-returns unless a webcam
or cursor sidecar is known, so a screen-only recording never enters the registry
and cannot be relinked by this mechanism at all. Issue getopenscreen#212's literal repro still
needs either the "locate this media" prompt it asks for, or fingerprints
persisted into the document.
@EtienneLescot
EtienneLescot force-pushed the codex/relink-moved-project-media branch from 8c1025c to 27df811 Compare August 4, 2026 13:36
@EtienneLescot
EtienneLescot merged commit 53242cf into getopenscreen:main Aug 4, 2026
14 of 15 checks passed
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.

A project created on Windows opens empty on macOS: absolute media paths are never relinked

3 participants