mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-09-19 23:52:10 +02:00
Fix movetv epg
This commit is contained in:
@@ -31,7 +31,41 @@ from .constants import MoveTVConfig
|
||||
|
||||
|
||||
class MoveTvEpgManager:
|
||||
"""Fetches and normalises EPG data from the MoveTV API."""
|
||||
"""
|
||||
Fetches and normalises EPG data from the MoveTV API.
|
||||
|
||||
Returns programme objects with the following fields:
|
||||
|
||||
Core identifiers:
|
||||
epg_id, schedule_id, live_id, live_name, content_id
|
||||
|
||||
Title & Description:
|
||||
title (cleaned), original_title, plot, episode_title
|
||||
|
||||
Episode Info (parsed from title):
|
||||
season_num, episode_num, has_episode_info
|
||||
|
||||
Time:
|
||||
start, end (ISO-8601 UTC strings)
|
||||
start_ms, end_ms (millisecond timestamps)
|
||||
|
||||
Genre & Categories:
|
||||
genre (name), genre_id (numeric)
|
||||
categories (list of names)
|
||||
category_ids (list of numeric IDs)
|
||||
category_images (list of icon URLs)
|
||||
|
||||
Credits:
|
||||
director, cast (list), producer
|
||||
|
||||
Metadata:
|
||||
year, rating
|
||||
|
||||
Images:
|
||||
thumbnail (background fanart) - legacy
|
||||
images dict containing any of:
|
||||
background, icon, poster, square_logo, poster_mark, original_title_logo
|
||||
"""
|
||||
|
||||
def __init__(self, authenticator: Any) -> None:
|
||||
"""
|
||||
@@ -338,38 +372,71 @@ class MoveTvEpgManager:
|
||||
"""
|
||||
Normalise raw API programme objects into a consistent internal format.
|
||||
|
||||
Field mapping
|
||||
-------------
|
||||
title ← item["title"]
|
||||
plot ← item["epgDesc"]
|
||||
start ← item["start"] (ms epoch → ISO-8601 UTC)
|
||||
end ← item["end"] (ms epoch → ISO-8601 UTC)
|
||||
start_ms ← item["start"] (raw ms, kept for easy sorting / math)
|
||||
end_ms ← item["end"] (raw ms)
|
||||
genre ← item["tagInfo"]["name"] (e.g. "SERIJA", "INFO")
|
||||
categories ← list of category name strings
|
||||
thumbnail ← item["picture"]["background"] (absolute URL)
|
||||
director ← item["director"]
|
||||
cast ← item["actor"] split on ","
|
||||
year ← item["year"]
|
||||
rating ← item["rating"] (parental-advisory integer)
|
||||
epg_id ← item["epgId"]
|
||||
schedule_id ← item["scheduleId"]
|
||||
live_id ← item["liveId"]
|
||||
live_name ← item["liveName"]
|
||||
Field mapping (enhanced)
|
||||
------------------------
|
||||
Basic info:
|
||||
epg_id, schedule_id, live_id, live_name, content_id
|
||||
title, original_title, plot
|
||||
|
||||
Time:
|
||||
start, end (ISO-8601 UTC)
|
||||
start_ms, end_ms (raw milliseconds)
|
||||
|
||||
Episode (parsed from title):
|
||||
season_num, episode_num
|
||||
episode_title (title without season/episode suffix)
|
||||
|
||||
Genre & Categories:
|
||||
genre (name from tagInfo)
|
||||
genre_id (numeric from tagInfo)
|
||||
categories (list of category names)
|
||||
category_ids (list of category IDs)
|
||||
category_images (list of category icon URLs)
|
||||
|
||||
Credits:
|
||||
director, cast (list), producer
|
||||
|
||||
Metadata:
|
||||
year, rating
|
||||
|
||||
Images (all available):
|
||||
thumbnail (background) - primary for backward compatibility
|
||||
images dict containing:
|
||||
- background (wide fanart)
|
||||
- icon (small channel/program icon)
|
||||
- poster (movie/show poster)
|
||||
- square_logo (square logo)
|
||||
- poster_mark (promotional image)
|
||||
- original_title_logo (title logo)
|
||||
"""
|
||||
import re
|
||||
|
||||
parsed: List[Dict[str, Any]] = []
|
||||
|
||||
for item in items:
|
||||
start_ms: Optional[int] = item.get("start")
|
||||
end_ms: Optional[int] = item.get("end")
|
||||
end_ms: Optional[int] = item.get("end")
|
||||
|
||||
categories: List[str] = [
|
||||
cat["name"]
|
||||
for cat in item.get("categories", [])
|
||||
if cat.get("name")
|
||||
]
|
||||
# ------------------------------------------------------------
|
||||
# Parse categories with their IDs and images
|
||||
# ------------------------------------------------------------
|
||||
categories = []
|
||||
category_ids = []
|
||||
category_images = []
|
||||
|
||||
for cat in item.get("categories", []):
|
||||
if cat.get("name"):
|
||||
categories.append(cat["name"])
|
||||
if cat.get("categoryId"):
|
||||
category_ids.append(cat["categoryId"])
|
||||
if cat.get("picture", {}).get("icon"):
|
||||
category_images.append(
|
||||
MoveTVConfig.build_image_url(cat["picture"]["icon"])
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Parse cast (split by comma)
|
||||
# ------------------------------------------------------------
|
||||
actor_raw: Optional[str] = item.get("actor")
|
||||
cast: List[str] = (
|
||||
[a.strip() for a in actor_raw.split(",") if a.strip()]
|
||||
@@ -377,28 +444,109 @@ class MoveTvEpgManager:
|
||||
else []
|
||||
)
|
||||
|
||||
parsed.append(
|
||||
{
|
||||
"epg_id": item.get("epgId"),
|
||||
"schedule_id": item.get("scheduleId"),
|
||||
"live_id": item.get("liveId"),
|
||||
"live_name": item.get("liveName"),
|
||||
"title": item.get("title"),
|
||||
"plot": item.get("epgDesc"),
|
||||
"start": self._ms_to_utc_str(start_ms),
|
||||
"end": self._ms_to_utc_str(end_ms),
|
||||
"start_ms": start_ms,
|
||||
"end_ms": end_ms,
|
||||
"genre": item.get("tagInfo", {}).get("name"),
|
||||
"categories": categories,
|
||||
"rating": item.get("rating", 0),
|
||||
"year": item.get("year"),
|
||||
"director": item.get("director"),
|
||||
"cast": cast,
|
||||
"thumbnail": MoveTVConfig.build_image_url(
|
||||
item.get("picture", {}).get("background")
|
||||
),
|
||||
}
|
||||
)
|
||||
# ------------------------------------------------------------
|
||||
# Parse season and episode from title
|
||||
# Supports multiple patterns:
|
||||
# - "Title S6:E2" (most common)
|
||||
# - "Title S6E2"
|
||||
# - "Title S6 E2"
|
||||
# - "Title (S6, E2)"
|
||||
# - "Title - Season 6 Episode 2"
|
||||
# ------------------------------------------------------------
|
||||
raw_title = item.get("title", "")
|
||||
title = raw_title
|
||||
season_num = None
|
||||
episode_num = None
|
||||
|
||||
# Pattern 1: S6:E2 (MoveTV format)
|
||||
match = re.search(r'S(\d+):E(\d+)', title, re.IGNORECASE)
|
||||
if not match:
|
||||
# Pattern 2: S6E2
|
||||
match = re.search(r'S(\d+)E(\d+)', title, re.IGNORECASE)
|
||||
if not match:
|
||||
# Pattern 3: S6 E2
|
||||
match = re.search(r'S(\d+)\s+E(\d+)', title, re.IGNORECASE)
|
||||
if not match:
|
||||
# Pattern 4: (S6, E2) or (S6 E2)
|
||||
match = re.search(r'\(S(\d+)[,\s]+E(\d+)\)', title, re.IGNORECASE)
|
||||
if not match:
|
||||
# Pattern 5: Season 6 Episode 2
|
||||
match = re.search(r'Season\s+(\d+)\s+Episode\s+(\d+)', title, re.IGNORECASE)
|
||||
|
||||
if match:
|
||||
season_num = int(match.group(1))
|
||||
episode_num = int(match.group(2))
|
||||
# Remove season/episode from title for cleaner display
|
||||
title = re.sub(r'\s*S\d+:[Ee]\d+\s*', '', title)
|
||||
title = re.sub(r'\s*S\d+[Ee]\d+\s*', '', title)
|
||||
title = re.sub(r'\s*\(S\d+[,\s]+E\d+\)\s*', '', title)
|
||||
title = re.sub(r'\s*Season\s+\d+\s+Episode\s+\d+\s*', '', title)
|
||||
title = title.strip()
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Collect all available images
|
||||
# ------------------------------------------------------------
|
||||
picture = item.get("picture", {})
|
||||
images = {
|
||||
"background": MoveTVConfig.build_image_url(picture.get("background")),
|
||||
"icon": MoveTVConfig.build_image_url(picture.get("icon")),
|
||||
"poster": MoveTVConfig.build_image_url(picture.get("poster")),
|
||||
"square_logo": MoveTVConfig.build_image_url(picture.get("squareLogo")),
|
||||
"poster_mark": MoveTVConfig.build_image_url(picture.get("posterMark")),
|
||||
"original_title_logo": MoveTVConfig.build_image_url(
|
||||
picture.get("originalTitleLogo")
|
||||
),
|
||||
}
|
||||
# Remove None values
|
||||
images = {k: v for k, v in images.items() if v}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Build the programme object
|
||||
# ------------------------------------------------------------
|
||||
parsed.append({
|
||||
# Core identifiers
|
||||
"epg_id": item.get("epgId"),
|
||||
"schedule_id": item.get("scheduleId"),
|
||||
"live_id": item.get("liveId"),
|
||||
"live_name": item.get("liveName"),
|
||||
"content_id": item.get("contentId"),
|
||||
|
||||
# Title and description
|
||||
"title": title,
|
||||
"original_title": item.get("originalTitle"),
|
||||
"plot": item.get("epgDesc"),
|
||||
"episode_title": raw_title if season_num else None, # Original with S/E
|
||||
|
||||
# Episode information (CRITICAL for PVR)
|
||||
"season_num": season_num,
|
||||
"episode_num": episode_num,
|
||||
"has_episode_info": season_num is not None,
|
||||
|
||||
# Time information
|
||||
"start": self._ms_to_utc_str(start_ms),
|
||||
"end": self._ms_to_utc_str(end_ms),
|
||||
"start_ms": start_ms,
|
||||
"end_ms": end_ms,
|
||||
|
||||
# Genre and categories (enhanced)
|
||||
"genre": item.get("tagInfo", {}).get("name"),
|
||||
"genre_id": item.get("tagInfo", {}).get("tagId"),
|
||||
"categories": categories,
|
||||
"category_ids": category_ids,
|
||||
"category_images": category_images if category_images else None,
|
||||
|
||||
# Rating and year
|
||||
"rating": item.get("rating", 0),
|
||||
"year": item.get("year"),
|
||||
|
||||
# Credits
|
||||
"director": item.get("director"),
|
||||
"cast": cast,
|
||||
"producer": item.get("producer"),
|
||||
|
||||
# Images (enhanced)
|
||||
"thumbnail": images.get("background"), # Keep for backward compatibility
|
||||
"images": images if images else None,
|
||||
})
|
||||
|
||||
return parsed
|
||||
Reference in New Issue
Block a user