mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-10-25 08:12:26 +01:00
Implement RTL VOD series/episodes
This commit is contained in:
@@ -418,22 +418,7 @@ class RTLPlusVodManager:
|
|||||||
# Program contents (handles movies, numbered seasons, monthly archives)
|
# Program contents (handles movies, numbered seasons, monthly archives)
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def _get_program_contents(
|
def _get_program_contents(self, program_id: str, cursor=None, page_size=24, slug=None):
|
||||||
self,
|
|
||||||
program_id: str,
|
|
||||||
cursor: Optional[str] = None,
|
|
||||||
page_size: int = 24,
|
|
||||||
slug: Optional[str] = None,
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Get program contents - handles movies, series with season selectors, or direct episodes.
|
|
||||||
|
|
||||||
Returns either:
|
|
||||||
- A direct movie (playable)
|
|
||||||
- Numbered seasons (Staffel 1, 2, etc.)
|
|
||||||
- Monthly archive selector (2026-05, 2026-04, etc.)
|
|
||||||
- Direct episodes (when no selector is present)
|
|
||||||
"""
|
|
||||||
seo = slug or f"p_{program_id}"
|
seo = slug or f"p_{program_id}"
|
||||||
location = f"{self.cfg.base_website}{seo}-p_{program_id}"
|
location = f"{self.cfg.base_website}{seo}-p_{program_id}"
|
||||||
|
|
||||||
@@ -452,8 +437,16 @@ class RTLPlusVodManager:
|
|||||||
logger.debug(f"Blocks count: {len(layout.get('blocks', []))}")
|
logger.debug(f"Blocks count: {len(layout.get('blocks', []))}")
|
||||||
|
|
||||||
# FIRST: Check if this is a movie (direct playable video)
|
# FIRST: Check if this is a movie (direct playable video)
|
||||||
movie_item = self._find_direct_video_in_layout(layout)
|
logger.debug("Calling _find_direct_video_in_layout")
|
||||||
|
try:
|
||||||
|
movie_item = self._find_direct_video_in_layout(layout)
|
||||||
|
logger.debug(f"_find_direct_video_in_layout returned: {movie_item is not None}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in _find_direct_video_in_layout: {e}", exc_info=True)
|
||||||
|
movie_item = None
|
||||||
|
|
||||||
if movie_item:
|
if movie_item:
|
||||||
|
logger.debug(f"Found movie: {movie_item.name}")
|
||||||
layout_title = (
|
layout_title = (
|
||||||
layout.get("entity", {}).get("metadata", {}).get("title")
|
layout.get("entity", {}).get("metadata", {}).get("title")
|
||||||
or layout.get("seo", {}).get("title")
|
or layout.get("seo", {}).get("title")
|
||||||
@@ -473,10 +466,11 @@ class RTLPlusVodManager:
|
|||||||
"total": 1,
|
"total": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
# SECOND: Extract monthly archive selector (for news/reality shows)
|
# SECOND: Extract monthly archive selector
|
||||||
|
logger.debug("Calling _extract_season_selector_with_episodes")
|
||||||
try:
|
try:
|
||||||
season_selector, current_episodes = self._extract_season_selector_with_episodes(layout)
|
season_selector, current_episodes = self._extract_season_selector_with_episodes(layout)
|
||||||
logger.debug(f"Monthly selector: {len(season_selector)} months, {len(current_episodes)} current episodes")
|
logger.debug(f"Got {len(season_selector)} months, {len(current_episodes)} episodes")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in _extract_season_selector_with_episodes: {e}", exc_info=True)
|
logger.error(f"Error in _extract_season_selector_with_episodes: {e}", exc_info=True)
|
||||||
season_selector, current_episodes = [], []
|
season_selector, current_episodes = [], []
|
||||||
@@ -678,55 +672,71 @@ class RTLPlusVodManager:
|
|||||||
return seasons
|
return seasons
|
||||||
|
|
||||||
def _find_direct_video_in_layout(self, layout: Dict) -> Optional[VodItem]:
|
def _find_direct_video_in_layout(self, layout: Dict) -> Optional[VodItem]:
|
||||||
"""
|
logger.debug("Entering _find_direct_video_in_layout")
|
||||||
Find a direct playable video item in a program layout.
|
|
||||||
Skips any blocks that are selectors (monthly or numbered seasons).
|
|
||||||
"""
|
|
||||||
if not layout or not isinstance(layout, dict):
|
if not layout or not isinstance(layout, dict):
|
||||||
|
logger.warning("Layout is None or not a dict")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for block in layout.get("blocks", []):
|
for block_idx, block in enumerate(layout.get("blocks", [])):
|
||||||
|
logger.debug(f"Processing block {block_idx} in _find_direct_video_in_layout")
|
||||||
|
|
||||||
if not block or not isinstance(block, dict):
|
if not block or not isinstance(block, dict):
|
||||||
|
logger.debug(f"Block {block_idx} is None or not dict")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip blocks that are selectors (they contain episodes, not direct video)
|
# Skip blocks that are selectors
|
||||||
alt_content = block.get("alternativeContent", {})
|
alt_content = block.get("alternativeContent", {})
|
||||||
if alt_content.get("selectorTemplateId") == "Selector":
|
if alt_content and alt_content.get("selectorTemplateId") == "Selector":
|
||||||
continue # This is a monthly selector block, skip it
|
logger.debug(f"Block {block_idx} is a Selector, skipping")
|
||||||
|
continue
|
||||||
|
|
||||||
# Check block type - skip season selectors
|
# Check block type - skip season selectors
|
||||||
tealium = block.get("analytics", {}).get("tealium", {})
|
tealium = block.get("analytics", {}).get("tealium", {})
|
||||||
if tealium.get("template_name") == "SelectorCardListM":
|
if tealium.get("template_name") == "SelectorCardListM":
|
||||||
continue # This is a numbered season selector, skip it
|
logger.debug(f"Block {block_idx} is SelectorCardListM, skipping")
|
||||||
|
continue
|
||||||
|
|
||||||
# Collect items from standard content location
|
# Collect items
|
||||||
content = block.get("content", {})
|
content = block.get("content", {})
|
||||||
items = list(content.get("items", []) or []) if isinstance(content, dict) else []
|
items = content.get("items", []) if isinstance(content, dict) else []
|
||||||
|
logger.debug(f"Block {block_idx} has {len(items)} items")
|
||||||
|
|
||||||
# Also check alternativeContent for concurrent blocks (only if not selector)
|
# Also check alternativeContent
|
||||||
if isinstance(alt_content, dict):
|
if isinstance(alt_content, dict):
|
||||||
for cb in alt_content.get("concurrentBlocks", []):
|
for cb in alt_content.get("concurrentBlocks", []):
|
||||||
if isinstance(cb, dict):
|
if isinstance(cb, dict):
|
||||||
cb_items = cb.get("content", {}).get("items", [])
|
cb_items = cb.get("content", {}).get("items", [])
|
||||||
if cb_items:
|
if cb_items:
|
||||||
items.extend(cb_items)
|
items.extend(cb_items)
|
||||||
|
logger.debug(f"Added {len(cb_items)} items from concurrentBlocks")
|
||||||
|
|
||||||
|
for item_idx, item in enumerate(items):
|
||||||
|
logger.debug(f"Processing item {item_idx}")
|
||||||
|
|
||||||
for item in items:
|
|
||||||
if not item or not isinstance(item, dict):
|
if not item or not isinstance(item, dict):
|
||||||
continue
|
continue
|
||||||
if item.get("itemType") != "classic":
|
if item.get("itemType") != "classic":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_content = item.get("itemContent", {})
|
item_content = item.get("itemContent")
|
||||||
if not item_content or not isinstance(item_content, dict):
|
if not item_content or not isinstance(item_content, dict):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Check for video in action target
|
||||||
action = item_content.get("action", {})
|
action = item_content.get("action", {})
|
||||||
if isinstance(action, dict):
|
if isinstance(action, dict):
|
||||||
target = action.get("target", {})
|
target = action.get("target", {})
|
||||||
if isinstance(target, dict):
|
if isinstance(target, dict):
|
||||||
|
# Handle lock-wrapped targets
|
||||||
|
if target.get("type") == "lock":
|
||||||
|
lock_value = target.get("value_lock", {})
|
||||||
|
if isinstance(lock_value, dict):
|
||||||
|
target = lock_value.get("originalTarget", {})
|
||||||
|
|
||||||
value_layout = target.get("value_layout", {})
|
value_layout = target.get("value_layout", {})
|
||||||
if isinstance(value_layout, dict) and value_layout.get("type") == "video":
|
if isinstance(value_layout, dict) and value_layout.get("type") == "video":
|
||||||
|
logger.debug(f"Found video in action target: {value_layout.get('id')}")
|
||||||
vod_item = self._extract_vod_item_from_block_item(item)
|
vod_item = self._extract_vod_item_from_block_item(item)
|
||||||
if vod_item:
|
if vod_item:
|
||||||
clip_id = value_layout.get("id")
|
clip_id = value_layout.get("id")
|
||||||
@@ -737,6 +747,7 @@ class RTLPlusVodManager:
|
|||||||
if item_content.get("type") == "video":
|
if item_content.get("type") == "video":
|
||||||
clip_id = item_content.get("id")
|
clip_id = item_content.get("id")
|
||||||
if clip_id:
|
if clip_id:
|
||||||
|
logger.debug(f"Found direct video: {clip_id}")
|
||||||
vod_item = VodItem.create_episode(
|
vod_item = VodItem.create_episode(
|
||||||
name=item_content.get("title", clip_id),
|
name=item_content.get("title", clip_id),
|
||||||
content_id=clip_id,
|
content_id=clip_id,
|
||||||
@@ -748,21 +759,7 @@ class RTLPlusVodManager:
|
|||||||
vod_item.logo_url = self._extract_thumbnail(item_content)
|
vod_item.logo_url = self._extract_thumbnail(item_content)
|
||||||
return vod_item
|
return vod_item
|
||||||
|
|
||||||
# Check alternative action locations
|
logger.debug("No video found in _find_direct_video_in_layout")
|
||||||
for action_key in ("onClickAction", "primaryAction", "secondaryAction"):
|
|
||||||
alt_action = item_content.get(action_key, {})
|
|
||||||
if isinstance(alt_action, dict):
|
|
||||||
alt_target = alt_action.get("target", {})
|
|
||||||
if isinstance(alt_target, dict):
|
|
||||||
alt_value = alt_target.get("value_layout", {})
|
|
||||||
if isinstance(alt_value, dict) and alt_value.get("type") == "video":
|
|
||||||
vod_item = self._extract_vod_item_from_block_item(item)
|
|
||||||
if vod_item:
|
|
||||||
clip_id = alt_value.get("id")
|
|
||||||
if clip_id:
|
|
||||||
vod_item.content_id = clip_id
|
|
||||||
return vod_item
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _extract_direct_episodes_from_layout(self, layout: Dict) -> List[VodItem]:
|
def _extract_direct_episodes_from_layout(self, layout: Dict) -> List[VodItem]:
|
||||||
|
|||||||
Reference in New Issue
Block a user