157 lines
4.9 KiB
Go
157 lines
4.9 KiB
Go
package catalog
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const episodeCatalogSelectBody = `(
|
|
SELECT
|
|
e.content_id,
|
|
e.air_date AS episode_air_date,
|
|
'episode'::text AS type,
|
|
COALESCE(NULLIF(BTRIM(e.title), ''), 'Episode ' || e.episode_number::text) AS title,
|
|
COALESCE(NULLIF(BTRIM(e.title), ''), 'Episode ' || e.episode_number::text) AS sort_title,
|
|
LOWER(COALESCE(NULLIF(BTRIM(e.title), ''), 'Episode ' || e.episode_number::text)) AS sort_key,
|
|
COALESCE(NULLIF(BTRIM(e.default_metadata_language), ''), COALESCE(si.default_metadata_language, '')) AS default_metadata_language,
|
|
''::text AS original_title,
|
|
COALESCE(si.year, EXTRACT(YEAR FROM e.air_date)::integer, 0) AS year,
|
|
COALESCE(si.genres, '{}'::text[]) AS genres,
|
|
COALESCE(si.content_rating, '') AS content_rating,
|
|
COALESCE(NULLIF(e.runtime, 0), COALESCE(si.runtime, 0)) AS runtime,
|
|
COALESCE(e.overview, '') AS overview,
|
|
''::text AS tagline,
|
|
e.rating_imdb,
|
|
e.rating_tmdb,
|
|
NULL::integer AS rating_rt_critic,
|
|
NULL::integer AS rating_rt_audience,
|
|
COALESCE(e.imdb_id, '') AS imdb_id,
|
|
COALESCE(e.tmdb_id, '') AS tmdb_id,
|
|
COALESCE(e.tvdb_id, '') AS tvdb_id,
|
|
COALESCE(NULLIF(s.poster_path, ''), NULLIF(si.poster_path, ''), NULLIF(e.still_path, ''), '') AS poster_path,
|
|
COALESCE(NULLIF(s.poster_thumbhash, ''), NULLIF(si.poster_thumbhash, ''), NULLIF(e.still_thumbhash, ''), '') AS poster_thumbhash,
|
|
COALESCE(si.backdrop_path, '') AS backdrop_path,
|
|
COALESCE(si.backdrop_thumbhash, '') AS backdrop_thumbhash,
|
|
COALESCE(si.logo_path, '') AS logo_path,
|
|
COALESCE(e.metadata_s3_path, '') AS metadata_s3_path,
|
|
COALESCE(e.metadata_etag, '') AS metadata_etag,
|
|
NULL::integer AS season_count,
|
|
COALESCE(si.studios, '{}'::text[]) AS studios,
|
|
COALESCE(si.networks, '{}'::text[]) AS networks,
|
|
COALESCE(si.countries, '{}'::text[]) AS countries,
|
|
COALESCE(si.keywords, '{}'::text[]) AS keywords,
|
|
COALESCE(si.original_language, '') AS original_language,
|
|
CASE WHEN e.air_date IS NULL THEN NULL ELSE e.air_date::text END AS release_date,
|
|
NULL::text AS first_air_date,
|
|
NULL::text AS last_air_date,
|
|
e.air_date AS last_air_date_at,
|
|
si.air_time,
|
|
COALESCE(si.show_status, '') AS show_status,
|
|
si.matched_at,
|
|
si.last_refreshed,
|
|
si.refresh_failures,
|
|
si.episode_metadata_incomplete,
|
|
si.episode_metadata_last_checked_at,
|
|
COALESCE(si.locked_fields, '{}'::integer[]) AS locked_fields,
|
|
COALESCE(NULLIF(BTRIM(si.status), ''), 'matched') AS status,
|
|
e.created_at,
|
|
e.updated_at
|
|
FROM episodes e
|
|
JOIN media_items si ON si.content_id = e.series_id
|
|
LEFT JOIN seasons s ON s.content_id = e.season_id
|
|
WHERE %s
|
|
) mi`
|
|
|
|
const episodeCatalogActiveLibraryExists = `EXISTS (
|
|
SELECT 1
|
|
FROM episode_libraries el
|
|
WHERE el.episode_id = e.content_id
|
|
)`
|
|
|
|
var episodeCatalogBaseRelation = fmt.Sprintf(episodeCatalogSelectBody, episodeCatalogActiveLibraryExists)
|
|
|
|
func isEpisodeCatalogScope(scope string) bool {
|
|
return scope == "episode"
|
|
}
|
|
|
|
func catalogBaseRelationForScope(scope string) string {
|
|
if isEpisodeCatalogScope(scope) {
|
|
return episodeCatalogBaseRelation
|
|
}
|
|
return "media_items mi"
|
|
}
|
|
|
|
func episodeCatalogBaseRelationForLibraries(
|
|
allowedLibraryIDs []int,
|
|
disabledLibraryIDs []int,
|
|
argIdx int,
|
|
) (string, []any, bool) {
|
|
if len(allowedLibraryIDs) == 0 && len(disabledLibraryIDs) == 0 {
|
|
return episodeCatalogBaseRelation, nil, false
|
|
}
|
|
|
|
libraryConditions := []string{
|
|
"el.episode_id = e.content_id",
|
|
}
|
|
args := make([]any, 0, len(allowedLibraryIDs)+len(disabledLibraryIDs))
|
|
|
|
if len(allowedLibraryIDs) > 0 {
|
|
libraryConditions = append(
|
|
libraryConditions,
|
|
fmt.Sprintf("el.media_folder_id = ANY($%d)", argIdx),
|
|
)
|
|
args = append(args, allowedLibraryIDs)
|
|
argIdx++
|
|
}
|
|
|
|
if len(disabledLibraryIDs) > 0 {
|
|
libraryConditions = append(
|
|
libraryConditions,
|
|
fmt.Sprintf("NOT (el.media_folder_id = ANY($%d))", argIdx),
|
|
)
|
|
args = append(args, disabledLibraryIDs)
|
|
argIdx++
|
|
}
|
|
|
|
relation := fmt.Sprintf(
|
|
episodeCatalogSelectBody,
|
|
fmt.Sprintf(
|
|
`EXISTS (
|
|
SELECT 1
|
|
FROM episode_libraries el
|
|
WHERE %s
|
|
)`,
|
|
strings.Join(libraryConditions, "\n\t\t AND "),
|
|
),
|
|
)
|
|
return relation, args, true
|
|
}
|
|
|
|
func catalogLibraryContentExprForScope(scope, alias string) string {
|
|
if isEpisodeCatalogScope(scope) {
|
|
return alias + ".content_id"
|
|
}
|
|
return alias + ".content_id"
|
|
}
|
|
|
|
func catalogLibraryMembershipTableAndKeyForScope(scope string) (string, string) {
|
|
if isEpisodeCatalogScope(scope) {
|
|
return "episode_libraries", "episode_id"
|
|
}
|
|
return "media_item_libraries", "content_id"
|
|
}
|
|
|
|
func catalogMediaFileJoinConditionForScope(scope, mediaFileAlias, itemAlias string) string {
|
|
if isEpisodeCatalogScope(scope) {
|
|
return mediaFileAlias + ".episode_id = " + itemAlias + ".content_id"
|
|
}
|
|
return mediaFileAlias + ".content_id = " + itemAlias + ".content_id"
|
|
}
|
|
|
|
func catalogMediaFileGroupExprForScope(scope, mediaFileAlias string) string {
|
|
if isEpisodeCatalogScope(scope) {
|
|
return mediaFileAlias + ".episode_id"
|
|
}
|
|
return mediaFileAlias + ".content_id"
|
|
}
|