0.2.76 - Icon Packs

This commit is contained in:
ponzischeme89
2026-08-18 14:59:29 +12:00
parent 36d171e51b
commit 8c847c59b8
70 changed files with 5267 additions and 673 deletions
+64
View File
@@ -120,6 +120,70 @@ func (s *Store) DeleteLibraryItemsBefore(ctx context.Context, cutoff time.Time)
return tag.RowsAffected(), nil
}
// NamedItem is the least a caller can be told about a catalogue row and still identify
// it: what it is called and, where the library knows, when it came out.
type NamedItem struct {
ID string
Name string
Year int
}
// LibraryItemsByName finds catalogue rows by title, case-insensitively.
//
// The comparison that decides the answer is not this one: the caller normalises both
// sides (punctuation and spacing are where an *arr and Emby actually differ) and picks by
// year. This is the narrowing query — a handful of rows out of twenty thousand — so that
// the matching rule can stay a pure function with one definition.
func (s *Store) LibraryItemsByName(ctx context.Context, itemType, name string) ([]NamedItem, error) {
trimmed := strings.TrimSpace(name)
if trimmed == "" {
return nil, nil
}
rows, err := s.pool.Query(ctx, `
SELECT id, name, COALESCE(production_year, 0)
FROM library_items
WHERE type = $1 AND lower(name) = lower($2)
LIMIT 50`, itemType, trimmed)
if err != nil {
return nil, fmt.Errorf("store: library items by name: %w", err)
}
defer rows.Close()
out := []NamedItem{}
for rows.Next() {
var item NamedItem
if err := rows.Scan(&item.ID, &item.Name, &item.Year); err != nil {
return nil, err
}
out = append(out, item)
}
return out, rows.Err()
}
// DeleteLibraryItem removes one item and anything derived from it.
//
// The credits marker goes with it, and that is the point of doing this in one place: the
// marker table is keyed on the item id and nothing else prunes it, so a title deleted from
// the library would otherwise leave a Skip Credits position behind for a file that no
// longer exists — and if that id were ever reused, in front of the wrong programme.
//
// Deleting a series takes its episodes with it, because Emby's own hierarchy is the only
// thing that made those rows meaningful.
func (s *Store) DeleteLibraryItem(ctx context.Context, itemID string) (int64, error) {
if strings.TrimSpace(itemID) == "" {
return 0, nil
}
tag, err := s.pool.Exec(ctx,
`DELETE FROM library_items WHERE id = $1 OR series_id = $1`, itemID)
if err != nil {
return 0, fmt.Errorf("store: delete library item: %w", err)
}
if _, err := s.pool.Exec(ctx,
`DELETE FROM credits_markers WHERE item_id = $1 OR series_id = $1`, itemID); err != nil {
return 0, fmt.Errorf("store: delete credits markers: %w", err)
}
return tag.RowsAffected(), nil
}
// SearchLibrary answers from the imported library rather than Emby.
//
// Full-text match first, with a trailing ILIKE so partial words ("sever") still hit