mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-27 04:49:49 -07:00
324 lines
12 KiB
TypeScript
324 lines
12 KiB
TypeScript
// Schema-version-18 shape of the tables the sync merge touches (plus the
|
|
// app's indexes), mirroring ensureSchema / ensureLifetimeSummaryTables /
|
|
// ensureStatsExcludedWordsTable in src/core/services/immersion-tracker/storage.ts.
|
|
export const IMMERSION_DB_FIXTURE_DDL = `
|
|
CREATE TABLE imm_schema_version (
|
|
schema_version INTEGER PRIMARY KEY,
|
|
applied_at_ms TEXT NOT NULL
|
|
);
|
|
CREATE TABLE imm_rollup_state(
|
|
state_key TEXT PRIMARY KEY,
|
|
state_value TEXT NOT NULL
|
|
);
|
|
CREATE TABLE imm_anime(
|
|
anime_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
normalized_title_key TEXT NOT NULL UNIQUE,
|
|
canonical_title TEXT NOT NULL,
|
|
anilist_id INTEGER UNIQUE,
|
|
title_romaji TEXT,
|
|
title_english TEXT,
|
|
title_native TEXT,
|
|
episodes_total INTEGER,
|
|
description TEXT,
|
|
metadata_json TEXT,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT
|
|
);
|
|
CREATE TABLE imm_videos(
|
|
video_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
video_key TEXT NOT NULL UNIQUE,
|
|
anime_id INTEGER,
|
|
canonical_title TEXT NOT NULL,
|
|
source_type INTEGER NOT NULL,
|
|
source_path TEXT,
|
|
source_url TEXT,
|
|
parsed_basename TEXT,
|
|
parsed_title TEXT,
|
|
parsed_season INTEGER,
|
|
parsed_episode INTEGER,
|
|
parser_source TEXT,
|
|
parser_confidence REAL,
|
|
parse_metadata_json TEXT,
|
|
watched INTEGER NOT NULL DEFAULT 0,
|
|
duration_ms INTEGER NOT NULL CHECK(duration_ms>=0),
|
|
file_size_bytes INTEGER CHECK(file_size_bytes>=0),
|
|
codec_id INTEGER, container_id INTEGER,
|
|
width_px INTEGER, height_px INTEGER, fps_x100 INTEGER,
|
|
bitrate_kbps INTEGER, audio_codec_id INTEGER,
|
|
hash_sha256 TEXT, screenshot_path TEXT,
|
|
metadata_json TEXT,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(anime_id) REFERENCES imm_anime(anime_id) ON DELETE SET NULL
|
|
);
|
|
CREATE TABLE imm_sessions(
|
|
session_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_uuid TEXT NOT NULL UNIQUE,
|
|
video_id INTEGER NOT NULL,
|
|
started_at_ms TEXT NOT NULL, ended_at_ms TEXT,
|
|
status INTEGER NOT NULL,
|
|
locale_id INTEGER, target_lang_id INTEGER,
|
|
difficulty_tier INTEGER, subtitle_mode INTEGER,
|
|
ended_media_ms INTEGER,
|
|
total_watched_ms INTEGER NOT NULL DEFAULT 0,
|
|
active_watched_ms INTEGER NOT NULL DEFAULT 0,
|
|
lines_seen INTEGER NOT NULL DEFAULT 0,
|
|
tokens_seen INTEGER NOT NULL DEFAULT 0,
|
|
cards_mined INTEGER NOT NULL DEFAULT 0,
|
|
lookup_count INTEGER NOT NULL DEFAULT 0,
|
|
lookup_hits INTEGER NOT NULL DEFAULT 0,
|
|
yomitan_lookup_count INTEGER NOT NULL DEFAULT 0,
|
|
pause_count INTEGER NOT NULL DEFAULT 0,
|
|
pause_ms INTEGER NOT NULL DEFAULT 0,
|
|
seek_forward_count INTEGER NOT NULL DEFAULT 0,
|
|
seek_backward_count INTEGER NOT NULL DEFAULT 0,
|
|
media_buffer_events INTEGER NOT NULL DEFAULT 0,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(video_id) REFERENCES imm_videos(video_id)
|
|
);
|
|
CREATE TABLE imm_session_telemetry(
|
|
telemetry_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_id INTEGER NOT NULL,
|
|
sample_ms TEXT NOT NULL,
|
|
total_watched_ms INTEGER NOT NULL DEFAULT 0,
|
|
active_watched_ms INTEGER NOT NULL DEFAULT 0,
|
|
lines_seen INTEGER NOT NULL DEFAULT 0,
|
|
tokens_seen INTEGER NOT NULL DEFAULT 0,
|
|
cards_mined INTEGER NOT NULL DEFAULT 0,
|
|
lookup_count INTEGER NOT NULL DEFAULT 0,
|
|
lookup_hits INTEGER NOT NULL DEFAULT 0,
|
|
yomitan_lookup_count INTEGER NOT NULL DEFAULT 0,
|
|
pause_count INTEGER NOT NULL DEFAULT 0,
|
|
pause_ms INTEGER NOT NULL DEFAULT 0,
|
|
seek_forward_count INTEGER NOT NULL DEFAULT 0,
|
|
seek_backward_count INTEGER NOT NULL DEFAULT 0,
|
|
media_buffer_events INTEGER NOT NULL DEFAULT 0,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(session_id) REFERENCES imm_sessions(session_id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_session_events(
|
|
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_id INTEGER NOT NULL,
|
|
ts_ms TEXT NOT NULL,
|
|
event_type INTEGER NOT NULL,
|
|
line_index INTEGER,
|
|
segment_start_ms INTEGER,
|
|
segment_end_ms INTEGER,
|
|
tokens_delta INTEGER NOT NULL DEFAULT 0,
|
|
cards_delta INTEGER NOT NULL DEFAULT 0,
|
|
payload_json TEXT,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(session_id) REFERENCES imm_sessions(session_id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_daily_rollups(
|
|
rollup_day INTEGER NOT NULL,
|
|
video_id INTEGER,
|
|
total_sessions INTEGER NOT NULL DEFAULT 0,
|
|
total_active_min REAL NOT NULL DEFAULT 0,
|
|
total_lines_seen INTEGER NOT NULL DEFAULT 0,
|
|
total_tokens_seen INTEGER NOT NULL DEFAULT 0,
|
|
total_cards INTEGER NOT NULL DEFAULT 0,
|
|
cards_per_hour REAL,
|
|
tokens_per_min REAL,
|
|
lookup_hit_rate REAL,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
PRIMARY KEY (rollup_day, video_id)
|
|
);
|
|
CREATE TABLE imm_monthly_rollups(
|
|
rollup_month INTEGER NOT NULL,
|
|
video_id INTEGER,
|
|
total_sessions INTEGER NOT NULL DEFAULT 0,
|
|
total_active_min REAL NOT NULL DEFAULT 0,
|
|
total_lines_seen INTEGER NOT NULL DEFAULT 0,
|
|
total_tokens_seen INTEGER NOT NULL DEFAULT 0,
|
|
total_cards INTEGER NOT NULL DEFAULT 0,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
PRIMARY KEY (rollup_month, video_id)
|
|
);
|
|
CREATE TABLE imm_words(
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
headword TEXT,
|
|
word TEXT,
|
|
reading TEXT,
|
|
part_of_speech TEXT,
|
|
pos1 TEXT,
|
|
pos2 TEXT,
|
|
pos3 TEXT,
|
|
first_seen REAL,
|
|
last_seen REAL,
|
|
frequency INTEGER,
|
|
frequency_rank INTEGER,
|
|
UNIQUE(headword, word, reading)
|
|
);
|
|
CREATE TABLE imm_kanji(
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
kanji TEXT,
|
|
first_seen REAL,
|
|
last_seen REAL,
|
|
frequency INTEGER,
|
|
UNIQUE(kanji)
|
|
);
|
|
CREATE TABLE imm_subtitle_lines(
|
|
line_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_id INTEGER NOT NULL,
|
|
event_id INTEGER,
|
|
video_id INTEGER NOT NULL,
|
|
anime_id INTEGER,
|
|
line_index INTEGER NOT NULL,
|
|
segment_start_ms INTEGER,
|
|
segment_end_ms INTEGER,
|
|
text TEXT NOT NULL,
|
|
secondary_text TEXT,
|
|
CREATED_DATE INTEGER,
|
|
LAST_UPDATE_DATE INTEGER,
|
|
FOREIGN KEY(session_id) REFERENCES imm_sessions(session_id) ON DELETE CASCADE,
|
|
FOREIGN KEY(event_id) REFERENCES imm_session_events(event_id) ON DELETE SET NULL,
|
|
FOREIGN KEY(video_id) REFERENCES imm_videos(video_id) ON DELETE CASCADE,
|
|
FOREIGN KEY(anime_id) REFERENCES imm_anime(anime_id) ON DELETE SET NULL
|
|
);
|
|
CREATE TABLE imm_word_line_occurrences(
|
|
line_id INTEGER NOT NULL,
|
|
word_id INTEGER NOT NULL,
|
|
occurrence_count INTEGER NOT NULL,
|
|
PRIMARY KEY(line_id, word_id),
|
|
FOREIGN KEY(line_id) REFERENCES imm_subtitle_lines(line_id) ON DELETE CASCADE,
|
|
FOREIGN KEY(word_id) REFERENCES imm_words(id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_kanji_line_occurrences(
|
|
line_id INTEGER NOT NULL,
|
|
kanji_id INTEGER NOT NULL,
|
|
occurrence_count INTEGER NOT NULL,
|
|
PRIMARY KEY(line_id, kanji_id),
|
|
FOREIGN KEY(line_id) REFERENCES imm_subtitle_lines(line_id) ON DELETE CASCADE,
|
|
FOREIGN KEY(kanji_id) REFERENCES imm_kanji(id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_media_art(
|
|
video_id INTEGER PRIMARY KEY,
|
|
anilist_id INTEGER,
|
|
cover_url TEXT,
|
|
cover_blob BLOB,
|
|
cover_blob_hash TEXT,
|
|
title_romaji TEXT,
|
|
title_english TEXT,
|
|
episodes_total INTEGER,
|
|
fetched_at_ms TEXT NOT NULL,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(video_id) REFERENCES imm_videos(video_id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_youtube_videos(
|
|
video_id INTEGER PRIMARY KEY,
|
|
youtube_video_id TEXT NOT NULL,
|
|
video_url TEXT NOT NULL,
|
|
video_title TEXT,
|
|
video_thumbnail_url TEXT,
|
|
channel_id TEXT,
|
|
channel_name TEXT,
|
|
channel_url TEXT,
|
|
channel_thumbnail_url TEXT,
|
|
uploader_id TEXT,
|
|
uploader_url TEXT,
|
|
description TEXT,
|
|
metadata_json TEXT,
|
|
fetched_at_ms TEXT NOT NULL,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(video_id) REFERENCES imm_videos(video_id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_cover_art_blobs(
|
|
blob_hash TEXT PRIMARY KEY,
|
|
cover_blob BLOB NOT NULL,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT
|
|
);
|
|
CREATE TABLE imm_lifetime_global(
|
|
global_id INTEGER PRIMARY KEY CHECK(global_id = 1),
|
|
total_sessions INTEGER NOT NULL DEFAULT 0,
|
|
total_active_ms INTEGER NOT NULL DEFAULT 0,
|
|
total_cards INTEGER NOT NULL DEFAULT 0,
|
|
active_days INTEGER NOT NULL DEFAULT 0,
|
|
episodes_started INTEGER NOT NULL DEFAULT 0,
|
|
episodes_completed INTEGER NOT NULL DEFAULT 0,
|
|
anime_completed INTEGER NOT NULL DEFAULT 0,
|
|
last_rebuilt_ms TEXT,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT
|
|
);
|
|
CREATE TABLE imm_lifetime_anime(
|
|
anime_id INTEGER PRIMARY KEY,
|
|
total_sessions INTEGER NOT NULL DEFAULT 0,
|
|
total_active_ms INTEGER NOT NULL DEFAULT 0,
|
|
total_cards INTEGER NOT NULL DEFAULT 0,
|
|
total_lines_seen INTEGER NOT NULL DEFAULT 0,
|
|
total_tokens_seen INTEGER NOT NULL DEFAULT 0,
|
|
episodes_started INTEGER NOT NULL DEFAULT 0,
|
|
episodes_completed INTEGER NOT NULL DEFAULT 0,
|
|
first_watched_ms TEXT,
|
|
last_watched_ms TEXT,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(anime_id) REFERENCES imm_anime(anime_id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_lifetime_media(
|
|
video_id INTEGER PRIMARY KEY,
|
|
total_sessions INTEGER NOT NULL DEFAULT 0,
|
|
total_active_ms INTEGER NOT NULL DEFAULT 0,
|
|
total_cards INTEGER NOT NULL DEFAULT 0,
|
|
total_lines_seen INTEGER NOT NULL DEFAULT 0,
|
|
total_tokens_seen INTEGER NOT NULL DEFAULT 0,
|
|
completed INTEGER NOT NULL DEFAULT 0,
|
|
first_watched_ms TEXT,
|
|
last_watched_ms TEXT,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(video_id) REFERENCES imm_videos(video_id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_lifetime_applied_sessions(
|
|
session_id INTEGER PRIMARY KEY,
|
|
applied_at_ms TEXT NOT NULL,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
FOREIGN KEY(session_id) REFERENCES imm_sessions(session_id) ON DELETE CASCADE
|
|
);
|
|
CREATE TABLE imm_stats_excluded_words(
|
|
headword TEXT NOT NULL,
|
|
word TEXT NOT NULL,
|
|
reading TEXT NOT NULL,
|
|
CREATED_DATE TEXT,
|
|
LAST_UPDATE_DATE TEXT,
|
|
PRIMARY KEY(headword, word, reading)
|
|
);
|
|
CREATE INDEX idx_anime_normalized_title ON imm_anime(normalized_title_key);
|
|
CREATE INDEX idx_anime_anilist_id ON imm_anime(anilist_id);
|
|
CREATE INDEX idx_videos_anime_id ON imm_videos(anime_id);
|
|
CREATE INDEX idx_sessions_video_started ON imm_sessions(video_id, started_at_ms DESC);
|
|
CREATE INDEX idx_sessions_status_started ON imm_sessions(status, started_at_ms DESC);
|
|
CREATE INDEX idx_sessions_started_at ON imm_sessions(started_at_ms DESC);
|
|
CREATE INDEX idx_sessions_ended_at ON imm_sessions(ended_at_ms DESC);
|
|
CREATE INDEX idx_telemetry_session_sample ON imm_session_telemetry(session_id, sample_ms DESC);
|
|
CREATE INDEX idx_telemetry_sample_ms ON imm_session_telemetry(sample_ms DESC);
|
|
CREATE INDEX idx_events_session_ts ON imm_session_events(session_id, ts_ms DESC);
|
|
CREATE INDEX idx_events_type_ts ON imm_session_events(event_type, ts_ms DESC);
|
|
CREATE INDEX idx_rollups_day_video ON imm_daily_rollups(rollup_day, video_id);
|
|
CREATE INDEX idx_rollups_month_video ON imm_monthly_rollups(rollup_month, video_id);
|
|
CREATE INDEX idx_words_headword_word_reading ON imm_words(headword, word, reading);
|
|
CREATE INDEX idx_words_frequency ON imm_words(frequency DESC);
|
|
CREATE INDEX idx_kanji_kanji ON imm_kanji(kanji);
|
|
CREATE INDEX idx_kanji_frequency ON imm_kanji(frequency DESC);
|
|
CREATE INDEX idx_subtitle_lines_session_line ON imm_subtitle_lines(session_id, line_index);
|
|
CREATE INDEX idx_subtitle_lines_video_line ON imm_subtitle_lines(video_id, line_index);
|
|
CREATE INDEX idx_subtitle_lines_anime_line ON imm_subtitle_lines(anime_id, line_index);
|
|
CREATE INDEX idx_word_line_occurrences_word ON imm_word_line_occurrences(word_id, line_id);
|
|
CREATE INDEX idx_kanji_line_occurrences_kanji ON imm_kanji_line_occurrences(kanji_id, line_id);
|
|
CREATE INDEX idx_media_art_cover_blob_hash ON imm_media_art(cover_blob_hash);
|
|
CREATE INDEX idx_media_art_anilist_id ON imm_media_art(anilist_id);
|
|
CREATE INDEX idx_media_art_cover_url ON imm_media_art(cover_url);
|
|
CREATE INDEX idx_youtube_videos_channel_id ON imm_youtube_videos(channel_id);
|
|
CREATE INDEX idx_youtube_videos_youtube_video_id ON imm_youtube_videos(youtube_video_id);
|
|
`;
|