mirror of
https://github.com/movixcorp/MovixOpenSource.git
synced 2026-08-18 05:55:41 +00:00
375 lines
14 KiB
Python
375 lines
14 KiB
Python
import builtins
|
|
import json
|
|
import re
|
|
from os.path import join
|
|
|
|
import requests
|
|
|
|
from utils.constants.macros import USER_ERROR, ERR_MSG, CACHE_DIR, APP_ERROR
|
|
from utils.main_service import main_service
|
|
from utils.structs import BaseElement, BaseService, CustomException
|
|
from utils.tools.args import check_range
|
|
from utils.tools.common import clean_url, get_valid_filename, dict_to_file, file_to_dict
|
|
|
|
|
|
class maoriplus_co_nz(BaseService):
|
|
DEMO_URLS = [
|
|
"https://www.maoriplus.co.nz/movie/bernie-the-dolphin/play",
|
|
"https://www.maoriplus.co.nz/movie/a-son/play",
|
|
"https://www.maoriplus.co.nz/show/ariki/play/6353035454112",
|
|
"https://www.maoriplus.co.nz/show/frenemies/play/5830339283001",
|
|
"https://www.maoriplus.co.nz/live-tv/whakaata-maori",
|
|
"https://www.maoriplus.co.nz/live-tv/te-reo",
|
|
"https://www.maoriplus.co.nz/show/marae-diy",
|
|
"https://www.maoriplus.co.nz/show/matariki-awards-2019",
|
|
"https://www.maoriplus.co.nz/show/nga-manu-korero-2024",
|
|
"https://www.maoriplus.co.nz/show/school-of-training",
|
|
]
|
|
|
|
PLAYBACK_URL = 'https://edge.api.brightcove.com/playback/v1/accounts/{account_id}/videos/{video_id}'
|
|
SESSION_URL = 'https://api.one.accedo.tv/session'
|
|
METADATA_URL = 'https://api.one.accedo.tv/metadata'
|
|
ENTRY_URL = 'https://api.one.accedo.tv/content/entry/alias/{alias}'
|
|
ENTRIES_URL = 'https://api.one.accedo.tv/content/entries'
|
|
EPISODES_URL = 'https://cms.maoriplus.co.nz/playlists/{season_id}/videos'
|
|
BASE_URL = 'https://www.maoriplus.co.nz'
|
|
|
|
API_DICT = None
|
|
CACHE_FILE = None
|
|
SESSION_KEY = None
|
|
PAGE_SIZE = 50
|
|
MPD_PRIORITY = {"": 0, "application/dash+xml": 1, "application/x-mpegURL": 2}
|
|
|
|
@staticmethod
|
|
def test_service():
|
|
main_service.run_service(maoriplus_co_nz)
|
|
|
|
@staticmethod
|
|
def is_content_livestream(content, additional):
|
|
return "/live-tv/" in content
|
|
|
|
@staticmethod
|
|
def credentials_needed():
|
|
return {"NONE_NEEDED": True}
|
|
|
|
@staticmethod
|
|
def get_session_key(app_key):
|
|
response = requests.get(
|
|
maoriplus_co_nz.SESSION_URL,
|
|
params={'uuid': 'uuid', 'appKey': app_key}
|
|
)
|
|
response = response.content.decode()
|
|
response = json.loads(response)
|
|
return response["sessionKey"]
|
|
|
|
@staticmethod
|
|
def get_api_dict():
|
|
try:
|
|
api_dict = file_to_dict(maoriplus_co_nz.CACHE_FILE)
|
|
assert len(api_dict.keys()) == 3
|
|
maoriplus_co_nz.SESSION_KEY = maoriplus_co_nz.get_session_key(api_dict["app_key"])
|
|
return api_dict
|
|
except:
|
|
pass
|
|
|
|
response = requests.get(maoriplus_co_nz.BASE_URL).content.decode()
|
|
response = re.findall(r'src="([^"]*index[^"]*\.js)"', response)[0]
|
|
if response[0] != "/":
|
|
response = "/" + response
|
|
response = maoriplus_co_nz.BASE_URL + response
|
|
|
|
response = requests.get(response).content.decode()
|
|
response = re.findall(r'="https://api.one.accedo.tv"[^"]*="([^"]*)"', response)
|
|
response = sorted(response, key=len, reverse=True)[0]
|
|
|
|
app_key = response
|
|
maoriplus_co_nz.SESSION_KEY = maoriplus_co_nz.get_session_key(app_key)
|
|
|
|
response = requests.get(
|
|
maoriplus_co_nz.METADATA_URL,
|
|
headers={'X-Session': maoriplus_co_nz.SESSION_KEY}
|
|
)
|
|
response = response.content.decode()
|
|
response = json.loads(response)
|
|
response = response["brightcovePlayer"]
|
|
|
|
api_dict = {
|
|
"app_key": app_key,
|
|
"account_id": response["accountId"],
|
|
"policy_key": response["playerPolicyKey"]
|
|
}
|
|
dict_to_file(maoriplus_co_nz.CACHE_FILE, api_dict)
|
|
return api_dict
|
|
|
|
@staticmethod
|
|
def initialize_service():
|
|
if maoriplus_co_nz.CACHE_FILE is None:
|
|
maoriplus_co_nz.CACHE_FILE = join(CACHE_DIR, f'{maoriplus_co_nz.__name__}.json')
|
|
if builtins.CONFIG.get("FRESH", False) is True:
|
|
dict_to_file(maoriplus_co_nz.CACHE_FILE, {})
|
|
|
|
if maoriplus_co_nz.API_DICT is None:
|
|
maoriplus_co_nz.API_DICT = maoriplus_co_nz.get_api_dict()
|
|
return maoriplus_co_nz
|
|
|
|
@staticmethod
|
|
def get_keys(challenge, additional):
|
|
raise Exception(
|
|
f'{BaseService.get_keys.__name__} must not be called '
|
|
f'for the {maoriplus_co_nz.__name__} service'
|
|
)
|
|
|
|
@staticmethod
|
|
def get_video_data(source_element):
|
|
if source_element.collection is None:
|
|
source_element.collection = join(
|
|
str(builtins.CONFIG["DOWNLOAD_COMMANDS"]["OUTPUT_MEDIA_PATH"]),
|
|
maoriplus_co_nz.__name__
|
|
)
|
|
|
|
video_id = None
|
|
if "/live-tv/" in source_element.url:
|
|
alias = source_element.url.split("/")[-1] + "-live"
|
|
offset = -maoriplus_co_nz.PAGE_SIZE
|
|
while True:
|
|
offset += maoriplus_co_nz.PAGE_SIZE
|
|
|
|
response = requests.get(
|
|
maoriplus_co_nz.ENTRIES_URL,
|
|
params={
|
|
'typeAlias': 'live-channels',
|
|
"size": maoriplus_co_nz.PAGE_SIZE,
|
|
"offset": offset
|
|
},
|
|
headers={'X-Session': maoriplus_co_nz.SESSION_KEY}
|
|
)
|
|
response = response.content.decode()
|
|
response = json.loads(response)
|
|
|
|
entries = response.get("entries", [])
|
|
if len(entries) == 0:
|
|
break
|
|
|
|
for entry in entries:
|
|
if entry.get("_meta", {}).get("entryAlias", "") == alias:
|
|
video_id = entry["bcChannelId"]
|
|
break
|
|
|
|
if video_id is not None:
|
|
break
|
|
|
|
if video_id is None:
|
|
raise CustomException(ERR_MSG.format(
|
|
type=f'{USER_ERROR}',
|
|
url=source_element.url,
|
|
reason="The content isn't available",
|
|
solution="Don't attempt to download it"
|
|
))
|
|
else:
|
|
alias = re.findall(r'/([^/]+)/play[^/]*', source_element.url)[0]
|
|
if not source_element.url.endswith("/play"):
|
|
video_id = source_element.url.split("/")[-1]
|
|
|
|
if video_id is None:
|
|
response = requests.get(
|
|
maoriplus_co_nz.ENTRY_URL.format(alias=alias),
|
|
headers={'X-Session': maoriplus_co_nz.SESSION_KEY}
|
|
)
|
|
response = response.content.decode()
|
|
response = json.loads(response)
|
|
|
|
if response.get("drmProtected", False) is True:
|
|
raise CustomException(ERR_MSG.format(
|
|
type=APP_ERROR,
|
|
url=source_element.url,
|
|
reason=f"DRM not supported. Widevine was found",
|
|
solution=f"Extend the {maoriplus_co_nz.__name__} service"
|
|
))
|
|
|
|
if source_element.element is None:
|
|
title = response.get("title", alias)
|
|
source_element.element = get_valid_filename(title)
|
|
video_id = response.get("brightcoveId", None)
|
|
|
|
if video_id is None:
|
|
raise CustomException(ERR_MSG.format(
|
|
type=f'{USER_ERROR}',
|
|
url=source_element.url,
|
|
reason="The content isn't available",
|
|
solution="Don't attempt to download it"
|
|
))
|
|
|
|
response = requests.get(
|
|
maoriplus_co_nz.PLAYBACK_URL.format(
|
|
account_id=maoriplus_co_nz.API_DICT["account_id"],
|
|
video_id=video_id
|
|
),
|
|
headers={'Accept': f'pk={maoriplus_co_nz.API_DICT["policy_key"]}'}
|
|
)
|
|
response = response.content.decode()
|
|
response = json.loads(response)
|
|
|
|
message = str(response).lower()
|
|
if "client_geo" in message and "access_denied" in message:
|
|
raise CustomException(ERR_MSG.format(
|
|
type=f'{USER_ERROR}',
|
|
url=source_element.url,
|
|
reason="Need New Zealander IP to access content",
|
|
solution="Use a VPN"
|
|
))
|
|
if "video_not_found" in message or "resource_not_found" in message:
|
|
raise CustomException(ERR_MSG.format(
|
|
type=f'{USER_ERROR}',
|
|
url=source_element.url,
|
|
reason="The content isn't available",
|
|
solution="Don't attempt to download it"
|
|
))
|
|
|
|
if "account_not_found" in message or "invalid_policy_key" in message:
|
|
raise CustomException(ERR_MSG.format(
|
|
type=USER_ERROR,
|
|
url=source_element.url,
|
|
reason="Can't access the video content because the account id/policy key expired",
|
|
solution=f'Delete the {maoriplus_co_nz.CACHE_FILE} cache manually or add the parameter --fresh to your command'
|
|
))
|
|
|
|
if source_element.element is None:
|
|
title = response.get("name", None)
|
|
if title in ["", None]:
|
|
title = alias
|
|
fields = response.get("custom_fields", {})
|
|
|
|
for f in ["season_number", "episode_number"]:
|
|
try:
|
|
title += " " + f[0] + fields[f]
|
|
except:
|
|
pass
|
|
source_element.element = get_valid_filename(title)
|
|
|
|
sources = response.get("sources", [])
|
|
sources = filter(lambda s: s.get("container", None) in ["", None], sources)
|
|
sources = sorted(
|
|
list(sources),
|
|
key=lambda s: maoriplus_co_nz.MPD_PRIORITY.get(s.get("type", ""), 0),
|
|
reverse=True
|
|
)
|
|
|
|
manifest = sources[0]["src"]
|
|
return manifest, None, {}
|
|
|
|
@staticmethod
|
|
def has_episodes(season_id):
|
|
episodes = json.loads(requests.get(
|
|
maoriplus_co_nz.EPISODES_URL.format(season_id=season_id),
|
|
params={'limit': 1, 'offset': 0}
|
|
).content.decode())
|
|
return type(episodes) is list and len(episodes) > 0
|
|
|
|
@staticmethod
|
|
def get_collection_elements(collection_url):
|
|
collection_url = clean_url(collection_url)
|
|
if "/play/" in collection_url or collection_url.endswith("/play") or "/live-tv/" in collection_url:
|
|
return [BaseElement(url=collection_url)]
|
|
if "/show/" not in collection_url:
|
|
return None
|
|
|
|
alias = collection_url.split("/")[-1]
|
|
response = requests.get(
|
|
maoriplus_co_nz.ENTRY_URL.format(alias=alias),
|
|
headers={'X-Session': maoriplus_co_nz.SESSION_KEY}
|
|
)
|
|
response = response.content.decode()
|
|
response = json.loads(response)
|
|
|
|
error = response.get("error", {})
|
|
message = error.get("status", "").lower()
|
|
code = error.get("code", "")
|
|
if "not found" in message or code in ["404"]:
|
|
raise CustomException(ERR_MSG.format(
|
|
type=f'{USER_ERROR}',
|
|
url=collection_url,
|
|
reason="The content isn't available",
|
|
solution="Don't attempt to download it"
|
|
))
|
|
|
|
collection_title = response.get("title", alias)
|
|
collection_title = join(
|
|
join(
|
|
str(builtins.CONFIG["DOWNLOAD_COMMANDS"]["OUTPUT_MEDIA_PATH"]),
|
|
maoriplus_co_nz.__name__
|
|
),
|
|
get_valid_filename(collection_title)
|
|
)
|
|
seasons_id = ",".join(response.get("seasons", []))
|
|
|
|
seasons_offset = -maoriplus_co_nz.PAGE_SIZE
|
|
season_index = 0
|
|
collection = []
|
|
while True:
|
|
seasons_offset += maoriplus_co_nz.PAGE_SIZE
|
|
|
|
seasons = requests.get(
|
|
maoriplus_co_nz.ENTRIES_URL,
|
|
headers={'X-Session': maoriplus_co_nz.SESSION_KEY},
|
|
params={
|
|
'id': seasons_id,
|
|
'offset': seasons_offset,
|
|
'size': maoriplus_co_nz.PAGE_SIZE
|
|
}
|
|
)
|
|
seasons = seasons.content.decode()
|
|
seasons = json.loads(seasons)
|
|
seasons = seasons.get("entries", [])
|
|
if len(seasons) == 0:
|
|
break
|
|
|
|
for season in seasons:
|
|
season_id = season["playlistId"]
|
|
if not maoriplus_co_nz.has_episodes(season_id):
|
|
continue
|
|
|
|
season_index += 1
|
|
check = check_range(True, season_index, None)
|
|
if check is True:
|
|
continue
|
|
elif check is False:
|
|
return collection
|
|
|
|
season_title = season.get("title", season.get("seasonTitle", ""))
|
|
if season_title is None:
|
|
season_title = ""
|
|
season_title = get_valid_filename(f'S{season_index} {season_title}')
|
|
|
|
episodes_offset = -maoriplus_co_nz.PAGE_SIZE
|
|
while True:
|
|
episodes_offset += maoriplus_co_nz.PAGE_SIZE
|
|
|
|
episodes = requests.get(
|
|
maoriplus_co_nz.EPISODES_URL.format(season_id=season_id),
|
|
params={
|
|
'limit': maoriplus_co_nz.PAGE_SIZE,
|
|
'offset': episodes_offset
|
|
}
|
|
)
|
|
episodes = episodes.content.decode()
|
|
episodes = json.loads(episodes)
|
|
if len(episodes) == 0:
|
|
break
|
|
|
|
for episode in episodes:
|
|
episode_index = int(episode["custom_fields"]["episode_number"])
|
|
check = check_range(False, season_index, episode_index)
|
|
if check in [True, False]:
|
|
continue
|
|
|
|
episode_title = episode.get("name", "")
|
|
episode_title = get_valid_filename(f'E{episode_index} {episode_title}')
|
|
episode_url = f'{collection_url}/play/{episode["id"]}'
|
|
|
|
collection.append(BaseElement(
|
|
url=episode_url,
|
|
collection=join(collection_title, season_title),
|
|
element=episode_title
|
|
))
|
|
|
|
return collection
|