Skip to main content

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:

  1. Scanning filesystem paths for media files
  2. Parsing filenames to extract candidate title + year
  3. Querying metadata providers (TMDB, TVDB) for art + descriptions
  4. 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

ComponentTechnology
Folder selectionGoogle Picker API (pre-built folder browser UI)
OAuthGoogle Identity Services (GIS) — token popup, no backend
File listingDrive API v3 files.list with folder filter
ROM identificationRange request (bytes 0-63) → parse N64 header
Game databaseNo-Intro DAT (bundled ~1MB JSON) for CRC → canonical name
Metadata + artIGDB API (cover art, year, genre, description)
CacheIndexedDB (persist across sessions)
ROM streamingDrive 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

  1. Create Google Cloud project
  2. Enable Drive API and Picker API
  3. Create OAuth 2.0 Web Client ID
  4. Add authorized JavaScript origins:
    • https://play.weshuber.com
    • http://localhost:8064 (dev)
  5. Configure consent screen

Implementation Plan

StepEffortDependency
Google Cloud project + OAuth setup1 hourNone
Google Picker integration (folder select)1 dayOAuth
Drive API file listing + Range requests1 dayOAuth
N64 header parser2 hoursNone
No-Intro DAT bundling (JSON)2 hoursNone
IGDB metadata fetcher1 dayTwitch dev account
Library grid UI (retro styled)2 daysMetadata
IndexedDB caching layer1 dayNone
ROM streaming → emulator1 dayLibrary UI
Total~1 week