3.3 KiB
Plan: Tape Identifier Dropzone on /zxdb
Context
We have 32,960 rows in software_hashes with MD5, CRC32, size, and inner_path for tape-image contents. This feature exposes that data to users: drop a tape file, get it identified against the ZXDB database.
Uses RSC (server actions) rather than an API endpoint to make bulk scripted identification harder.
Architecture
Client-side: Compute MD5 + file size in the browser, then call a server action with just those two values (file never leaves the client).
Server-side: A Next.js Server Action looks up software_hashes by MD5 (and optionally size_bytes for disambiguation), joins to downloads and entries to return the entry title, download details, and a link.
Client-side MD5: Web Crypto doesn't support MD5. Include a small pure-JS MD5 utility (~80 lines, well-known algorithm). No new npm dependencies.
Files to Create/Modify
1. src/utils/md5.ts — Pure-JS MD5 for browser use
- Exports
async function computeMd5(file: File): Promise<string> - Reads file as ArrayBuffer, computes MD5, returns hex string
- Standard MD5 algorithm implementation, typed for TypeScript
2. src/app/zxdb/actions.ts — Server Action
'use server'directiveidentifyTape(md5: string, sizeBytes: number)- Queries
software_hashesJOINdownloadsJOINentriesby MD5 - If multiple matches and size_bytes narrows it, filter further
- Returns array of
{ downloadId, entryId, entryTitle, innerPath, md5, crc32, sizeBytes }
3. src/app/zxdb/TapeIdentifier.tsx — Client Component
'use client'- States:
idle→hashing→identifying→results/not-found - Dropzone UI:
- Dashed border card, large tape icon, "Drop a tape file to identify it"
- Lists supported formats:
.tap .tzx .pzx .csw .p .o - Also has a hidden
<input type="file">with a "or choose file" link - Drag-over highlight state
- On file drop/select:
- Validate extension against supported list
- Show spinner + "Computing hash..."
- Compute MD5 + size client-side
- Call server action
identifyTape(md5, size) - Show spinner + "Searching ZXDB..."
- Results view (replaces dropzone):
- Match found: entry title as link to
/zxdb/entries/{id}, inner filename, MD5, file size - Multiple matches: list all
- No match: "No matching tape found in ZXDB"
- "Identify another tape" button to reset
- Match found: entry title as link to
4. src/app/zxdb/page.tsx — Add TapeIdentifier section
- Insert
<TapeIdentifier />as a new section between the hero and "Start exploring" grid - Wrap in a card with distinct styling to make it visually prominent
5. src/server/repo/zxdb.ts — Add lookup function
lookupByMd5(md5: string)— joinssoftware_hashes→downloads→entries- Returns download_id, entry_id, entry title, inner_path, hash details
Verification
- Visit http://localhost:4000/zxdb
- Dropzone should be visible and prominent between hero and navigation grid
- Drop a known .tap/.tzx file → should show the identified entry with a link
- Drop an unknown file → should show "No matching tape found"
- Click "Identify another tape" → resets to dropzone
- Check file never leaves browser (Network tab: only the server action call with md5 + size)
- Verify non-supported extensions are rejected with helpful message