TPTV and updates

This commit is contained in:
VineFeeder
2026-06-15 17:06:59 +01:00
parent 62f7423f44
commit 8599327199
9 changed files with 314 additions and 160 deletions
@@ -23,8 +23,8 @@ uv run unshackle dl -vl all -al orig -sl ko,en,ja -q 1080,2160 -v h.264,h.265 -r
## Tips ## Tips
- To enable the web refresh token-based login method, please comment out or delete the DSNP section under credentials in `unshackle.yaml`. - To enable the web refresh token-based login method, please comment out or delete the DSNP section under credentials in `envied.yaml`.
웹 리프레시 토큰 기반 로그인 방식을 활성화하려면 `unshackle.yaml`의 credentials에서 DSNP부분을 주석 처리하거나 삭제하세요. 웹 리프레시 토큰 기반 로그인 방식을 활성화하려면 `envied.yaml`의 credentials에서 DSNP부분을 주석 처리하거나 삭제하세요.
``` ```
credentials: credentials:
@@ -32,8 +32,8 @@ uv run unshackle dl -vl all -al orig -sl ko,en,ja -q 1080,2160 -v h.264,h.265 -r
# DSNP: example@example.com:example # DSNP: example@example.com:example
``` ```
- Configure user settings within the `unshackle.yaml` file. - Configure user settings within the `envied.yaml` file.
사용자 설정은 `unshackle.yaml`에서 다음과 같이 사용하세요. 사용자 설정은 `envied.yaml`에서 다음과 같이 사용하세요.
``` ```
services: services:
@@ -66,8 +66,8 @@ uv run unshackle dl -vl all -al orig -sl ko,en,ja -q 1080,2160 -v h.264,h.265 -r
# manifest_log: "info" # manifest_log: "info"
``` ```
- To enable the tier_unlimits command by default, add the following to `unshackle.yaml`. - To enable the tier_unlimits command by default, add the following to `envied.yaml`.
tier_unlimits 명령을 기본값으로 활성화하려면 `unshackle.yaml`에 다음을 추가하세요. tier_unlimits 명령을 기본값으로 활성화하려면 `envied.yaml`에 다음을 추가하세요.
``` ```
dl: dl:
... ...
@@ -1,8 +1,8 @@
## DO NOT EDIT THIS FILE ## DO NOT EDIT THIS FILE
# 해당 config 파일은 개발자 외에는 수정하지 마세요. # 해당 config 파일은 개발자 외에는 수정하지 마세요.
# This configuration file should not be modified by anyone other than developers. # This configuration file should not be modified by anyone other than developers.
# 사용자 환경설정은 반드시 "unshackle.yaml"에서만 사용하세요. # 사용자 환경설정은 반드시 "envied.yaml"에서만 사용하세요.
# User configuration must be performed only in "unshackle.yaml". # User configuration must be performed only in "envied.yaml".
certificate: | certificate: |
CAUSugUKtAIIAxIQbj3s4jO5oUyWjDWqjfr9WRjA2afZBSKOAjCCAQoCggEBALhKWfnyA+FGn5P3tl6ffDjoGq2Oq86hKGl6aZIaGaF7XHPO5mIk7Q35ml CAUSugUKtAIIAxIQbj3s4jO5oUyWjDWqjfr9WRjA2afZBSKOAjCCAQoCggEBALhKWfnyA+FGn5P3tl6ffDjoGq2Oq86hKGl6aZIaGaF7XHPO5mIk7Q35ml
@@ -23,8 +23,8 @@ uv run unshackle dl -vl all -al orig -sl ko,en,ja -q 1080,2160 -v h.264,h.265 -r
## Tips ## Tips
- To enable the web refresh token-based login method, please comment out or delete the DSNP section under credentials in `unshackle.yaml`. - To enable the web refresh token-based login method, please comment out or delete the DSNP section under credentials in `envied.yaml`.
웹 리프레시 토큰 기반 로그인 방식을 활성화하려면 `unshackle.yaml`의 credentials에서 DSNP부분을 주석 처리하거나 삭제하세요. 웹 리프레시 토큰 기반 로그인 방식을 활성화하려면 `envied.yaml`의 credentials에서 DSNP부분을 주석 처리하거나 삭제하세요.
``` ```
credentials: credentials:
@@ -32,8 +32,8 @@ uv run unshackle dl -vl all -al orig -sl ko,en,ja -q 1080,2160 -v h.264,h.265 -r
# DSNP: example@example.com:example # DSNP: example@example.com:example
``` ```
- Configure user settings within the `unshackle.yaml` file. - Configure user settings within the `envied.yaml` file.
사용자 설정은 `unshackle.yaml`에서 다음과 같이 사용하세요. 사용자 설정은 `envied.yaml`에서 다음과 같이 사용하세요.
``` ```
services: services:
@@ -66,8 +66,8 @@ uv run unshackle dl -vl all -al orig -sl ko,en,ja -q 1080,2160 -v h.264,h.265 -r
# manifest_log: "info" # manifest_log: "info"
``` ```
- To enable the tier_unlimits command by default, add the following to `unshackle.yaml`. - To enable the tier_unlimits command by default, add the following to `envied.yaml`.
tier_unlimits 명령을 기본값으로 활성화하려면 `unshackle.yaml`에 다음을 추가하세요. tier_unlimits 명령을 기본값으로 활성화하려면 `envied.yaml`에 다음을 추가하세요.
``` ```
dl: dl:
... ...
@@ -1,8 +1,8 @@
## DO NOT EDIT THIS FILE ## DO NOT EDIT THIS FILE
# 해당 config 파일은 개발자 외에는 수정하지 마세요. # 해당 config 파일은 개발자 외에는 수정하지 마세요.
# This configuration file should not be modified by anyone other than developers. # This configuration file should not be modified by anyone other than developers.
# 사용자 환경설정은 반드시 "unshackle.yaml"에서만 사용하세요. # 사용자 환경설정은 반드시 "envied.yaml"에서만 사용하세요.
# User configuration must be performed only in "unshackle.yaml". # User configuration must be performed only in "envied.yaml".
certificate: | certificate: |
CAUSugUKtAIIAxIQbj3s4jO5oUyWjDWqjfr9WRjA2afZBSKOAjCCAQoCggEBALhKWfnyA+FGn5P3tl6ffDjoGq2Oq86hKGl6aZIaGaF7XHPO5mIk7Q35ml CAUSugUKtAIIAxIQbj3s4jO5oUyWjDWqjfr9WRjA2afZBSKOAjCCAQoCggEBALhKWfnyA+FGn5P3tl6ffDjoGq2Oq86hKGl6aZIaGaF7XHPO5mIk7Q35ml
@@ -14,6 +14,10 @@ from envied.core.service import Service
from envied.core.titles import Episode, Movie, Movies, Series from envied.core.titles import Episode, Movie, Movies, Series
from envied.core.tracks import Chapters, Subtitle, Tracks from envied.core.tracks import Chapters, Subtitle, Tracks
import requests import requests
import uuid
# debugging
from rich.console import Console
console = Console()
def get_widevine_license_url(manifest_str): def get_widevine_license_url(manifest_str):
# Try JSON first # Try JSON first
@@ -40,7 +44,7 @@ class TPTV(Service):
\b \b
version 1.0.4 version 1.0.4
Date: June 2025 Date: June 2026
Author: A_n_g_e_l_a Author: A_n_g_e_l_a
Authorization: email/password for service in envied.yaml Authorization: email/password for service in envied.yaml
Robustness: Robustness:
@@ -50,23 +54,36 @@ class TPTV(Service):
Note: Note:
TPTV will not allow the usual -w S01-S04 syntax as TPTV is eclictic in what it serves. TPTV will not allow the usual -w S01-S04 syntax as TPTV is eclictic in what it serves.
Series and episodes carry little meaning on this platform. Series and episodes carry little meaning on this platform.
It is not possible to remove S00E00 from the end of a video title - envied insists. It is not possible to remove S00E00 from the end of a video title - envied insists.
This service is best used with Vinefeeder for its front end.
Then use a search term in preference to a url.
Dealing with COLLECTION:
In envied, any url with COLLECTION in capitals will attempt to list the whole.
In vinefeeder, COLLECTION is ignored and the descriptive in the url is used as a search term.
eg https://tptvencore.co.uk/details/COLLECTION/collection/01KRKTC8WZ7X584YPA5NBFYJSS/the-danziger-collection
will be treated as a search for "the danziger collection" in vinefeeder, but will attempt to list the whole collection in envied.
Series and episodes are not well defined in TPTV, the collection listing is one way to find the episodes of a series.
If you look on ay COLLECTON page at TPTVencore there are tabs showing series listings. Use vinefeeder to search for the series name
and to produce a selectable list.
If COLLECTION name only returns one tab of results then manually from the web-page select each link url.
They are of the form https://tptvencore.co.uk/playback/item/6386553443112
Some restricted content will not download.
\b \b
Tips: Tips:
Use complete url in all cases. Use complete url in all cases.
SERIES: https://tptvencore.co.uk/collection/1717422888871355373 SERIES: https://tptvencore.co.uk/details/COLLECTION/collection/01KRKTC8WZ7X584YPA5NBFYJSS/the-danziger-collection
Note: TPTV do not specify Series and Episodes numbers in any meaningful and organized way. Note: TPTV do not specify Series and Episodes numbers in any meaningful and organized way.
They MAY sometimes be in the program title, but often incomplete. They MAY sometimes be in the program title, but often incomplete.
FILM: https://tptvencore.co.uk/product/the-importance-of-being-earnest-6290333578001 FILM: https://tptvencore.co.uk/details/VIDEO/item/6390689314112/the-manster
EPISODE: https://tptvencore.co.uk/product/sherlock-holmes---the-case-of-greystone-inscription-s1-ep16-6282604132001 EPISODE: https://tptvencore.co.uk/details/VIDEO/item/6348388311112/maigret-peter-the-lett
EPISODE: https://tptvencore.co.uk/playback/item/6386553443112
TPTV makes no distinction between films and episodes, so the above is just a convention to help you find what you want.
\b
Examples:
SERIES: https://tptvencore.co.uk/collection/1717422888871355373
EPISODE: https://tptvencore.co.uk/product/sherlock-holmes---the-case-of-greystone-inscription-s1-ep16-6282604132001
FILM: https://tptvencore.co.uk/product/the-importance-of-being-earnest-6290333578001
""" """
@@ -129,15 +146,35 @@ class TPTV(Service):
tokens = cache.data tokens = cache.data
else: else:
self.log.info(" + Logging in...") self.log.info(" + Logging in...")
payload = {"email": credential.username, "password": credential.password} random_uuid = str(uuid.uuid4())
r = self.session.post( r = self.session.post(
self.config["endpoints"]["login"], self.config["endpoints"]["login"],
headers=self.session.headers, headers=self.session.headers,
json={
"email": credential.username, json= {
"password": credential.password, "deviceInfo": {
"id": random_uuid,
"hardware": {
"manufacturer": "UNKNOWN/UNKNOWN",
"model": "Zimbadoo",
"version": "151.0"
},
"os": {
"name": "RisingHillOS",
"version": "x86_64"
},
"display": {
"width": 3840,
"height": 2048,
"formFactor": "THEATRE"
},
"legal": {}
}, },
"values": {
"email": credential.username,
"password": credential.password
}
}
) )
try: try:
res = r.json() res = r.json()
@@ -152,74 +189,13 @@ class TPTV(Service):
self.authorization = tokens self.authorization = tokens
def search(self) -> Generator[SearchResult, None, None]: def search(self) -> Generator[SearchResult, None, None]:
query = self.title.replace(" ", "+") print(f"Searching not implemented in Envied for TPTVencore. Please use VineFeeder instead.")
search_url = self.config["endpoints"]["search"].replace("{query}", query) return None
session_id = self.session.headers['session']
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0',
'Accept': '*/*',
'Accept-Language': 'en-GB,en;q=0.5',
'Referer': 'https://tptvencore.co.uk/',
'session': session_id,
'tenant': 'encore',
'Origin': 'https://tptvencore.co.uk',
'DNT': '1',
'Connection': 'keep-alive',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'cross-site',
'Priority': 'u=4',
}
r = self.session.get(search_url, headers=headers)
if r.status_code != 200:
raise ConnectionError(f"Search failed with {r.status_code}: {r.text}")
results = r.json()["data"]
myitems = []
if isinstance(results, list):
for result in results:
# do lookup on list item product or collection
if result.startswith('collection_'):
id = result.replace('collection_','')
collection_url = f"https://prod.suggestedtv.com/api/client/v1/collection/by-reference/{id}?extend=label"
response = self.session.get(collection_url, headers=headers)
if response.status_code == 200:
data = response.json()
for item in data['children']:
myitems.append(item['id'].replace('product_',''))
else:
print(f"Error: {response.status_code} - {response.text}")
else:
myitems.append(result.replace('product_',''))
mystring = ",".join(myitems)
continued_search_url = "https://prod.suggestedtv.com/api/client/v1/product?ids=" + mystring + "&extend=label" '''def get_titles(self) -> Union[Movies, Series]:
response = self.session.get(continued_search_url, headers=headers)
response.raise_for_status
results = response.json()
if isinstance(results, list):
items = results
elif isinstance(results, dict) and "data" in results:
items = results["data"]
else:
raise ValueError("Unexpected search result format")
for item in items:
try:
yield SearchResult(
id_=item.get("reference"),
title=item.get("name"),
description=item.get("description", "").replace('\n', ' '),
label=item.get("type", ""),
url=f"https://tptvencore.co.uk/product/{item.get('reference')}",
)
except Exception as e:
print(f"Failed to yield item: {e}")
def get_titles(self) -> Union[Movies, Series]:
data = self.get_data(self.title) data = self.get_data(self.title)
ids = ",".join(data) #ids = ",".join(data)
headers = { headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0',
@@ -237,30 +213,220 @@ class TPTV(Service):
} }
session_id = self.session.headers['session'] session_id = self.session.headers['session']
headers['session'] = session_id headers['session'] = session_id
params = { se_pattern = re.compile(
'ids': ids, r"\(\s*S\s*(?P<series>\d+)\s*,\s*EP\s*(?P<episode>\d+)\s*\)",
'extend': 'label', re.IGNORECASE
)
if len(data) > 1:
# multiple items, treat as a series
episodes = []
for item_id in data:
print(len(str(item_id)))
if len(str(item_id)) > 20: # Skip item IDs that too long
continue
# Skip this item if it too long to be valid.
try: # two possible endpoints for item data, try both
response = self.session.get(f'https://tptvencore.co.uk/api/core/catalog/item/{item_id}?locale=en', headers=headers)
if response.status_code != 200:
raise Exception()
except:
try:
response = self.session.get(f'https://tptvencore.co.uk/api/core/catalog/collection/{item_id}?locale=en', headers=headers)
if response.status_code != 200:
raise Exception()
except:
continue # next item if both endpoints fail, thus ignore.
mydata = json.loads(response.text)
f = open("tptv_debug.json", "w")
f.write(json.dumps(mydata, indent=4))
f.close()
console.print_json(data=mydata)
exit(0)
item = mydata['data']
title = mydata.get("title")
# some series exist with season/episode info,(most do not),
# so we try to extract it from the title if possible
match = se_pattern.search(title)
if match:
season = int(match.group("series"))
number = int(match.group("episode"))
else:
season = 0
number = 0
episodes.append(
Episode(
id_=item["id"],
service=self.__class__,
title=title,
season=season or 0,
number=number or 0,
name="",
language="en",
data=item,
)
)
#return Series(episodes)
else:
try:
response = self.session.get(f'https://tptvencore.co.uk/api/core/catalog/collection/{data[0]}?page=1&pageSize=20&locale=en', headers=headers)
if response.status_code != 200:
raise ConnectionRefusedError()
except:
try:
response = self.session.get(f'https://tptvencore.co.uk/api/core/catalog/item/{data[0]}?locale=en', headers=headers)
except:
raise ConnectionError("Failed to retrieve title data")
mydata = json.loads(response.text)
titles = mydata['data']
episodes = []
season = 0
number = 0
if (len(mydata)) > 1:
for item in titles:
title = item["title"]
# some series exist with season/episode info,(most do not),
# so we try to extract it from the title if possible
match = se_pattern.search(title)
if match:
season = int(match.group("series"))
number = int(match.group("episode"))
episodes.append(
Episode(
id_=item["id"],
service=self.__class__,
title=title,
season=season or 0,
number=number or 0,
name="",
language="en",
data=item,
)
)
else:
# single item, treat as a movie
item = titles
title = item["title"]
# some series exist with season/episode info,(most do not),
# so we try to extract it from the title if possible
match = se_pattern.search(title)
if match:
season = int(match.group("series"))
number = int(match.group("episode"))
episodes.append(
Episode(
id_=item["id"],
service=self.__class__,
title=item["title"],
season=season or 0,
number=number or 0,
name="",
language="en",
data=item,
)
)
return Series(episodes)'''
def get_titles(self) -> Union[Movies, Series]:
data = self.get_data(self.title)
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0",
"Accept": "*/*",
"Accept-Language": "en-GB,en;q=0.5",
"Referer": "https://tptvencore.co.uk/",
"tenant": "encore",
"Origin": "https://tptvencore.co.uk",
"DNT": "1",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "cross-site",
"Priority": "u=4",
"session": self.session.headers["session"],
} }
response = self.session.get('https://prod.suggestedtv.com/api/client/v1/product', params=params, headers=headers) se_pattern = re.compile(
if response.status_code == 200: r"\(\s*S\s*(?P<season>\d+)\s*,\s*EP\s*(?P<episode>\d+)\s*\)",
mydata=json.loads(response.text) re.IGNORECASE,
titles = mydata['data'] )
episodes =[
Episode(
id_=episode["id"],
service=self.__class__,
title=episode["name"],
season=0,
number=0,
name = '',
language="en", # TODO: language detection
data=episode,
)
for episode in titles
]
return Series(episodes)
def extract_se(title: str) -> tuple[int, int]:
match = se_pattern.search(title or "")
if not match:
return 0, 0
return int(match.group("season")), int(match.group("episode"))
def fetch_json(item_id: str | int) -> dict | None:
"""
Try item endpoint first, then collection endpoint.
Return decoded JSON or None if both fail.
"""
urls = [
f"https://tptvencore.co.uk/api/core/catalog/item/{item_id}?locale=en",
f"https://tptvencore.co.uk/api/core/catalog/collection/{item_id}?page=1&pageSize=20&locale=en",
f"https://tptvencore.co.uk/api/core/catalog/collection/{item_id}?locale=en",
]
for url in urls:
response = self.session.get(url, headers=headers)
if response.status_code == 200:
return response.json()
return None
def make_episode(item: dict) -> Episode:
title = item.get("title", "")
season, number = extract_se(title)
return Episode(
id_=item["id"],
service=self.__class__,
title=title,
season=season,
number=number,
name="",
language="en",
data=item,
)
episodes = []
for item_id in data:
# Skip bogus IDs
if len(str(item_id)) > 20:
continue
mydata = fetch_json(item_id)
if not mydata:
continue
titles = mydata.get("data")
if not titles:
continue
# Collection endpoint may return a list of items
if isinstance(titles, list):
for item in titles:
episodes.append(make_episode(item))
# Item endpoint returns a single dict
elif isinstance(titles, dict):
episodes.append(make_episode(titles))
return Series(episodes)
def get_tracks(self, title: Union[Movie, Episode]) -> Tracks: def get_tracks(self, title: Union[Movie, Episode]) -> Tracks:
playlist = f"https://edge.api.brightcove.com/playback/v1/accounts/6272132012001/videos/{title.data.get('id')}" playlist = f"https://edge.api.brightcove.com/playback/v1/accounts/6272132012001/videos/{title.data.get('id')}"
@@ -314,24 +480,25 @@ class TPTV(Service):
def get_data(self, url: str) -> dict: def get_data(self, url: str) -> dict:
self.session.headers.update({'tenant': 'encore'}) self.session.headers.update({'tenant': 'encore'})
if 'details' in url: if 'COLLECTION' in url:
prod_id = url.split('item/')[-2] prod_id = url.split('/')[-2]
url = f"https://prod.suggestedtv.com/api/client/v1/collection/by-reference/{prod_id}?extend=label" url = f"https://tptvencore.co.uk/api/core/catalog/collection/{prod_id}"
r = self.session.get(url) r = self.session.get(url)
if r.status_code != 200: if r.status_code != 200:
raise ConnectionError(r.text) raise ConnectionError(r.text)
myjson = r.json() myjson = r.json()
children = myjson.get('children') data = myjson.get("data", {})
product_links = [] product_links = []
for child in children: for child in data:
product_links.append(child['id']) if 'product' in child.get('classification') else None product_links.append(child.get('id', {})) if child.get('id', {}) else None
return product_links return product_links
elif 'playback' in url: # single item elif 'playback' in url: # single item
prod_id = url.split('/')[-1] prod_id = url.split('/')[-1]
return [prod_id] return [prod_id]
elif 'VIDEO' in url: elif 'details' in url:
prod_id = url.split('/')[-2] prod_id = url.split('/')[-2]
return [prod_id] return [prod_id]
else: else:
raise ValueError("URL format not recognized for data retrieval.") raise ValueError("URL format not recognized for data retrieval in proc get_data().")
@@ -17,7 +17,7 @@ session:
api-key: zq5pyPd0RTbNg3Fyj52PrkKL9c2Af38HHh4itgZTKDaCzjAyhd api-key: zq5pyPd0RTbNg3Fyj52PrkKL9c2Af38HHh4itgZTKDaCzjAyhd
endpoints: endpoints:
login: https://prod.suggestedtv.com/api/client/v1/session/login login: https://tptvencore.co.uk/api/core/auth/login?locale=en
session: https://prod.suggestedtv.com/api/client/v1/session session: https://prod.suggestedtv.com/api/client/v1/session
search: https://prod.suggestedtv.com/api/client/v2/search/{query} search: https://tptvencore.co.uk/api/core/search?q={query}
@@ -115,7 +115,7 @@ class BaseLoader:
def fetch_and_parse(self, url: str, headers): def fetch_and_parse(self, url: str, headers):
# deals with response.text as xml # deals with response.text as xml
# first apporach - request json # first approach - request json
# second approach convert xml to json # second approach convert xml to json
with httpx.Client(timeout=30) as client: with httpx.Client(timeout=30) as client:
resp = client.get(url, headers=headers) resp = client.get(url, headers=headers)
@@ -1,3 +1,3 @@
BATCH_DOWNLOAD: false BATCH_DOWNLOAD: false
TERMINAL: gnome-terminal TERMINAL: gnome-terminal
TERMINAL_RESET: true TERMINAL_RESET: false
@@ -82,13 +82,13 @@ class TptvLoader(BaseLoader):
if opts: if opts:
TptvLoader.options = opts TptvLoader.options = opts
self.options_list = split_options(TptvLoader.options) self.options_list = split_options(TptvLoader.options)
# direct download # direct download
if "http" in search_term and inx == 1: if "http" in search_term and not 'COLLECTION' in search_term and inx == 1:
try: try:
self.options_list = split_options(TptvLoader.options) #self.options_list = split_options(TptvLoader.options)
if self.options_list[0] == "": if not opts:
command = ['uv', 'run', 'envied', "dl", "TPTV", search_term] command = ['uv', 'run', 'envied', "dl", "TPTV", search_term]
else: else:
command = ['uv', 'run', 'envied', "dl", *self.options_list, "TPTV", search_term] command = ['uv', 'run', 'envied', "dl", *self.options_list, "TPTV", search_term]
@@ -101,7 +101,10 @@ class TptvLoader(BaseLoader):
) )
return return
elif "COLLECTION" in search_term and inx == 1:
search_term = search_term.split('/')[-1]
inx = 3
return self.fetch_videos(search_term)
# keyword search # keyword search
elif inx == 3: elif inx == 3:
print(f"Searching for {search_term}") print(f"Searching for {search_term}")
@@ -128,13 +131,13 @@ class TptvLoader(BaseLoader):
) # search_term here holds a URL!!! ) # search_term here holds a URL!!!
else: else:
print(f"Unknown error when searching for {search_term}") print(f"Either an incorrect url was entered or an unknown error when searching for {search_term}")
return return
def fetch_videos(self, search_term): def fetch_videos(self, search_term):
suggested_url = f"https://prod.suggestedtv.com/api/client/v2/search/{search_term}" suggested_url = f"https://tptvencore.co.uk/api/core/search?q={search_term}"
@@ -152,38 +155,21 @@ class TptvLoader(BaseLoader):
get_headers['Access-Control-Request-Headers'] = 'session,tenant' get_headers['Access-Control-Request-Headers'] = 'session,tenant'
get_headers['Access-Control-Request-Method'] = 'GET' get_headers['Access-Control-Request-Method'] = 'GET'
get_headers['session'] = self.session_id get_headers['session'] = self.session_id
get_headers['tenant'] = 'encore' #get_headers['tenant'] = 'encore'
response = self.get_data(suggested_url, headers=get_headers) response = self.get_data(suggested_url, headers=get_headers)
myjson = json.loads(response) myjson = json.loads(response)
myitems = []
for item in myjson['data']:
if item.startswith('collection_'):
id = item.replace('collection_','')
collection_url = f"https://prod.suggestedtv.com/api/client/v1/collection/by-reference/{id}?extend=label"
response = self.get_data(collection_url, headers=get_headers)
if response:
data = json.loads(response)
for item in data['children']:
myitems.append(item['id'].replace('product_',''))
elif item.startswith('product_'):
myitems.append(item.replace('product_',''))
mystring = ",".join(myitems)
continued_search_url = "https://prod.suggestedtv.com/api/client/v1/product?ids=" + mystring + "&extend=label"
response = self.get_data(continued_search_url, headers=get_headers)
data = json.loads(response)
try: try:
for item in data['data']: for item in myjson['data']:
vid_id = item['reference'] url = item['video']['playback'].replace("api/core/play", "playback")
title = item['name'] title = item['title']
synopsis = item['description'].replace('\n', ' ') synopsis = item['description'].replace('\n', ' ')
if len(synopsis) > 300: if len(synopsis) > 300:
synopsis = f"{synopsis[:300]} ...snip" synopsis = f"{synopsis[:300]} ...snip"
episode = { episode = {
"title": title, "title": title,
"url": vid_id, "url": url,
"synopsis": synopsis, "synopsis": synopsis,
} }
self.add_episode(title,episode) self.add_episode(title,episode)
@@ -211,7 +197,8 @@ class TptvLoader(BaseLoader):
self.options_list = split_options(self.options) self.options_list = split_options(self.options)
for item in selected: for item in selected:
url = item[2] url = item[2]
url = f"https://tptvencore.co.uk/product/{url}" id = url.split("/")[-1].replace("?locale=en", "")
url = f'https://tptvencore.co.uk/playback/item/{id}'
#print(url) #print(url)
try: try:
if self.options_list[0] == "": if self.options_list[0] == "":