2026-08-29 16:57:34 +02:00
|
|
|
import type { DB } from './db.js'
|
2026-08-29 18:44:08 +02:00
|
|
|
import { isConfidentMatch, normalize } from './matcher.js'
|
2026-08-29 16:57:34 +02:00
|
|
|
|
|
|
|
|
export type RipStatus = 'ripped' | 'not_ripped'
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resolution order per spec:
|
|
|
|
|
* 1. manual rip_override (non-null) wins
|
|
|
|
|
* 2. else a stored match_link means ripped
|
|
|
|
|
* 3. else confident fuzzy match against the user's digital_albums
|
|
|
|
|
*/
|
|
|
|
|
export function resolveRipStatus(db: DB, userId: number, itemId: number): RipStatus {
|
|
|
|
|
const item = db
|
|
|
|
|
.prepare('SELECT id, title, artist, rip_override FROM collection_items WHERE id = ? AND user_id = ?')
|
|
|
|
|
.get(itemId, userId) as
|
|
|
|
|
| { id: number; title: string; artist: string; rip_override: number | null }
|
|
|
|
|
| undefined
|
|
|
|
|
if (!item) return 'not_ripped'
|
|
|
|
|
|
|
|
|
|
if (item.rip_override !== null && item.rip_override !== undefined) {
|
|
|
|
|
return item.rip_override === 1 ? 'ripped' : 'not_ripped'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const link = db.prepare('SELECT album_id FROM match_links WHERE item_id = ?').get(itemId)
|
|
|
|
|
if (link) return 'ripped'
|
|
|
|
|
|
|
|
|
|
const albums = db
|
|
|
|
|
.prepare('SELECT title, artist FROM digital_albums WHERE user_id = ?')
|
|
|
|
|
.all(userId) as { title: string; artist: string }[]
|
|
|
|
|
return albums.some((a) => isConfidentMatch(item, a)) ? 'ripped' : 'not_ripped'
|
|
|
|
|
}
|
2026-08-29 18:44:08 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Batch resolution for list views: loads albums and match links once per
|
|
|
|
|
* user instead of per item (GET /api/collection is otherwise O(items x albums)).
|
|
|
|
|
* Resolution order matches resolveRipStatus.
|
|
|
|
|
*/
|
|
|
|
|
export function resolveRipStatusBatch(
|
|
|
|
|
db: DB,
|
|
|
|
|
userId: number,
|
|
|
|
|
items: { id: number; title: string; artist: string; rip_override: number | null }[]
|
|
|
|
|
): RipStatus[] {
|
|
|
|
|
const albums = db
|
|
|
|
|
.prepare('SELECT title, artist FROM digital_albums WHERE user_id = ?')
|
|
|
|
|
.all(userId) as { title: string; artist: string }[]
|
|
|
|
|
const albumKeys = new Set(
|
|
|
|
|
albums.map((a) => `${normalize(a.title)}|${normalize(a.artist)}`)
|
|
|
|
|
)
|
|
|
|
|
const linked = new Set(
|
|
|
|
|
(
|
|
|
|
|
db.prepare('SELECT item_id FROM match_links WHERE user_id = ?').all(userId) as {
|
|
|
|
|
item_id: number
|
|
|
|
|
}[]
|
|
|
|
|
).map((r) => r.item_id)
|
|
|
|
|
)
|
|
|
|
|
return items.map((item) => {
|
|
|
|
|
if (item.rip_override !== null && item.rip_override !== undefined) {
|
|
|
|
|
return item.rip_override === 1 ? 'ripped' : 'not_ripped'
|
|
|
|
|
}
|
|
|
|
|
if (linked.has(item.id)) return 'ripped'
|
|
|
|
|
const key = `${normalize(item.title)}|${normalize(item.artist)}`
|
|
|
|
|
return albumKeys.has(key) ? 'ripped' : 'not_ripped'
|
|
|
|
|
})
|
|
|
|
|
}
|