feat(dictionary): add Hachidori backend support (#266)

This commit is contained in:
sudacode authored and GitHub committed 2026-10-24 21:08:18 -07:00
1 parent e690ffba07
commit cbaa4626e1
143 files changed
+5564 -305

No files matched your search

@@ -6,6 +6,7 @@ import test from 'node:test';
import { createCoverArtFetcher, stripFilenameTags } from './cover-art-fetcher.js';
import { Database } from '../immersion-tracker/sqlite.js';
import {
applyPragmas,
ensureSchema,
getOrCreateAnimeRecord,
getOrCreateVideoRecord,
@@ -50,6 +51,7 @@ test('stripFilenameTags normalizes common media-title formats', () => {
async function backfillMissingCoverBlob(): Promise<void> {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const videoId = getOrCreateVideoRecord(db, 'local:/tmp/cover-fetcher-test.mkv', {
canonicalTitle: 'Cover Fetcher Test',
@@ -114,6 +116,7 @@ test(
async function reuseCachedAnimeCoverArt(): Promise<void> {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const firstVideoId = getOrCreateVideoRecord(db, 'local:/tmp/cover-fetcher-cache-1.mkv', {
canonicalTitle: 'Shared Cover Show',
@@ -203,6 +206,7 @@ function createJsonResponse(payload: unknown): Response {
test('fetchIfMissing uses guessit primary title and season when available', async () => {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const videoId = getOrCreateVideoRecord(db, 'local:/tmp/cover-fetcher-season-test.mkv', {
canonicalTitle:
@@ -308,6 +312,7 @@ test('fetchIfMissing uses guessit primary title and season when available', asyn
test('fetchIfMissing falls back to internal parser when guessit throws', async () => {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const videoId = getOrCreateVideoRecord(db, 'local:/tmp/cover-fetcher-fallback-test.mkv', {
canonicalTitle: 'School Vlog S01E01',
@@ -372,6 +377,7 @@ test('fetchIfMissing falls back to internal parser when guessit throws', async (
test('fetchIfMissing caches a no-match when the season cannot be resolved', async () => {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const videoId = getOrCreateVideoRecord(db, 'local:/tmp/cover-fetcher-unresolved.mkv', {
canonicalTitle: 'Unresolved Show (2013) - S03E01 - Something [1080p].mkv',
@@ -450,6 +456,7 @@ test('fetchIfMissing caches a no-match when the season cannot be resolved', asyn
test('fetchIfMissing re-resolves an unresolved season once AniList publishes the relation', async () => {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const videoId = getOrCreateVideoRecord(db, 'local:/tmp/cover-fetcher-recovers.mkv', {
canonicalTitle: 'Recovering Show (2013) - S02E01 - Something [1080p].mkv',
@@ -557,6 +564,7 @@ for (const linkedToAnilist of [false, true]) {
test(`TMDB fallback preserves AniList identity when linked=${linkedToAnilist}`, async () => {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const videoId = getOrCreateVideoRecord(db, 'local:/tmp/hanzawa-01.mkv', {
canonicalTitle: 'Hanzawa Naoki - 01.mkv',
@@ -674,6 +682,7 @@ for (const linkedToAnilist of [false, true]) {
test('fetchIfMissing skips AniList for an entry already linked to TMDB', async () => {
const dbPath = makeDbPath();
const db = new Database(dbPath);
applyPragmas(db);
ensureSchema(db);
const videoId = getOrCreateVideoRecord(db, 'local:/tmp/hanzawa-02.mkv', {
canonicalTitle: 'Hanzawa Naoki - 02.mkv',