Files
explorer/docs/plans/tape-identifier.md
2026-02-17 16:30:13 +00:00

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' directive
  • identifyTape(md5: string, sizeBytes: number)
  • Queries software_hashes JOIN downloads JOIN entries by 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: idlehashingidentifyingresults / 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

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) — joins software_hashesdownloadsentries
  • 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