今日开源[第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 项目优点

  1. 开箱即用:Docker 一键部署,15 分钟内完成设置,新手友好。
  2. 界面精美:响应式设计,桌面和移动端体验一致,v5.0 更引入 3D 盒装封面和手柄导航。
  3. 元数据丰富:支持 10+ 个元数据源,覆盖 454 个平台,自动获取封面、描述、评分、成就。
  4. 完全自托管:数据完全由用户掌控,无隐私泄露风险,无追踪、无付费升级。
  5. 社区活跃:Discord 社区、183 个 releases、100+ 贡献者,更新频繁,问题响应快。
  6. 生态完善:官方 + 社区客户端覆盖桌面(Playnite)、移动端(Android/iOS)、掌机(Steam Deck、Switch)等全平台。
  7. 浏览器游玩:集成 EmulatorJS 和 RuffleRS,无需额外客户端即可在浏览器中游玩经典游戏。
  8. 多用户支持:完整的权限系统 + OIDC SSO(Authelia、Authentik、PocketID、Zitadel),适合家庭或小团队共享。

5.3 项目不足

  1. 依赖 Docker:对不熟悉 Docker 的用户有一定学习门槛,小型部署也需 Docker 环境。
  2. 元数据源配置复杂:需要分别申请多个 API Key(IGDB 需 Twitch 账号+手机验证,MobyGames 需付费),配置门槛较高。
  3. 不提供 ROM 文件:仅管理已有 ROM,不提供游戏下载(合法合规但需用户自行准备)。
  4. 浏览器模拟器性能有限:EmulatorJS 对高性能平台(如 PS2、Wii)支持有限,复杂游戏仍需本地模拟器。
  5. 数据库要求:推荐使用 MariaDB,小型部署(SQLite)在大量 ROM 时性能可能不足。
  6. v5.0 仍在测试:最新 UI 重构处于 beta 阶段,可能存在不稳定因素,生产环境建议使用 v4.9.x。
  7. AGPL v3.0 许可证:对商业使用和二次开发有一定限制,需要衍生作品同样开源。
  8. ROM 文件命名要求:需要遵循平台的命名规范,文件名不规范可能导致匹配失败。

参考来源

posted @ 2026-07-06 23:24  zhang-yd  阅读(50)  评论(0)    收藏  举报