今日开源[第29期]RomM (ROM Manager)
RomM (ROM Manager) 项目分析报告
分析日期:2026-07-06
一、项目介绍
1.1 项目概述
RomM(ROM Manager)是一个美观、强大、自托管的 ROM 管理器和游戏播放器。它允许用户扫描、增强、浏览和游玩自己的游戏收藏,拥有干净且响应式的界面。支持多平台、多种命名方案和自定义标签,是模拟器玩家的必备工具 [1]。
项目理念是"为使用者而建,不为股东而建"——自托管、开源、无追踪、无付费升级。核心应用使用 AGPLv3 许可,其他伞下项目使用 GPLv3 和 CC0 许可 [2]。
1.2 项目信息
| 项目 | 详情 |
|---|---|
| 项目名称 | RomM (ROM Manager) |
| 项目地址 | https://github.com/rommapp/romm |
| 项目官网 | https://romm.app |
| 在线演示 | https://demo.romm.app |
| 文档地址 | https://docs.romm.app |
| 作者/组织 | zurdi15(创始人)/ rommapp 组织 |
| Stars | 10,200+(截至 2026 年 7 月) |
| 当前稳定版 | v4.9.2(2026 年 6 月 17 日发布) |
| 最新测试版 | v5.0.0-beta.1(2026 年 7 月 4 日发布) |
| 开源协议 | GNU AGPL v3.0 |
| 主要语言 | Python 44.4%、Vue 43.0%、TypeScript 11.5%、CSS 0.6% |
| 仓库创建 | 2023 年 3 月 8 日 |
| 首次正式发布 | v1.0(2023 年 3 月 28 日) |
| 总提交数 | 10,000+ commits |
| 总发布数 | 183 个 releases |
1.3 项目示意图
项目 README 展示了桌面端和移动端的双界面预览截图,呈现了精美的游戏库画廊视图和游戏详情页(含封面、元数据、成就等)。v5.0.0-beta.1 新增了以下视觉特性 [1]:
- 交互式 3D 盒装封面:游戏封面以 3D 旋转盒装形式展示
- 手柄调试视图:实时显示手柄输入状态
- 实时日志流:可查看系统运行日志
- 响应式游戏库画廊:桌面端和移动端体验一致
二、项目亮点
2.1 多源元数据聚合
支持从 10+ 个元数据源获取游戏信息,实现自动封面、描述、评分、成就等数据填充 [1][3]:
| 元数据源 | 提供内容 |
|---|---|
| IGDB | 游戏封面、描述、评分、类型、发行日期 |
| ScreenScraper | 游戏截图、封面、logo、视频预览 |
| MobyGames | 游戏描述、评分、截图 |
| LaunchBox | 本地元数据 XML 导入 |
| Hasheous | 基于哈希值的游戏识别 |
| PlayMatch | 社区驱动的游戏匹配 |
| Flashpoint | Flash 游戏元数据 |
| HowLongToBeat | 游戏通关时间 |
| SteamGridDB | 封面、banner、logo 图像 |
| RetroAchievements | 成就系统 |
| ES-DE gamelist.xml | 模拟器前端元数据导入 |
2.2 浏览器直接游玩
集成 EmulatorJS(浏览器内嵌模拟器)和 RuffleRS(Flash 游戏播放器),无需下载客户端即可在浏览器中直接畅玩经典游戏,支持存档功能 [1]。这意味着用户可以在任何设备上(包括手机、平板)通过浏览器游玩 ROM 库中的游戏。
2.3 多平台全面支持
支持 454 个平台,从经典 FC 到现代 Switch 全覆盖 [1][4]:
| 类别 | 代表平台 |
|---|---|
| 任天堂 | FC、SFC、N64、GameCube、Wii、Wii U、Switch、GB、GBA、NDS、3DS |
| 索尼 | PS1、PS2、PS3、PSP、PS Vita |
| 世嘉 | MD、SS、DC、GG |
| 微软 | Xbox、Xbox 360 |
| 经典电脑 | Amiga、Atari、Commodore 64、MSX、PC-98 |
| 街机 | MAME、Neo Geo、CPS1/2/3 |
2.4 多用户权限系统
支持多用户、角色权限、OIDC SSO(支持 Authelia、Authentik、PocketID、Zitadel),可分享游戏库给朋友,各自拥有独立存档和成就 [2]。
2.5 v5.0 全新前端
v5.0.0-beta.1 于 2026 年 7 月 4 日发布,包含重大更新 [5]:
| 新特性 | 说明 |
|---|---|
| 全新设计系统 | 现代化 UI 重构 |
| 统一输入模型 | 手柄/键盘/触摸/鼠标统一支持 |
| 控制器支持 | 原生手柄导航 |
| 实时游戏会话追踪 | 追踪游玩时长和状态 |
| 实时日志流 | WebSocket 实时日志 |
| 共享存档/截图 | 多人共享游戏进度 |
| 交互式 3D 盒装封面 | Three.js 3D 渲染 |
| QR 码设备授权 | OAuth device flow (RFC 8628) |
2.6 丰富的官方和社区客户端
| 类型 | 客户端 | 说明 |
|---|---|---|
| 官方 | Playnite 插件 | Windows 游戏库集成 |
| 官方 | Argosy(Android) | 移动端管理应用 |
| 官方 | Grout | CFW 下载工具 |
| 社区 | iOS 应用 | 苹果移动端 |
| 社区 | Electron 桌面客户端 | 跨平台桌面端 |
| 社区 | Steam Deck 工具 | 掌机优化 |
| 社区 | Switch 自制程序 | 任天堂掌机 |
2.7 与同类项目的差异化优势
| 功能特性 | RomM | Gaseous | LaunchBox | Steam ROM Manager |
|---|---|---|---|---|
| 自托管部署 | Docker 原生支持 | 依赖复杂配置 | 仅 Windows | 跨平台 |
| 元数据源数量 | 10+ 数据源 | 单数据源 | 需手动配置 | 依赖 SteamGridDB |
| 网页直接游玩 | EmulatorJS + RuffleRS | 需外部模拟器 | 需本地客户端 | 不支持 |
| 多用户/权限 | 完整权限系统 + OIDC | 单用户 | 单用户 | 单用户 |
| 移动端适配 | 响应式设计 | 桌面优先 | 桌面优先 | 桌面优先 |
| 浏览器游戏管理 | 上传/下载/删除 | 有限 | 客户端操作 | 不支持 |
| 开源许可 | AGPL v3.0 | MIT | 专有软件 | GPL v3.0 |
三、项目运行环境
3.1 硬件要求
| 级别 | 要求 |
|---|---|
| 最低配置 | 可运行 Docker 的任意设备(NAS、VPS、PC、树莓派等) |
| 推荐配置 | 2 核 CPU、1GB+ RAM、足够的存储空间存放 ROM 文件 |
| 实测性能 | 万级 ROM 库扫描 < 5 分钟,内存占用均值 < 500MB,页面加载 < 1s,支持 50+ 并发用户 [6] |
3.2 操作系统支持
- 通过 Docker 部署,支持所有主流操作系统(Linux、Windows、macOS)
- 提供 NAS 系统专用指南:Unraid、Synology、TrueNAS [2]
3.3 软件依赖
| 组件 | 说明 |
|---|---|
| Docker | 必须,推荐使用 Docker Compose |
| 数据库 | MariaDB(推荐)或 SQLite(轻量级替代) |
| Redis | 缓存后台任务数据 |
| Python | 后端运行时(uv 包管理器) |
| Node.js | 前端构建(npm >= 9) |
| 元数据 API Key | IGDB Client ID/Secret(推荐但不必须) |
3.4 安装步骤(Docker Compose 快速部署)
# 1. 下载 docker-compose 模板
wget https://raw.githubusercontent.com/rommapp/romm/release/docker-compose.example.yml
mv docker-compose.example.yml docker-compose.yml
# 2. 编辑 docker-compose.yml,配置以下内容:
# - MARIADB_ROOT_PASSWORD 和 MARIADB_PASSWORD(数据库密码)
# - ROMM_AUTH_SECRET_KEY(生成方式:openssl rand -hex 32)
# - 元数据源 API Key(IGDB、ScreenScraper、RetroAchievements 等)
# - ROM 库路径、资源路径、配置文件路径挂载
# 3. 启动服务
docker compose up -d
# 4. 访问 http://localhost:80 ,完成初始化设置向导
3.5 ROM 文件夹结构
library/
├── roms/ # 推荐结构
│ ├── gba/ # 平台文件夹名必须与文档一致
│ │ ├── game1.gba
│ │ └── game2.gba
│ ├── ps/
│ │ └── 多碟游戏/ # 多文件游戏以文件夹形式组织
│ │ ├── CD1.iso
│ │ └── CD2.iso
│ └── snes/
├── bios/ # BIOS 文件(可选)
│ └── ps/
└── config.yml # 配置文件
四、项目代码介绍
4.1 代码架构图
romm/
├── backend/ # 后端(Python / FastAPI)
│ ├── main.py # 应用入口
│ ├── startup.py # 启动初始化逻辑
│ ├── watcher.py # 文件系统监控(watchdog)
│ ├── sync_watcher.py # 同步监听器
│ ├── endpoints/ # REST API 路由
│ │ ├── roms.py # ROM 相关 API
│ │ ├── platforms.py # 平台相关 API
│ │ ├── auth.py # 认证 API(OAuth2)
│ │ ├── users.py # 用户管理 API
│ │ ├── saves.py # 存档管理 API
│ │ ├── device_auth.py # 设备授权(v5.0 新增)
│ │ ├── permissions.py # 权限引擎(v5.0 新增)
│ │ └── activity.py # 活动流(v5.0 新增)
│ ├── models/ # 数据库模型(SQLAlchemy ORM)
│ │ ├── platform.py # 平台模型
│ │ ├── rom.py # ROM 模型
│ │ ├── user.py # 用户模型
│ │ └── save.py # 存档模型
│ ├── handler/ # 业务逻辑处理层
│ │ ├── scan_handler.py # 扫描逻辑
│ │ ├── match_handler.py # 元数据匹配逻辑
│ │ └── rom_handler.py # ROM 操作逻辑
│ ├── adapters/ # 元数据源适配器
│ │ ├── igdb.py # IGDB 适配器
│ │ ├── screenscraper.py # ScreenScraper 适配器
│ │ ├── mobygames.py # MobyGames 适配器
│ │ ├── hasheous.py # Hasheous 适配器
│ │ ├── steamgriddb.py # SteamGridDB 适配器
│ │ └── retroachievements.py # RetroAchievements 适配器
│ ├── config/ # 配置管理
│ ├── tasks/ # 后台任务(Celery / Redis)
│ ├── decorators/ # 装饰器(认证、权限)
│ ├── exceptions/ # 异常处理
│ ├── logger/ # 日志模块
│ ├── utils/ # 工具函数
│ ├── alembic/ # 数据库迁移
│ └── tests/ # 测试用例
├── frontend/ # 前端(Vue 3 + Vite + TypeScript)
│ ├── src/ # 源代码
│ │ ├── components/ # Vue 组件
│ │ ├── views/ # 页面视图
│ │ ├── router/ # 路由配置
│ │ ├── stores/ # Pinia 状态管理
│ │ └── api/ # API 调用封装
│ ├── assets/ # 平台图标等静态资源
│ ├── public/ # 公共资源
│ └── vite.config.js # Vite 构建配置
├── docker/ # Docker 配置
├── examples/ # 配置示例文件
├── docs/ # 文档源文件
├── Dockerfile # 容器构建文件
├── docker-compose.yml # Docker Compose 编排
├── pyproject.toml # Python 项目配置(uv)
└── entrypoint.sh # 容器入口脚本
4.2 核心模块介绍
| 模块 | 功能 | 技术栈 |
|---|---|---|
| backend/main.py | FastAPI 应用入口,注册路由、中间件、生命周期事件 | FastAPI + Uvicorn |
| backend/watcher.py | 文件系统监听器,实时检测 ROM 库变更并触发增量扫描 | watchdog 库 |
| backend/handler/ | 核心业务逻辑,包含扫描、匹配、元数据获取等操作 | Python |
| backend/adapters/ | 适配器模式封装各元数据源 API(IGDB、ScreenScraper、MobyGames 等) | requests / httpx |
| backend/endpoints/ | RESTful API 端点,按功能分为 roms、platforms、auth、users、saves 等模块 | FastAPI Router |
| backend/models/ | SQLAlchemy ORM 模型,定义 Platform、Rom、User、Save 等数据表 | SQLAlchemy + Alembic |
| backend/tasks/ | 后台异步任务,如定时扫描、元数据更新、缓存刷新 | Celery + Redis |
| frontend/src/ | Vue 3 组件,构建游戏库画廊、详情页、设置页等 UI | Vue 3 + Vite + TypeScript |
4.3 核心代码解析
4.3.1 启动入口(backend/main.py)
# backend/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.config import settings
from backend.startup import lifespan
# 创建 FastAPI 应用实例
app = FastAPI(
title="RomM",
description="RomM - ROM Manager",
version=settings.ROMM_VERSION,
lifespan=lifespan, # 生命周期管理(启动时执行数据库迁移和初始化)
)
# CORS 中间件配置
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
from backend.endpoints import roms, platforms, auth, users, saves
app.include_router(roms.router, prefix="/api")
app.include_router(platforms.router, prefix="/api")
app.include_router(auth.router, prefix="/api")
app.include_router(users.router, prefix="/api")
app.include_router(saves.router, prefix="/api")
# 挂载前端静态文件(生产模式)
if settings.SERVE_FRONTEND:
from fastapi.staticfiles import StaticFiles
app.mount("/", StaticFiles(directory="frontend/dist", html=True))
后端采用 FastAPI 框架,通过 lifespan 生命周期管理启动时的数据库迁移和初始化。CORS 中间件配置支持跨域请求,路由按功能模块化组织,生产模式下自动挂载前端静态文件,实现单容器部署。
4.3.2 文件系统监听器(backend/watcher.py)
# backend/watcher.py
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class RomEventHandler(FileSystemEventHandler):
"""ROM 文件变更事件处理器"""
def __init__(self, scan_callback):
self.scan_callback = scan_callback
self._pending_events = set()
def on_created(self, event):
if not event.is_directory:
self._pending_events.add(event.src_path)
self._schedule_scan()
def on_deleted(self, event):
if not event.is_directory:
self._pending_events.add(event.src_path)
self._schedule_scan()
def on_moved(self, event):
if not event.is_directory:
self._pending_events.add(event.dest_path)
self._schedule_scan()
def _schedule_scan(self):
"""防抖处理:延迟触发增量扫描"""
# 使用定时器延迟执行,避免频繁触发
if hasattr(self, '_timer'):
self._timer.cancel()
self._timer = threading.Timer(5.0, self._trigger_scan)
self._timer.start()
def _trigger_scan(self):
"""触发增量扫描"""
paths = list(self._pending_events)
self._pending_events.clear()
self.scan_callback(paths)
def start_watcher(roms_path, scan_callback):
"""启动文件系统监听器"""
observer = Observer()
event_handler = RomEventHandler(scan_callback)
observer.schedule(event_handler, roms_path, recursive=True)
observer.start()
return observer
使用 watchdog 库实现文件系统实时监控。当 ROM 库目录发生变更(创建/删除/移动)时,通过防抖机制(5 秒延迟)聚合事件,自动触发增量扫描。这是实现"实时库监控"特性的关键代码,用户添加 ROM 文件后无需手动刷新。
4.3.3 元数据适配器模式(backend/adapters/)
# backend/adapters/base.py
from abc import ABC, abstractmethod
class BaseMetadataAdapter(ABC):
"""元数据适配器抽象基类"""
@abstractmethod
async def search_rom(self, platform: str, name: str) -> dict | None:
"""按平台和名称搜索 ROM 元数据"""
pass
@abstractmethod
async def get_rom_details(self, rom_id: str) -> dict:
"""获取 ROM 详细信息"""
pass
@abstractmethod
async def get_cover_url(self, rom_id: str) -> str | None:
"""获取封面 URL"""
pass
# backend/adapters/igdb.py
class IGDBAdapter(BaseMetadataAdapter):
"""IGDB 适配器"""
def __init__(self, client_id: str, client_secret: str):
self.client_id = client_id
self.client_secret = client_secret
self._access_token = None
async def _authenticate(self):
"""获取 IGDB API 访问令牌(OAuth2 Client Credentials)"""
resp = await httpx.post(
"https://id.twitch.tv/oauth2/token",
params={
"client_id": self.client_id,
"client_secret": self.client_secret,
"grant_type": "client_credentials",
}
)
self._access_token = resp.json()["access_token"]
async def search_rom(self, platform: str, name: str) -> dict | None:
"""通过 IGDB API 搜索游戏"""
if not self._access_token:
await self._authenticate()
# 构建 IGDB API 查询,按平台和名称匹配
query = f'search "{name}"; fields name,cover,summary,rating,genres,platforms;'
resp = await httpx.post(
"https://api.igdb.com/v4/games",
headers={
"Client-ID": self.client_id,
"Authorization": f"Bearer {self._access_token}",
},
data=query,
)
return resp.json()[0] if resp.json() else None
采用适配器模式抽象各元数据源的 API 调用。每个适配器实现统一接口(BaseMetadataAdapter),封装 API 认证、请求构建、响应解析等逻辑。支持 IGDB、ScreenScraper、MobyGames、Hasheous、PlayMatch、LaunchBox、SteamGridDB、RetroAchievements 等十余个源,通过配置即可灵活组合使用。这种设计使得添加新元数据源只需实现接口即可,无需修改核心逻辑。
4.3.4 ROM 扫描与匹配流程(backend/handler/)
# backend/handler/scan_handler.py(简化逻辑)
async def scan_platform(platform_slug: str, roms_path: str):
"""扫描单个平台的 ROM 文件并匹配元数据"""
platform_dir = roms_path / platform_slug
# 1. 遍历平台目录,识别 ROM 文件
rom_files = []
for ext in SUPPORTED_EXTENSIONS:
rom_files.extend(platform_dir.glob(f"**/*.{ext}"))
# 2. 对每个 ROM 文件:解析文件名 → 提取游戏名称 → 计算哈希
for rom_file in rom_files:
game_name = parse_rom_filename(rom_file.name)
file_hash = compute_sha256(rom_file)
# 3. 按优先级查询元数据源
metadata = None
for adapter in get_enabled_adapters():
metadata = await adapter.search_rom(platform_slug, game_name)
if metadata:
break # 找到第一个匹配即停止
# 4. 创建或更新 ROM 记录
rom = await upsert_rom(
platform=platform_slug,
name=game_name,
file_path=rom_file,
file_hash=file_hash,
metadata=metadata,
)
扫描流程:遍历平台目录 → 识别 ROM 文件 → 解析文件名 → 计算文件哈希 → 按优先级查询元数据源 → 获取封面/描述/评分 → 存入数据库。支持增量扫描(仅处理新增或变更的文件)和全量扫描(重新扫描所有文件)。
五、项目应用与评价
5.1 应用场景
| 场景 | 说明 |
|---|---|
| 个人游戏收藏管理 | 将散落在硬盘各处的 ROM 文件统一整理为可视化游戏库,自动获取封面、描述、评分等元数据 |
| 家庭/朋友共享游戏库 | 通过多用户权限系统,将游戏库分享给家人或朋友,各自拥有独立存档和成就 |
| NAS 自建游戏中心 | 在群晖/Unraid/TrueNAS 等 NAS 设备上部署,打造私有游戏媒体服务器 |
| 浏览器即玩模拟器 | 在任何设备上通过浏览器直接游玩经典游戏,无需安装模拟器客户端 |
| 模拟器前端集成 | 通过 Playnite 插件、muOS 应用、Tinfoil/pkgj 等集成,作为模拟器前端的元数据源 |
| 复古游戏收藏展示 | 精美的游戏库画廊界面,适合展示和浏览游戏收藏 |
5.2 项目优点
- 开箱即用:Docker 一键部署,15 分钟内完成设置,新手友好。
- 界面精美:响应式设计,桌面和移动端体验一致,v5.0 更引入 3D 盒装封面和手柄导航。
- 元数据丰富:支持 10+ 个元数据源,覆盖 454 个平台,自动获取封面、描述、评分、成就。
- 完全自托管:数据完全由用户掌控,无隐私泄露风险,无追踪、无付费升级。
- 社区活跃:Discord 社区、183 个 releases、100+ 贡献者,更新频繁,问题响应快。
- 生态完善:官方 + 社区客户端覆盖桌面(Playnite)、移动端(Android/iOS)、掌机(Steam Deck、Switch)等全平台。
- 浏览器游玩:集成 EmulatorJS 和 RuffleRS,无需额外客户端即可在浏览器中游玩经典游戏。
- 多用户支持:完整的权限系统 + OIDC SSO(Authelia、Authentik、PocketID、Zitadel),适合家庭或小团队共享。
5.3 项目不足
- 依赖 Docker:对不熟悉 Docker 的用户有一定学习门槛,小型部署也需 Docker 环境。
- 元数据源配置复杂:需要分别申请多个 API Key(IGDB 需 Twitch 账号+手机验证,MobyGames 需付费),配置门槛较高。
- 不提供 ROM 文件:仅管理已有 ROM,不提供游戏下载(合法合规但需用户自行准备)。
- 浏览器模拟器性能有限:EmulatorJS 对高性能平台(如 PS2、Wii)支持有限,复杂游戏仍需本地模拟器。
- 数据库要求:推荐使用 MariaDB,小型部署(SQLite)在大量 ROM 时性能可能不足。
- v5.0 仍在测试:最新 UI 重构处于 beta 阶段,可能存在不稳定因素,生产环境建议使用 v4.9.x。
- AGPL v3.0 许可证:对商业使用和二次开发有一定限制,需要衍生作品同样开源。
- ROM 文件命名要求:需要遵循平台的命名规范,文件名不规范可能导致匹配失败。

浙公网安备 33010602011771号