Game Library — Google Drive Integration
Connect a Google Drive folder. We scan it, identify your ROMs, fetch cover art, and present a library UI.
:::warning STATUS: PLANNED This is a Phase 3 feature. Architecture researched, implementation pending. :::
The Vision
Architecture
Inspired by Jellyfin
Jellyfin handles media libraries by:
- Scanning filesystem paths for media files
- Parsing filenames to extract candidate title + year
- Querying metadata providers (TMDB, TVDB) for art + descriptions
- Caching results locally
Jellyfin has no native Google Drive integration — it uses rclone mount to present Drive as a local filesystem. For our browser-only app, we replace rclone with the Google Drive API directly.
Our Approach: Browser-Only, No Backend
| Component | Technology |
|---|---|
| Folder selection | Google Picker API (pre-built folder browser UI) |
| OAuth | Google Identity Services (GIS) — token popup, no backend |
| File listing | Drive API v3 files.list with folder filter |
| ROM identification | Range request (bytes 0-63) → parse N64 header |
| Game database | No-Intro DAT (bundled ~1MB JSON) for CRC → canonical name |
| Metadata + art | IGDB API (cover art, year, genre, description) |
| Cache | IndexedDB (persist across sessions) |
| ROM streaming | Drive API v3 files.get?alt=media → ArrayBuffer → emulator |
ROM Identification
N64 ROM Header (first 64 bytes)
Offset Size Description
0x00 4 Byte order magic (80 37 12 40 = .z64 big-endian)
0x04 4 Clock rate
0x08 4 Program counter
0x0C 4 Release address
0x10 4 CRC1
0x14 4 CRC2
0x20 20 Internal name (ASCII, padded with spaces)
0x3B 4 Game ID code
0x3E 1 Country code
We fetch just 64 bytes per ROM via HTTP Range request — no need to download the full file for identification:
// Fetch only the header from Google Drive
const resp = await fetch(
`https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`,
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Range': 'bytes=0-63'
}
}
);
const header = new Uint8Array(await resp.arrayBuffer());
const internalName = new TextDecoder().decode(header.slice(0x20, 0x34)).trim();
No-Intro DAT Lookup
For accurate identification, compute CRC32 and look up in the No-Intro DAT:
// Bundled as n64-nointro.json (~1MB)
// { "CRC32_HEX": { "title": "...", "region": "...", "revision": "..." } }
const game = noIntroDB[crc32hex];
const canonicalTitle = game?.title || internalName;
Metadata Sources
IGDB (Primary)
IGDB (owned by Twitch/Amazon) has comprehensive N64 coverage:
- Cover art (multiple sizes)
- Release year
- Genre
- Description/summary
- Screenshots
- Rating
Requires: free Twitch developer account → Client ID + Client Secret → OAuth token.
// Search IGDB for game metadata
const resp = await fetch('https://api.igdb.com/v4/games', {
method: 'POST',
headers: {
'Client-ID': TWITCH_CLIENT_ID,
'Authorization': `Bearer ${igdbToken}`,
},
body: `search "${canonicalTitle}"; fields name,cover.url,first_release_date,genres.name,summary; where platforms = (4); limit 1;`
});
Platform ID 4 = Nintendo 64 in IGDB.
TheGamesDB (Fallback)
Open-source alternative if IGDB is unavailable. REST API, covers N64 well.
Library UI
The game library renders as a grid matching the retro terminal aesthetic:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Cover │ │ Cover │ │ Cover │ │ Cover │
│ Art │ │ Art │ │ Art │ │ Art │
│ │ │ │ │ │ │ │
├─────────┤ ├─────────┤ ├─────────┤ ├─────────┤
│ THPS │ │ SM64 │ │ Zelda │ │ MK64 │
│ 1999 │ │ 1996 │ │ 1998 │ │ 1996 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
Features:
- Grid view with cover art thumbnails
- Search/filter by name, year, genre
- "Last played" sorting
- Click to stream ROM from Drive → emulator
- Cached metadata persists in IndexedDB
Google Cloud Setup Required
- Create Google Cloud project
- Enable Drive API and Picker API
- Create OAuth 2.0 Web Client ID
- Add authorized JavaScript origins:
https://play.weshuber.comhttp://localhost:8064(dev)
- Configure consent screen
Implementation Plan
| Step | Effort | Dependency |
|---|---|---|
| Google Cloud project + OAuth setup | 1 hour | None |
| Google Picker integration (folder select) | 1 day | OAuth |
| Drive API file listing + Range requests | 1 day | OAuth |
| N64 header parser | 2 hours | None |
| No-Intro DAT bundling (JSON) | 2 hours | None |
| IGDB metadata fetcher | 1 day | Twitch dev account |
| Library grid UI (retro styled) | 2 days | Metadata |
| IndexedDB caching layer | 1 day | None |
| ROM streaming → emulator | 1 day | Library UI |
| Total | ~1 week |