New leak
This commit is contained in:
parent
803a747616
commit
3ef6dbd5e1
15
BraviaCORE.toml
Normal file
15
BraviaCORE.toml
Normal file
@ -0,0 +1,15 @@
|
||||
# these keys are currently unused, probably isn't helpful, they appeared on an old patched internal
|
||||
# endpoint API's license URL. The bypass_key was url-encoded, and the cd_key was sent as-is.
|
||||
bypass_key = 'pLgRrNn!p$3&297EtrMOGQxnn3wSve4g'
|
||||
cd_key = 'eyJXVk1TTCI6MX0='
|
||||
device_model = 'XR-75Z9J'
|
||||
software_version = '8.0.0'
|
||||
|
||||
[playlists]
|
||||
unlimited = 'FCF0DFE0-D6C8-46D3-B95F-90E3539A0E72' # free titles
|
||||
library = '8CB878F7-256D-4A0F-AA93-F84B67FA1378' # on-demand titles via credits
|
||||
|
||||
[endpoints]
|
||||
login = 'https://service.privilegemovies.com/user/v6/login'
|
||||
redeem = 'https://service.privilegemovies.com/user/v6/productredemption'
|
||||
credits = 'https://service.privilegemovies.com/user/v6/credits'
|
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Bravia-Core-Script
|
||||
|
||||
You need to have a mandatory account
|
||||
If this L3 does not work, try another L3.
|
||||
enjoy
|
||||
|
||||
![image](https://www.avpasion.com/wp-content/uploads/2021/01/Bravia-Core-2.jpg)
|
651
braviacore.py
Normal file
651
braviacore.py
Normal file
@ -0,0 +1,651 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Module: BRAVIA-CORE
|
||||
# Created on: 01-12-2021
|
||||
# Authors: -∞WKS∞-
|
||||
# Version: 1.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from enum import Enum
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
import click
|
||||
import jsonpickle
|
||||
import requests
|
||||
import braviacoreConfig
|
||||
from click import Context
|
||||
|
||||
|
||||
class BraviaCORE(BaseService):
|
||||
"""
|
||||
Service code for Sony's Bravia CORE streaming service (https://electronics.sony.com/bravia-core).
|
||||
|
||||
\b
|
||||
Authorization: Credentials
|
||||
Security: UHD@L3 HD@L3
|
||||
|
||||
\b
|
||||
Tip: It's currently using unintentionally open internal API endpoints, use while you can!
|
||||
"""
|
||||
|
||||
ALIASES = ["CORE", "braviacore"]
|
||||
|
||||
@staticmethod
|
||||
@click.command(name="BraviaCORE", short_help="https://electronics.sony.com/bravia-core")
|
||||
@click.argument("title", type=str)
|
||||
@click.option("-x", "--internal", is_flag=True, default=False,
|
||||
help="Use the weird unintentionally open API endpoint with unrestricted title access.")
|
||||
@click.option("-vp", "--vprofile", default=None,
|
||||
type=click.Choice(["h264", "sdr", "hdr", "imax"], case_sensitive=False),
|
||||
help="Video Profile. Default will be highest quality/best compression.")
|
||||
@click.pass_context
|
||||
def cli(ctx: Context, **kwargs: Any) -> BraviaCORE:
|
||||
return BraviaCORE(ctx, **kwargs)
|
||||
|
||||
def __init__(self, ctx: Context, title: str, internal: bool, vprofile: Optional[str]):
|
||||
self.title = int(title) if title != "list" else title
|
||||
self.internal = internal
|
||||
self.vprofile = vprofile
|
||||
super().__init__(ctx)
|
||||
|
||||
self.session_id: str
|
||||
self.credits: int
|
||||
|
||||
self.configure()
|
||||
|
||||
def get_titles(self) -> Union[Title, list[Title]]:
|
||||
if self.title == "list":
|
||||
if self.internal:
|
||||
pages = self.get_cache("manifest_internal")
|
||||
if not pages.is_dir() or len(list(pages.iterdir())) == 0:
|
||||
self.log.exit(" - Endpoint was patched. Can only search cached pages, which you have none of.")
|
||||
raise
|
||||
samples = [
|
||||
sample
|
||||
for page in pages.iterdir()
|
||||
for sample in jsonpickle.decode(page.read_text("utf8"))
|
||||
]
|
||||
samples = sorted(samples, key=lambda s: int(s["ppId"]))
|
||||
for sample in samples:
|
||||
self.log.info(
|
||||
"{} | {} [{}] [{}]".format(
|
||||
sample["ppId"],
|
||||
sample["parent_product_name"],
|
||||
sample["alpha"],
|
||||
sample["quality"]
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.list_playlist(self.config["playlists"]["unlimited"], "Unlimited Streaming")
|
||||
self.list_playlist(self.config["playlists"]["library"], "Library")
|
||||
sys.exit(0)
|
||||
|
||||
res = self.session.get(
|
||||
url=f"https://service.privilegemovies.com/content/v6/metadata/{self.title}",
|
||||
params={"width": "300"}
|
||||
)
|
||||
title = res.json()
|
||||
if title["responseCode"] >= 19999:
|
||||
raise ValueError(
|
||||
f"Could not get metadata for {self.title}. "
|
||||
f"Error: {repr(ResponseCode(title['responseCode']))}. "
|
||||
f"URL: {res.request.url}"
|
||||
)
|
||||
|
||||
title["id"] = self.title
|
||||
|
||||
search = next(filter(lambda x: x["parentProductId"] == title["id"], self.search(title["title"])), None)
|
||||
if not search:
|
||||
self.log.exit(f"Could not get search result for {self.title}.")
|
||||
raise
|
||||
title["transactionTypes"] = sorted(search["transactionTypes"])
|
||||
|
||||
return Title(
|
||||
id_=title["id"],
|
||||
type_=Title.Types.MOVIE,
|
||||
name=title["title"],
|
||||
year=title["year"],
|
||||
original_lang=title["language"],
|
||||
source=self.ALIASES[0],
|
||||
service_data=title
|
||||
)
|
||||
|
||||
def get_tracks(self, title: Title) -> Tracks:
|
||||
profiles = sorted(
|
||||
[x.lower() for x in title.service_data['availableProfiles']],
|
||||
key=["imax", "hdr", "sdr", "h264"].index
|
||||
)
|
||||
profile = None
|
||||
if self.vprofile and self.vprofile in profiles:
|
||||
profile = self.vprofile.lower()
|
||||
elif profiles:
|
||||
profile = profiles[0]
|
||||
self.log.debug(f"Available Profiles: {profiles}")
|
||||
|
||||
if self.internal:
|
||||
res = self.search_manifest_internal(pp_id=title.service_data["id"])
|
||||
# disabled for now until true full format is discovered, specifically "s" (signature).
|
||||
# res["uri"] = self.prepare_manifest_url(res["uri"], res["movieId"])
|
||||
else:
|
||||
res = self.get_video(
|
||||
title.service_data["id"],
|
||||
transaction_type=title.service_data["transactionTypes"][-1],
|
||||
profile=profile
|
||||
)
|
||||
|
||||
tracks = Tracks.from_mpds(
|
||||
data=requests.get(res["uri"]).text,
|
||||
url=res["uri"].replace("service.privilegemovies.com/mg/drm", "cf.privilegemovies.com/drm"),
|
||||
lang=title.original_lang,
|
||||
source=self.ALIASES[0]
|
||||
)
|
||||
|
||||
for sub in res.get("subtitles") or []:
|
||||
if sub["extension"] == "vtt":
|
||||
continue # SRT should be available for the exact same sub
|
||||
if sub["languageCode"].lower() == "pp":
|
||||
sub["languageCode"] = "pt-BR"
|
||||
if sub["languageCode"].lower() == "cn":
|
||||
sub["languageCode"] = "zh-Hant"
|
||||
if sub["languageCode"].lower() == "zh":
|
||||
sub["languageCode"] = "zh-Hans"
|
||||
if self.session.head(sub["subtitleUrl"]).status_code == 404:
|
||||
self.log.warning(f" - Subtitle returned 404, skipping: {sub['subtitleUrl']}")
|
||||
continue
|
||||
tracks.add(TextTrack(
|
||||
id_="{}_{}_{}_sub".format(
|
||||
self.title,
|
||||
sub["languageCode"],
|
||||
hashlib.md5(sub["subtitleUrl"].encode()).hexdigest()[0:6]
|
||||
),
|
||||
source=self.ALIASES[0],
|
||||
url=sub["subtitleUrl"],
|
||||
# metadata
|
||||
codec=sub["extension"],
|
||||
language=sub["languageCode"],
|
||||
is_original_lang=title.original_lang and is_close_match(sub["languageCode"], [title.original_lang]),
|
||||
forced=sub["forced"],
|
||||
sdh="_CC_" in sub["subtitleUrl"]
|
||||
))
|
||||
|
||||
for track in tracks:
|
||||
if not track.language and title.original_lang:
|
||||
track.language = title.original_lang
|
||||
if isinstance(track, VideoTrack):
|
||||
track.hdr10 = profile in ("hdr", "imax") # TODO: What about DV? Could it be DV?
|
||||
track.extra = {"license_url": res["widevineLicenseServer"]}
|
||||
|
||||
return tracks
|
||||
|
||||
def get_chapters(self, title: Title) -> list[MenuTrack]:
|
||||
return []
|
||||
|
||||
def certificate(self, **kwargs: Any) -> bytes:
|
||||
# TODO: Hardcode the certificate
|
||||
return self.license(**kwargs)
|
||||
|
||||
def license(self, challenge: bytes, track: Track, **_: Any) -> bytes:
|
||||
for n in range(5):
|
||||
# even the official APK seems to need to retry at least twice
|
||||
res = self.session.post(
|
||||
url=track.extra["license_url"],
|
||||
data=challenge, # expects bytes
|
||||
# TODO: Need session ID? headers={"Session": self.session_id}
|
||||
).content
|
||||
if res and res != b"Unauthorized":
|
||||
print(res)
|
||||
return res
|
||||
self.log.exit(" - License api call failed, unable to get certificate or license.")
|
||||
raise
|
||||
|
||||
# Service specific functions
|
||||
|
||||
def configure(self) -> None:
|
||||
self.session.headers.update({
|
||||
"ApiKey": self.config["api_key"],
|
||||
"AppLanguage": "EN"
|
||||
})
|
||||
self.session_id = self.login()
|
||||
self.credits = self.get_available_credits()
|
||||
self.log.info(f" - Credits available: {self.credits}.")
|
||||
|
||||
def login(self) -> str:
|
||||
"""
|
||||
Log in to BraviaCORE and return a Session ID.
|
||||
:returns: Session ID.
|
||||
"""
|
||||
if not self.credentials:
|
||||
self.log.exit(" - No credentials provided, unable to log in.")
|
||||
raise
|
||||
res = self.session.post(
|
||||
url=self.config["endpoints"]["login"],
|
||||
json={
|
||||
"deviceIdentifier": self.config["device_id"],
|
||||
"deviceModel": self.config["device_model"],
|
||||
"softwareVersion": self.config["software_version"],
|
||||
"email": self.credentials.username,
|
||||
"password": self.credentials.password,
|
||||
}
|
||||
)
|
||||
try:
|
||||
data = res.json()
|
||||
except json.JSONDecodeError:
|
||||
self.log.exit(f" - Failed to get Session ID, response was not JSON: {res.text}")
|
||||
raise
|
||||
if data["responseCode"] >= 19999:
|
||||
self.log.exit(f" - Failed to log in. Error: {repr(ResponseCode(data['responseCode']))}.")
|
||||
raise
|
||||
return data["session"]
|
||||
|
||||
def get_available_credits(self) -> int:
|
||||
"""Get the amount of available credits in the account."""
|
||||
if not self.session_id:
|
||||
self.log.exit(" - Cannot get available credits, you must log in first")
|
||||
raise
|
||||
res = self.session.get(
|
||||
url=self.config["endpoints"]["credits"],
|
||||
headers={"Session": self.session_id}
|
||||
)
|
||||
try:
|
||||
data = res.json()
|
||||
except json.JSONDecodeError:
|
||||
self.log.debug(res.text)
|
||||
self.log.exit(" - Failed to get available credits, response was not JSON")
|
||||
raise
|
||||
return data["creditsAvailable"]
|
||||
|
||||
def redeem(self, pp_id: int) -> None:
|
||||
"""Redeem title by Parent Product ID using available credits."""
|
||||
if not self.session_id:
|
||||
self.log.exit(f" - Cannot redeem title {pp_id}, you must log in first")
|
||||
raise
|
||||
definition = "-6" # TODO: what's the -6? api refers to it as a "definition", seen -2 in v1.1.0 apk
|
||||
res = self.session.post(
|
||||
url=self.config["endpoints"]["redeem"],
|
||||
json={"parentProductIds": f"{pp_id}{definition}"}, # can be multiple values separated by ','.
|
||||
headers={"Session": self.session_id}
|
||||
)
|
||||
res_code = ResponseCode(res.json()["productResponseCodes"][0]["responseCode"])
|
||||
if res_code not in [ResponseCode.SUCCESS_REDEEMED, ResponseCode.SUCCESS_ALREAD_REDEEMED]:
|
||||
self.log.exit(f" - Failed to redeem title {pp_id}{definition}. Error: {repr(res_code)}")
|
||||
raise
|
||||
self.log.info(f" - Redeemed title {pp_id}{definition} [{repr(res_code)}]")
|
||||
|
||||
def get_video(self, pp_id: int, profile: Optional[str] = None, quality: int = 3000, sub_type: str = "srt",
|
||||
is_3d: bool = False, is_4k: bool = True, stream_type: int = 2, transaction_type: int = 1,
|
||||
restrictions_enabled: bool = True) -> dict:
|
||||
"""
|
||||
Get Video Manifest Information.
|
||||
|
||||
Parameters:
|
||||
pp_id: Parent Product ID.
|
||||
profile: Profile. imax, hdr, h264, None (best?)
|
||||
quality: Quality. 3000, 2160, 1080
|
||||
sub_type: Subtitle Format. vtt, srt
|
||||
is_3d: Request 3D Video.
|
||||
is_4k: Request UHD Video.
|
||||
stream_type: ? 2 Seems to be hardcoded.
|
||||
transaction_type: ? 1 is typical default.
|
||||
restrictions_enabled: ? True Seems to be hardcoded.
|
||||
"""
|
||||
res = self.session.get(
|
||||
url=f"https://service.privilegemovies.com/content/v6/video/{pp_id}",
|
||||
params={
|
||||
"profile": profile,
|
||||
"quality": quality,
|
||||
"subType": sub_type,
|
||||
"is3D": is_3d,
|
||||
"is4K": is_4k,
|
||||
"streamType": stream_type,
|
||||
"transactionType": transaction_type,
|
||||
"restrictionsEnabled": restrictions_enabled
|
||||
},
|
||||
headers={"Session": self.session_id}
|
||||
)
|
||||
res.raise_for_status()
|
||||
video = res.json()
|
||||
if video["responseCode"] > 19999:
|
||||
raise ValueError(
|
||||
f"Could not get manifest for {pp_id}. "
|
||||
f"Error: {repr(ResponseCode(video['responseCode']))}. "
|
||||
f"URL: {res.request.url}"
|
||||
)
|
||||
return dict(
|
||||
alpha=video["alpha"],
|
||||
audioLanguages=video["audioLanguages"].split(","),
|
||||
downloadable=video["downloadable"],
|
||||
expiryDate=video["linkExpiry"],
|
||||
fairPlayLicenseServer=video["fairPlayLicenseServer"],
|
||||
playReadyLicenseServer=video["playReadyLicenseServer"],
|
||||
widevineLicenseServer=video["widevineLicenseServer"],
|
||||
movieId=video["movieId"],
|
||||
trackingId=video["trackingId"],
|
||||
tracks=video["productTracks"],
|
||||
uri=next((x["url"] for x in video["productTracks"] if x["fileType"] in (20, 11)), None)
|
||||
)
|
||||
|
||||
def search_manifest_internal(self, pp_id: int, movie_id: Optional[int] = None) -> dict:
|
||||
"""
|
||||
Gets all manifest pages from the internal endpoint that was briefly open and returns only wanted title.
|
||||
Since it was closed/fixed, only the cached pages are searchable.
|
||||
It intentionally gets all manifests before checking for a match to have a safe sorted() check.
|
||||
"""
|
||||
samples = []
|
||||
pages = self.get_cache("manifest_internal")
|
||||
if pages.is_dir():
|
||||
samples = [
|
||||
sample
|
||||
for page in pages.iterdir()
|
||||
for sample in jsonpickle.decode(page.read_text("utf8"))
|
||||
if sample["ppId"] == pp_id
|
||||
]
|
||||
if samples:
|
||||
alphas = list(set(x["alpha"].upper() for x in samples))
|
||||
if len(alphas) > 1:
|
||||
print("Alpha List:")
|
||||
for i, a in enumerate(alphas):
|
||||
sub_count = sum(len(x.get('subtitles') or []) for x in samples if x['alpha'].upper() == a)
|
||||
print(f"{i + 1:02}: {a} (Has up to {sub_count} Subtitles)")
|
||||
alpha = input("Which alpha (version) do you wish to get? (#): ")
|
||||
alpha = alphas[int(alpha or 1) - 1]
|
||||
else:
|
||||
alpha = alphas[0]
|
||||
samples = [x for x in samples if x["alpha"].upper() == alpha.upper()]
|
||||
samples = sorted(samples, key=lambda t: int(t["job_number"] or 0))
|
||||
samples = sorted(samples, key=lambda t: "SDR" in t["quality"])
|
||||
samples = sorted(samples, key=lambda t: "HDR" in t["quality"])
|
||||
samples = sorted(samples, key=lambda t: "4K" in t["quality"])
|
||||
samples = sorted(samples, key=lambda t: "IMAX" in t["quality"])
|
||||
if movie_id:
|
||||
samples = sorted(samples, key=lambda t: int(t["movieId"]) == movie_id)
|
||||
chosen = samples[-1]
|
||||
if not chosen.get("subtitles"):
|
||||
chosen["subtitles"] = []
|
||||
for sample in samples:
|
||||
if sample["alpha"] != chosen["alpha"]:
|
||||
continue
|
||||
for subtitle in (sample.get("subtitles") or []):
|
||||
subtitle_data = "".join(reversed(subtitle["subtitleUrl"])).split("_", 1)[-1]
|
||||
if not any([
|
||||
"".join(reversed(x["subtitleUrl"])).split("_", 1)[-1] == subtitle_data
|
||||
for x in chosen["subtitles"]
|
||||
]):
|
||||
chosen["subtitles"].append(subtitle)
|
||||
chosen["widevineLicenseServer"] = chosen["drm_license_url"]
|
||||
return chosen
|
||||
self.log.exit(" - Title was not found in the internal endpoint, possibly in broken pages :/")
|
||||
raise
|
||||
|
||||
def get_playlist(self, playlist: str) -> list[Title]:
|
||||
titles = []
|
||||
page = 0
|
||||
while True:
|
||||
page += 1
|
||||
res = self.session.get(
|
||||
url=f"https://service.privilegemovies.com/content/v6/playlist/{playlist}/content",
|
||||
params={
|
||||
"kids": "false",
|
||||
"width": "300",
|
||||
"PageSize": "48",
|
||||
"PageNumber": str(page)
|
||||
}
|
||||
).json()
|
||||
res = res["products"]
|
||||
titles.extend([Title(
|
||||
id_=x["parentProductId"],
|
||||
type_=Title.Types.MOVIE if x["contentType"] == 1 else Title.Types.TV,
|
||||
name=x["title"],
|
||||
year=x["year"],
|
||||
season=x.get("season"),
|
||||
episode=x.get("episode"),
|
||||
episode_name=None, # TODO: Implement episode_name
|
||||
original_lang=x["language"],
|
||||
source=self.ALIASES[0],
|
||||
service_data=x
|
||||
) for x in res])
|
||||
if len(res) < 48:
|
||||
break
|
||||
return titles
|
||||
|
||||
def list_playlist(self, playlist: str, name: str) -> None:
|
||||
titles = self.get_playlist(playlist)
|
||||
self.log.info(f" > {name} ({len(titles)}):")
|
||||
for title in titles:
|
||||
self.log.info(
|
||||
"{} | {} ({}) [{}]".format(
|
||||
title.id,
|
||||
title.name,
|
||||
title.year or "???",
|
||||
",".join(map(str, title.service_data["transactionTypes"]))
|
||||
)
|
||||
)
|
||||
|
||||
def search(self, query: str) -> list[dict]:
|
||||
res = self.session.get(
|
||||
url=f"https://service.privilegemovies.com/content/v6/search/{query}",
|
||||
params={
|
||||
"kids": "false",
|
||||
"width": "0"
|
||||
}
|
||||
).json()
|
||||
return res["results"]
|
||||
|
||||
@staticmethod
|
||||
def prepare_manifest_url(url: str, movie_id: int) -> str:
|
||||
if "cf.privilegemovies.com/drm" in url:
|
||||
mr = base64.b64encode(json.dumps({
|
||||
"v": "7", # version
|
||||
"m": movie_id, # movie id, title.service_data["parentId"] maybe?
|
||||
"u": url, # original uri
|
||||
"minB": "0", # min bitrate
|
||||
"e": "Production", # environment
|
||||
"maxB": "2147483647", # max bitrate
|
||||
"mvas": "false", # ?
|
||||
"al": ["EN", "en-US", "ENG", "UKE", "UKH", "ENH", "ENA", "en-EN"], # audio languages, what purpose?
|
||||
"up": "2021-04-19T16:03:10.373", # when the file was uploaded
|
||||
"o": "cf", # output, CDN maybe?
|
||||
"f": base64.b64encode("-".join([
|
||||
# string format of above?
|
||||
str(movie_id),
|
||||
"manifest.mpd",
|
||||
"0-2147483647",
|
||||
"False",
|
||||
"cf",
|
||||
"637544449903730000",
|
||||
"Production",
|
||||
"7",
|
||||
"EN-en-US-ENG-UKE-UKH-ENH-ENA-en-EN"
|
||||
]).encode()).decode() + ".mpd",
|
||||
"s": "CRhH/PTdzH6zzowZu2k3jnRh7zw=" # hmac signature of f?
|
||||
}).encode()).decode()
|
||||
url = url.replace("cf.privilegemovies.com/drm", "service.privilegemovies.com/mg/drm")
|
||||
url += f"?mr={mr}"
|
||||
return url
|
||||
|
||||
|
||||
class ResponseCode(Enum):
|
||||
ACCEPTANCE_REQUIRED = 40070
|
||||
ACCOUNT_EXISTS = 40016
|
||||
AGE_NOT_CHECKED = 40015
|
||||
AUTO_REDEMPTION_UNAVAILABLE = 40027
|
||||
CANNOT_SET_EMPTY_WEBHOOK_URL = 40115
|
||||
CANT_DELETE_LAST_CONSUMER_PROFILE = 40092
|
||||
CANT_EXPIRE_LAST_PROFILE = 40108
|
||||
CANT_MAKE_LAST_PROFILE_KIDS = 40107
|
||||
CATEGORY_DEFINITION_ALREADY_EXISTS = 40135
|
||||
CATEGORY_DEFINITION_NOT_FOUND = 40134
|
||||
CHILD_PRODUCT_NOT_ACTIVE = 40031
|
||||
CODE_GENERATION_ERROR = 20006
|
||||
CONCURRENT_STREAM_LIMIT_REACHED = 40079
|
||||
CONSUMER_BLACK_LISTED = 40047
|
||||
CONSUMER_DEVICE_NOT_AUTHENTICATED = 40082
|
||||
CONSUMER_NOT_FOUND = 40103
|
||||
CONTENT_NOT_RENTED = 40128
|
||||
CREDIT_BUNDLE_NOT_FOUND = 40111
|
||||
CREDIT_BUNDLE_PRICE_NOT_FOUND = 40113
|
||||
DECLINED_PRIVACY_POLICY = 40013
|
||||
DECLINED_TERMS_AND_CONDITIONS = 40014
|
||||
DEFINITION_REQUIRED = 40065
|
||||
DELIVERY_TYPE_NOT_ALLOWED = 40028
|
||||
DEVICE_ACTIVATED_TOO_SOON = 40083
|
||||
DEVICE_BLACK_LISTED = 40049
|
||||
DEVICE_ID_REQUIRED = 40012
|
||||
DEVICE_LIMIT_REACHED = 40081
|
||||
DEVICE_MEMBERSHIP_NOT_FOUND = 20020
|
||||
DEVICE_MODEL_NOT_FOUND = 40140
|
||||
DEVICE_MODEL_REQUIRED = 40022
|
||||
DEVICE_NO_LONGER_ACTIVE = 40045
|
||||
DOWNLOAD_LIMIT_EXCEEDED = 40032
|
||||
DOWNLOAD_UNAVAILABLE = 40037
|
||||
EMAIL_ALREADY_IN_USE = 40102
|
||||
EMAIL_BLACK_LISTED = 40048
|
||||
FACEBOOK_LOGIN_DISABLED = 30002
|
||||
FACEBOOK_LOGIN_REQUIRED = 40072
|
||||
FAILED_TO_BLOCK = 40094
|
||||
FAILED_TO_DELETE_BLOCKED = 40095
|
||||
FAILED_TO_REDEEM_PRODUCT = 40023
|
||||
FAILED_TO_REDEEM_PRODUCT_DEFINITION = 40059
|
||||
FAILED_TO_REDEEM_VOUCHER = 20013
|
||||
FAILED_TO_REGISTER_DEVICE = 20010
|
||||
FAILED_TO_SEND_EMAIL = 20012
|
||||
FAILED_TO_UPDATE_DOWNLOAD_STATE = 20011
|
||||
FORCED_REDEMPTION_DOES_NOT_EXIST = 40064
|
||||
GCM_FAILED_TO_UPDATE_SERVICE = 40074
|
||||
GCM_INVALID_INSTANCE_ID = 40073
|
||||
GENERIC_NETWORK_ERROR = -1
|
||||
INCORRECT_PAYMENT_STATE = 40124
|
||||
INSUFFICIENT_PERMISSIONS = 40101
|
||||
INVALID_ACCEPTANCE_FORMAT = 40071
|
||||
INVALID_ACCESS_TOKEN = 40090
|
||||
INVALID_API_KEY = 30001
|
||||
INVALID_CHARACTERS_DETECTED = 40093
|
||||
INVALID_CONCURRENT_STREAM_EVENT = 40091
|
||||
INVALID_CONSUMER_DEVICE = 40080
|
||||
INVALID_CONSUMER_PROFILE = 40089
|
||||
INVALID_CONTENT_SELECTION_TYPE = 40053
|
||||
INVALID_COUNTRY_CODE = 40010
|
||||
INVALID_DOWNLOAD_REQUEST_CODE = 40006
|
||||
INVALID_EMAIL_FORMAT = 40008
|
||||
INVALID_FACEBOOK_TOKEN = 40051
|
||||
INVALID_IP_COUNTRY = 40038
|
||||
INVALID_IP_FORMAT = 40084
|
||||
INVALID_LICENSE_REQUEST_CODE = 40007
|
||||
INVALID_NONCE = 40000
|
||||
INVALID_PASSWORD = 40003
|
||||
INVALID_PASSWORD_FORMAT = 40009
|
||||
INVALID_PIN = 40096
|
||||
INVALID_PIN_FORMAT = 40097
|
||||
INVALID_PLAYSTATION_AUTH_CODE = 40139
|
||||
INVALID_PURCHASE_OPTION = 40109
|
||||
INVALID_PUSH_NOTIFICATION_DEVICE = 40086
|
||||
INVALID_QUALITY = 40060
|
||||
INVALID_REDEEM_DEVICE = 40138
|
||||
INVALID_REDEMPTION = 40026
|
||||
INVALID_SESSION_ID = 40001
|
||||
INVALID_SOFTWARE_VERSION = 40046
|
||||
INVALID_SPDID = 40041
|
||||
INVALID_TEMPORARY_PASSWORD = 40002
|
||||
INVALID_URL_PROVIDED = 40117
|
||||
INVALID_USERNAME = 40137
|
||||
INVALID_USERNAME_OR_PASSWORD = 40005
|
||||
INVALID_VOUCHER_CODE = 40004
|
||||
IP_BLACK_LISTED = 40050
|
||||
MOVIE_CREDIT_REDEMPTION_UNAVAILABLE = 40036
|
||||
MOVIE_NOT_FOUND = 40119
|
||||
MOVIE_TRACK_NOT_FOUND = 40118
|
||||
NOT_ENOUGH_CREDITS = 40021
|
||||
NOT_PRIMARY_DEVICE = 40033
|
||||
NO_CONSUMER_PREFERENCE_FOUND = 40078
|
||||
NO_CONTENT_FOUND = 40025
|
||||
NO_CONTENT_PATH = 40039
|
||||
NO_EMAIL_ADDRESS_RETRIEVED_FROM_FACEBOOK = 40069
|
||||
NO_MOVIES_OF_THE_MONTH_DEFINED = 40133
|
||||
NO_PIN_SET = 40098
|
||||
NO_STATIC_BANNER_FOUND = 40077
|
||||
OUT_DATED_SOFTWARE_VERSION = 40062
|
||||
PARENT_PRODUCT_DEFINITION_NOT_REDEEMED = 40061
|
||||
PARENT_PRODUCT_DEFINITION_NOT_RENTED = 40131
|
||||
PARENT_PRODUCT_DOES_NOT_EXIST = 40058
|
||||
PARENT_PRODUCT_EXISTS_IN_PLAYLIST = 40106
|
||||
PARENT_PRODUCT_IDS_MISSING = 40063
|
||||
PARENT_PRODUCT_ID_REQUIRED = 40068
|
||||
PARENT_PRODUCT_NOT_ACTIVE = 40056
|
||||
PARENT_PRODUCT_NOT_FOUND = 40110
|
||||
PARENT_PRODUCT_NOT_FOUND_IN_PLAYLIST = 40105
|
||||
PARENT_PRODUCT_NOT_REDEEMED = 40029
|
||||
PARENT_PRODUCT_UNAVAILABLE = 40044
|
||||
PASSWORDS_DO_NOT_MATCH = 40024
|
||||
PLAYLIST_CUSTOM_LIST_TYPE_NOT_SET = 40132
|
||||
PLAYLIST_NOT_FOUND = 40088
|
||||
PRODUCT_UNKNOWN_ERROR = 20007
|
||||
PROFILE_EXPIRED = 40099
|
||||
PROFILE_NAME_EXISTS = 40104
|
||||
PROMOTION_UNAVAILABLE = 40035
|
||||
PROMO_STATE_NOT_FOUND = 20018
|
||||
PURCHASE_LOCATION_REQUIRED = 40030
|
||||
REGISTRATION_FAILED = 20009
|
||||
RENTAL_PERIOD_ALREADY_EXISTS = 40126
|
||||
RENTAL_PERIOD_NOT_FOUND = 40125
|
||||
RENTING_DEVICE_LIMIT_REACHED = 40130
|
||||
RENTING_NOT_SUPPORTED_BY_WHITE_LABEL_CAMPAIGN = 40129
|
||||
REQUIREMENTS_NOT_FOUND = 40085
|
||||
SECONDARY_EMAIL_EXISTS = 40100
|
||||
SERIES_NOT_FOUND = 40136
|
||||
SERVER_ERROR = 20000
|
||||
SESSION_ERROR = 20008
|
||||
SESSION_EXPIRED = 40011
|
||||
SOFTWARE_VERSION_NOT_FOUND = 20019
|
||||
SPDID_EXPIRED = 40042
|
||||
SPDID_NO_SETUP = 20015
|
||||
SPDID_RAW_DEVICE_INVALID = 40043
|
||||
SPDID_REQUIRED = 40040
|
||||
SUBSCRIPTION_ALREADY_CANCELLED = 40127
|
||||
SUBSCRIPTION_INVOICE_NOT_FOUND = 40123
|
||||
SUBSCRIPTION_NOT_FOUND = 40122
|
||||
SUBSCRIPTION_PLAN_NOT_FOUND = 40121
|
||||
SUBTITLE_NOT_FOUND = 40120
|
||||
SUCCESS = 10000
|
||||
SUCCESS_ACCEPTANCE_REQUIRED = 10008
|
||||
SUCCESS_ALREAD_REDEEMED = 10002
|
||||
SUCCESS_END = 19999
|
||||
SUCCESS_FAILED_EMAIL = 10005
|
||||
SUCCESS_FAILED_REDEEMED = 10007
|
||||
SUCCESS_INVALID_LANGUAGE = 10001
|
||||
SUCCESS_INVALID_VOUCHER = 10004
|
||||
SUCCESS_OK_ALREADY_REDEEMED_VOUCHER_CODE = 10011
|
||||
SUCCESS_OK_ALREADY_RENTING = 10013
|
||||
SUCCESS_OK_INVALID_NOTIFICATION_MESSAGE = 10010
|
||||
SUCCESS_OK_SOME_FAILED = 10012
|
||||
SUCCESS_REDEEMED = 10006
|
||||
SUCCESS_TEMPORARY_PASSWORD_USED = 10003
|
||||
TEAM_NOT_FOUND = 40114
|
||||
THIRD_PARTY_INACTIVE = 30000
|
||||
TIER_PRICE_NOT_FOUND = 40112
|
||||
UNKNOWN_CAMPAIN_GROUP_ERROR = 20002
|
||||
UNKNOWN_CONSUMER_ERROR = 20004
|
||||
UNKNOWN_DEVICE_ERROR = 20003
|
||||
UNKNOWN_SESSION_ERROR = 20005
|
||||
UNKNOWN_SESSION_ERROR_2 = 20014
|
||||
UNKNOWN_THEME_ERROR = 20016
|
||||
UNKNOWN_WHITE_LABEL_CAMPAIGN_ERROR = 20017
|
||||
UNKNOWN_WHITE_LABEL_ERROR = 20001
|
||||
VIDEO_UNLIMITED_NOT_SUPPORTED = 30003
|
||||
VOUCHER_BUNDLE_NOT_ACTIVE = 40057
|
||||
VOUCHER_BUNDLE_NOT_STARTED = 40075
|
||||
VOUCHER_CODE_EXPIRED = 40019
|
||||
VOUCHER_CODE_HASNT_STARTED = 40055
|
||||
VOUCHER_CODE_INVALID = 40018
|
||||
VOUCHER_CODE_INVALID_COUNTRY = 40054
|
||||
VOUCHER_CODE_INVALID_USER_TYPE = 40052
|
||||
VOUCHER_CODE_REQUIRED = 40017
|
||||
VOUCHER_CODE_USED = 40020
|
||||
VOUCHER_CODE_WRONG_PROMO = 40076
|
||||
VOUCHER_REDEMPTION_UNAVAILABLE = 40034
|
||||
VOUCHER_RULE_INVALID_DEVICE = 40067
|
||||
WEBHOOK_EVENT_NOT_FOUND = 40116
|
||||
WHITE_LABEL_CAMPAIGN_DOWNLOAD_DISABLED = 40066
|
||||
WHITE_LABEL_CAMPAIGN_NOT_FOUND = 40087
|
BIN
device_client_id_blob
Normal file
BIN
device_client_id_blob
Normal file
Binary file not shown.
27
device_private_key
Normal file
27
device_private_key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA4sUKDpvMG/idF8oCH5AVSwFd5Mk+rEwOBsLZMYdliXWe1hn9
|
||||
mdE6u9pjsr+bLrZjlKxMFqPPxbIUcC1Ii7BFSje2Fd8kxnaIprQWxDPgK+NSSx7v
|
||||
Un452TyB1L9lx39ZBt0PlRfwjkCodX+I9y+oBga73NRh7hPbtLzXe/r/ubFBaEu+
|
||||
aRkDZBwYPqHgH1RoFLuyFNMjfqGcPosGxceDtvPysmBxB93Hk2evml5fjdYGg6tx
|
||||
z510g+XFPDFv7GSy1KuWqit83MqzPls9qAQMkwUc05ggjDhGCKW4/p97fn23WDFE
|
||||
3TzSSsQvyJLKA3s9oJbtJCD/gOHYqDvnWn8zPwIDAQABAoIBAQDCWe1Mp+o+7sx0
|
||||
XwWC15HoPruiIXg9YtGCqexLrqcvMEd5Z70Z32BfL8TSpbTyTA78lM6BeNPRs9Yg
|
||||
bi8GyYQZH7ZG+IAkN+LWPPJmJa+y7ZjSGSkzoksiC+GZ3I/2cwZyA3Qfa+0XfgLi
|
||||
8PMKJyXyREMt+DgWO57JQC/OakhRdCR19mM6NKd+ynd/IEz/NIbjMLDVKwW8HEPx
|
||||
N3r5CU9O96nr62DI68KVj3jwUR3cDi/5xfhosYhCQjHJuobNbeFR18dY2nQNLWYd
|
||||
S0wtskla1fl9eYHwYAzwru4wHT4WJC7+V4pscfCI0YZB6PslxDKrv73l5H1tz4cf
|
||||
Vy58NRSBAoGBAPSmjoVtQzTvQ6PZIs81SF1ulJI9kUpyFaBoSSgt+2ZkeNtF6Hih
|
||||
Zm7OVJ9wg9sfjpB3SFBUjuhXz/ts/t6dkA2PgCbrvhBMRKSGbfyhhtM2gRf002I4
|
||||
bJ7Y0C/ont4WzC/XbXEkAmh+fG2/JRvbdVQaIdyS6MmVHtCtRsHEQZS5AoGBAO1K
|
||||
IXOKAFA+320+Hkbqskfevmxrv+JHIdetliaREZwQH+VYUUM8u5/Kt3oyMat+mH90
|
||||
rZOKQK2zM8cz4tKclTUT54nrtICxeo6UHVc56FqXZ6sVvVgm8Cnvt1md4XwG4FwQ
|
||||
r/OlaM6Hr5HRf8dkzuzqm4ZQYRHGzZ6AMphj8Xu3AoGAdmo7p5dIJVH98kuCDrsi
|
||||
iJ6iaNpF/buUfiyb5EfFXD0bRj7jE6hDdTSHPxjtqVzv2zrxFHipJwqBz5dlEYlA
|
||||
FWA0ziHiv+66dsveZp4kLQ0/lMHaorre0E/vDJFSe/qa4DksbsvYIo2+WjxfkMk7
|
||||
U/bGFwZAiHmWDbkg+16rw3kCgYEAyyodWf9eJVavlakJ404vNrnP8KSQtfyRTUii
|
||||
toKewTBNHuBvM1JckoPOdCFlxZ+ukfIka56DojU8r+IM4qaOWdOg+sWE1mses9S9
|
||||
CmHaPzZC3IjQhRlRp5ZHNcOnu7lnf2wKOmH1Sl+CQydMcDwvr0lvv6AyfDXq9zps
|
||||
F2365CECgYEAmYgs/qwnh9m0aGDw/ZGrASoE0TxlpizPvsVDGx9t9UGC2Z+5QvAE
|
||||
ZcQeKoLCbktr0BnRLI+W1g+KpXQGcnSF9VX/qwUlf72XA6C6kobQvW+Yd/H/IN5d
|
||||
jPqoL/m41rRzm+J+9/Tfc8Aiy1kkllUYnVJdC5QLAIswuhI8lkaFTN4=
|
||||
-----END RSA PRIVATE KEY-----
|
Reference in New Issue
Block a user