MCPcopy Create free account
hub / github.com/Dispatcharr/Dispatcharr / _resolve_poster_for_program

Function _resolve_poster_for_program

apps/channels/tasks.py:2986–3191  ·  view source on GitHub ↗

Resolve poster URL and/or Logo id for a recording program. Callers should enrich the program dict via _match_epg_program_by_timeslot before invoking this function so that EPG data is already available. Pipeline: EPG images → VOD logo → TMDB/OMDb → TVMaze/iTunes → direct program fie

(channel_name, program, channel_logo_id=None)

Source from the content-addressed store, hash-verified

2984 _ws('error', {"reason": str(e)})
2985 return f"error:{e}"
2986def _resolve_poster_for_program(channel_name, program, channel_logo_id=None):
2987 """Resolve poster URL and/or Logo id for a recording program.
2988
2989 Callers should enrich the program dict via _match_epg_program_by_timeslot
2990 before invoking this function so that EPG data is already available.
2991
2992 Pipeline: EPG images → VOD logo → TMDB/OMDb → TVMaze/iTunes →
2993 direct program fields → Logo table → Logo creation → channel logo.
2994 Returns (poster_logo_id, poster_url) where either may be None.
2995 """
2996 poster_logo_id = None
2997 poster_url = None
2998 epg_props = None
2999
3000 _title = ((program.get("title") if isinstance(program, dict) else None) or "").strip() or None
3001
3002 # Guard: if the "title" is really just the channel name (common when EPG
3003 # has no real program data), don't use it for external API searches —
3004 # those queries produce false-positive artwork from unrelated shows.
3005 _title_is_channel_name = False
3006 if _title and channel_name:
3007 def _norm_channel(s):
3008 return s.lower().replace("*", "").replace("-", " ").strip()
3009 _title_is_channel_name = _norm_channel(_title) == _norm_channel(channel_name)
3010
3011 # Stage 1: EPG Program images/icon (with URL validation)
3012 try:
3013 from apps.epg.models import ProgramData
3014 prog_id = program.get("id") if isinstance(program, dict) else None
3015 if prog_id:
3016 epg_program = ProgramData.objects.filter(id=prog_id).only("custom_properties").first()
3017 if epg_program and epg_program.custom_properties:
3018 epg_props = epg_program.custom_properties or {}
3019 poster_url = _pick_best_image_from_epg_props(epg_props)
3020 if poster_url and not _validate_url(poster_url):
3021 poster_url = None
3022 if not poster_url:
3023 icon = epg_props.get("icon")
3024 if isinstance(icon, str) and icon and _validate_url(icon):
3025 poster_url = icon
3026 except Exception:
3027 pass
3028
3029 # Stage 2: VOD logo fallback by title
3030 if not poster_url and not poster_logo_id and _title and not _title_is_channel_name:
3031 try:
3032 from apps.vod.models import Movie, Series
3033 vod_logo = None
3034 movie = Movie.objects.filter(name__iexact=_title).select_related("logo").first()
3035 if movie and movie.logo:
3036 vod_logo = movie.logo
3037 if not vod_logo:
3038 series = Series.objects.filter(name__iexact=_title).select_related("logo").first()
3039 if series and series.logo:
3040 vod_logo = series.logo
3041 if vod_logo:
3042 poster_logo_id = vod_logo.id
3043 except Exception:

Callers 4

_callMethod · 0.90
run_recordingFunction · 0.85
_background_refreshMethod · 0.85

Calls 6

_norm_channelFunction · 0.85
_validate_urlFunction · 0.85
extendMethod · 0.80
getMethod · 0.45
filterMethod · 0.45

Tested by 1

_callMethod · 0.72