feat(podcasts): generic-surface exclusions — podcasts never render as books (task 1.3)
§9 / D3 / D13 / D15 / D16: podcasts-type libraries are excluded from every generic book surface. Under RLS these are pure UX/type filters (engine already hides other users' episodes); their job is preventing episode grids in surfaces designed for books. The discriminator is the denormalized media_items.library_type_name (IS DISTINCT FROM 'podcasts' — NULL rows predate the column and stay visible). SQL (queries.sql, marked '-- podcasts exclusion (plan §9)'): ListMediaItems/ListMediaItemsSorted (All Books) · SearchMediaItems + SearchMediaItemsUnified (generic search) · FacetCountsUnderFilters (all 14 arms) + the six Search*Values facet-suggestion queries · GetDistinctSeries + Count (series pages) · GetLibraryItems (collections picker) · dashboard system sections (Continue Reading / Recently Added / Recently Read / Not Started) · GetPopularBooks + GetTopBooksByMinutes (book rankings must not rank episodes). Go: statistics summary counts episodes' sessions into time-spent (D15 keeps that) but skips them in BooksActive/BooksFinished — an episode is not a book. OPDS filters podcasts libraries via one helper at all 7 library-walk sites (D16). Templ: LibrarySwitcher skips podcasts entries and TotalMediaCount excludes them — presentation-layer filter so every switcher consumer (dashboard, collections, custom-section builder) is correct at once (D13); podcasts surface via /podcasts only.
This commit is contained in:
1 parent
c0e6993273
commit
9a148c2344
8 files changed
+174
-47
No files matched your search
@@ -606,6 +606,9 @@ type Querier interface {
|
|||||||
// collapsible "Ignored" section so a misclick is recoverable.
|
// collapsible "Ignored" section so a misclick is recoverable.
|
||||||
ListIgnoredUnlinkedBooksForUser(ctx context.Context, userID pgtype.UUID) ([]ListIgnoredUnlinkedBooksForUserRow, error)
|
ListIgnoredUnlinkedBooksForUser(ctx context.Context, userID pgtype.UUID) ([]ListIgnoredUnlinkedBooksForUserRow, error)
|
||||||
ListLibraries(ctx context.Context) ([]ListLibrariesRow, error)
|
ListLibraries(ctx context.Context) ([]ListLibrariesRow, error)
|
||||||
|
// Podcasts exclusion (plan §9): generic book surfaces never list
|
||||||
|
// podcast episodes (library_type_name is the denormalized, trigger/
|
||||||
|
// fetcher-maintained discriminator; NULL rows predate it and stay).
|
||||||
ListMediaItems(ctx context.Context, arg ListMediaItemsParams) ([]ListMediaItemsRow, error)
|
ListMediaItems(ctx context.Context, arg ListMediaItemsParams) ([]ListMediaItemsRow, error)
|
||||||
ListMediaItemsByLibrary(ctx context.Context, libraryID pgtype.UUID) ([]ListMediaItemsByLibraryRow, error)
|
ListMediaItemsByLibrary(ctx context.Context, libraryID pgtype.UUID) ([]ListMediaItemsByLibraryRow, error)
|
||||||
ListMediaItemsByLibraryIncludingArchived(ctx context.Context, libraryID pgtype.UUID) ([]ListMediaItemsByLibraryIncludingArchivedRow, error)
|
ListMediaItemsByLibraryIncludingArchived(ctx context.Context, libraryID pgtype.UUID) ([]ListMediaItemsByLibraryIncludingArchivedRow, error)
|
||||||
|
|||||||
@@ -2620,6 +2620,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
CROSS JOIN LATERAL unnest(mi.authors) AS a
|
CROSS JOIN LATERAL unnest(mi.authors) AS a
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -2688,6 +2689,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
CROSS JOIN LATERAL unnest(mi.publishers) AS p
|
CROSS JOIN LATERAL unnest(mi.publishers) AS p
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -2755,6 +2757,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
CROSS JOIN LATERAL unnest(mi.narrators) AS n
|
CROSS JOIN LATERAL unnest(mi.narrators) AS n
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -2824,6 +2827,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
CROSS JOIN LATERAL unnest(mi.tags) AS t
|
CROSS JOIN LATERAL unnest(mi.tags) AS t
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -2892,6 +2896,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -2963,6 +2968,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3034,6 +3040,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3105,6 +3112,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3172,6 +3180,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3239,6 +3248,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3306,6 +3316,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3377,6 +3388,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3448,6 +3460,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -3519,6 +3532,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
@@ -4822,6 +4836,7 @@ INNER JOIN (
|
|||||||
ORDER BY media_item_id, last_read_at DESC
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
) rp ON rp.media_item_id = mi.id
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
@@ -5621,6 +5636,7 @@ WHERE ($2::uuid IS NULL OR library_id = $2::uuid)
|
|||||||
AND ($3::uuid[] IS NULL OR library_id = ANY($3::uuid[]))
|
AND ($3::uuid[] IS NULL OR library_id = ANY($3::uuid[]))
|
||||||
AND archived_at IS NULL
|
AND archived_at IS NULL
|
||||||
AND missing_scan_count = 0
|
AND missing_scan_count = 0
|
||||||
|
AND (library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9: series pages)
|
||||||
AND series IS NOT NULL AND series != ''
|
AND series IS NOT NULL AND series != ''
|
||||||
-- Multi-field search (series page): the query matches the series
|
-- Multi-field search (series page): the query matches the series
|
||||||
-- NAME, any book's AUTHOR, or any book's TITLE in the group. It is
|
-- NAME, any book's AUTHOR, or any book's TITLE in the group. It is
|
||||||
@@ -5754,6 +5770,7 @@ SELECT COUNT(*)::int FROM (
|
|||||||
AND ($3::uuid[] IS NULL OR library_id = ANY($3::uuid[]))
|
AND ($3::uuid[] IS NULL OR library_id = ANY($3::uuid[]))
|
||||||
AND archived_at IS NULL
|
AND archived_at IS NULL
|
||||||
AND missing_scan_count = 0
|
AND missing_scan_count = 0
|
||||||
|
AND (library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9: series pages)
|
||||||
AND series IS NOT NULL AND series != ''
|
AND series IS NOT NULL AND series != ''
|
||||||
AND ($4::text IS NULL OR
|
AND ($4::text IS NULL OR
|
||||||
series ILIKE '%' || $4::text || '%'
|
series ILIKE '%' || $4::text || '%'
|
||||||
@@ -6488,6 +6505,7 @@ SELECT mi.id, mi.library_id, mi.title, mi.subtitle, mi.author, mi.isbn, mi.descr
|
|||||||
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9: collections picker)
|
||||||
ORDER BY mi.created_at DESC
|
ORDER BY mi.created_at DESC
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -8928,6 +8946,7 @@ func (q *Queries) GetNextRetryTime(ctx context.Context) (interface{}, error) {
|
|||||||
const GetNotStartedItems = `-- name: GetNotStartedItems :many
|
const GetNotStartedItems = `-- name: GetNotStartedItems :many
|
||||||
SELECT mi.id, mi.library_id, mi.title, mi.subtitle, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.narrators, mi.authors, mi.authors_search, mi.publishers, mi.publishers_search, mi.abridged, mi.explicit, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.metadata_overrides, mi.missing_scan_count, mi.archived_at, mi.chapter_metadata, mi.library_type_name, mi.audio_metadata, mi.tags_search, mi.contributors_search, mi.narrators_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, mi.source, mi.source_feed_id, mi.episode_guid, mi.feed_position, mi.season_number, mi.episode_number, mi.enclosure_url, mi.declared_duration_seconds, mi.transcript_url, mi.chapters_url, mi.hosts, mi.guests, mi.download_state, mi.download_error, mi.download_attempts FROM media_items mi
|
SELECT mi.id, mi.library_id, mi.title, mi.subtitle, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.narrators, mi.authors, mi.authors_search, mi.publishers, mi.publishers_search, mi.abridged, mi.explicit, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.metadata_overrides, mi.missing_scan_count, mi.archived_at, mi.chapter_metadata, mi.library_type_name, mi.audio_metadata, mi.tags_search, mi.contributors_search, mi.narrators_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, mi.source, mi.source_feed_id, mi.episode_guid, mi.feed_position, mi.season_number, mi.episode_number, mi.enclosure_url, mi.declared_duration_seconds, mi.transcript_url, mi.chapters_url, mi.hosts, mi.guests, mi.download_state, mi.download_error, mi.download_attempts FROM media_items mi
|
||||||
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND NOT EXISTS (
|
AND NOT EXISTS (
|
||||||
@@ -9435,6 +9454,7 @@ SELECT
|
|||||||
FROM media_items mi
|
FROM media_items mi
|
||||||
JOIN reading_history rh ON rh.media_item_id = mi.id
|
JOIN reading_history rh ON rh.media_item_id = mi.id
|
||||||
WHERE rh.user_id = $1
|
WHERE rh.user_id = $1
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
GROUP BY mi.id, mi.title, mi.author
|
GROUP BY mi.id, mi.title, mi.author
|
||||||
ORDER BY read_count DESC
|
ORDER BY read_count DESC
|
||||||
LIMIT $2
|
LIMIT $2
|
||||||
@@ -9658,6 +9678,7 @@ func (q *Queries) GetReadingSpeed(ctx context.Context, arg GetReadingSpeedParams
|
|||||||
const GetRecentlyAddedItems = `-- name: GetRecentlyAddedItems :many
|
const GetRecentlyAddedItems = `-- name: GetRecentlyAddedItems :many
|
||||||
SELECT mi.id, mi.library_id, mi.title, mi.subtitle, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.narrators, mi.authors, mi.authors_search, mi.publishers, mi.publishers_search, mi.abridged, mi.explicit, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.metadata_overrides, mi.missing_scan_count, mi.archived_at, mi.chapter_metadata, mi.library_type_name, mi.audio_metadata, mi.tags_search, mi.contributors_search, mi.narrators_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, mi.source, mi.source_feed_id, mi.episode_guid, mi.feed_position, mi.season_number, mi.episode_number, mi.enclosure_url, mi.declared_duration_seconds, mi.transcript_url, mi.chapters_url, mi.hosts, mi.guests, mi.download_state, mi.download_error, mi.download_attempts FROM media_items mi
|
SELECT mi.id, mi.library_id, mi.title, mi.subtitle, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.narrators, mi.authors, mi.authors_search, mi.publishers, mi.publishers_search, mi.abridged, mi.explicit, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.metadata_overrides, mi.missing_scan_count, mi.archived_at, mi.chapter_metadata, mi.library_type_name, mi.audio_metadata, mi.tags_search, mi.contributors_search, mi.narrators_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, mi.source, mi.source_feed_id, mi.episode_guid, mi.feed_position, mi.season_number, mi.episode_number, mi.enclosure_url, mi.declared_duration_seconds, mi.transcript_url, mi.chapters_url, mi.hosts, mi.guests, mi.download_state, mi.download_error, mi.download_attempts FROM media_items mi
|
||||||
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
ORDER BY mi.imported_at DESC NULLS LAST, mi.created_at DESC
|
ORDER BY mi.imported_at DESC NULLS LAST, mi.created_at DESC
|
||||||
@@ -9789,6 +9810,7 @@ INNER JOIN (
|
|||||||
ORDER BY media_item_id, last_read_at DESC
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
) rp ON rp.media_item_id = mi.id
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
@@ -10619,6 +10641,7 @@ SELECT
|
|||||||
FROM reading_history rh
|
FROM reading_history rh
|
||||||
JOIN media_items mi ON mi.id = rh.media_item_id
|
JOIN media_items mi ON mi.id = rh.media_item_id
|
||||||
WHERE rh.user_id = $1
|
WHERE rh.user_id = $1
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND COALESCE(rh.reading_session_end, rh.created_at) >= $2
|
AND COALESCE(rh.reading_session_end, rh.created_at) >= $2
|
||||||
AND COALESCE(rh.reading_session_end, rh.created_at) <= $3
|
AND COALESCE(rh.reading_session_end, rh.created_at) <= $3
|
||||||
AND ($4::uuid[] IS NULL OR mi.library_id = ANY($4::uuid[]))
|
AND ($4::uuid[] IS NULL OR mi.library_id = ANY($4::uuid[]))
|
||||||
@@ -12947,6 +12970,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
JOIN library_types lt ON l.library_type_id = lt.id
|
JOIN library_types lt ON l.library_type_id = lt.id
|
||||||
WHERE mi.archived_at IS NULL
|
WHERE mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts')
|
||||||
AND ($3::uuid[] IS NULL OR mi.library_id = ANY($3::uuid[]))
|
AND ($3::uuid[] IS NULL OR mi.library_id = ANY($3::uuid[]))
|
||||||
ORDER BY mi.created_at DESC LIMIT $1 OFFSET $2
|
ORDER BY mi.created_at DESC LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
@@ -13051,6 +13075,9 @@ type ListMediaItemsRow struct {
|
|||||||
LibraryTypeName_2 string `db:"library_type_name_2" json:"library_type_name_2"`
|
LibraryTypeName_2 string `db:"library_type_name_2" json:"library_type_name_2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Podcasts exclusion (plan §9): generic book surfaces never list
|
||||||
|
// podcast episodes (library_type_name is the denormalized, trigger/
|
||||||
|
// fetcher-maintained discriminator; NULL rows predate it and stay).
|
||||||
func (q *Queries) ListMediaItems(ctx context.Context, arg ListMediaItemsParams) ([]ListMediaItemsRow, error) {
|
func (q *Queries) ListMediaItems(ctx context.Context, arg ListMediaItemsParams) ([]ListMediaItemsRow, error) {
|
||||||
rows, err := q.db.Query(ctx, ListMediaItems, arg.Limit, arg.Offset, arg.VisibleLibraryIds)
|
rows, err := q.db.Query(ctx, ListMediaItems, arg.Limit, arg.Offset, arg.VisibleLibraryIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -13837,6 +13864,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
JOIN library_types lt ON l.library_type_id = lt.id
|
JOIN library_types lt ON l.library_type_id = lt.id
|
||||||
WHERE mi.archived_at IS NULL
|
WHERE mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.library_id = $1
|
AND mi.library_id = $1
|
||||||
ORDER BY
|
ORDER BY
|
||||||
CASE
|
CASE
|
||||||
@@ -15921,6 +15949,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||||
@@ -15981,6 +16010,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||||
@@ -16041,6 +16071,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||||
@@ -16101,6 +16132,7 @@ LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
|||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||||
AND (
|
AND (
|
||||||
mi.title ILIKE $3 OR
|
mi.title ILIKE $3 OR
|
||||||
@@ -16413,6 +16445,7 @@ LEFT JOIN reading_progress rp ON rp.media_item_id = mi.id AND rp.user_id = $1
|
|||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND ($2::uuid IS NULL
|
AND ($2::uuid IS NULL
|
||||||
OR mi.library_id = $2::uuid)
|
OR mi.library_id = $2::uuid)
|
||||||
-- Fuzzy author filter — per-element against the authors[] array (A2c
|
-- Fuzzy author filter — per-element against the authors[] array (A2c
|
||||||
@@ -17018,6 +17051,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
CROSS JOIN LATERAL unnest(mi.narrators) AS narrator
|
CROSS JOIN LATERAL unnest(mi.narrators) AS narrator
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||||
AND narrator IS NOT NULL
|
AND narrator IS NOT NULL
|
||||||
AND word_similarity($1, narrator) > 0.3
|
AND word_similarity($1, narrator) > 0.3
|
||||||
@@ -17075,6 +17109,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||||
@@ -17136,6 +17171,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
CROSS JOIN LATERAL unnest(mi.tags_search) AS tag
|
CROSS JOIN LATERAL unnest(mi.tags_search) AS tag
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||||
AND tag IS NOT NULL
|
AND tag IS NOT NULL
|
||||||
AND word_similarity($1, tag) > 0.3
|
AND word_similarity($1, tag) > 0.3
|
||||||
|
|||||||
@@ -180,12 +180,16 @@ SELECT * FROM media_items WHERE library_id = $1 AND file_path = $2;
|
|||||||
SELECT * FROM media_items WHERE id = $1;
|
SELECT * FROM media_items WHERE id = $1;
|
||||||
|
|
||||||
-- name: ListMediaItems :many
|
-- name: ListMediaItems :many
|
||||||
|
-- Podcasts exclusion (plan §9): generic book surfaces never list
|
||||||
|
-- podcast episodes (library_type_name is the denormalized, trigger/
|
||||||
|
-- fetcher-maintained discriminator; NULL rows predate it and stay).
|
||||||
SELECT mi.*, l.name as library_name, lt.name as library_type_name
|
SELECT mi.*, l.name as library_name, lt.name as library_type_name
|
||||||
FROM media_items mi
|
FROM media_items mi
|
||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
JOIN library_types lt ON l.library_type_id = lt.id
|
JOIN library_types lt ON l.library_type_id = lt.id
|
||||||
WHERE mi.archived_at IS NULL
|
WHERE mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts')
|
||||||
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR mi.library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR mi.library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
||||||
ORDER BY mi.created_at DESC LIMIT $1 OFFSET $2;
|
ORDER BY mi.created_at DESC LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
@@ -214,6 +218,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
JOIN library_types lt ON l.library_type_id = lt.id
|
JOIN library_types lt ON l.library_type_id = lt.id
|
||||||
WHERE mi.archived_at IS NULL
|
WHERE mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.library_id = sqlc.narg('library_id')
|
AND mi.library_id = sqlc.narg('library_id')
|
||||||
ORDER BY
|
ORDER BY
|
||||||
CASE
|
CASE
|
||||||
@@ -660,6 +665,7 @@ LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.na
|
|||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
AND (
|
AND (
|
||||||
mi.title ILIKE sqlc.narg('search_pattern') OR
|
mi.title ILIKE sqlc.narg('search_pattern') OR
|
||||||
@@ -750,6 +756,7 @@ LEFT JOIN reading_progress rp ON rp.media_item_id = mi.id AND rp.user_id = sqlc.
|
|||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL
|
AND (sqlc.narg('library_id')::uuid IS NULL
|
||||||
OR mi.library_id = sqlc.narg('library_id')::uuid)
|
OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
-- Fuzzy author filter — per-element against the authors[] array (A2c
|
-- Fuzzy author filter — per-element against the authors[] array (A2c
|
||||||
@@ -934,6 +941,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -953,6 +961,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -973,6 +982,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
CROSS JOIN LATERAL unnest(mi.tags_search) AS tag
|
CROSS JOIN LATERAL unnest(mi.tags_search) AS tag
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
AND tag IS NOT NULL
|
AND tag IS NOT NULL
|
||||||
AND word_similarity(sqlc.narg('search_query'), tag) > 0.3
|
AND word_similarity(sqlc.narg('search_query'), tag) > 0.3
|
||||||
@@ -990,6 +1000,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
CROSS JOIN LATERAL unnest(mi.narrators) AS narrator
|
CROSS JOIN LATERAL unnest(mi.narrators) AS narrator
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
AND narrator IS NOT NULL
|
AND narrator IS NOT NULL
|
||||||
AND word_similarity(sqlc.narg('search_query'), narrator) > 0.3
|
AND word_similarity(sqlc.narg('search_query'), narrator) > 0.3
|
||||||
@@ -1006,6 +1017,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1025,6 +1037,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1063,6 +1076,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
CROSS JOIN LATERAL unnest(mi.authors) AS a
|
CROSS JOIN LATERAL unnest(mi.authors) AS a
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1131,6 +1145,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
CROSS JOIN LATERAL unnest(mi.publishers) AS p
|
CROSS JOIN LATERAL unnest(mi.publishers) AS p
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1198,6 +1213,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
CROSS JOIN LATERAL unnest(mi.narrators) AS n
|
CROSS JOIN LATERAL unnest(mi.narrators) AS n
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1267,6 +1283,7 @@ JOIN libraries l ON mi.library_id = l.id
|
|||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
CROSS JOIN LATERAL unnest(mi.tags) AS t
|
CROSS JOIN LATERAL unnest(mi.tags) AS t
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1335,6 +1352,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1406,6 +1424,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1477,6 +1496,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1548,6 +1568,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1615,6 +1636,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1682,6 +1704,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1749,6 +1772,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1820,6 +1844,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1891,6 +1916,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -1962,6 +1988,7 @@ FROM media_items mi
|
|||||||
JOIN libraries l ON mi.library_id = l.id
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
|
||||||
WHERE COALESCE(lv.is_visible, true) = true
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
@@ -2606,6 +2633,7 @@ SELECT
|
|||||||
FROM media_items mi
|
FROM media_items mi
|
||||||
JOIN reading_history rh ON rh.media_item_id = mi.id
|
JOIN reading_history rh ON rh.media_item_id = mi.id
|
||||||
WHERE rh.user_id = $1
|
WHERE rh.user_id = $1
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
GROUP BY mi.id, mi.title, mi.author
|
GROUP BY mi.id, mi.title, mi.author
|
||||||
ORDER BY read_count DESC
|
ORDER BY read_count DESC
|
||||||
LIMIT $2;
|
LIMIT $2;
|
||||||
@@ -2668,6 +2696,7 @@ SELECT
|
|||||||
FROM reading_history rh
|
FROM reading_history rh
|
||||||
JOIN media_items mi ON mi.id = rh.media_item_id
|
JOIN media_items mi ON mi.id = rh.media_item_id
|
||||||
WHERE rh.user_id = sqlc.arg('user_id')
|
WHERE rh.user_id = sqlc.arg('user_id')
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND COALESCE(rh.reading_session_end, rh.created_at) >= sqlc.arg('start_time')
|
AND COALESCE(rh.reading_session_end, rh.created_at) >= sqlc.arg('start_time')
|
||||||
AND COALESCE(rh.reading_session_end, rh.created_at) <= sqlc.arg('end_time')
|
AND COALESCE(rh.reading_session_end, rh.created_at) <= sqlc.arg('end_time')
|
||||||
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR mi.library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR mi.library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
||||||
@@ -3964,6 +3993,7 @@ INNER JOIN (
|
|||||||
ORDER BY media_item_id, last_read_at DESC
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
) rp ON rp.media_item_id = mi.id
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
@@ -3972,6 +4002,7 @@ LIMIT sqlc.narg('limit');
|
|||||||
-- name: GetRecentlyAddedItems :many
|
-- name: GetRecentlyAddedItems :many
|
||||||
SELECT mi.* FROM media_items mi
|
SELECT mi.* FROM media_items mi
|
||||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
ORDER BY mi.imported_at DESC NULLS LAST, mi.created_at DESC
|
ORDER BY mi.imported_at DESC NULLS LAST, mi.created_at DESC
|
||||||
@@ -3987,6 +4018,7 @@ INNER JOIN (
|
|||||||
ORDER BY media_item_id, last_read_at DESC
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
) rp ON rp.media_item_id = mi.id
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
@@ -3995,6 +4027,7 @@ LIMIT sqlc.narg('limit');
|
|||||||
-- name: GetNotStartedItems :many
|
-- name: GetNotStartedItems :many
|
||||||
SELECT mi.* FROM media_items mi
|
SELECT mi.* FROM media_items mi
|
||||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
AND NOT EXISTS (
|
AND NOT EXISTS (
|
||||||
@@ -4081,6 +4114,7 @@ SELECT mi.* FROM media_items mi
|
|||||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||||
AND mi.archived_at IS NULL
|
AND mi.archived_at IS NULL
|
||||||
AND mi.missing_scan_count = 0
|
AND mi.missing_scan_count = 0
|
||||||
|
AND (mi.library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9: collections picker)
|
||||||
ORDER BY mi.created_at DESC;
|
ORDER BY mi.created_at DESC;
|
||||||
|
|
||||||
-- name: GetDistinctSeries :many
|
-- name: GetDistinctSeries :many
|
||||||
@@ -4113,6 +4147,7 @@ WHERE (sqlc.narg('library_id')::uuid IS NULL OR library_id = sqlc.narg('library_
|
|||||||
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
||||||
AND archived_at IS NULL
|
AND archived_at IS NULL
|
||||||
AND missing_scan_count = 0
|
AND missing_scan_count = 0
|
||||||
|
AND (library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9: series pages)
|
||||||
AND series IS NOT NULL AND series != ''
|
AND series IS NOT NULL AND series != ''
|
||||||
-- Multi-field search (series page): the query matches the series
|
-- Multi-field search (series page): the query matches the series
|
||||||
-- NAME, any book's AUTHOR, or any book's TITLE in the group. It is
|
-- NAME, any book's AUTHOR, or any book's TITLE in the group. It is
|
||||||
@@ -4173,6 +4208,7 @@ SELECT COUNT(*)::int FROM (
|
|||||||
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
AND (sqlc.narg('visible_library_ids')::uuid[] IS NULL OR library_id = ANY(sqlc.narg('visible_library_ids')::uuid[]))
|
||||||
AND archived_at IS NULL
|
AND archived_at IS NULL
|
||||||
AND missing_scan_count = 0
|
AND missing_scan_count = 0
|
||||||
|
AND (library_type_name IS DISTINCT FROM 'podcasts') -- podcasts exclusion (plan §9: series pages)
|
||||||
AND series IS NOT NULL AND series != ''
|
AND series IS NOT NULL AND series != ''
|
||||||
AND (sqlc.narg('series_query')::text IS NULL OR
|
AND (sqlc.narg('series_query')::text IS NULL OR
|
||||||
series ILIKE '%' || sqlc.narg('series_query')::text || '%'
|
series ILIKE '%' || sqlc.narg('series_query')::text || '%'
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bookhoard/internal/database"
|
"bookhoard/internal/database"
|
||||||
"bookhoard/internal/opds"
|
"bookhoard/internal/opds"
|
||||||
@@ -18,6 +17,24 @@ import (
|
|||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// opdsVisibleLibraries is GetUserVisibleLibraries minus podcasts-type
|
||||||
|
// libraries (plan D16): feeds are not OPDS inventory day one — they
|
||||||
|
// surface via the /podcasts endpoints only.
|
||||||
|
func opdsVisibleLibraries(ctx context.Context, q *database.Queries, userID pgtype.UUID) ([]database.GetUserVisibleLibrariesRow, error) {
|
||||||
|
libs, err := q.GetUserVisibleLibraries(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]database.GetUserVisibleLibrariesRow, 0, len(libs))
|
||||||
|
for _, lib := range libs {
|
||||||
|
if lib.TypeName == services.LibraryTypePodcasts {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, lib)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
type OPDSHandler struct {
|
type OPDSHandler struct {
|
||||||
db *database.Queries
|
db *database.Queries
|
||||||
libraryService *services.LibraryService
|
libraryService *services.LibraryService
|
||||||
@@ -209,7 +226,7 @@ func (h *OPDSHandler) GetDeviceCatalog(c *echo.Context) error {
|
|||||||
|
|
||||||
// Get user's visible libraries
|
// Get user's visible libraries
|
||||||
userID := device.UserID.Bytes
|
userID := device.UserID.Bytes
|
||||||
libraries, err := h.db.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userID, Valid: true})
|
libraries, err := opdsVisibleLibraries(c.Request().Context(), h.db, pgtype.UUID{Bytes: userID, Valid: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
||||||
}
|
}
|
||||||
@@ -318,7 +335,7 @@ func (h *OPDSHandler) serveAllBooksCatalog(c *echo.Context) error {
|
|||||||
|
|
||||||
// Get user's visible libraries
|
// Get user's visible libraries
|
||||||
userID := device.UserID.Bytes
|
userID := device.UserID.Bytes
|
||||||
libraries, err := h.db.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userID, Valid: true})
|
libraries, err := opdsVisibleLibraries(c.Request().Context(), h.db, pgtype.UUID{Bytes: userID, Valid: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
||||||
}
|
}
|
||||||
@@ -570,7 +587,7 @@ func (h *OPDSHandler) GetLibraryCatalog(c *echo.Context) error {
|
|||||||
|
|
||||||
// The library must be visible to the device owner.
|
// The library must be visible to the device owner.
|
||||||
userID := device.UserID.Bytes
|
userID := device.UserID.Bytes
|
||||||
libraries, err := h.db.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userID, Valid: true})
|
libraries, err := opdsVisibleLibraries(c.Request().Context(), h.db, pgtype.UUID{Bytes: userID, Valid: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
||||||
}
|
}
|
||||||
@@ -677,7 +694,7 @@ func (h *OPDSHandler) SearchDeviceCatalog(c *echo.Context) error {
|
|||||||
|
|
||||||
// Get user's visible libraries
|
// Get user's visible libraries
|
||||||
userID := device.UserID.Bytes
|
userID := device.UserID.Bytes
|
||||||
libraries, err := h.db.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userID, Valid: true})
|
libraries, err := opdsVisibleLibraries(c.Request().Context(), h.db, pgtype.UUID{Bytes: userID, Valid: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
return c.XML(http.StatusInternalServerError, opds.NewErrorFeed("Failed to get libraries"))
|
||||||
}
|
}
|
||||||
@@ -818,7 +835,7 @@ func (h *OPDSHandler) DownloadBook(c *echo.Context) error {
|
|||||||
|
|
||||||
// Get user's visible libraries
|
// Get user's visible libraries
|
||||||
userID := device.UserID.Bytes
|
userID := device.UserID.Bytes
|
||||||
libraries, err := h.db.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userID, Valid: true})
|
libraries, err := opdsVisibleLibraries(c.Request().Context(), h.db, pgtype.UUID{Bytes: userID, Valid: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to get libraries"})
|
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to get libraries"})
|
||||||
}
|
}
|
||||||
@@ -974,7 +991,7 @@ func (h *OPDSHandler) GetCoverImage(c *echo.Context) error {
|
|||||||
|
|
||||||
// Get user's visible libraries
|
// Get user's visible libraries
|
||||||
userID := device.UserID.Bytes
|
userID := device.UserID.Bytes
|
||||||
libraries, err := h.db.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userID, Valid: true})
|
libraries, err := opdsVisibleLibraries(c.Request().Context(), h.db, pgtype.UUID{Bytes: userID, Valid: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to get libraries"})
|
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to get libraries"})
|
||||||
}
|
}
|
||||||
@@ -1115,7 +1132,7 @@ func (h *OPDSHandler) ListFormats(c *echo.Context) error {
|
|||||||
|
|
||||||
// Get user's visible libraries
|
// Get user's visible libraries
|
||||||
userID := device.UserID.Bytes
|
userID := device.UserID.Bytes
|
||||||
libraries, err := h.db.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userID, Valid: true})
|
libraries, err := opdsVisibleLibraries(c.Request().Context(), h.db, pgtype.UUID{Bytes: userID, Valid: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to get libraries"})
|
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to get libraries"})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,6 +296,13 @@ func (s *StatisticsService) UserSummary(ctx context.Context, userID pgtype.UUID,
|
|||||||
entry.Sessions++
|
entry.Sessions++
|
||||||
summary.SessionCount++
|
summary.SessionCount++
|
||||||
|
|
||||||
|
// Podcast episodes (plan D15): sessions flow into time-spent
|
||||||
|
// (the minutes/pages/sessions math above already ran), but an
|
||||||
|
// episode is not a book — never enters BooksActive/BooksFinished.
|
||||||
|
if strings.EqualFold(strings.TrimSpace(sess.LibraryTypeName.String), "podcasts") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
mediaUUID, err := uuid.FromBytes(sess.MediaItemID.Bytes[0:16])
|
mediaUUID, err := uuid.FromBytes(sess.MediaItemID.Bytes[0:16])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
key := mediaUUID.String()
|
key := mediaUUID.String()
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ templ LibrarySwitcher(libData []LibraryData, currentLibraryID string, title stri
|
|||||||
>
|
>
|
||||||
<option value="">All Libraries ({ TotalMediaCount(libData) })</option>
|
<option value="">All Libraries ({ TotalMediaCount(libData) })</option>
|
||||||
for _, lib := range libData {
|
for _, lib := range libData {
|
||||||
|
// Podcasts libraries are feed-managed (plan D13): never
|
||||||
|
// in the book-library switcher — feeds surface via
|
||||||
|
// /podcasts only. Presentation-layer filter so every
|
||||||
|
// switcher consumer is correct at once.
|
||||||
|
if !IsBookLibrary(lib.TypeName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if lib.Offline {
|
if lib.Offline {
|
||||||
if lib.ID == currentLibraryID {
|
if lib.ID == currentLibraryID {
|
||||||
<option value={ lib.ID } selected>{ lib.Name } ({ lib.MediaCount }) — storage offline</option>
|
<option value={ lib.ID } selected>{ lib.Name } ({ lib.MediaCount }) — storage offline</option>
|
||||||
|
|||||||
@@ -82,187 +82,201 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
for _, lib := range libData {
|
for _, lib := range libData {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !IsBookLibrary(lib.TypeName) {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "continue")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
if lib.Offline {
|
if lib.Offline {
|
||||||
if lib.ID == currentLibraryID {
|
if lib.ID == currentLibraryID {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<option value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<option value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 31, Col: 29}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 38, Col: 29}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" selected>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" selected>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 31, Col: 51}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 38, Col: 51}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, " (")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " (")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 31, Col: 71}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 38, Col: 71}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, ") — storage offline</option>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, ") — storage offline</option>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<option value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<option value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 33, Col: 29}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 40, Col: 29}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 33, Col: 42}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 40, Col: 42}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " (")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " (")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var9 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 33, Col: 62}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 40, Col: 62}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, ") — storage offline</option>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, ") — storage offline</option>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if lib.ID == currentLibraryID {
|
} else if lib.ID == currentLibraryID {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<option value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<option value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var10 string
|
var templ_7745c5c3_Var10 string
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 36, Col: 28}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 43, Col: 28}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" selected>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" selected>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var11 string
|
var templ_7745c5c3_Var11 string
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 36, Col: 50}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 43, Col: 50}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " (")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, " (")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var12 string
|
var templ_7745c5c3_Var12 string
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 36, Col: 70}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 43, Col: 70}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, ")</option>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, ")</option>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<option value=\"")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<option value=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var13 string
|
var templ_7745c5c3_Var13 string
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 38, Col: 28}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 45, Col: 28}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var14 string
|
var templ_7745c5c3_Var14 string
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 38, Col: 41}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 45, Col: 41}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " (")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " (")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var15 string
|
var templ_7745c5c3_Var15 string
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 38, Col: 61}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 45, Col: 61}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, ")</option>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, ")</option>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</select></div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</select></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -273,7 +287,7 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(actions) > 0 {
|
if len(actions) > 0 {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<div class=\"flex items-center gap-2\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<div class=\"flex items-center gap-2\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -283,12 +297,12 @@ func LibrarySwitcher(libData []LibraryData, currentLibraryID string, title strin
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><div id=\"loading-spinner\" class=\"hidden fixed inset-0 flex items-center justify-center z-50\" style=\"background-color: color-mix(in srgb, var(--bg-primary) 55%, transparent);\"><div class=\"animate-spin rounded-full h-12 w-12 border-b-2\" style=\"border-color: var(--accent);\"></div></div></div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</div><div id=\"loading-spinner\" class=\"hidden fixed inset-0 flex items-center justify-center z-50\" style=\"background-color: color-mix(in srgb, var(--bg-primary) 55%, transparent);\"><div class=\"animate-spin rounded-full h-12 w-12 border-b-2\" style=\"border-color: var(--accent);\"></div></div></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -317,7 +331,7 @@ func DashboardActions() templ.Component {
|
|||||||
templ_7745c5c3_Var16 = templ.NopComponent
|
templ_7745c5c3_Var16 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<button data-action=\"open-dashboard-settings\" class=\"icon-btn\" title=\"Customize Dashboard\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<button data-action=\"open-dashboard-settings\" class=\"icon-btn\" title=\"Customize Dashboard\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -325,7 +339,7 @@ func DashboardActions() templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</button> <button data-action=\"reload-page\" class=\"icon-btn\" title=\"Refresh\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</button> <button data-action=\"reload-page\" class=\"icon-btn\" title=\"Refresh\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -333,7 +347,7 @@ func DashboardActions() templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</button>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</button>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -117,10 +117,10 @@ func MediaBadgeIcon(badge string) string {
|
|||||||
func TotalMediaCount(libs []LibraryData) int64 {
|
func TotalMediaCount(libs []LibraryData) int64 {
|
||||||
var total int64
|
var total int64
|
||||||
for _, l := range libs {
|
for _, l := range libs {
|
||||||
// Unmounted storage contributes nothing visible: an offline
|
// Podcasts libraries are feed-managed (plan D13): not book-
|
||||||
// library's holdings stay in the database but out of the UI
|
// library inventory — excluded here so switcher totals stay
|
||||||
// until its folders return.
|
// consistent with the switcher's podcast-filtered option list.
|
||||||
if l.Offline {
|
if l.Offline || l.TypeName == "podcasts" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
total += l.MediaCount
|
total += l.MediaCount
|
||||||
@@ -128,6 +128,13 @@ func TotalMediaCount(libs []LibraryData) int64 {
|
|||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBookLibrary reports whether a library type participates in the
|
||||||
|
// generic book surfaces (switcher, All Books, series). Podcasts are
|
||||||
|
// feed-managed and surface via /podcasts only (plan §9/D13).
|
||||||
|
func IsBookLibrary(typeName string) bool {
|
||||||
|
return typeName != "podcasts"
|
||||||
|
}
|
||||||
|
|
||||||
func uuidToString(id pgtype.UUID) string {
|
func uuidToString(id pgtype.UUID) string {
|
||||||
if !id.Valid {
|
if !id.Valid {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
Reference in new issue
Block a user