Skip to content

Surface never-scanned platform folders in the scan picker#3847

Merged
gantoine merged 4 commits into
masterfrom
fix/first-scan-platform-created-as-folder
Jul 20, 2026
Merged

Surface never-scanned platform folders in the scan picker#3847
gantoine merged 4 commits into
masterfrom
fix/first-scan-platform-created-as-folder

Conversation

@gantoine

Copy link
Copy Markdown
Member

Description
Explain the changes or enhancements you are proposing with this pull request.

Fixes #3832.

A platform folder created on disk (and optionally bound in Library Management) has no database row until its first scan imports a ROM, so it was invisible to the Scan platform picker and a first scan could not be triggered for it. The picker also filtered to rom_count > 0, hiding even zero-ROM database platforms (e.g. ones created via romm-cli). The backend scanner already works in folder-slug space, so the gap was purely that a never-scanned folder could not be seen or targeted.

Backend

  • utils/platforms.py: extracted the shared "build a PlatformSchema from local metadata" logic into _build_unmatched_platform(), and added get_filesystem_platforms() returning folders on disk without a database row yet (slug resolved via PLATFORMS_BINDING/PLATFORMS_VERSIONS, id: -1, rom_count: 0, no network calls).
  • endpoints/platform.py: new GET /platforms/filesystem (scope PLATFORMS_READ), declared before /{id}.
  • endpoints/sockets/scan.py: scan_platforms() and the scan socket handler accept platform_fs_slugs. Selected database platforms (by id or slug) are always honored; a bare slug is accepted when it maps to a folder on disk; an empty selection still scans everything.

Frontend (v2, the default UI)

  • services/api/platform.ts + stores/platforms.ts: getFilesystemPlatforms(), a filesystemPlatforms state slice, fetchFilesystemPlatforms(), and a scannablePlatforms getter (all database platforms including zero-ROM plus unscanned folders).
  • v2/components/shared/PlatformSelect.vue: supports item-key="fs_slug" and an opt-in mark-unscanned + unscanned-label marker for id < 0 folders.
  • v2/views/Scan.vue: the picker is keyed by fs_slug, fetches filesystem folders on mount, tags never-scanned folders, and emits platform_fs_slugs.
  • Added the scan.folder-not-scanned key, translated across all 18 locales.

v1 (src/views/Scan.vue) is frozen and left unchanged; it keeps working against the backwards-compatible backend (it still sends platform ids).

AI assistance disclosure

This PR was written with AI assistance (Claude Code, Opus 4.8). The implementation, tests, and translations were AI-generated and reviewed by the author.

Checklist
Please check all that apply.

  • I've tested the changes locally
  • I've updated relevant comments
  • I've assigned reviewers for this PR
  • I've added unit tests that cover the changes

Screenshots (if applicable)

N/A — verified via typecheck, backend + frontend test suites, i18n parity/sort checks, and Trunk. A full in-browser pass (both themes / input modalities) was not run and is recommended before merge.

🤖 Generated with Claude Code

A platform folder created on disk (optionally bound in Library
Management) has no database row until its first scan imports a ROM, so
it was invisible to the scan platform picker and a first scan could not
be triggered for it. The picker also filtered to rom_count > 0, hiding
even zero-ROM database platforms (e.g. those created via romm-cli).

Backend:
- Add GET /platforms/filesystem returning folders on disk without a
  database row yet (locally-resolved metadata, id -1, rom_count 0).
- scan_platforms / the scan socket now accept platform_fs_slugs so a
  folder can be scanned by slug; database platforms stay accepted by id.

Frontend (v2):
- Fetch filesystem folders, key the scan picker by fs_slug, include
  zero-ROM database platforms, and tag never-scanned folders.

Fixes #3832

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 02:13

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes never-scanned platform folders available in the v2 scan picker. The main changes are:

  • Adds a protected endpoint for filesystem-only platform folders.
  • Accepts filesystem slugs when starting a scan.
  • Includes zero-ROM and never-scanned platforms in the v2 picker.
  • Adds an unscanned-folder marker across all supported locales.

Confidence Score: 4/5

Explicit invalid selections can start a full scan, and completed scans leave stale duplicate picker entries.

  • Valid ID and filesystem-slug selections remain compatible.
  • A stale or renamed selected folder can expand into an unintended full-library scan.
  • Newly scanned folders remain duplicated and incorrectly marked as unscanned.

backend/endpoints/sockets/scan.py; frontend/src/stores/platforms.ts

Important Files Changed

Filename Overview
backend/endpoints/sockets/scan.py Adds slug-based targeting, but rejected explicit selections can fall through to a full-library scan.
backend/utils/platforms.py Builds local platform schemas and lists filesystem folders without database rows.
backend/endpoints/platform.py Adds the protected filesystem-platform route before the numeric platform route.
frontend/src/stores/platforms.ts Combines database and filesystem platforms without reconciling stale entries after scans.
frontend/src/v2/views/Scan.vue Loads filesystem folders and sends scan selections as filesystem slugs.
frontend/src/v2/components/shared/PlatformSelect.vue Supports filesystem-slug keys and an opt-in unscanned marker.
frontend/src/services/api/platform.ts Adds the client call for the filesystem-platform endpoint.

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
backend/endpoints/sockets/scan.py:760
**Rejected Selection Scans Everything**

When a client submits only stale, renamed, or invalid slugs, the preceding filter leaves `selected_slugs` empty and this fallback starts a scan of every filesystem platform. A targeted request must use the scan-all fallback only when the client supplied no platform selection.

```suggestion
    has_selection = bool(platform_ids or platform_fs_slugs)
    platform_list = sorted(selected_slugs if has_selection else fs_platforms)
```

### Issue 2 of 2
frontend/src/stores/platforms.ts:38-41
**Scanned Folders Stay Unscanned**

After a first scan creates a database row, the scan lifecycle refreshes `allPlatforms` but leaves the old entry in `filesystemPlatforms`. This getter then shows the same `fs_slug` twice, including a stale `id: -1` row still marked as unscanned; deduplicate by `fs_slug` while preferring the database entry.

```suggestion
    scannablePlatforms: ({ allPlatforms: all, filesystemPlatforms: fs }) =>
      uniqBy([...all, ...fs], "fs_slug").sort((a, b) =>
        a.display_name.localeCompare(b.display_name),
      ),
```

Reviews (1): Last reviewed commit: "Surface never-scanned platform folders i..." | Re-trigger Greptile

Comment thread backend/endpoints/sockets/scan.py Outdated
Comment thread frontend/src/stores/platforms.ts
@gantoine
gantoine merged commit 137242c into master Jul 20, 2026
14 checks passed
@gantoine
gantoine deleted the fix/first-scan-platform-created-as-folder branch July 20, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] First Scan not possible if platform created as folder

2 participants