Skip to content

feat(link): expand crawler galleries into per-image rows - #173

Merged
mpiton merged 2 commits into
mainfrom
feat/mat-134-gallery-e2e
Jul 17, 2026
Merged

feat(link): expand crawler galleries into per-image rows#173
mpiton merged 2 commits into
mainfrom
feat/mat-134-gallery-e2e

Conversation

@mpiton

@mpiton mpiton commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Implements MAT-134 (Lot 2): a supported gallery URL pasted into the Link Grabber now resolves into one selectable, downloadable row per image.

handle_resolve_links gains a gallery branch for crawler plugins: when extract_links answers with a kind: "gallery" payload, the gallery URL expands 1→N, mirroring the existing hoster expansion. The branch is gated on category == Crawler && !is_media_url(url) so yt-dlp-backed crawlers (YouTube, Vimeo, SoundCloud) keep the cheap probe path and never shell out per pasted URL.

Changes

  • application/commands/resolve_links_gallery.rs (new): parses the plugin's gallery JSON and builds per-image ResolvedLinkDto rows — gallery order preserved, plugin filenames kept (URL basename as fallback), mediaType: "image", isMedia: false so no yt-dlp dialog, requiresOnlineProbe: false since the provider API already vouched for the URLs
  • Per-item errors (R-05): an invalid image entry becomes an individual error row naming the offending item; the valid images survive. An empty gallery reports a single NoFile error row. A non-gallery payload falls through to the generic HEAD probe
  • resolve_links.rs: crawler gate wiring; gallery rows go through push_bounded_result so the MAX_URLS cap holds
  • types.ts: mediaType union gains "image"
  • No frontend logic change was needed for R-03: image rows start through the existing download_start command path

Testing

  • 6 new host tests in resolve_links_gallery_tests.rs: order/names/types, empty gallery, partially invalid gallery, extraction failure, non-gallery fallthrough, media-host crawlers never call extract_links
  • 3 new vitest cases in GalleryLinks.test.tsx: rows render in gallery order without a media button, Start Selected invokes download_start per selected image, a per-item error row doesn't block the valid images
  • cargo clippy --workspace -- -D warnings clean, 1690 backend tests pass, npx oxlint . clean, 728 frontend tests pass

Companion PR on the plugin repo declares the Imgur/Flickr config keys (R-01): mpiton/vortex-mod-gallery#4

MAT-134


Summary by cubic

Pasting a supported gallery URL now expands into one selectable row per image in Link Grabber. Order and filenames are preserved, per-item failures show as error rows, and the expansion is hardened with strict validation and limits.

  • New Features

    • Crawler gallery expansion: when a crawler returns kind: "gallery", one URL becomes N image rows (mediaType: "image", isMedia: false, requiresOnlineProbe: false).
    • Gated to crawler pages only (!is_media_url) so yt-dlp-backed hosts (YouTube, Vimeo, SoundCloud) keep the cheap probe path; non-gallery payloads fall back to the generic probe.
    • Empty galleries return a single NoFile error; invalid items produce named error rows without dropping valid images. Frontend adds "image" to mediaType; downloads still start via existing Tauri commands.
    • Satisfies Linear MAT-134 R-02–R-06 (R-01 is handled in the companion plugin repo).
  • Bug Fixes

    • Malformed extract_links JSON now yields a plugin error row instead of probing the gallery page.
    • Expansion is capped to the remaining resolve limit; oversized galleries truncate instead of failing the whole request.
    • Image URLs are validated with url (http/https, host required, scheme case-insensitive).

Written for commit 967ab66. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Gallery links from Imgur, Flickr, and supported webpages now expand into selectable per-image download rows.
    • Gallery order, filenames, and image type are preserved.
    • Individual image failures are reported without stopping the rest of the gallery.
    • Empty galleries display a dedicated error.
    • Selected gallery images can be downloaded through the existing download flow.
  • Bug Fixes

    • Non-gallery and media links continue using their appropriate existing resolution behavior.

A pasted gallery URL now resolves into one selectable row per image,
in gallery order, with the plugin's filenames and an "image" media
type. Per-image failures become individual error rows so a partially
invalid gallery keeps its valid entries, and an empty gallery reports
a NoFile error instead of silently vanishing. The expansion is gated
on crawler plugins whose URL is not a media host, so yt-dlp-backed
crawlers keep the cheap probe path.

MAT-134
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Gallery-aware crawler resolution now expands gallery payloads into ordered image rows, reports individual failures, preserves non-gallery probing and media handling, and supports frontend rendering and downloads through the existing Link Grabber flow.

Changes

Gallery resolution

Layer / File(s) Summary
Gallery extraction and resolver wiring
src-tauri/src/application/commands/..., src/views/LinkGrabberView/types.ts
Crawler gallery payloads produce per-image rows with filenames, image metadata, validation errors, and fallback probing for non-gallery responses.
Backend gallery behavior validation
src-tauri/src/application/commands/resolve_links_gallery_tests.rs
Tests cover ordered expansion, empty galleries, invalid items, extraction failures, probe fallback, and media-host handling.
Link Grabber gallery rendering and downloads
src/views/LinkGrabberView/__tests__/GalleryLinks.test.tsx, CHANGELOG.md
Frontend tests verify ordered rendering, selected and valid-row downloads, error display, and documented gallery behavior.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LinkGrabberView
  participant TauriResolver
  participant PluginLoader
  participant DownloadCommand
  User->>LinkGrabberView: Analyze gallery URL
  LinkGrabberView->>TauriResolver: link_resolve
  TauriResolver->>PluginLoader: extract_links
  PluginLoader-->>TauriResolver: gallery image payload
  TauriResolver-->>LinkGrabberView: ordered image and error rows
  User->>LinkGrabberView: Start selected images
  LinkGrabberView->>DownloadCommand: download_start for valid selected rows
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: expanding crawler galleries into per-image rows.

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: 3

🤖 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 `@src-tauri/src/application/commands/resolve_links_gallery.rs`:
- Line 55: Update the response parsing flow around GalleryResponse
deserialization to distinguish malformed plugin payloads from valid non-gallery
responses: convert serde_json::from_str failures into a Plugin error row, while
continuing to return None only when a successfully parsed payload has a
non-"gallery" kind. Add a regression test beside the existing non-gallery
fallback test covering malformed responses.
- Around line 67-74: Bound parsed.images before mapping and collecting gallery
rows so allocations never exceed MAX_URLS. Update the surrounding method that
calls push_bounded_result to pass remaining capacity into this gallery
conversion, then limit or reject parsed.images before gallery_image_row runs
while preserving existing indexing and result behavior.
- Around line 84-98: The image URL validation in the gallery item resolution
flow must parse the complete URL rather than checking only its prefix. Update
the validation around image.url to accept only URLs with a valid HTTP(S) scheme,
a host, and case-insensitive scheme handling; keep invalid items routed through
gallery_error_row as individual plugin error rows.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef9f3bec-0146-4509-9459-872b502ce23a

📥 Commits

Reviewing files that changed from the base of the PR and between 0bc4b44 and 520b892.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • src-tauri/src/application/commands/mod.rs
  • src-tauri/src/application/commands/resolve_links.rs
  • src-tauri/src/application/commands/resolve_links_gallery.rs
  • src-tauri/src/application/commands/resolve_links_gallery_tests.rs
  • src/views/LinkGrabberView/__tests__/GalleryLinks.test.tsx
  • src/views/LinkGrabberView/types.ts

Comment thread src-tauri/src/application/commands/resolve_links_gallery.rs Outdated
Comment thread src-tauri/src/application/commands/resolve_links_gallery.rs
Comment thread src-tauri/src/application/commands/resolve_links_gallery.rs

@cubic-dev-ai cubic-dev-ai 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.

cubic analysis

All reported issues were addressed across 7 files

Linked issue analysis

Linked issue: MAT-134: [Lot 2] Intégrer le plugin Gallery de bout en bout

Status Acceptance criteria Notes
R-02 — A supported gallery appears as a group of selectable elements in Link Grabber. The resolver expands a gallery into one ResolvedLinkDto per image and the frontend tests assert rows render with selection checkboxes.
R-03 — Selection starts downloads via Tauri commands, without direct frontend network requests. Image rows are produced with requires_online_probe: false and the frontend test verifies Start Selected/Start All call 'download_start' per selected image.
R-04 — Gallery order, filenames (names) and media types are preserved. Resolver preserves input order, uses plugin filename or URL basename as fallback, and sets mediaType to 'image'; tests assert order, filenames and media_type.
R-05 — A per-media error is reported without cancelling valid gallery items. Malformed image entries produce individual error rows while valid images remain; unit and frontend tests cover a partially invalid gallery scenario.
R-06 — Native/ABI/WASM and frontend tests cover valid gallery, empty gallery and partially invalid response. New host tests exercise valid, empty, partially invalid, extraction failure, and non-gallery fallthrough; three new vitest cases cover frontend rendering and download behavior.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src-tauri/src/application/commands/resolve_links_gallery.rs
Comment thread src-tauri/src/application/commands/resolve_links_gallery.rs Outdated
Comment thread src-tauri/src/application/commands/resolve_links.rs
Comment thread src-tauri/src/application/commands/resolve_links_gallery.rs
Comment thread src-tauri/src/application/commands/resolve_links_gallery.rs Outdated
@codspeed-hq

codspeed-hq Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will degrade performance by 3.46%

⚡ 2 improved benchmarks
❌ 4 (👁 4) regressed benchmarks
✅ 20 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
from_status_code_500 123.9 ns 94.7 ns +30.79%
create_valid 281.4 ns 223.1 ns +26.15%
👁 reject_invalid 493.6 ns 551.9 ns -10.57%
👁 from_status_code_404 123.9 ns 153.1 ns -19.06%
👁 full_lifecycle 230.6 ns 259.7 ns -11.23%
👁 from_status_code_200 94.2 ns 123.3 ns -23.65%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing feat/mat-134-gallery-e2e (967ab66) with main (0bc4b44)

Open in CodSpeed

Malformed extract_links JSON now yields a plugin error row instead of
falling through to a HEAD probe of the gallery page. Expansion is capped
at the resolve output's remaining capacity, so an oversized gallery
truncates (with a warn log) instead of failing the whole request after
partial work. Image URLs are validated with url::Url (http/https scheme,
case-insensitive, host required) instead of a string prefix check.
@mpiton

mpiton commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codspeedbot fix this regression

@codspeed-hq

codspeed-hq Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@codspeedbot fix this regression

I looked into the flagged regression and I don't think there's a real regression here to fix — the numbers are measurement noise rather than a code slowdown. Here's the reasoning:

The regressed benchmarks measure functions this PR never touches.

Benchmark What it measures In this PR's diff?
from_status_code_200 / _404 LinkStatus::from_status_code — a match on a u16 (domain/model/link.rs) No
segment_operations::full_lifecycle Segment state-machine cycle (domain/model) No
checksum_detection::reject_invalid ChecksumAlgorithm::detect_from_hex on an invalid string (domain/model/checksum.rs) No

This PR only adds gallery resolution in application/commands/ (resolve_links_gallery.rs, wiring in resolve_links.rs, a mediaType union in types.ts, plus tests). None of it reaches the pure domain functions above.

The pattern is classic layout noise, not a slowdown.

These are sub-microsecond micro-benchmarks (≈94–550 ns). Under the Simulation instrument, functions this small are very sensitive to binary layout/alignment, which shifts whenever unrelated code is added elsewhere in the crate. The tell-tale sign here is that the "improvements" mirror the "regressions" almost symmetrically:

  • from_status_code_200 −23.65% but from_status_code_500 +30.79% — two arms of the same match function moving in opposite directions.
  • priority::create_valid +26.15%.

A genuine algorithmic regression wouldn't make one variant of the same trivial function slower and another faster; that's the fingerprint of code being relaid-out, not doing more work.

What I'd recommend instead of a code change:

Making edits to unrelated domain code just to nudge these counts would add churn and risk without fixing anything real. So I'm not committing a change. Instead you can safely acknowledge these on CodSpeed. If you'd like to make these micro-benchmarks less noisy going forward, the durable fix is to give the tiny domain benchmarks more work per iteration (e.g. iterate over a small batch of inputs inside b.iter) so per-call layout jitter averages out — happy to open a separate PR for that if you want.

If you believe one of these paths is genuinely on the gallery code path and I've missed a link, point me at it and I'll dig in further.

Summary
  • Warming up the performance wizard...
  • Investigating performance regression — Determined the flagged regressions are measurement noise in unrelated nanosecond-scale domain micro-benchmarks, not a real regression introduced by this PR
  • Handling comment — Comment handling finished successfully

Was this response helpful? React with 👍 or 👎 to help us improve CodSpeedBot.

@mpiton
mpiton merged commit 64f61ac into main Jul 17, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation frontend rust

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant