A release the window closed on with one file named by its stem or post, the name with a number and no archive, volume, sidecar or random name, waits in the album of its poster and template (the name's digit runs as #, the extension apart) posted within the window of the album's first file; the album closes a window after that span and is written as one release (source album, no file total) named by the members' shared numbers, a lone member as the release it was. The batch store writes an album's member names as release_texts kind 6 (member). A counter total of 100 or more under which one poster started 20 or more releases is a batch counter: those releases get no file total. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
61 lines
2.4 KiB
TypeScript
61 lines
2.4 KiB
TypeScript
/**
|
|
* What a searchable string of a release is, as release_texts.kind stores it (a smallint; voyager 3, migration 6200).
|
|
* The table holds every string search matches against (the release name too), appended by whoever learns one: the
|
|
* batch store the name, the prober the file names inside an archive, later passes NFO titles, par2 set names,
|
|
* media titles. The numbers never change meaning once written.
|
|
*/
|
|
export const TEXT_KINDS = {
|
|
/** The release name (releases.name, once more for the one trigram index). */
|
|
name: 1,
|
|
/** A file name inside an archive (the prober's inner names). */
|
|
file: 2,
|
|
/** A title read from an NFO. */
|
|
nfo: 3,
|
|
/** A par2 set's name when it differs from the release name. */
|
|
par2: 4,
|
|
/** A title from a media container's metadata. */
|
|
media: 5,
|
|
/** An album's member, by its own file name (the edge's albums: one poster's files differing by a number sequence). */
|
|
member: 6,
|
|
} as const
|
|
|
|
export type TextKind = keyof typeof TEXT_KINDS
|
|
|
|
/** The longest string stored (release_texts.text is a varchar(255)); longer ones are cut, not refused. */
|
|
export const TEXT_MAX = 255
|
|
/** The most strings one writer appends per release and kind (an archive of two thousand files is not two thousand rows). */
|
|
export const TEXTS_PER_KIND = 20
|
|
|
|
const NAMES = Object.fromEntries(
|
|
Object.entries(TEXT_KINDS).map(([name, code]) => [code, name])
|
|
) as Record<number, TextKind>
|
|
|
|
/** The code of a kind; throws for an unknown one (a typo would write a wrong number for good). */
|
|
export function textKindCode(name: string): number {
|
|
const code = TEXT_KINDS[name as TextKind]
|
|
if (code === undefined) throw new Error(`Unknown release text kind "${name}"`)
|
|
return code
|
|
}
|
|
|
|
/** The name of a stored code; "unknown" for one not listed. */
|
|
export function textKindName(code: number | string | null | undefined): TextKind | 'unknown' {
|
|
return NAMES[Number(code)] ?? 'unknown'
|
|
}
|
|
|
|
/**
|
|
* The strings a writer appends for one kind: trimmed, cut to TEXT_MAX, empty ones and repeats dropped, the first
|
|
* TEXTS_PER_KIND kept in the order given (the writer puts the most telling first).
|
|
*/
|
|
export function textsToStore(texts: Iterable<string | null | undefined>): string[] {
|
|
const seen = new Set<string>()
|
|
const out: string[] = []
|
|
for (const raw of texts) {
|
|
const text = (raw ?? '').trim().slice(0, TEXT_MAX)
|
|
if (!text || seen.has(text)) continue
|
|
seen.add(text)
|
|
out.push(text)
|
|
if (out.length === TEXTS_PER_KIND) break
|
|
}
|
|
return out
|
|
}
|