Repository navigation
Keep Frame.io's signed media links out of the disk cache - #47
Merged
Merged
Conversation
Frame.io's docs ask clients not to store or cache media_links. The JSON
cache wrote file, folder and version responses to disk with their
signed links, and folder previews and project covers stored bare
thumbnail links.
The disk copy now goes through DiskScrub: every media link loses its
url, download_url and inline_url and records which it had ("stripped"),
and every ImageRef drops its signed_url. Memory keeps the full values,
so a session works as before. Images get a stable cache key (asset id +
rendition, ImageRef) separate from the link: RemoteImage and
RemoteImageChain show the cached image by that key when no link is at
hand, without a network call, so covers and thumbnails still show
offline after a restart. Previews and covers read back without links
fetch fresh ones when online, as expired ones already did.
Copies read back from disk still know a picture or a download exists
(MediaLink.hasStream / hasDownload), so the download actions and the
"downloads disabled" notice don't change. The viewer waits for fresh
links instead of calling such a copy's media withheld, and the EXIF and
zip readers restart once the links arrive. Entries written by earlier
versions are scrubbed once in the background; old previews are dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
File.renameTo doesn't replace an existing file on Windows, where the unit tests also run; Files.move with REPLACE_EXISTING does, atomically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The role-aware menus (#42) check original.downloadUrl, which this branch strips from disk copies; use hasDownload so a file read from the cache keeps its download actions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 of 3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
Before: the API cache wrote file, folder, version and comment responses to disk with Frame.io's signed
media_linksinside. Folder previews and project covers also stored bare signed thumbnail links. Frame.io asks clients not to: "Do not store or cache these URLs. Always request a fresh set of media_links each time" (developer.frame.io/platform/v4/docs/guides/media-links.md and the FAQ).After: signed media links live in memory only. What a user sees stays the same:
How
DiskScrub, used byJsonCache.put):media_linksobject loses itsurl,download_urlandinline_url, and records which of them it had instripped;signed_urlfield is dropped;ImageRef(key, url)):frameio:<asset id>:<rendition>(for stacks, the head version's id), separate from the temporary link;Asset.thumbnailandAsset.thumbnailCandidatesreturnImageRefs, andFolderPreview.thumbnailsis a list of them;RemoteImage(ImageRef)/RemoteImageChain(List<ImageRef>)with no link make a cache-only Coil request (networkCachePolicy(DISABLED), on an.invalidhost, so there is never a network call). When a link arrives, the request restarts.MediaLink.hasStream/hasDownloadreadstripped. So a copy read from disk still counts as pictured, still offers Download, and doesn't show "downloads disabled". The actions themselves already fetch the file fresh.JsonCache.scrubLegacyFiles()runs once in the background at start-up (a marker file next to the cache folder records it). It rewrites each entry through the scrub, keeping its age, and deletes old previews whose thumbnails were bare links.Not changed:
avatar_url(people's avatars, refreshed by Refresh cached folder previews and project covers once their links expire #24/Refresh guest comment authors whose avatar link expired #25),Account.imageandProject.imageare other signed links that still reach the disk. They are notmedia_links; a follow-up can give them the same treatment.Checks
gradlew.bat :app:testDebugUnitTest :app:verifyRoborazziDebug :app:lintDebugon the workstation: 240 tests, only the 2 known Windows-only screenshot differences (notifications,tabletNotifications); lint "No issues found". New tests:DiskScrubTest(6): what is stripped and what stays (scrub-sheet metadata, empty links), idempotence, a scrubbed asset still knowing its thumbnail key and download,ImageRefscrubbing, the cache keeping links in memory but not on disk, the one-time legacy migration;ProjectCoverTest(+4): no signed link on disk, covers after a restart (online: fresh links under the same keys; offline: keys only), a file after a restart offline (thumbnail key, download still known);ModelsTest: the stable keys.project_coverspasses withImageRefs)🤖 Generated with Claude Code