From 6390ed08d7c1447117886ba9a7df4c958f42387b Mon Sep 17 00:00:00 2001 From: VineFeeder Date: Fri, 3 Jul 2026 13:59:08 +0100 Subject: [PATCH] updates and additions --- .../src/envied/services/NINE/__init__.py | 49 ++++---- .../src/vinefeeder/services/STV/__init__.py | 106 +++++++----------- 2 files changed, 72 insertions(+), 83 deletions(-) diff --git a/packages/envied/src/envied/services/NINE/__init__.py b/packages/envied/src/envied/services/NINE/__init__.py index db895e5..fe81d56 100644 --- a/packages/envied/src/envied/services/NINE/__init__.py +++ b/packages/envied/src/envied/services/NINE/__init__.py @@ -34,7 +34,7 @@ class NINE(Service): \b Tips: - Search by show name: - envied.dl NINE "Travel Guides" + unshackle dl NINE "Travel Guides" - Use complete URLs: SERIES: https://www.9now.com.au/travel-guides EPISODE: https://www.9now.com.au/travel-guides/season-9/episode-2 @@ -386,40 +386,36 @@ class NINE(Service): @staticmethod def _best_source(sources: list[dict]) -> dict | None: - dash_drm = next( - ( - source for source in sources - if source.get("type") == "application/dash+xml" + dash_drm = NINE._first_source( + sources, + lambda source: ( + source.get("type") == "application/dash+xml" and (source.get("key_systems") or {}).get("com.widevine.alpha") - and source.get("src") ), - None, ) if dash_drm: return dash_drm - dash_clear = next( - ( - source for source in sources - if source.get("type") == "application/dash+xml" and source.get("src") - ), - None, - ) + dash_clear = NINE._first_source(sources, lambda source: source.get("type") == "application/dash+xml") if dash_clear: return dash_clear - return next( - ( - source for source in sources - if source.get("type") == "application/x-mpegURL" - and source.get("src", "").startswith("https://") - ), + return NINE._first_source(sources, lambda source: source.get("type") == "application/x-mpegURL") + + @staticmethod + def _first_source(sources: list[dict], predicate: Any) -> dict | None: + matches = [source for source in sources if predicate(source) and source.get("src")] + return next((source for source in matches if source["src"].startswith("https://")), None) or next( + iter(matches), None, ) @staticmethod def _add_subtitles(tracks: Tracks, text_tracks: list[dict]) -> None: for text_track in text_tracks: + if not NINE._is_caption_track(text_track): + continue + source_url = text_track.get("src") if not source_url and text_track.get("sources"): source_url = next( @@ -442,6 +438,19 @@ class NINE(Service): ) ) + @staticmethod + def _is_caption_track(text_track: dict) -> bool: + kind = (text_track.get("kind") or "").lower() + label = (text_track.get("label") or "").lower() + source_url = (text_track.get("src") or "").lower() + + if kind in ("metadata", "thumbnail", "thumbnails") or label in ("thumbnail", "thumbnails"): + return False + if "/thumbnail/" in source_url or "thumbnail.webvtt" in source_url: + return False + + return kind in ("captions", "subtitles") or bool(text_track.get("srclang")) + @staticmethod def _subtitle_codec(text_track: dict, source_url: str) -> str: mime_type = (text_track.get("mime_type") or "").lower() diff --git a/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py b/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py index 16bab4b..532e734 100644 --- a/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py @@ -1,3 +1,5 @@ +import json + from vinefeeder.base_loader import BaseLoader from vinefeeder.parsing_utils import extract_script_with_id_json, parse_json, split_options from rich.console import Console @@ -101,53 +103,21 @@ class StvLoader(BaseLoader): return def fetch_videos(self, search_term): - headers = { - "Accept": "*/*", - "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", - "Connection": "keep-alive", - "Origin": "https://player.stv.tv", - "Referer": "https://player.stv.tv/", - "Host": "search-api.swiftype.com", - "Access-Control-Request-Method": "POST", - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36", - } - url = "https://search-api.swiftype.com/api/v1/public/engines/search.json" + + url = f"https://v4.api.stv.tv/v4/discovery/search?query={search_term}&indexUid=programme_index&limit=20&offset=0&facets=" - response = self.get_options(url, headers=headers) - - xdata = response["x-request-id"] - headers = { - "Accept": "*/*", - "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", - "Connection": "keep-alive", - "Origin": "https://player.stv.tv", - "Referer": "https://player.stv.tv/", - "Host": "search-api.swiftype.com", - "Access-Control-Request-Method": "POST", - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36", - "x-request-id": f"{xdata}", - } - json = { - "engine_key": "S1jgssBHdk8ZtMWngK_y", - "per_page": 100, - "page": 1, - "fetch_fields": {"page": ["title", "body", "resultDescriptionTx", "url"]}, - "highlight_fields": {"page": {"title": {"size": 100, "fallback": True}}}, - "search_fields": {"page": ["title^3", "body", "category", "sections"]}, - "q": search_term, - "spelling": "strict", - } - - response = self.post_data(url, headers=headers, json=json) - parsed_data = response.json() # - mydata = parsed_data["records"]["page"] + response = self.get_data(url) + parsed_data = json.loads(response) + mydata = parsed_data["data"] for item in mydata: - title = item["title"] + title = item['attributes']["title"] + url = item['attributes']["permalink"] + synopsis = item['attributes']["long_description"] episode = { - "title": item["title"], - "url": item["url"], - "synopsis": item["resultDescriptionTx"], + "title": title, + "url": "https://player.stv.tv/" + url.strip('/'), + "synopsis": synopsis, } self.add_episode(title, episode) @@ -179,13 +149,11 @@ class StvLoader(BaseLoader): self.clear_series_data() # Clear existing series data '''console.print_json(data=parsed_data) # for debugging - f = open("1stv.json",'w') + f = open("stv.json",'w') f.write(json.dumps(parsed_data)) f.close()''' - series_data = parsed_data["props"]["pageProps"]["data"]["programmeHeader"][ - "name" - ] + series_data = parsed_data["props"]["pageProps"]["data"]["programmeHeader"]["name"] tabs = len(parsed_data["props"]["pageProps"]["data"]["tabs"]) headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0', @@ -224,23 +192,28 @@ class StvLoader(BaseLoader): if "Episode" in series_no: series_no = 100 else: - series_no = int(series_no.split(" ")[1]) - title = item["title"] - url = f"https://player.stv.tv{item['link']}" # https://player.stv.tv/episode/4nlk/loose-women - synopsis = item["summary"] - - episode = { - "series_no": series_no, - "title": title.replace( - ", ", "-" - ), # data with comma messes up later when selecting url - "url": url, - "synopsis": synopsis, - } - self.add_episode_remove_duplicates(series_data, episode) - + if "-" in series_no: + series_no = int(series_no.split("-")[0]) + else: + series_no = int(series_no.split(" ")[1]) + old_series_no = series_no except KeyError as e: print(f"Error: {e}") + title = item["title"] + url = f"https://player.stv.tv{item['link']}" # https://player.stv.tv/episode/4nlk/loose-women + synopsis = item["summary"] + + episode = { + "series_no": series_no, + "title": title.replace( + ", ", "-" + ), # data with comma messes up later when selecting url + "url": url, + "synopsis": synopsis, + } + self.add_episode_remove_duplicates(series_data, episode) + + if tabs > 1: for index in range(1, tabs): @@ -271,12 +244,19 @@ class StvLoader(BaseLoader): for item in next_parsed_data["results"]: try: series_no = item["playerSeries"]["name"] + if "Episode" in series_no: + series_no = 100 + else: + if "-" in series_no: + series_no = int(series_no.split("-")[0]) + else: + series_no = int(series_no.split(" ")[1]) title = item["title"] url = item["_permalink"] synopsis = item["summary"] episode = { - "series_no": int(series_no.replace("Series ", "")), + "series_no": int(series_no), "title": title, "url": url, # "synopsis": synopsis,