mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-29 19:21:33 -07:00
fix(immersion): use localtime rollup keys and guard sentence search limi
- Remove UTC-based UNION branch from getRollupGroupsForSessions; keep only localtime rollup - Add resolveSentenceSearchLimit to clamp/sanitize infinity and negative limit values - Switch onMiningImageEvent from queued to latest-value IPC listener (drop stale frames) - Add regression tests for rollup key locality and sentence search limit edge cases
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
|
||||
test('getRollupGroupsForSessions uses only localtime rollup keys', () => {
|
||||
const source = fs.readFileSync(
|
||||
path.join(process.cwd(), 'src/core/services/immersion-tracker/maintenance.ts'),
|
||||
'utf8',
|
||||
);
|
||||
const start = source.indexOf('export function getRollupGroupsForSessions');
|
||||
const end = source.indexOf('export function refreshRollupsForGroupsInTransaction');
|
||||
const functionSource = source.slice(start, end);
|
||||
|
||||
assert.match(functionSource, /'unixepoch', 'localtime'/);
|
||||
assert.doesNotMatch(functionSource, /UNION/);
|
||||
assert.doesNotMatch(functionSource, /86400000/);
|
||||
});
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
upsertCoverArt,
|
||||
} from '../query.js';
|
||||
import {
|
||||
getLocalEpochDay,
|
||||
getShiftedLocalDaySec,
|
||||
getStartOfLocalDayTimestamp,
|
||||
toDbTimestamp,
|
||||
@@ -3776,6 +3777,8 @@ test('searchSubtitleSentences searches known subtitle lines and returns media co
|
||||
]);
|
||||
|
||||
assert.deepEqual(searchSubtitleSentences(db, 'monsters', 10), []);
|
||||
assert.doesNotThrow(() => searchSubtitleSentences(db, '魔物', Number.POSITIVE_INFINITY));
|
||||
assert.equal(searchSubtitleSentences(db, '魔物', -1).length, 1);
|
||||
} finally {
|
||||
db.close();
|
||||
cleanupDbPath(dbPath);
|
||||
@@ -4196,8 +4199,14 @@ test('deleteSession removes zero-session media from library and trends', () => {
|
||||
|
||||
const startedAtMs = 9_000_000;
|
||||
const endedAtMs = startedAtMs + 120_000;
|
||||
const rollupDay = Math.floor(startedAtMs / 86_400_000);
|
||||
const rollupMonth = 197001;
|
||||
const rollupDay = getLocalEpochDay(db, startedAtMs);
|
||||
const rollupMonth = (
|
||||
db
|
||||
.prepare(
|
||||
"SELECT CAST(strftime('%Y%m', CAST(? AS REAL) / 1000, 'unixepoch', 'localtime') AS INTEGER) AS rollupMonth",
|
||||
)
|
||||
.get(startedAtMs) as { rollupMonth: number }
|
||||
).rollupMonth;
|
||||
const { sessionId } = startSessionRecord(db, videoId, startedAtMs);
|
||||
|
||||
db.prepare(
|
||||
|
||||
@@ -319,16 +319,9 @@ export function getRollupGroupsForSessions(db: DatabaseSync, sessionIds: number[
|
||||
video_id
|
||||
FROM imm_sessions
|
||||
WHERE session_id IN (${placeholders})
|
||||
UNION
|
||||
SELECT DISTINCT
|
||||
CAST(CAST(started_at_ms AS REAL) / 86400000 AS INTEGER) AS rollup_day,
|
||||
CAST(strftime('%Y%m', CAST(started_at_ms AS REAL) / 1000, 'unixepoch') AS INTEGER) AS rollup_month,
|
||||
video_id
|
||||
FROM imm_sessions
|
||||
WHERE session_id IN (${placeholders})
|
||||
`,
|
||||
)
|
||||
.all(...sessionIds, ...sessionIds) as RollupGroupRow[];
|
||||
.all(...sessionIds) as RollupGroupRow[];
|
||||
|
||||
return rows.map((row) => ({
|
||||
rollupDay: row.rollup_day,
|
||||
|
||||
@@ -21,6 +21,15 @@ import { nowMs } from './time';
|
||||
|
||||
const VOCABULARY_STATS_FILTER_OVERSAMPLE_FACTOR = 4;
|
||||
const VOCABULARY_STATS_FILTER_OVERSAMPLE_MIN = 100;
|
||||
const SENTENCE_SEARCH_DEFAULT_LIMIT = 50;
|
||||
const SENTENCE_SEARCH_MAX_LIMIT = 100;
|
||||
|
||||
function resolveSentenceSearchLimit(limit: number): number {
|
||||
if (!Number.isFinite(limit)) return SENTENCE_SEARCH_DEFAULT_LIMIT;
|
||||
const normalized = Math.floor(limit);
|
||||
if (normalized <= 0) return SENTENCE_SEARCH_DEFAULT_LIMIT;
|
||||
return Math.min(normalized, SENTENCE_SEARCH_MAX_LIMIT);
|
||||
}
|
||||
|
||||
function splitSearchTerms(query: string): string[] {
|
||||
return query
|
||||
@@ -228,10 +237,11 @@ export function getKanjiOccurrences(
|
||||
export function searchSubtitleSentences(
|
||||
db: DatabaseSync,
|
||||
query: string,
|
||||
limit = 50,
|
||||
limit = SENTENCE_SEARCH_DEFAULT_LIMIT,
|
||||
): SentenceSearchResultRow[] {
|
||||
const terms = splitSearchTerms(query);
|
||||
if (terms.length === 0) return [];
|
||||
const resolvedLimit = resolveSentenceSearchLimit(limit);
|
||||
|
||||
const clauses: string[] = [];
|
||||
const params: string[] = [];
|
||||
@@ -270,7 +280,7 @@ export function searchSubtitleSentences(
|
||||
LIMIT ?
|
||||
`,
|
||||
)
|
||||
.all(...params, limit) as unknown as SentenceSearchResultRow[];
|
||||
.all(...params, resolvedLimit) as unknown as SentenceSearchResultRow[];
|
||||
}
|
||||
|
||||
export function getSessionEvents(
|
||||
|
||||
Reference in New Issue
Block a user