+
{name}
+
+
+ Processing your database file runs entirely in your browser. Depending on the number
+ of scores, this may use significant CPU for a few seconds. This is expected.
+
+ {fileInputs.map(({ key, label }) => (
+
+ {label}
+ {
+ const file = e.target.files?.[0] ?? null;
+ setFiles((prev) => ({ ...prev, [key]: file }));
+ // Reset convert state when a file changes
+ setConvertState({ phase: "idle" });
+ }}
+ type="file"
+ />
+
+ ))}
+
+ {extraControls}
+
+ {convertState.phase === "error" && (
+
+ Error: {convertState.phase === "error" && convertState.message}
+
+ )}
+
+ {convertState.phase === "preview" && (
+ <>
+
+ Ready to import
+
+ {convertState.results.map((r, i) => {
+ const bm = r as {
+ meta: { game: string; playtype: string };
+ scores: unknown[];
+ };
+ return (
+ -
+
+ {bm.meta.game} {bm.meta.playtype}
+
+ : {bm.scores.length} score
+ {bm.scores.length !== 1 ? "s" : ""}
+
+ );
+ })}
+
+
+ {convertState.warnings.length > 0 && (
+
+ {convertState.warnings.length} warning(s):
+
+ {convertState.warnings.map((w, i) => (
+ -
+ {w.message}
+
+ ))}
+
+
+ )}
+ >
+ )}
+
+
+ {convertState.phase !== "preview" ? (
+
+ ) : !bothDone ? (
+
+ ) : null}
+
+
+ {importState1.state !== "not_started" && (
+ <>
+
+
+ >
+ )}
+ {importState2.state !== "not_started" && (
+ <>
+
+
+ >
+ )}
+
+ );
+}
diff --git a/typescript/client/src/util/db-converters/beatoraja.ts b/typescript/client/src/util/db-converters/beatoraja.ts
new file mode 100644
index 000000000..ce1f76160
--- /dev/null
+++ b/typescript/client/src/util/db-converters/beatoraja.ts
@@ -0,0 +1,236 @@
+/**
+ * Port of tachi-import-scripts/src-tauri/src/backend/beatoraja.rs
+ *
+ * Converts Beatoraja score.db + chart.db into Batch Manual documents
+ * (one per gamemode: 7K, 14K) suitable for /ir/direct-manual/import.
+ */
+
+import { type Database } from "sql.js";
+
+import { queryAll, queryOne } from "./sql-loader";
+
+const SERVICE = "tachi-import-scripts";
+
+interface BeatorajaScoreRow {
+ sha256: string;
+ clear: number;
+ epg: number;
+ egr: number;
+ egd: number;
+ ebd: number;
+ epr: number;
+ ems: number;
+ lpg: number;
+ lgr: number;
+ lgd: number;
+ lbd: number;
+ lpr: number;
+ lms: number;
+ combo: number;
+ minbp: number;
+ random: number;
+ date: number;
+}
+
+interface BeatorajaChartRow {
+ title: string;
+ subtitle: string;
+ feature: number;
+ mode: number;
+}
+
+export interface BeatorajaConvertWarning {
+ message: string;
+ level: "error" | "warn";
+}
+
+interface BMSBatchManual {
+ meta: {
+ game: "bms";
+ playtype: "7K" | "14K";
+ service: string;
+ version: null;
+ };
+ scores: unknown[];
+ classes: Record