How RomM Scans the Filesystem to Detect Platforms and ROMs
RomM initiates filesystem scans through either a real-time watcher or scheduled task, which triggers a multi-stage pipeline that identifies platform folders by their filesystem slugs, queries metadata providers to enrich platform data, and then hashes each ROM file to perform parallel metadata lookups before merging results according to user-defined priority settings.
RomM is a self-hosted ROM manager that continuously synchronizes your game library with rich metadata from multiple online sources. The platform's ability to scan the filesystem and detect platforms and ROMs operates through a coordinated pipeline involving filesystem monitoring, platform identification, and comprehensive ROM processing. This architecture ensures your library stays synchronized with disk changes while automatically enriching entries with artwork, descriptions, and game IDs from providers like IGDB and MobyGames.
The Three-Stage Scanning Architecture
RomM's detection process follows a strict pipeline that moves from filesystem observation to database persistence. The flow coordinates between the watcher subsystem, socket endpoints, and the core scan handler.
Stage 1: Initiation via Watcher or Scheduled Tasks
The scanning process begins through one of two entry points defined in the codebase:
- Real-time filesystem monitoring: The [
backend/watcher.py](https://github.com/rommapp/romm/blob/master/backend/watcher.py) component monitors the library root for changes. When it detects new folders or modifications, it creates a job that forwards to thescan_platformssocket endpoint. - Scheduled execution: The [
backend/tasks/scheduled/scan_library.py](https://github.com/rommapp/romm/blob/master/backend/tasks/scheduled/scan_library.py) task runs at startup (or on a configured schedule) and triggers the same scanning entry point for routine library maintenance.
Both paths ultimately emit a scan:scan_platforms event through the Socket.IO manager to begin the detection sequence.
Stage 2: Platform Discovery and Normalization
Once initiated, the socket endpoint [backend/endpoints/sockets/scan.py](https://github.com/rommapp/romm/blob/master/backend/endpoints/sockets/scan.py) orchestrates platform identification. For each folder slug found on disk, the system calls scan_platform in [backend/handler/scan_handler.py](https://github.com/rommapp/romm/blob/master/backend/handler/scan_handler.py).
The platform detection logic performs these operations:
- Slug normalization: It extracts the filesystem slug (folder name) and normalizes it against the user-defined configuration via
config_manager. - Metadata hydration: It queries multiple providers (IGDB, MobyGames, RetroAchievements, LaunchBox, etc.) in parallel to fetch platform IDs, logos, and official names.
- Database persistence: The resulting
Platformmodel is stored viadb_platform_handler, with logging output such as "Folder n64 identified as Nintendo 64".
Unknown folders are handled according to the scan type configuration, with options to ignore or create placeholder entries depending on the user's SCAN_METADATA_PRIORITY settings.
Stage 3: ROM Hashing and Metadata Enrichment
After platform registration, the system processes individual ROM files through the scan_rom function in [backend/handler/scan_handler.py](https://github.com/rommapp/romm/blob/master/backend/handler/scan_handler.py). This stage involves:
- Filesystem enumeration: The [
backend/handler/filesystem/roms_handler.py](https://github.com/rommapp/romm/blob/master/backend/handler/filesystem/roms_handler.py) returns a list of ROM files for the current platform. - Hash calculation: Each file is hashed to generate unique identifiers for database storage and matching.
- Parallel metadata fetching: The handler runs concurrent lookups across all enabled providers (IGDB, MobyGames, Screenscraper, RetroAchievements, Hasheous, etc.).
- Priority merging: Results are merged according to the
SCAN_METADATA_PRIORITYandSCAN_ARTWORK_PRIORITYconfiguration arrays, with the first successful provider taking precedence for each field. - Asset scanning: Helper functions
scan_save,scan_state, andscan_screenshotprocess related assets using the shared_scan_assetutility to compute file sizes and optional hashes.
The final Rom entity is persisted via db_rom_handler.add_rom, with the socket manager streaming progress events (scan:scanning_rom, scan:scanning_platform) to connected clients.
Triggering Scans Programmatically
You can initiate the scanning pipeline outside the web UI using Socket.IO clients or direct Python imports.
Trigger a full library rescan via Socket.IO:
import socketio
import asyncio
async def rescan_library():
sio = socketio.AsyncClient()
await sio.connect("http://localhost:3000")
await sio.emit(
"scan:scan_platforms",
{
"scan_type": "complete", # Options: new_platforms, quick, update, unmatched, complete, hashes
"metadata_sources": ["igdb", "ra", "ss", "launchbox"],
},
)
await sio.disconnect()
asyncio.run(rescan_library())
Programmatic platform discovery (as used by the watcher):
from handler.scan_handler import scan_platform
async def detect_platform():
platform = await scan_platform(
fs_slug="n64",
fs_platforms=["n64", "snes", "genesis"]
)
print(f"Detected: {platform.name}") # Output: "Nintendo 64"
Scanning a single ROM with metadata lookup:
from handler.scan_handler import ScanType, scan_rom
from handler.filesystem.roms_handler import get_fs_rom
async def process_single_rom():
fs_rom = await get_fs_rom(
platform_slug="n64",
rom_path="/roms/n64/Super Mario 64.n64"
)
rom = await scan_rom(
scan_type=ScanType.NEW_PLATFORMS,
platform=platform, # Platform object from previous detection
rom=Rom(), # Empty placeholder for new entry
fs_rom=fs_rom,
metadata_sources=["igdb", "ra", "ss"],
newly_added=True,
)
print(f"Enriched: {rom.name} (IGDB ID: {rom.igdb_id})")
Summary
- Dual initiation: RomM supports both real-time filesystem watching via [
backend/watcher.py](https://github.com/rommapp/romm/blob/master/backend/watcher.py) and scheduled scans through [backend/tasks/scheduled/scan_library.py](https://github.com/rommapp/romm/blob/master/backend/tasks/scheduled/scan_library.py). - Slug-based identification: Platform folders are identified by normalized filesystem slugs matched against configuration and enriched via
scan_platform. - Parallel metadata aggregation: ROMs are hashed and processed through
scan_rom, which queries multiple providers simultaneously and merges results according to priority settings. - Asset handling: Save states, screenshots, and firmware receive dedicated scanning passes using shared utility functions.
- Live progress: The Socket.IO layer streams scan status to the frontend for real-time monitoring.
Frequently Asked Questions
How does RomM handle folders that don't match known platforms?
When scan_platform encounters an unrecognized folder slug, it checks the config_manager for user-defined mappings. If no match exists and the scan type is set to complete or new_platforms, it attempts to fetch metadata using the slug as a search term. Unmatched folders can be configured to create placeholder entries or be ignored entirely based on the SCAN_METADATA_PRIORITY array settings.
What is the difference between a quick scan and a complete scan?
A quick scan (specified via ScanType.QUICK or the "quick" scan type string) only processes new files that don't exist in the database, skipping existing entries to save time. A complete scan (ScanType.COMPLETE) re-evaluates all platforms and ROMs, clearing previously stored IDs for disabled sources and re-downloading metadata even for existing entries. The update and unmatched scan types provide intermediate granularity for specific maintenance tasks.
How does RomM decide which metadata provider to use for a specific ROM?
The system queries all enabled providers in parallel but merges results according to the SCAN_METADATA_PRIORITY configuration list. Providers are checked in the order specified (e.g., ["igdb", "mobygames", "screenscraper"]), and the first successful response for each metadata field (name, description, cover art) is used. For artwork specifically, SCAN_ARTWORK_PRIORITY provides a separate ordering independent of metadata sources.
Can RomM scan ROMs without internet connectivity for metadata?
Yes, the core scan the filesystem and detect platforms and ROMs functionality works offline. The scan_rom function calculates file hashes and creates database entries regardless of connectivity. When offline, metadata enrichment steps simply return empty results, and the ROM remains in the library with basic file information (filename, size, hash). You can later trigger a metadata-only rescan when connectivity returns by using the update scan type with specific metadata sources enabled.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →