Skip to content

Give every previewable asset a working preview URL - #15509

Open
synap5e wants to merge 1 commit into
masterfrom
synap5e/fix/asset-preview-url-for-all-roots
Open

Give every previewable asset a working preview URL#15509
synap5e wants to merge 1 commit into
masterfrom
synap5e/fix/asset-preview-url-for-all-roots

Conversation

@synap5e

@synap5e synap5e commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

preview_url on an asset response was built as a /api/view link whose type was chosen by matching the asset's tags against input, then output. Anything ComfyUI writes anywhere else fell off the end of that chain and came back with no preview at all — most visibly images from preview nodes, which land in the temp directory and otherwise get a complete asset record. And because the choice ran off tags, and tags are user-editable, removing one silently destroyed the URL.

This derives the preview from where the file actually sits, using the same path helper the response already uses for display_name:

  • the namespace (input, output, temp) becomes the view type;
  • the path below it becomes filename, with any leading directories split out into subfolder;
  • both halves are URL-encoded.

That covers every root /api/view serves, temp included, and depends on neither tags nor a filename in user_metadata — which an API-created reference need not carry, so those had no preview before either.

A file outside those roots gets no preview URL, which is the honest answer: /api/view has no directory type that could address it. Neither does content a client cannot render from the bytes themselves — a checkpoint has no preview of its own, and pointing at its bytes would only make the client download the whole file to discover that. Images, video, audio and text do render, so those get a URL; a mesh does not, and reaches a preview the other way, through preview_id. Previewability follows the same resolution /api/view itself uses: the stored mime type, falling back to the filename, so an asset recorded by a scan that skipped metadata extraction does not lose its preview.

Why /api/view and not the asset content endpoint. The content endpoint resolves any reference regardless of directory, which makes it a tempting single answer, but it is a download endpoint and previews are not downloads. /api/view returns a FileResponse, so previews keep byte-range seeking — native <video> and <audio> need it to seek and read tail metadata, and some browsers will not play a source without it. It also needs no user header, which matters because a browser fetching <img src> cannot attach one. And it records no access, so rendering a list of thumbnails performs no writes; under sort=last_access_time those writes would otherwise mutate the sort key mid-scroll and make paginated rows skip or repeat.

Nominated previews. When a reference nominates another as its preview, the preview's own path is used. Those are resolved a page at a time in a single query rather than one per row, so a long list costs one extra query, not one per asset. A nominated preview that has been deleted or is not visible to the caller drops out of that lookup and is simply not advertised, rather than yielding a URL that 404s.

Testing:

  • tests-unit/assets_test/services/test_asset_response_preview_url.py — URL derivation across all three view roots, subfolder splitting and encoding, independence from tags and from user_metadata, the preview_id indirection, the mime fallback, and every case that should yield no URL (models, paths outside every root, no file path, no content).
  • tests-unit/assets_test/test_preview_url.py — against a live server: the URL serves the asset's bytes, honours a Range request with a 206, resolves with no user header, survives removing the tag that used to select the view type, follows a nominated preview, drops a soft-deleted one, and withholds a URL for model weights from both the detail and the list route.
  • Query count for the preview lookup measured flat in page size: one SELECT at 1, 50 and 500 rows, and none when no asset on the page nominates a preview.
  • Admitting text/ does not weaken the inline-content guard: a .html asset gets a preview URL and fetching it is still forced to application/octet-stream + attachment, asserted end to end.
  • Full pytest tests-unit and ruff check . green.

@synap5e synap5e added the cursor-review Trigger multi-model Cursor code review label Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c7fdb292-a7d3-46e1-b0cf-0be1d8c9e51d

📥 Commits

Reviewing files that changed from the base of the PR and between 14158fc and ee82765.

📒 Files selected for processing (2)
  • app/assets/api/routes.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
📝 Walkthrough

Walkthrough

The asset API now resolves owner-visible preview paths in bulk. It constructs /api/view URLs only for validated paths and previewable MIME types, except for nominated previews. Asset list, detail, creation, upload, and update responses use this resolution. New tests cover path validation, MIME handling, nominated and deleted previews, range requests, anonymous access, and forced downloads for HTML content.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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 clearly summarizes the main change: adding working preview URLs for previewable assets.
Description check ✅ Passed The description directly explains preview URL derivation, MIME handling, nominated previews, endpoint behavior, and test coverage.

Comment @coderabbitai help to get the list of available commands.

@synap5e
synap5e force-pushed the synap5e/fix/asset-preview-url-for-all-roots branch from 019f9e3 to 5916f69 Compare August 11, 2026 23:31

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 019f9e38da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/assets/api/routes.py Outdated
Comment on lines +214 to +218
def _has_previewable_content(asset: schemas.AssetData | None) -> bool:
if asset is None:
return False
mime = (asset.mime_type or "").split(";", 1)[0].strip().lower()
return mime.startswith(PREVIEWABLE_MIME_PREFIXES)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fall back to the filename when MIME metadata is missing

During the fast asset scan, records are deliberately created without metadata, so their asset.mime_type is None even when the filename is a previewable image, audio, or video. This check consequently removes preview URLs that the previous input/output path supplied, potentially permanently for fast-only or failed enrichment scans, although the /content endpoint itself already falls back to MIME detection from the reference name. Use the same filename fallback here before deciding the content is not previewable.

AGENTS.md reference: AGENTS.md:L20-L21

Useful? React with 👍 / 👎.

Comment thread app/assets/api/routes.py Outdated
Comment on lines +228 to +230
if result.ref.preview_id:
preview_detail = get_asset_detail(result.ref.preview_id)
if preview_detail:
preview_url = _build_preview_url_from_view(preview_detail.tags, preview_detail.ref.user_metadata)
else:
preview_url = None
# A nominated preview is one whatever it holds, so no media check here.
preview_url = _build_preview_url(result.ref.preview_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Verify nominated previews before advertising their URL

When a nominated preview reference is subsequently soft-deleted, this branch still emits its content URL because soft deletion leaves the foreign key in place; /api/assets/{id}/content filters deleted references and therefore returns 404. The same mismatch occurs if a preview ID refers to a reference not visible to the requesting owner, since preview assignment only checks that the row exists. Preserve the previous availability check, or otherwise ensure the nominated reference is active and visible before returning a broken preview_url.

AGENTS.md reference: AGENTS.md:L359-L361

Useful? React with 👍 / 👎.

@synap5e
synap5e force-pushed the synap5e/fix/asset-preview-url-for-all-roots branch from 5916f69 to c03f92e Compare August 11, 2026 23:42
@synap5e

synap5e commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Both P2s triaged.

The mime fallback is fixed in c03f92e — previewability is now resolved the same way /content resolves the type it serves.

On verifying nominated previews: not doing that one; rationale is in the PR description.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔍 Cursor Review — Consolidated panel

Triggered by @synap5e.

Found 6 finding(s).

Severity Count
🟡 Medium 4
🟢 Low 2

Panel: 8/8 reviewers contributed findings.

Comment thread app/assets/api/routes.py Outdated
else:
preview_url = None
# A nominated preview is one whatever it holds, so no media check here.
preview_url = _build_preview_url(result.ref.preview_id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium — The preview_id branch now builds /api/assets/{preview_id}/content unconditionally, dropping the old get_asset_detail existence check. Since soft-delete only sets deleted_at and never clears inbound preview_id pointers, a parent whose nominated preview is soft-deleted or no longer owner-visible permanently advertises a URL that /content 404s, instead of falling back to preview_url = None as before; re-validate the preview reference before emitting its URL. Raised by 5 of 8 reviewers (claude-opus-4-8-thinking-max adversarial, gpt-5.6-sol-max adversarial, claude-opus-4-8-thinking-max edge-case, gpt-5.6-sol-max edge-case, kimi-k2.7-code edge-case).

Comment thread app/assets/api/routes.py Outdated
else:
return None
# Anything else has no visual form: a preview of its own bytes would just make the client download it all.
PREVIEWABLE_MIME_PREFIXES = ("image/", "video/", "audio/")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium — Advertising audio/video previews through /content swaps the old FileResponse for a manual streaming response that ignores HTTP Range requests. Native <video>/<audio> elements depend on byte ranges to seek and read tail metadata, so previewing large media now forces a full-file download and breaks seeking. Raised by 2 of 8 reviewers (gpt-5.6-sol-max adversarial, gpt-5.6-sol-max edge-case).

Comment thread app/assets/api/routes.py Outdated

def _build_preview_url(reference_id: str) -> str:
# Asking for inline does not weaken /content: it still forces dangerous types to download.
return f"/api/assets/{reference_id}/content?disposition=inline"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium/api/assets/{id}/content is owner-scoped and derives the user from the Comfy-User header, which browser-native <img>/<video>/<audio> fetches cannot attach. Under --multi-user, previews for non-default users will 401/404, whereas the previous /api/view URLs did not require the header. Raised by 1 of 8 reviewers (gpt-5.6-sol-max adversarial).

Comment thread app/assets/api/routes.py Outdated

def _build_preview_url(reference_id: str) -> str:
# Asking for inline does not weaken /content: it still forces dangerous types to download.
return f"/api/assets/{reference_id}/content?disposition=inline"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium — Every preview now targets /content, which resolves via resolve_asset_for_download and commits a last_access_time update on each fetch, so rendering a list issues a DB write per thumbnail. When the list is sorted by last_access_time with offset pagination, those writes mutate the sort key mid-scroll and cause rows to be skipped or duplicated across pages. Raised by 2 of 8 reviewers (gpt-5.6-sol-max adversarial, gpt-5.6-sol-max edge-case).

Comment thread app/assets/api/routes.py Outdated
def _has_previewable_content(asset: schemas.AssetData | None) -> bool:
if asset is None:
return False
mime = (asset.mime_type or "").split(";", 1)[0].strip().lower()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Low_has_previewable_content returns False whenever mime_type is NULL, so legacy/unenriched images with a renderable extension get no preview_url even though /content can serve them via its mimetypes.guess_type fallback. Such rows previously received an /api/view preview, so this silently regresses pre-enrichment assets. Raised by 1 of 8 reviewers (gpt-5.6-sol-max edge-case).

Comment thread app/assets/api/routes.py Outdated

def _build_preview_url(reference_id: str) -> str:
# Asking for inline does not weaken /content: it still forces dangerous types to download.
return f"/api/assets/{reference_id}/content?disposition=inline"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Lowreference_id is interpolated into the URL path without urllib.parse.quote(..., safe=''), dropping the encoding the old /api/view builder applied. It is not currently exploitable because ids and preview_ids are server-generated, reference-validated UUIDs (so the raised path-traversal/CSRF concern is unfounded), but restoring the encoding hardens the URL-construction boundary. Raised by 3 of 8 reviewers (claude-opus-4-8-thinking-max adversarial, gemini-3.1-pro adversarial, gemini-3.1-pro edge-case).

@synap5e

synap5e commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Changed approach in 1ee0050: the preview URL is now derived from the file's own path back onto /api/view, rather than pointing at the asset content endpoint as the comment above describes.

/api/view returns a FileResponse, so previews keep byte-range seeking (native <video>/<audio> need it) and resolve without a user header, which a browser <img src> fetch cannot attach. Details in the PR description.

This also resolves the nominated-preview point I'd declined: resolving the preview's path means a deleted or non-visible preview now drops out and is no longer advertised.

Comment thread app/assets/api/routes.py Outdated
"""Build a /api/view preview URL from asset tags and user_metadata filename."""
if not user_metadata:
# Anything else has no visual form: a preview of its own bytes would just make the client download it all.
PREVIEWABLE_MIME_PREFIXES = ("image/", "video/", "audio/")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

are text and 3d omitted on purpose?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Following up as promised: text/ is in as of 14158fc — a one-line addition to the previewable set, so a .txt/.md/.csv asset now gets a preview URL for that snippet component to fetch.

Two tests came with it: a text asset's preview URL serves its content, and a .html one is still forced to application/octet-stream + attachment when fetched, so widening the set doesn't let markup render inline in the app origin.

3d needs no change — nominated previews already cover it, and Media3DTop.vue is already written that way.

Thanks for catching the text case.

@synap5e
synap5e force-pushed the synap5e/fix/asset-preview-url-for-all-roots branch from 1ee0050 to 14158fc Compare August 12, 2026 09:55

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

🤖 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 `@app/assets/api/routes.py`:
- Around line 219-224: The _has_previewable_content fallback currently uses the
editable name; change it to infer MIME type from asset.ref.file_path, preferably
using its basename, while retaining stored mime_type precedence and existing
normalization. Add coverage where the asset name and file-path filename have
different extensions, ensuring preview eligibility follows the file path.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d71fe2f8-ad59-4edc-86b3-8e10cc3d196a

📥 Commits

Reviewing files that changed from the base of the PR and between bd34f33 and 14158fc.

📒 Files selected for processing (8)
  • app/assets/api/routes.py
  • app/assets/database/queries/__init__.py
  • app/assets/database/queries/asset_reference.py
  • app/assets/services/__init__.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/services/test_asset_response_loader_path.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
  • tests-unit/assets_test/test_preview_url.py
📜 Review details
⏰ Context from checks skipped due to timeout. (9)
  • GitHub Check: test (windows-2022)
  • GitHub Check: test (macos-latest)
  • GitHub Check: Run Pylint
  • GitHub Check: test (windows-latest)
  • GitHub Check: test (ubuntu-latest)
  • GitHub Check: test
  • GitHub Check: test (macos-latest)
  • GitHub Check: test (ubuntu-latest)
  • GitHub Check: Run Pylint
🧰 Additional context used
📓 Path-based instructions (5)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Keep changes small, direct, and limited to the narrowest necessary code path and smallest number of files.
Prefer practical fixes, minimal dependencies, and existing repository patterns; remove obsolete, dead, unreachable, or unused code.
Preserve existing APIs, node names, model-loading behavior, file layout, and workflow compatibility unless replacement is explicitly intended.
Core ComfyUI must not add outbound internet requests, telemetry, tracking, reporting, remote configuration, or background network activity. User-authorized model downloads are limited to the requested artifact and must exclude telemetry and unrelated metadata.

Files:

  • app/assets/services/__init__.py
  • app/assets/database/queries/__init__.py
  • app/assets/database/queries/asset_reference.py
  • tests-unit/assets_test/services/test_asset_response_loader_path.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/test_preview_url.py
  • app/assets/api/routes.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Keep state and capability flags on the object that owns the behavior. Prefer explicit parent-owned attributes over probing child objects with getattr; use child checks only when the child owns the delegated behavior.
Preserve shared method signatures, argument order, return shapes, side effects, and error behavior unless every affected caller and interface is intentionally updated.
Do not add unused compatibility parameters, flags, attributes, constructor options, or model-specific options to shared helpers; keep one-off behavior at the integration boundary.
Normalize third-party return conventions at integration boundaries so core code receives the expected type and shape; avoid undocumented caller-side unwrapping.
Do not add torch.no_grad, torch.inference_mode, or inference-mode wrappers. Do not add model freeze/unfreeze toggles; only disable globally enabled inference mode when a training path requires gradients.
Remove inference-only training behavior such as dropout while preserving checkpoint and state-dict compatibility; use nn.Identity when deleting a module would alter keys or ordering.
Keep imports at module scope except established optional-backend probes or imports required to avoid cycles; avoid unnecessary try/except blocks and use specific exceptions with useful fallbacks.
Do not add workarounds for unsupported library versions, especially PyTorch exception-and-float-cast retries, unless a comment names the exact versions still requiring them.
Let unsupported model formats, invalid quantization metadata, and bad states fail with clear errors instead of silently degrading output.
Match local style, keep comments sparse and useful, and remove comments that merely restate obvious code.
Treat dtype, device placement, VRAM use, and offloading as correctness concerns across CPU, CUDA, ROCm, MPS, DirectML, XPU, NPU, and low-VRAM environments.
Prefer existing ComfyUI and Comfy Kitchen operations, quantization helpers, cast/offload helpe...

Files:

  • app/assets/services/__init__.py
  • app/assets/database/queries/__init__.py
  • app/assets/database/queries/asset_reference.py
  • tests-unit/assets_test/services/test_asset_response_loader_path.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/test_preview_url.py
  • app/assets/api/routes.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
**/*.{py,json}

📄 CodeRabbit inference engine (AGENTS.md)

Treat legacy combo, io.Combo, and io.DynamicCombo values affecting filesystem access as untrusted; revalidate them at load/save boundaries with folder_paths, containment checks, or fixed allowlists.

Files:

  • app/assets/services/__init__.py
  • app/assets/database/queries/__init__.py
  • app/assets/database/queries/asset_reference.py
  • tests-unit/assets_test/services/test_asset_response_loader_path.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/test_preview_url.py
  • app/assets/api/routes.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
**/*.{py,md,txt,json}

📄 CodeRabbit inference engine (AGENTS.md)

Keep warning and info messages short and actionable, remove noisy or misleading logging, and make documentation edits concise, factual, and tied to changed behavior.

Files:

  • app/assets/services/__init__.py
  • app/assets/database/queries/__init__.py
  • app/assets/database/queries/asset_reference.py
  • tests-unit/assets_test/services/test_asset_response_loader_path.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/test_preview_url.py
  • app/assets/api/routes.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
**

⚙️ CodeRabbit configuration file

**: IMPORTANT: Only comment on issues directly introduced by this PR's code changes.
Treat AGENTS.md as mandatory repository policy, not optional style guidance.
Flag PR changes that violate AGENTS.md even when the code is otherwise functional.
In particular, enforce architecture boundaries, dtype/device/memory rules,
interface contracts, import style, no unnecessary try/except blocks, no inline
imports, no outbound internet paths in core ComfyUI, and narrow scoped fixes.
Prefer direct findings over suggestions when a rule is violated. Only ignore
AGENTS.md when it clearly conflicts with a newer explicit maintainer instruction
in the PR.
Do NOT flag pre-existing issues in code that was merely moved, re-indented,
de-indented, or reformatted without logic changes. If code appears in the diff
only due to whitespace or structural reformatting (e.g., removing a with: block),
treat it as unchanged. Contributors should not feel obligated to address
pre-existing issues outside the scope of their contribution.

Files:

  • app/assets/services/__init__.py
  • app/assets/database/queries/__init__.py
  • app/assets/database/queries/asset_reference.py
  • tests-unit/assets_test/services/test_asset_response_loader_path.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/test_preview_url.py
  • app/assets/api/routes.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
🧠 Learnings (1)
📚 Learning: 2026-02-21T14:01:41.482Z
Learnt from: pythongosssss
Repo: Comfy-Org/ComfyUI PR: 12555
File: comfy_extras/nodes_glsl.py:719-724
Timestamp: 2026-02-21T14:01:41.482Z
Learning: In PyOpenGL, bare Python scalars can be accepted for 1-element array parameters by NumberHandler. This means you can pass an int/float directly to OpenGL texture deletion (e.g., glDeleteTextures(tex)) without wrapping in a list. Verify function-specific expectations and ensure types match what the OpenGL call expects; use explicit lists only when the API requires an array.

Applied to files:

  • app/assets/services/__init__.py
  • app/assets/database/queries/__init__.py
  • app/assets/database/queries/asset_reference.py
  • tests-unit/assets_test/services/test_asset_response_loader_path.py
  • app/assets/services/asset_management.py
  • tests-unit/assets_test/test_preview_url.py
  • app/assets/api/routes.py
  • tests-unit/assets_test/services/test_asset_response_preview_url.py
🪛 ast-grep (0.45.1)
tests-unit/assets_test/test_preview_url.py

[info] 101-101: use jsonify instead of json.dumps for JSON output
Context: json.dumps(["models", "model_type:checkpoints", "unit-tests", scope])
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🔇 Additional comments (8)
app/assets/database/queries/asset_reference.py (1)

1067-1087: LGTM!

app/assets/database/queries/__init__.py (1)

31-31: LGTM!

Also applies to: 105-105

app/assets/services/asset_management.py (1)

24-24: LGTM!

Also applies to: 428-438

app/assets/services/__init__.py (1)

7-7: LGTM!

Also applies to: 87-87

app/assets/api/routes.py (1)

5-5: LGTM!

Also applies to: 36-36, 45-45, 212-218, 227-252, 348-351, 390-390, 521-521, 612-612, 642-642

tests-unit/assets_test/services/test_asset_response_loader_path.py (1)

46-46: LGTM!

Also applies to: 70-70, 80-80

tests-unit/assets_test/services/test_asset_response_preview_url.py (1)

1-274: LGTM!

tests-unit/assets_test/test_preview_url.py (1)

1-217: LGTM!

Comment thread app/assets/api/routes.py Outdated
@synap5e
synap5e force-pushed the synap5e/fix/asset-preview-url-for-all-roots branch from 14158fc to dd3b7dd Compare August 12, 2026 10:02
preview_url was assembled from a /api/view link whose type was chosen by
matching the asset's tags against "input" then "output". Anything written
anywhere else - temp above all, where preview nodes put their images - fell
off the end of that chain and came back with no preview at all. Tags are
user-editable, so removing one also silently destroyed the URL.

Derive the URL from where the file actually sits instead. That covers every
root /api/view serves, temp included, and no longer depends on tags or on a
filename in user_metadata. A file outside those roots, or content no client
can render from its own bytes, gets no preview URL rather than one that
cannot work.

Nominated previews are resolved a page at a time rather than per row, so a
list costs one extra query however long it is. A preview that is soft-deleted
or not visible to the caller drops out of that lookup and is no longer
advertised.
@synap5e
synap5e force-pushed the synap5e/fix/asset-preview-url-for-all-roots branch from dd3b7dd to ee82765 Compare August 12, 2026 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cursor-review Trigger multi-model Cursor code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants