Skip to content

Media manager#2358

Open
hahn-kev wants to merge 35 commits into
developfrom
media-manager
Open

Media manager#2358
hahn-kev wants to merge 35 commits into
developfrom
media-manager

Conversation

@hahn-kev

@hahn-kev hahn-kev commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator
image

Lets users view media associated with the project. Manually trigger downloading all media or upload pending.

This has highlighted an issue with our current strategy/design. Right now the metadata for media files only lives on lexbox, if the file isn't downloaded we don't even know the file name or type. It would be really nice to store the metadata with the resource, so that gets synced between clients automatically. This may require a harmony change, but there could be other ways to do this.

hahn-kev added 3 commits June 16, 2026 13:55
- Introduced MediaFileDetail component for displaying file details and handling file downloads.
- Implemented GetFileStream method in MediaFilesServiceJsInvokable to retrieve file streams.
- Updated ILcmFileMetadata to include SizeInBytes property.
- Enhanced IMediaFilesServiceJsInvokable interface with getFileStream method.
@hahn-kev hahn-kev marked this pull request as draft June 16, 2026 09:18
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 25fa860d-9c37-4358-8b03-c6c888503cb3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds file size and metadata (author, upload date, MIME type) to media files end-to-end: backend storage/sync/upload flows, a typed RemoteResource<LcmFileMetadata> model with an EF migration, extended LcmCrdt/FwLite media services, a new JS-invokable service, and a new frontend Media Manager UI with related localization strings.

Changes

Media File Metadata Feature

Layer / File(s) Summary
Core metadata model and size handling
backend/FwLite/MiniLcm/Media/FileMetadata.cs, backend/LexCore/Entities/MediaFile.cs, backend/Directory.Packages.props, backend/Testing/FwHeadless/MediaFileTests.cs
LcmFileMetadata gains SizeInBytes; FileMetadata.SizeInBytes widened from int? to long? without capping; SIL.Harmony packages bumped; tests updated to assert uncapped sizes.
FwHeadless upload/sync metadata flow
backend/FwHeadless/Controllers/MediaFileController.cs, backend/FwHeadless/Media/MediaFileService.cs, backend/FwHeadless/Models/MediaFileModels.cs
Upload responses include file metadata; ETag hashing now only computes when missing; sync logic reconciles metadata into Harmony remote resources.
LcmCrdt typed remote resource storage
backend/FwLite/LcmCrdt/MediaServer/*, backend/FwLite/LcmCrdt/Migrations/*, backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
LcmMediaService/IMediaServerClient typed to LcmFileMetadata; new EF migration adds Metadata jsonb column; DI wiring registers the typed remote resource entity.
LcmCrdt tests and snapshots
backend/FwLite/LcmCrdt.Tests/*
Regression/snapshot data and exclusion configs updated to reflect RemoteResource<LcmFileMetadata> and new SetRemoteResourceMetadataChange.
FwLite JS-invokable MediaFilesService
backend/FwLite/FwLiteShared/Services/*, ReinforcedFwLiteTypingConfig.cs
New MediaFilesServiceJsInvokable wraps resource listing/download/upload/metadata/stream operations; wired into DotnetService, FwLiteProvider, and ProjectScope.
Frontend generated types and service registry
frontend/viewer/src/lib/dotnet-types/..., service-provider.ts
New TS interfaces (IMediaFilesServiceJsInvokable, IHarmonyResource) and extended existing types register the media service in the typed registry.
Media Manager UI, routing, and project context
frontend/viewer/src/lib/media-manager/*, ShadcnProjectView.svelte, DotnetProjectView.svelte, ProjectSidebar.svelte, project-context.svelte.ts
New MediaManagerView/MediaFileDetail components list, preview, load, download, upload, and export media files; new route and sidebar entry added.
Audio input loader sentinel refactor
audio-input.svelte
Replaces a local sentinel with an exported AUDIO_LOADER_HANDLED symbol for loader-result signaling.
Localization strings
frontend/viewer/src/locales/*.po
Adds Media Manager UI labels, statuses, and error messages across en, es, fr, id, ko, ms, sw, and vi locales.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: 💻 FW Lite, 📦 Lexbox

Suggested reviewers: rmunn

Poem

🐇 A rabbit hops through bytes and streams,
Metadata now fulfills its dreams —
Size and author, date and name,
Media Manager joins the game.
Across eight tongues the labels bloom,
Hop along, there's always room! 🎵

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches the main feature added: a media manager.
Description check ✅ Passed The description accurately describes the media manager view, offline download, upload pending, and metadata concerns.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch media-manager

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.

@github-actions github-actions Bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label Jun 16, 2026
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

UI unit Tests

  1 files  ±0   62 suites  ±0   29s ⏱️ +2s
186 tests ±0  186 ✅ ±0  0 💤 ±0  0 ❌ ±0 
258 runs  ±0  258 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit e19ea6f. ± Comparison against base commit fa14cfa.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

C# FwHeadless Unit Tests

48 tests   48 ✅  18s ⏱️
 5 suites   0 💤
 1 files     0 ❌

Results for commit e19ea6f.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

C# Unit Tests

165 tests   165 ✅  20s ⏱️
 23 suites    0 💤
  1 files      0 ❌

Results for commit e19ea6f.

♻️ This comment has been updated with latest results.

@myieye

myieye commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

You're fast ;).

It's a little funny how much the right panel looks like an email 😄

I'm wondering how users will find things? Or maybe they don't actually want to?

What are the real use cases for this?

  • Deleting orphaned files
  • Downloading individual files or bulk making everything available offline
  • ...Anything else?

If that's all, then, this is probably fine. Maybe we just need a filter for "Unused media" i.e. orphaned stuff.

Nit: can we make the resource "names" align vertically e.g. by either stacking the cloud and local icons vertically instead of horizontally or putting them in the second row? Maybe like the part of speech badge 🤔 or maybe put the icons into the "Local and remote" text?

I'm guessing the "Download all" is actually for making stuff available offline, rather than saving a copy of everything somewhere on your device right? "Downloading" sounds like "Saving" to me. "Make all media available offline" is a bit long for a button text 😆. Maybe we just change it to "Download all..." (i.e. with 3-dots), so users know it opens a dialog and the dialog then has an explanation and submit button?

The GDocs UI for making things available offline is a bit weird. I first clicked a button in this overlay to make my doc "ready for offline use":
image

After that I was able to open, edit and comment on the document while offline.
But even after doing that and reloading the page multiple times, I still have a separate menu option for making it available offline:
image

When I click it, it changes to:
image

I see no differences in behaviour after enabling that second option.

@hahn-kev

hahn-kev commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Another use case I could see is removing files locally (to free up space), not sure how realistic that is though. On android there's a distinction between cache and data, we should probably figure that out to download stuff into cache.

Yes you noticed another terminology issue we stumbled in to. I think download in this context could mean either, export so I can use it for whatever, or download to my device from the server. So we should probably just not use the word download. What if we just used the term "Load" and "Load all" for the current "Download all" button which loads for offline use. We currently use "Save As" for getting files out of the app, I think that can stay as is.

image

@argos-ci

argos-ci Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Jul 8, 2026, 8:39 AM

@github-actions github-actions Bot added the 📦 Lexbox issues related to any server side code, fw-headless included label Jun 17, 2026
hahn-kev and others added 6 commits July 2, 2026 11:06
Co-authored-by: Cursor <cursoragent@cursor.com>
The media-manager branch adopted Harmony's generic IRemoteResourceService<LcmFileMetadata> API but was still pinned to rc.202, which caused CS0308 build failures in CI.
Follows the SIL.Harmony rc.211 bump that made remote resources generic on metadata.
@hahn-kev hahn-kev marked this pull request as ready for review July 6, 2026 07:06
hahn-kev and others added 2 commits July 8, 2026 13:44
Resolve conflicts in CRDT regression snapshots and i18n catalogs, regenerating DataModelSnapshotTests verified files after merge.

Co-authored-by: Cursor <cursoragent@cursor.com>

@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: 13

Caution

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

⚠️ Outside diff range comments (1)
backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs (1)

151-164: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Dispose MediaFilesService in the cleanup callback to prevent a DotNetObjectReference leak.

HistoryService?.Dispose() and SyncService?.Dispose() are called during cleanup, but MediaFilesService is not. DotNetObjectReference holds a GCHandle that must be explicitly disposed; omitting it leaks memory for every CRDT project that is opened and closed.

🛡️ Proposed fix
            HistoryService?.Dispose();
            SyncService?.Dispose();
+           MediaFilesService?.Dispose();

            MiniLcm.Value.Dispose();
🤖 Prompt for 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.

In `@backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs` around lines
151 - 164, The cleanup callback in ProjectServicesProvider is disposing
HistoryService and SyncService but omits MediaFilesService, which leaves its
DotNetObjectReference-backed GCHandle undisposed. Update the cleanup logic in
the async Defer callback to also dispose MediaFilesService alongside the other
project-scoped services, keeping the disposal order consistent with
HistoryService, SyncService, MiniLcm, and serviceScope.
♻️ Duplicate comments (8)
frontend/viewer/src/locales/fr.po (2)

1275-1277: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Loaded file".

Same issue as flagged in es.po: msgid "Loaded file" has no msgstr line before the next comment block, unlike en.po.

🐛 Proposed fix
 #: src/lib/media-manager/MediaManagerView.svelte
 msgid "Loaded file"
+msgstr ""
 #. Accessible label on the activity view loading spinner while filtered commits are fetched.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/fr.po` around lines 1275 - 1277, The French PO
entry for Loaded file is missing its msgstr block, which breaks the gettext
entry structure. Update the locale entry around msgid "Loaded file" in fr.po to
include a proper msgstr line before the next comment block, matching the pattern
used in en.po and the other locale files. Keep the surrounding
MediaManagerView.svelte translation entry intact and ensure the PO syntax
remains valid.

1661-1663: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Offline, unable to open file".

Same defect as flagged in es.po: no msgstr line follows msgid "Offline, unable to open file".

🐛 Proposed fix
 #: src/lib/media-manager/MediaFileDetail.svelte
 msgid "Offline, unable to open file"
+msgstr ""
 #. Sort option in activity view — earliest commit first.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/fr.po` around lines 1661 - 1663, The French
locale entry for the MediaFileDetail string is missing the required msgstr line
after msgid "Offline, unable to open file", which breaks PO syntax. Update the
affected translation entry in fr.po so it follows the same structure as the
other locale files, using the msgid/msgstr pair for "Offline, unable to open
file" and keeping the surrounding comment/entry formatting consistent.
frontend/viewer/src/locales/ko.po (2)

1661-1663: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Offline, unable to open file".

Same defect as flagged in other locale files: no msgstr line follows msgid "Offline, unable to open file".

🐛 Proposed fix
 #: src/lib/media-manager/MediaFileDetail.svelte
 msgid "Offline, unable to open file"
+msgstr ""
 #. Sort option in activity view — earliest commit first.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/ko.po` around lines 1661 - 1663, The PO entry for
the MediaFileDetail.svelte string is malformed because msgid "Offline, unable to
open file" is not followed by a msgstr line. Update the ko.po translation entry
so it includes the missing msgstr line immediately after that msgid, matching
the structure used by the surrounding locale entries and keeping the PO syntax
valid.

1275-1277: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Loaded file".

Same issue as flagged in other locale files: msgid "Loaded file" has no msgstr line before the next comment block.

🐛 Proposed fix
 #: src/lib/media-manager/MediaManagerView.svelte
 msgid "Loaded file"
+msgstr ""
 #. Accessible label on the activity view loading spinner while filtered commits are fetched.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/ko.po` around lines 1275 - 1277, The PO entry for
“Loaded file” is missing its msgstr line, which breaks the locale file
structure. In ko.po, update the translation block for the msgid tied to
MediaManagerView so it includes a proper msgstr before the next comment block,
matching the format used by the other locale files and preserving valid PO
syntax.
frontend/viewer/src/locales/ms.po (2)

1275-1277: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Loaded file".

Same issue as flagged in other locale files: msgid "Loaded file" has no msgstr line before the next comment block.

🐛 Proposed fix
 #: src/lib/media-manager/MediaManagerView.svelte
 msgid "Loaded file"
+msgstr ""
 #. Accessible label on the activity view loading spinner while filtered commits are fetched.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/ms.po` around lines 1275 - 1277, The ms.po entry
for “Loaded file” is missing its required msgstr line, which breaks PO syntax
and leaves the next comment block attached to the wrong entry. Update the
translation entry associated with msgid "Loaded file" in this locale file by
adding the missing msgstr line before the following comment block, matching the
same structure used by the other locale files and keeping the entry aligned with
the MediaManagerView.svelte accessibility label.

1661-1663: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Offline, unable to open file".

Same defect as flagged in other locale files: no msgstr line follows msgid "Offline, unable to open file". Since this same pattern repeats identically across all 5 non-English locale files for exactly these two strings, it's likely a bug in the extraction/Crowdin-sync tooling for this PR rather than a one-off typo — worth checking the generation pipeline in addition to patching these files.

🐛 Proposed fix
 #: src/lib/media-manager/MediaFileDetail.svelte
 msgid "Offline, unable to open file"
+msgstr ""
 #. Sort option in activity view — earliest commit first.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/ms.po` around lines 1661 - 1663, The PO entry for
the “Offline, unable to open file” string is missing its msgstr, breaking syntax
in this locale file. Update the affected translation entry in the locales
generated from MediaFileDetail.svelte so every msgid is immediately followed by
a valid msgstr line, and verify the same pattern in the other non-English locale
files for this exact string pair. Also check the extraction/Crowdin-sync
pipeline that produced these locale files to prevent the missing msgstr from
recurring.
frontend/viewer/src/locales/id.po (2)

1661-1663: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Offline, unable to open file".

Same defect as flagged in other locale files: no msgstr line follows msgid "Offline, unable to open file".

🐛 Proposed fix
 #: src/lib/media-manager/MediaFileDetail.svelte
 msgid "Offline, unable to open file"
+msgstr ""
 #. Sort option in activity view — earliest commit first.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/id.po` around lines 1661 - 1663, Add the missing
msgstr entry for the "Offline, unable to open file" translation in the id.po
locale so the PO block is syntactically complete; update the
MediaFileDetail.svelte-related entry to include a proper msgstr line immediately
after the msgid, matching the pattern used by the other locale files and keeping
the surrounding activity-view comment intact.

1275-1277: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Missing msgstr line breaks PO syntax for "Loaded file".

Same issue as flagged in es.po/fr.po: msgid "Loaded file" has no msgstr line before the next comment block.

🐛 Proposed fix
 #: src/lib/media-manager/MediaManagerView.svelte
 msgid "Loaded file"
+msgstr ""
 #. Accessible label on the activity view loading spinner while filtered commits are fetched.
🤖 Prompt for 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.

In `@frontend/viewer/src/locales/id.po` around lines 1275 - 1277, The PO entry for
"Loaded file" is missing its msgstr line, which breaks the syntax before the
next comment block. Update the locale entry in the id.po translation file so the
msgid "Loaded file" is immediately followed by a proper msgstr line, matching
the pattern used in the other locale files and keeping the surrounding comment
for the loading spinner entry intact.
🧹 Nitpick comments (2)
backend/FwLite/LcmCrdt/MediaServer/LcmMediaService.cs (1)

208-217: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

GetFileMetadata fetches metadata from the server but never persists it locally.

When resource exists but Metadata is null, every call hits the server. The AddMissingMetadata method (line 42) exists for exactly this purpose but isn't called here. Consider caching the fetched metadata to avoid repeated remote calls.

♻️ Proposed refactor
 public async Task<LcmFileMetadata> GetFileMetadata(Guid fileId)
 {
     var resource = await resourceService.GetResource(fileId);
     if (resource is not { Metadata: not null })
     {
         var mediaClient = await MediaServerClient();
-        return await mediaClient.GetFileMetadata(fileId);
+        var metadata = await mediaClient.GetFileMetadata(fileId);
+        if (resource is not null)
+        {
+            await AddMissingMetadata(resource, metadata);
+        }
+        return metadata;
     }
     return resource.Metadata;
 }
🤖 Prompt for 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.

In `@backend/FwLite/LcmCrdt/MediaServer/LcmMediaService.cs` around lines 208 -
217, GetFileMetadata currently falls back to MediaServerClient when
resource.Metadata is null, but it returns the remote metadata without saving it
locally, so repeated calls keep hitting the server. Update
LcmMediaService.GetFileMetadata to use AddMissingMetadata after fetching from
the media server, so the retrieved LcmFileMetadata is cached on the resource
before returning it.
backend/FwHeadless/Media/MediaFileService.cs (1)

116-131: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider isolating per-file errors during sync.

AddMissingMetadata, AddExistingRemoteResource, and DeleteResource are awaited sequentially per file with no try/catch; a single problematic resource (e.g., missing local file, server error) would abort the whole sync loop and leave remaining files unsynced for that pass.

🤖 Prompt for 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.

In `@backend/FwHeadless/Media/MediaFileService.cs` around lines 116 - 131, The
sync loop in MediaFileService currently awaits AddMissingMetadata,
AddExistingRemoteResource, and DeleteResource without any per-resource error
handling, so one failure can abort the rest of the pass. Wrap the per-file work
in the sync routine that processes lcmResources and existingDbFile entries with
try/catch so each resource is handled independently, log or record the failure
with the relevant resource id/path, and continue to the next item instead of
stopping the whole loop.
🤖 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 `@backend/FwLite/LcmCrdt.Tests/Changes/UseChangesTests.cs`:
- Around line 285-297: The test setup in UseChangesTests is reusing the same
entity id for both the pending-upload chain and the create/delete branch, which
makes the dependency graph order-dependent. Update the create/delete branch to
use its own distinct entity id in the local variables around
createRemoteResourceChange and DeleteRemoteResourceChange, while keeping the
pending-upload flow on createRemoteResourcePendingUploadChange.EntityId. Make
sure the yielded ChangeWithDependencies entries still reference the correct
entity-specific ids and dependencies so CanAddAllChangeTypes() exercises
independent chains.

In `@backend/FwLite/LcmCrdt.Tests/ConfigRegistrationTests.cs`:
- Around line 17-23: The exclusion list in ConfigRegistrationTests is using the
wrong delete type for remote resources; update the test data to exclude
DeleteRemoteResourceChange<LcmFileMetadata> instead of
DeleteChange<RemoteResource<LcmFileMetadata>>. Make the change in the collection
that includes JsonPatchChange and DeleteChange entries so it matches the
concrete type actually used by the remote-resource delete path.

In `@backend/FwLite/LcmCrdt/MediaServer/IMediaServerClient.cs`:
- Around line 27-39: `FileMetadata.SizeInBytes` is still using `int?`, which no
longer matches the widened server contract and can break large file uploads.
Update the `FileMetadata` DTO in `IMediaServerClient` to use `long?`, then
propagate the same type through `LcmMediaService.UploadResource` and any related
`LcmFileMetadata` mapping so `response.Metadata?.SizeInBytes` is handled as a
long consistently.

In `@frontend/viewer/src/lib/media-manager/MediaFileDetail.svelte`:
- Around line 202-231: The Save As flow in loadBlobForSave and saveAs can fail
silently when mediaFilesService is unavailable. Add user-facing feedback before
returning undefined in loadBlobForSave, and ensure saveAs surfaces that failure
path instead of just exiting; use the existing AppNotification.error pattern and
keep the fix centered on the loadBlobForSave/saveAs logic in MediaFileDetail.
- Around line 81-100: The metadata resolver in MediaFileDetail.svelte is
swallowing service fetch failures and never falling back to the synced
file.metadata value, which can leave metadata.current undefined for remote-only
files. Update the resource callback tied to file.id, file.remote,
file.localPath, and mediaFilesService to prefer file.metadata as a fallback (or
initial value) before/after calling service.getFileMetadata(), matching the
behavior used by MediaManagerView.svelte’s displayName logic. Keep the
localPath-based guess as the last fallback so the UI can still render a
meaningful state when the network lookup fails.
- Around line 142-168: The preview loader in MediaFileDetail’s resource callback
can outlive a superseded run and create an unreleased blob URL. Thread the
resource abort signal through the preview-loading path by updating the
loadFileBlob call inside the preview resource to accept and honor cancellation,
or ensure the URL is revoked in the loader’s cleanup when the request is
abandoned. Use the preview resource and loadFileBlob symbols to locate the
affected flow.

In `@frontend/viewer/src/lib/media-manager/MediaManagerView.svelte`:
- Around line 29-51: Extract the duplicated media helper logic into a shared
media-manager utility module and use it from MediaManagerView.svelte and
MediaFileDetail.svelte. Move the LocationStatus type, locationLabels map, and
the filename-resolution helper currently in displayName() into a common helper
(for example a media-utils module), then update both components to import and
call the shared symbols instead of maintaining separate copies. Keep the shared
helper as the single source of truth for metadata filename handling, localPath
fallback, and status labels.

In `@frontend/viewer/src/locales/es.po`:
- Around line 1275-1277: The Spanish PO catalog is missing the translation entry
for the “Loaded file” msgid, leaving the block malformed. Update the entry in
the es.po locale file by adding the missing msgstr for the msgid "Loaded file"
before the next #:/#. comment block, keeping the PO structure consistent with
the surrounding entries.
- Around line 1661-1663: The PO entry for the MediaFileDetail string is
malformed because the translation block for msgid "Offline, unable to open file"
is missing its msgstr line. Update this entry in the locale file so it includes
a proper msgstr for that msgid, keeping the surrounding comment and metadata
intact, and verify other nearby entries in the same section remain correctly
formatted.

In `@frontend/viewer/src/locales/sw.po`:
- Around line 1661-1663: The PO entry for the “Offline, unable to open file”
string is incomplete because it is missing its msgstr line, causing the next
translation entry to be parsed inside the same block. In the sw locale file, add
the missing empty msgstr for the msgid "Offline, unable to open file" before the
following “Oldest first” entry, and make the same correction in the vi locale
file. Use the existing PO entry structure around the affected msgid/msgstr pairs
to restore a valid translation block.
- Around line 1275-1277: The PO entries are malformed because the `msgid` lines
for `Loaded file` and `Offline, unable to open file` are missing their
corresponding `msgstr ""` lines, causing adjacent entries to merge. Update the
affected entries in the `sw.po` locale file so each `msgid` is immediately
followed by an empty `msgstr` placeholder, keeping the existing entry structure
intact and preventing translation compilation errors.

In `@frontend/viewer/src/locales/vi.po`:
- Around line 1661-1663: The PO entry for the msgid “Offline, unable to open
file” is missing its msgstr field, causing the following entry to be parsed as
part of the same block. Update the vi.po localization entry near the
MediaFileDetail.svelte translation so that the msgid has a corresponding empty
msgstr line before the next comment or msgid starts.
- Around line 1275-1277: The vi.po translation entries for the strings handled
by MediaManagerView.svelte are malformed because they end at msgid without a
matching msgstr. Add the missing msgstr "" line for both “Loaded file” and
“Offline, unable to open file” in frontend/viewer/src/locales/vi.po so each PO
entry is complete and parseable.

---

Outside diff comments:
In `@backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs`:
- Around line 151-164: The cleanup callback in ProjectServicesProvider is
disposing HistoryService and SyncService but omits MediaFilesService, which
leaves its DotNetObjectReference-backed GCHandle undisposed. Update the cleanup
logic in the async Defer callback to also dispose MediaFilesService alongside
the other project-scoped services, keeping the disposal order consistent with
HistoryService, SyncService, MiniLcm, and serviceScope.

---

Duplicate comments:
In `@frontend/viewer/src/locales/fr.po`:
- Around line 1275-1277: The French PO entry for Loaded file is missing its
msgstr block, which breaks the gettext entry structure. Update the locale entry
around msgid "Loaded file" in fr.po to include a proper msgstr line before the
next comment block, matching the pattern used in en.po and the other locale
files. Keep the surrounding MediaManagerView.svelte translation entry intact and
ensure the PO syntax remains valid.
- Around line 1661-1663: The French locale entry for the MediaFileDetail string
is missing the required msgstr line after msgid "Offline, unable to open file",
which breaks PO syntax. Update the affected translation entry in fr.po so it
follows the same structure as the other locale files, using the msgid/msgstr
pair for "Offline, unable to open file" and keeping the surrounding
comment/entry formatting consistent.

In `@frontend/viewer/src/locales/id.po`:
- Around line 1661-1663: Add the missing msgstr entry for the "Offline, unable
to open file" translation in the id.po locale so the PO block is syntactically
complete; update the MediaFileDetail.svelte-related entry to include a proper
msgstr line immediately after the msgid, matching the pattern used by the other
locale files and keeping the surrounding activity-view comment intact.
- Around line 1275-1277: The PO entry for "Loaded file" is missing its msgstr
line, which breaks the syntax before the next comment block. Update the locale
entry in the id.po translation file so the msgid "Loaded file" is immediately
followed by a proper msgstr line, matching the pattern used in the other locale
files and keeping the surrounding comment for the loading spinner entry intact.

In `@frontend/viewer/src/locales/ko.po`:
- Around line 1661-1663: The PO entry for the MediaFileDetail.svelte string is
malformed because msgid "Offline, unable to open file" is not followed by a
msgstr line. Update the ko.po translation entry so it includes the missing
msgstr line immediately after that msgid, matching the structure used by the
surrounding locale entries and keeping the PO syntax valid.
- Around line 1275-1277: The PO entry for “Loaded file” is missing its msgstr
line, which breaks the locale file structure. In ko.po, update the translation
block for the msgid tied to MediaManagerView so it includes a proper msgstr
before the next comment block, matching the format used by the other locale
files and preserving valid PO syntax.

In `@frontend/viewer/src/locales/ms.po`:
- Around line 1275-1277: The ms.po entry for “Loaded file” is missing its
required msgstr line, which breaks PO syntax and leaves the next comment block
attached to the wrong entry. Update the translation entry associated with msgid
"Loaded file" in this locale file by adding the missing msgstr line before the
following comment block, matching the same structure used by the other locale
files and keeping the entry aligned with the MediaManagerView.svelte
accessibility label.
- Around line 1661-1663: The PO entry for the “Offline, unable to open file”
string is missing its msgstr, breaking syntax in this locale file. Update the
affected translation entry in the locales generated from MediaFileDetail.svelte
so every msgid is immediately followed by a valid msgstr line, and verify the
same pattern in the other non-English locale files for this exact string pair.
Also check the extraction/Crowdin-sync pipeline that produced these locale files
to prevent the missing msgstr from recurring.

---

Nitpick comments:
In `@backend/FwHeadless/Media/MediaFileService.cs`:
- Around line 116-131: The sync loop in MediaFileService currently awaits
AddMissingMetadata, AddExistingRemoteResource, and DeleteResource without any
per-resource error handling, so one failure can abort the rest of the pass. Wrap
the per-file work in the sync routine that processes lcmResources and
existingDbFile entries with try/catch so each resource is handled independently,
log or record the failure with the relevant resource id/path, and continue to
the next item instead of stopping the whole loop.

In `@backend/FwLite/LcmCrdt/MediaServer/LcmMediaService.cs`:
- Around line 208-217: GetFileMetadata currently falls back to MediaServerClient
when resource.Metadata is null, but it returns the remote metadata without
saving it locally, so repeated calls keep hitting the server. Update
LcmMediaService.GetFileMetadata to use AddMissingMetadata after fetching from
the media server, so the retrieved LcmFileMetadata is cached on the resource
before returning it.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5af5ec3b-46f7-48ae-ba67-3b3fe8ee4fef

📥 Commits

Reviewing files that changed from the base of the PR and between 9e5502d and 9289124.

📒 Files selected for processing (52)
  • LexBox.sln
  • backend/Directory.Packages.props
  • backend/FwHeadless/Controllers/MediaFileController.cs
  • backend/FwHeadless/Media/MediaFileService.cs
  • backend/FwHeadless/Models/MediaFileModels.cs
  • backend/FwLite/FwLiteShared/Services/FwLiteProvider.cs
  • backend/FwLite/FwLiteShared/Services/MediaFilesServiceJsInvokable.cs
  • backend/FwLite/FwLiteShared/Services/ProjectServicesProvider.cs
  • backend/FwLite/FwLiteShared/TypeGen/ReinforcedFwLiteTypingConfig.cs
  • backend/FwLite/LcmCrdt.Tests/Changes/ChangeDeserializationRegressionData.latest.verified.txt
  • backend/FwLite/LcmCrdt.Tests/Changes/ChangeDeserializationRegressionData.legacy.verified.txt
  • backend/FwLite/LcmCrdt.Tests/Changes/UseChangesTests.cs
  • backend/FwLite/LcmCrdt.Tests/ConfigRegistrationTests.cs
  • backend/FwLite/LcmCrdt.Tests/Data/SnapshotDeserializationRegressionData.latest.verified.txt
  • backend/FwLite/LcmCrdt.Tests/Data/SnapshotDeserializationRegressionData.legacy.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyChangeModels.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyDbModel.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyIObjectBaseModels.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.VerifyIObjectWithIdModels.verified.txt
  • backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs
  • backend/FwLite/LcmCrdt.Tests/EntityCopyMethodTests.cs
  • backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
  • backend/FwLite/LcmCrdt/MediaServer/IMediaServerClient.cs
  • backend/FwLite/LcmCrdt/MediaServer/LcmMediaService.cs
  • backend/FwLite/LcmCrdt/Migrations/20260617054015_AddRemoteResourceMetadataColumn.Designer.cs
  • backend/FwLite/LcmCrdt/Migrations/20260617054015_AddRemoteResourceMetadataColumn.cs
  • backend/FwLite/LcmCrdt/Migrations/LcmCrdtDbContextModelSnapshot.cs
  • backend/FwLite/MiniLcm/Media/FileMetadata.cs
  • backend/LexCore/Entities/MediaFile.cs
  • backend/Testing/FwHeadless/MediaFileTests.cs
  • frontend/viewer/src/DotnetProjectView.svelte
  • frontend/viewer/src/ShadcnProjectView.svelte
  • frontend/viewer/src/lib/components/field-editors/audio-input.svelte
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/DotnetService.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/IMediaFilesServiceJsInvokable.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/FwLiteShared/Services/IProjectScope.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/MiniLcm/Media/ILcmFileMetadata.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/SIL/Harmony/Resource/IHarmonyResource.ts
  • frontend/viewer/src/lib/dotnet-types/generated-types/SIL/Harmony/Resource/IRemoteResource.ts
  • frontend/viewer/src/lib/media-manager/MediaFileDetail.svelte
  • frontend/viewer/src/lib/media-manager/MediaManagerView.svelte
  • frontend/viewer/src/lib/services/service-provider.ts
  • frontend/viewer/src/locales/en.po
  • frontend/viewer/src/locales/es.po
  • frontend/viewer/src/locales/fr.po
  • frontend/viewer/src/locales/id.po
  • frontend/viewer/src/locales/ko.po
  • frontend/viewer/src/locales/ms.po
  • frontend/viewer/src/locales/sw.po
  • frontend/viewer/src/locales/vi.po
  • frontend/viewer/src/project/ProjectSidebar.svelte
  • frontend/viewer/src/project/project-context.svelte.ts

Comment thread backend/FwLite/LcmCrdt.Tests/Changes/UseChangesTests.cs
Comment thread backend/FwLite/LcmCrdt.Tests/ConfigRegistrationTests.cs
Comment thread backend/FwLite/LcmCrdt/MediaServer/IMediaServerClient.cs
Comment thread frontend/viewer/src/lib/media-manager/MediaFileDetail.svelte
Comment thread frontend/viewer/src/lib/media-manager/MediaFileDetail.svelte
Comment thread frontend/viewer/src/locales/es.po
Comment thread frontend/viewer/src/locales/sw.po
Comment thread frontend/viewer/src/locales/sw.po
Comment thread frontend/viewer/src/locales/vi.po
Comment thread frontend/viewer/src/locales/vi.po
hahn-kev added 6 commits July 8, 2026 14:36
…and file loading processes. Introduce throwIfAborted function to manage abort scenarios, ensuring graceful handling of aborted requests. Update resource loading logic to utilize abort signals, improving responsiveness during file operations.
@hahn-kev hahn-kev requested a review from myieye July 8, 2026 08:04
hahn-kev and others added 2 commits July 8, 2026 15:09
Replace the dependency-only $effect with runed watch so clearing stale metadata/preview on row change passes lint.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related 📦 Lexbox issues related to any server side code, fw-headless included

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants