MovixOpenSource/API/proxiesembed/drmproxy/services/vix_com.py
Movix 1401dc20f3 tird release
mon compte github a été détecté comme du spam je crois
2026-04-28 17:55:00 +02:00

336 lines
13 KiB
Python

import builtins
import json
import threading
from json import JSONDecodeError
from os.path import join
import requests
from utils.constants.macros import USER_ERROR, ERR_MSG
from utils.main_service import main_service
from utils.structs import BaseElement, CustomException, BaseService
from utils.tools.args import check_range
from utils.tools.cdm import get_pssh_from_cenc_pssh
from utils.tools.common import get_valid_filename, clean_url
class vix_com(BaseService):
DEMO_URLS = [
"https://vix.com/es-es/video/video-4564160",
"https://vix.com/es-es/video/video-4326106",
"https://vix.com/es-es/video/video-4561911",
"https://vix.com/es-es/detail/series-502",
"https://vix.com/es-es/detail/series-4715",
"https://vix.com/es-es/detail/series-4027",
]
PLAY_URL = "https://nxs.mp.lura.live/v1/play/{play_id}"
TOKEN_URL = 'https://vix.com/api/video/token'
ANON_URL = 'https://vix.com/api/proxy/user/create-anon-user'
GRAPHQL_URL = 'https://client-api.vix.com/gql/v2'
VIDEO_URL = "https://vix.com/video/video-{video_id}"
MANIFEST_PRIORITY = {"application/dash+xml": 1, 'application/x-mpegurl': 0}
BEARER_TOKEN = None
LOCK = None
PAGE_SIZE = 50
APP_PLATFORM = 'web'
APP_VERSION = 'v0.0.0'
APP_DEVICE = 'desktop'
@staticmethod
def test_service():
main_service.run_service(vix_com)
@staticmethod
def credentials_needed():
return {"NONE_NEEDED": True}
@staticmethod
def initialize_service():
if vix_com.LOCK is None:
vix_com.LOCK = threading.Lock()
return vix_com
@staticmethod
def get_keys(challenge, additional):
licence = requests.post(additional["license_url"], data=challenge)
licence.raise_for_status()
return licence.content
@staticmethod
def get_video_data(source_element):
play_id = source_element.url.split("/video-")[-1]
response = requests.get(
vix_com.TOKEN_URL,
params={'videoId': play_id},
headers={'x-video-type': 'VOD'}
)
response = response.content.decode()
try:
response = json.loads(response)
except JSONDecodeError:
raise CustomException(ERR_MSG.format(
type=f'{USER_ERROR}',
url=source_element.url,
reason="Need Colombian IP to access content",
solution="Use a VPN"
))
response = requests.post(
vix_com.PLAY_URL.format(play_id=play_id),
data={'token': response["token"]}
)
status_code = response.status_code
response = response.content.decode()
message = response.lower()
if status_code in [404] and "not found" in message:
raise CustomException(ERR_MSG.format(
type=f'{USER_ERROR}',
url=source_element.url,
reason="The content isn't available",
solution="Do not attempt to download it"
))
response = json.loads(response)
response = response["content"]
if source_element.element is None:
title = response.get("title", None)
if title in ["", None]:
title = f"Video_{play_id}"
source_element.element = get_valid_filename(title)
if source_element.collection is None:
source_element.collection = join(
str(builtins.CONFIG["DOWNLOAD_COMMANDS"]["OUTPUT_MEDIA_PATH"]),
vix_com.__name__
)
manifest = []
media = response.get("media", [])
if media is None:
media = []
for m in media:
try:
m["type"] = m["type"].lower()
except:
continue
if m["type"] in vix_com.MANIFEST_PRIORITY.keys():
manifest.append(m)
manifest = sorted(manifest, key=lambda mn: vix_com.MANIFEST_PRIORITY[mn["type"]], reverse=True)
manifest = manifest[0]
license_url = manifest.get('licenseUrl', None)
manifest = manifest["url"]
manifest_content = requests.get(manifest).content.decode()
try:
pssh_value = get_pssh_from_cenc_pssh(manifest_content, xml_node=":pssh")
except:
pssh_value = None
if pssh_value is None:
license_url = None
return manifest, pssh_value, {"license_url": license_url}
@staticmethod
def get_collection_elements(collection_url):
collection_url = clean_url(collection_url)
if "/video/" in collection_url and "/video-" in collection_url:
return [BaseElement(url=collection_url)]
if "/canales/" in collection_url:
return None
if "/detail/" not in collection_url or "/series-" not in collection_url:
return None
if vix_com.BEARER_TOKEN is None:
with vix_com.LOCK:
if vix_com.BEARER_TOKEN is None:
response = requests.post(
vix_com.ANON_URL, json={'installationId': 'installationId'}
)
response = response.content.decode()
try:
response = json.loads(response)
vix_com.BEARER_TOKEN = response["accessToken"]
assert len(vix_com.BEARER_TOKEN) > 0
except (JSONDecodeError, KeyError, AssertionError):
raise CustomException(ERR_MSG.format(
type=f'{USER_ERROR}',
url=collection_url,
reason="Need Colombian IP to access content",
solution="Use a VPN"
))
show_id = collection_url.split("/series-")[-1]
series_id = f'series:mcp:{show_id}'
collection_title = None
collection = []
season_after_id = None
season_index = 0
while True:
seasons_response = requests.post(
vix_com.GRAPHQL_URL,
headers={
'authorization': f'Bearer {vix_com.BEARER_TOKEN}',
'x-vix-platform': vix_com.APP_PLATFORM,
'x-vix-app-version': vix_com.APP_VERSION,
'x-vix-device-type': vix_com.APP_DEVICE
},
json={
'operationName': 'DetailData',
'variables': {
'id': series_id, 'navigationSection': {'urlPath': ''},
'pagination': {'first': vix_com.PAGE_SIZE, 'after': season_after_id}
},
'query': '''
query DetailData($id: ID!, $pagination: PaginationParams!) {
videoById(id: $id) {
...VideoContentFullFragment videoTypeData {
... on VideoTypeSeriesData { ...SeasonsConnectionBasicFragment }
}
}
}
fragment VideoContentFullFragment on VideoContent { ...VideoContentBasicFragment }
fragment VideoContentBasicFragment on VideoContent { title videoType }
fragment SeasonsConnectionBasicFragment on VideoTypeSeriesData {
seasonsConnection(pagination: $pagination) { edges { node { id title } } }
}
'''
}
)
seasons_response = seasons_response.content.decode()
seasons_response = json.loads(seasons_response)
if collection_title is None:
try:
collection_title = seasons_response["data"]["videoById"]["title"]
collection_title = get_valid_filename(collection_title)
assert len(collection_title) > 0
except:
collection_title = f"Series {show_id}"
collection_title = join(
join(
str(builtins.CONFIG["DOWNLOAD_COMMANDS"]["OUTPUT_MEDIA_PATH"]),
vix_com.__name__
),
get_valid_filename(collection_title)
)
try:
seasons = seasons_response["data"]["videoById"]["videoTypeData"]["seasonsConnection"]["edges"]
except:
seasons = []
if len(seasons) == 0:
break
season_after_id = None
for season in seasons:
season = season.get("node", None)
if season is None:
continue
season_id = season.get("id", "")
if season_id == "":
continue
season_after_id = season_id
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_title = join(
collection_title,
get_valid_filename(f'Season_{season_index} {season_title}')
)
episode_after_id = None
while True:
episodes_response = requests.post(
vix_com.GRAPHQL_URL,
headers={
'authorization': f'Bearer {vix_com.BEARER_TOKEN}',
'x-vix-platform': vix_com.APP_PLATFORM,
'x-vix-app-version': vix_com.APP_VERSION,
'x-vix-device-type': vix_com.APP_DEVICE
},
json={
'operationName': 'SeasonById',
'variables': {
'seriesId': series_id, 'seasonId': season_id,
'episodePagination': {
'first': vix_com.PAGE_SIZE,
'after': episode_after_id
},
'navigationSection': {'urlPath': ''}
},
'query': '''
query SeasonById(
$seriesId: ID!, $seasonId: ID!, $episodePagination: PaginationParams!
) {
seasonById(seriesId: $seriesId, seasonId: $seasonId) { ...SeasonByIdFragment }
}
fragment SeasonByIdFragment on Season {
episodesConnection(pagination: $episodePagination) {
edges { node { ...EpisodeFullFragment } }
}
}
fragment EpisodeFullFragment on VideoContent {
id title videoTypeData { ...VideoTypeEpisodeFullFragment }
}
fragment VideoTypeEpisodeFullFragment on VideoTypeEpisodeData { episodeNumber }
'''
}
)
episodes_response = episodes_response.content.decode()
episodes_response = json.loads(episodes_response)
try:
episodes = episodes_response["data"]["seasonById"]["episodesConnection"]["edges"]
except:
episodes = []
if len(episodes) == 0:
break
episode_after_id = None
for episode in episodes:
episode = episode.get("node", None)
if episode is None:
continue
episode_id = episode.get("id", "").split(":")[-1]
if episode_id == "":
continue
episode_after_id = episode["id"]
episode_index = episode["videoTypeData"]["episodeNumber"]
check = check_range(False, season_index, episode_index)
if check is True:
continue
elif check is False:
return collection
episode_url = vix_com.VIDEO_URL.format(video_id=episode_id)
episode_title = episode.get("title", None)
if episode_title in ["", None]:
episode_title = f"Id_{episode_id}"
episode_title = f'Episode_{episode_index} {episode_title}'
episode_title = get_valid_filename(episode_title)
collection.append(BaseElement(
url=episode_url,
collection=season_title,
element=episode_title
))
if episode_after_id is None:
break
if season_after_id is None:
break
return collection