patch-1
Khan 2021-09-01 02:57:54 +05:00
parent 9df940f1fd
commit bf3c3712dd
222 changed files with 1007430 additions and 0 deletions

13
#movie.bat 100644
View File

@ -0,0 +1,13 @@
@ECHO OFF
ECHO Put Netflix Id here :
set /p MPD=
ECHO Quality Select :
set /p qu=
NFripper.py %MPD% -o Downloads -q %qu% --main --prv ca-tor.pvdata.host --alang hin eng
echo
pause
@ECHO OFF

16
#series.bat 100644
View File

@ -0,0 +1,16 @@
@ECHO OFF
ECHO Put Netflix Id here :
set /p MPD=
ECHO Season No :
set /p se=
ECHO Quality Select :
set /p qu=
NFripper.py %MPD% -o Downloads -q %qu% -s %se% --main --prv ca-tor.pvdata.host --alang hin eng
echo
pause
@ECHO OFF

128
NFripper.py 100644
View File

@ -0,0 +1,128 @@
import argparse, json, os, logging
from configs.config import tool
from helpers.proxy_environ import proxy_env
from datetime import datetime
from services.netflix import netflix
script_name = "NF Ripper"
script_ver = "2.0.1.0"
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=f">>> {script_name} {script_ver} <<<")
parser.add_argument("content", nargs="?", help="Content URL or ID")
parser.add_argument("-q", dest="customquality", nargs=1, help="For configure quality of video.", default=[])
parser.add_argument("-o", dest="output", help="download all assets to directory provided.")
parser.add_argument("-f", dest="output_folder", help="force mux .mkv files to directory provided", action="store", default=None)
parser.add_argument("--nv", dest="novideo", help="dont download video", action="store_true")
parser.add_argument("--na", dest="noaudio", help="dont download audio", action="store_true")
parser.add_argument("--ns", dest="nosubs", help="dont download subs", action="store_true")
parser.add_argument("-e", dest="episodeStart", help="it will start downloading the season from that episode.", default=None)
parser.add_argument("-s", dest="season", help="it will start downloading the from that season.", default=None)
parser.add_argument("--keep", dest="keep", help="well keep all files after mux, by default all erased.", action="store_true")
parser.add_argument("--only-2ch-audio", dest="only_2ch_audio", help="to force get only eac3 2.0 Ch audios.", action="store_true")
parser.add_argument("--alang", dest="audiolang", nargs="*", help="download only selected audio languages", default=[],)
parser.add_argument("--AD", '--adlang', dest="AD", nargs="*", help="download only selected audio languages", default=[],)
parser.add_argument("--slang", dest="sublang", nargs="*", help="download only selected subtitle languages", default=[],)
parser.add_argument("--flang", dest="forcedlang", nargs="*", help="download only selected forced subtitle languages", default=[],)
parser.add_argument('-t', "--title", dest="titlecustom", nargs=1, help="Customize the title of the show", default=[],)
parser.add_argument('-p', "--prompt", dest="prompt", help="will Enable the yes/no prompt when URLs are grabbed.", action="store_true")
parser.add_argument('-keys', "--license", dest="license", help="print all profiles keys and exit.", action="store_true")
parser.add_argument("--audio-bitrate", dest="custom_audio_bitrate", nargs=1, help="For configure bitrate of audio.", default=[])
parser.add_argument("--aformat-2ch","--audio-format-2ch", dest="aformat_2ch",nargs=1, help="For configure format of audio.", default=[],)
parser.add_argument("--aformat-51ch","--audio-format-51ch", dest="aformat_51ch",nargs=1, help="For configure format of audio.", default=[],)
parser.add_argument("--android-login", dest="android_login", help="will log netflix using android api and save cookies nd build.", action="store_true",)
parser.add_argument("--search", action="store", dest="search", help="download using netflix search for the movie/show.", default=0,)
parser.add_argument("--hevc", dest="hevc", help="will return HEVC profile", action="store_true")
parser.add_argument("--hdr", dest="hdr", help="will return HDR profile", action="store_true")
parser.add_argument("--high", dest="video_high", help="return MSL High Video manifest for hpl videos, usually small size low bitrate.", action="store_true",)
parser.add_argument("--main", dest="video_main", help="return MSL Main Video manifest for mpl videos, usually Big size High bitrate.", action="store_true",)
parser.add_argument("--check", dest="check", help="hpl vs mpl.", action="store_true",)
parser.add_argument("--all-audios", dest="allaudios", help="all download audios of the movie/show", action="store_true",)
parser.add_argument("--all-forced", dest="allforcedlang", help="all download forced subs of the movie/show", action="store_true",)
parser.add_argument("--no-aria2c", dest="noaria2c", help="not use aria2c for download, will use python downloader.", action="store_true",)
# PROXY
parser.add_argument("--nrd", action="store", dest="nordvpn", help="add country for nordvpn proxies.", default=0,)
parser.add_argument("--prv", action="store", dest="privtvpn", help="add country for privtvpn proxies.", default=0,)
parser.add_argument("--no-dl-proxy", dest="no_download_proxy", help="do not use proxy will downloading files", action="store_true", default=False,)
# PACK
parser.add_argument("--gr", dest="muxer_group", help="add group name to use that will override the one in config", action="store", default=None)
parser.add_argument("--upload", dest="upload_ftp", help="upload the release after packing", action="store_true", default=None)
parser.add_argument("--pack", dest="muxer_pack", help="pack the release", action="store_true", default=None)
parser.add_argument("--confirm", dest="confirm_upload", help="ask confirming before upload the packed release", action="store_true", default=None)
parser.add_argument("--imdb", dest="muxer_imdb", help="add imdb for the title for packing", action="store", default=None)
parser.add_argument("--scheme", dest="muxer_scheme", help="set muxer scheme name", default=None)
# cleaner
parser.add_argument("--clean-add", dest="clean_add", nargs="*", help="add more extension of files to be deleted", default=[],)
parser.add_argument("--clean-exclude", dest="clean_exclude", nargs="*", help="add more extension of files to not be deleted", default=[],)
parser.add_argument("--log-level", default="info", dest="log_level", choices=["debug", "info", "error", "warning"], help="choose level")
parser.add_argument("--log-file", dest="log_file", help="set log file for debug", default=None)
args = parser.parse_args()
start = datetime.now()
if args.log_file:
logging.basicConfig(
filename=args.log_file,
format="%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %I:%M:%S %p",
level=logging.DEBUG,
)
else:
if args.log_level.lower() == "info":
logging.basicConfig(format="%(message)s", level=logging.INFO)
elif args.log_level.lower() == "debug":
logging.basicConfig(
format="%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %I:%M:%S %p",
level=logging.DEBUG,
)
elif args.log_level.lower() == "warning":
logging.basicConfig(
format="%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %I:%M:%S %p",
level=logging.WARNING,
)
elif args.log_level.lower() == "error":
logging.basicConfig(
format="%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %I:%M:%S %p",
level=logging.ERROR,
)
logging.getLogger(__name__)
group = {
"UPLOAD": args.upload_ftp,
"IMDB": args.muxer_imdb,
"SCHEME": args.muxer_scheme,
"PACK": args.muxer_pack,
"GROUP": args.muxer_group,
"CONFIRM": args.confirm_upload,
"EXTRA_FOLDER": args.output_folder,
}
# ~ commands
proxy, ip = proxy_env(args).Load()
commands = {"aria2c_extra_commands": proxy, "group": group}
logging.debug(commands)
if args.license:
args.prompt = False
l = "\n__________________________\n"
print(
f"\n-- {script_name} --{l}\nVERSION: {script_ver}{l}\nIP: {ip}{l}"
)
netflix_ = netflix(args, commands)
netflix_.main_netflix()
print(
"\nNFripper took {} Sec".format(
int(float((datetime.now() - start).total_seconds()))
)
) # total seconds

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

13
checker.bat 100644
View File

@ -0,0 +1,13 @@
@ECHO OFF
ECHO Put Netflix Id here :
set /p MPD=
ECHO Quality Select :
set /p qu=
NFripper.py %MPD% -o Downloads -q %qu% --check
echo
pause
@ECHO OFF

View File

@ -0,0 +1,113 @@
{
"BUILD_IDENTIFIER": "v970d0ec4",
"cookies": {
"_ga": [
"GA1.2.1140108570.1617215940",
0
],
"NetflixId": [
"v%3D2%26ct%3DBQAOAAEBEHOzu6KP28kdADXLobUUrROB0PoBjq3JxcX1vGfDcXUv4xVDQq5H6XsQyE1Frj5SPGO99pNqlZl9A5O3CZUdxiur6u9F5dqAyA0veRgT2bVpQnJ-h7kBmZdPSr3HyGHD9fMnj07AjgioUxeWFsJPky9pVuFRADdnSQXFFqK1NpW2TpDRpGEFRInaAZ9Hr-y6y4J7ggTgHQhbL8CI6NgAHFs8QgPEC9jcyvyFKQmRIBUeU-HS2-WSNlNwSxquZ4bNGBasoTIMGE_8A3R2Mo4lc-qio2Df8Qdv-elVwj8S6VeH8v-brCDpPk-VcDbQp1O2sPb9oKnbCS0DNYL3TqGXzMBNVrL_nHfxDEbPss8929Tc9Ra5HA3635DKbxEJc-tq3u9xUeb_DszXHIS5O-sg6595Pn-LPWBxphkPKdftdLYsQD1RzR0ena_UNrvr-8lIE5TxCXJhLXK0msAfypuevVH1XKXN2FsHJyR0JbbzkOYN2KNxCmRfNkYezY28fKRoufM1F6gF5HDMxuLzw_KjrDV6is18lbNbcmP32_TY2aJnWfHqSaGw5DWnQ3shDKo4SltLUjCuJzYGY7YJAEM-3ifbtwN-QgKyWTUsCwtrrYlDnpUy4rs3rnJyAu8EElWymMkV%26bt%3Ddbl%26ch%3DAQEAEAABABSprqjfXbW6YL0tVF0tz16hvWyTr9LF4jE.%26mac%3DAQEAEAABABRoyiGmz8qurX494b0O21l13oiiV94g9CU.",
0
],
"OptanonConsent": [
"isIABGlobal=false&amp;datestamp=Fri+Jun+18+2021+07%3A58%3A49+GMT-0700+(Pacific+Daylight+Time)&amp;version=6.6.0&amp;consentId=a93027f9-5a1c-410f-8272-e0a1ea3e658d&amp;interactionCount=1&amp;landingPath=NotLandingPage&amp;groups=C0001%3A1%2CC0002%3A1%2CC0004%3A0&amp;hosts=H1%3A1%2CH12%3A1%2CH13%3A1%2CH27%3A0%2CH28%3A0%2CH30%3A0&amp;AwaitingReconsent=false",
0
],
"SecureNetflixId": [
"v%3D2%26mac%3DAQEAEQABABS0Wg3y7pHcIuzNaFVA9OL1hOBszVcpdKc.%26dt%3D1623992368587",
0
],
"cf_token": [
"5d16d7e4-f422-4505-b81a-f13f81d06b71",
0
],
"flwssn": [
"f30c6737-15f8-4a97-8102-a0162bac69bc",
0
],
"memclid": [
"5eb11c58-f94f-46a1-9916-a877c78cc950",
0
],
"nfvdid": [
"BQFmAAEBEHVhRA5F47wt0VzEXxA4lotgv2JbGEnTuU3t2ACmhEmOKuDHWsixD22MEvRfk1SyBSysA4pNyu4UnaMbEBYYAAhCvmGD6sG8LGI_wNxCLpENpUPoF1FF8p3ZOUGBcLkMl5UDunPJGTSHibJ6rP5eZwgX",
0
],
"pas": [
"%7B%22supplementals%22%3A%7B%22muted%22%3Atrue%7D%7D",
0
],
"playerPerfMetrics": [
"%7B%22uiValue%22%3A%7B%22throughput%22%3A24045%2C%22throughputNiqr%22%3A0.1387332986123521%7D%2C%22mostRecentValue%22%3A%7B%22throughput%22%3A24044.9%2C%22throughputNiqr%22%3A0.1387332986123521%7D%7D",
0
],
"profilesNewSession": [
"0",
0
],
"__cfduid": [
"ddc5ba21ab87d546424a73381309e8af61619034211",
0
],
"_admrla": [
"2.0-a88ab21a-c44f-b78a-d6e3-05b3cde370fc",
0
],
"_awl": [
"2.1619452424.0.4-7dc0a353-a88ab21ac44fb78ad6e305b3cde370fc-6763652d617369612d6561737431-6086e208-1",
0
],
"_gid": [
"GA1.2.962657418.1619405560",
0
],
"ccpaApplies": [
"false",
0
],
"ccpaUUID": [
"e05cd352-14f3-4d80-9500-b1d81fee7cd0",
0
],
"dnsDisplayed": [
"true",
0
],
"signedLspa": [
"false",
0
],
"thx_guid": [
"8a067b33cfaa47539ea40975e2b80069",
0
],
"_sp_v1_csv": [
"null",
0
],
"_sp_v1_data": [
"2:334049:1619034213:0:9:0:9:0:0:_:-1",
0
],
"_sp_v1_lt": [
"1:",
0
],
"_sp_v1_opt": [
"1:",
0
],
"_sp_v1_ss": [
"1:H4sIAAAAAAAAAItWqo5RKimOUbKKhjHySnNydGKUUpHYJWCJ6traWFwSSjrUNwiffqVYAG6Fhl26AAAA",
0
],
"_sp_v1_uid": [
"1:365:378b6189-c59f-4807-abd1-95bcd2c5779d",
0
],
"consentUUID": [
"ef33150e-dde3-41af-b14c-18d858e5a51b",
0
]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
{
"mastertoken": {
"tokendata": "eyJzZXNzaW9uZGF0YSI6IkJRQ0FBQUVCRUF6SENReUt4ZklvZ3dQZ2FvMndhNGVCd0xTckZoK29QTGJHL20vWU9oQjQyZHloUUYxTmxkZm5NamNSTy96NUVKdnZJOGp4MllFYytWZk0rSlJ0RURMZFA1VTcyUWx5bS9RbnJXTmFuYlEyM3Fpa2YvbHRGYWFqbXlJU3NzdnpRU2VvL3hPL1p0TFZieGR6cWhYMG9DdmtLWlk1RFRXN3Y2aVh3dERZZlJhR0JRYXVLZjBpdERsY0ZWVVN1alExR2xSVklGZk42YVE3WEFKd3JFTFIvTlVqQmJmYnl6K2FrN1ZZVEhBZnFScHQ4WjBXZk0xa0VySjVJYjgvcE9iUU9FZHdPTlY3YW9lSjRMT2hNTnMycE9sZ3MwNkd6eUZ1Nk13RytPSHFqMURaVzdNL3hUOWdkM01ncGdKejFkY0c4ODlTU0hUdVVSc2JST0hmVy80akU2SlhySTUrSjZLT0UyeGRBdVVoNjY3TnlNMmtoOURBM25lOFBLaHRrTjlmdG1HdEszZ2l1S01xTERmdlQ3RDVGUGVaU1RKY25wenFjMk9YWW12RHBoY0JmWFVXZEhTUWRUbUtjU01zL1o4TUt5UVB3emhLMzFVVldWWllUbEIyODMzWG1WNDArSXZrK1gzdUN1MGlMUjJaVkRXYzh2T1V4dEFSWi9vM3ZEOC9heUtQMTh3UmI1WndzNkl2R0dRSkhPU2MwTHB6RS8zUmdxUXU2Z2FYVU5tWjdlMTEzWEVMdFZZQktzRmFZMktJQVViNFVvT1F1VnVkVnNMNTExUk5OQzJZYnA4Q1hvWlZlY2tNaU9ydDZNS3hNUFE9IiwicmVuZXdhbHdpbmRvdyI6MTYyNDA4NjI2Miwic2VyaWFsbnVtYmVyIjo1MDkzNTMyMzUxNTM2MjQsImV4cGlyYXRpb24iOjE2MjUyMDk0NjIsInNlcXVlbmNlbnVtYmVyIjoxfQ==",
"signature": "AQEAgQABASD1vbC9zQfnxwNr/7nLFkWyxRW7Z52QGWLEGk97hYBSYtNcJnM="
},
"sequence_number": 1,
"encryption_key": "YAeCFbGwVf/aukgoxE3tSg==",
"sign_key": "75QOynO/ysXBPimd9vCBSCI5/JQ7VCxAe4Ev/6hLJc4=",
"RSA_KEY": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAvMQdqQ7cG30cZdxYRamhySuvTWvUtsZp6EVmV4U1msqzceb5\nevK1D8rac87mdUUL9e5syaKTarVJe9bzl5lPnugSDPUEWuXS3vAynJA9pzUHEJdN\nZVkv43IdyXI+eBV+aJ4f+DB06MYvUxyDgPyfvCIKJPZ1Pj0ZHN2rhpK2bx2xnZh4\nBAoSIqlSYeChWB9O55sDYkCooAXd9zNJ7EBerqNlQr5sImY0izD1sPdfJiEfZLKj\n/+xr0PPrdTKeQmcR/r30K+4lVD1tYVPSy4MZsDVu3KgHtMBDPvx+DyaXdtdAdxpC\nkR6uqw+Q0H7KoedzdmlOMyvTVHLhgbVtxDEkuwIDAQABAoH/P6ydjoIypF12y3oE\nPDElo+f3dA14Dj/4geVauSLTCvifj1CUQpqUFeCyU9okZyhW+UvOsNqiOlyJjiwU\n9wYStIekZdNI1lhddsXRhorfSW8VoGpQf6WeWH3LK1o9I5+H0uu1eeoYXEvIP5Nm\n+iPqlKykv1+MwZsk1TTHyFLu1Afd7hG80c2UHHvvGioD0YregsrKXcLXi5OEkAS+\n5SiwKpbelwLcqK58SVN+ajnRvz6na6Fm4wjWq4wynrtX0RPD2P8+aw0X805o9fsP\nU3zRx4Dy3huazIBnadTqAEGbF73OWSA+Tow+LMRvUSqdWZMiX2ikLRwkTKFaaq+N\nObPhAoGBANqo5TbysRdRTerXxXqNlewEiYNH3Vn5pz4t1W/iRIT8BDKAD2FHcZ25\n4bfGcRmQ8wO3gcPPgVyXGf100RU0IR6lVjrcJT3w29lPs7AOhElDWMBC0fcA5JSd\nkWFMDUSobaXxZ8HYQJG6eQTEuQebm2PffppZUnVKTY2rhDaOCpmLAoGBAN0AXWiC\ngA8/6P48EYc8sQq/7KZ1g2Urxqxhc/wNU6FdKs582HMEeRYBA2PJsamYh0n/Wp5k\nfFJfb7HU4yCISzT7iHHjhLuuUR0UMmMkwY/x4YE5Ri1hlrS3SdGdfV6ufqe9jiGg\nboJ1rI0ieJmdyVWmhTK9CXuF/gB/6ik55CeRAoGBAMOr9orIfW9HY7mvY1n7T9lI\naiJf8hZtUZtT+rdHvVdgCwWCEcFU5LhnujTx0Q425zFBS0+F5taLpUdp/RzDbIv3\nGwZLMMyQOLzsFPmM1BaXvNk4MpqeYu8XXhy6qPjy3ERulhIiyg1e2KNKw+Wp+1FR\nlALdweuSFXqcrREA5T1nAoGBAIYoeou+7M5VFbN/84QNK8xCxf4myCTadjie0DHq\nRSJn1FyVHTB1PqxE4THqdpdlqHsbMH+GsJGwrbVebqKJGl6Hc0TvwNvN7h+g6xWU\ncoxXYXV4t0lFPJ9nxMAiwsB/XROm1mlDYtJ/bMggbOWUC2ybMbCjYOZDaPYUsKlm\nI0KBAoGAC0EsDBZZ6/ymQgenaNbTErN04i6Zii0U9XZ4Do72cRQTYsRELKqXyNwF\nwy//GzwqOXPFTaaY/RL0ISaV3xWU2MkI2Scmr+oGyEEKeE1uul9ZCo9l/2Jin2X9\n6gsH4QryiP+we7L1p2e1sdYmY3QWufuWjYI4e6iJ/vJX+WHM95g=\n-----END RSA PRIVATE KEY-----",
"expiration": 1625209462
}

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

163
configs/config.py 100644
View File

@ -0,0 +1,163 @@
import sys, os, random, string, platform
from os.path import dirname
from os.path import join
from pywidevine.cdm import cdm, deviceconfig
dirPath = dirname(dirname(__file__)).replace("\\", "/")
class utils:
def __init__(self):
self.dir = dirPath
def random_hex(self, length: int) -> str:
"""return {length} of random string"""
return "".join(random.choice("0123456789ABCDEF") for _ in range(length))
utils_ = utils()
#####################################(DEVICES)#####################################
devices_dict = {
"android_general": deviceconfig.device_android_general,
}
DEVICES = {
"NETFLIX-MANIFEST": devices_dict["android_general"],
"NETFLIX-LICENSE": devices_dict["android_general"],
}
#####################################(MUXER)#####################################
MUXER = {
"muxer_file": f"{dirPath}/bin/muxer.json",
"mkv_folder": None,
"DEFAULT": False, # to use the normal renaming. EX: Stranger Things S01E01 [1080p].mkv
"AUDIO": "hin", # default audio language.
"SUB": "None", # default subtitle language. EX: "eng" or "spa"
"GROUP": "Tandav", # to change the group name!. it's also possible to use this "--gr LOL", on the ripping commands.
"noTitle": False, # this will remove titles from the episodes EX: (The Witcher S01E01). insstead of (The Witcher S01E01 The End's Beginning).
"scheme": "p2p", # add/change any needed scheme naming. it's also possible to use this "--muxscheme repack", on the ripping commands.
"schemeslist": {
"p2p": "{t}.{r}.{s}.WEB-DL.{ac}.{vc}-{gr}",
"test": "{t}.{r}.{s}.WEB-DL-{gr}",
},
"EXTRAS": [], # extra mkvmerge.exe commands.
"FPS24": [],
}
#####################################(PATHS)#####################################
PATHS = {
"DL_FOLDER": "E:/#rips", #
"DIR_PATH": f"{dirPath}",
"BINARY_PATH": f"{dirPath}/bin",
"COOKIES_PATH": f"{dirPath}/configs/Cookies",
"KEYS_PATH": f"{dirPath}/configs/KEYS",
"TOKENS_PATH": f"{dirPath}/configs/Tokens",
"JSON_PATH": f"{dirPath}/json",
"LOGA_PATH": f"{dirPath}/bin/tools/aria2c",
}
ARIA2C = {
"enable_logging": False, # True
}
SETTINGS = {
"skip_video_demux": [],
}
#####################################(VPN)#####################################
VPN = {
"proxies": None, # "http://151.253.165.70:8080",
"nordvpn": {
"port": "80",
"email": "xxx",
"passwd": "xxx",
"http": "http://{email}:{passwd}@{ip}:{port}",
},
"private": {
"port": "8080",
"email": "abdalhmohmd8@gmail.com",
"passwd": "123456",
"http": "http://{email}:{passwd}@{ip}:{port}",
},
}
#####################################(BIN)#####################################
BIN = {
"mp4decrypt_moded": f"{dirPath}/bin/tools/mp4decrypt.exe",
"mp4dump": f"{dirPath}/bin/tools/mp4dump.exe",
"ffmpeg": f"{dirPath}/bin/tools/ffmpeg.exe",
"ffprobe": f"{dirPath}/bin/tools/ffprobe.exe",
"MediaInfo": f"{dirPath}/bin/tools/MediaInfo.exe",
"mkvmerge": f"{dirPath}/bin/tools/mkvmerge.exe",
"aria2c": f"{dirPath}/bin/tools/aria2c.exe",
}
#####################################(Config)#####################################
Config = {}
Config["NETFLIX"] = {
"cookies_file": f"{dirPath}/configs/Cookies/cookies_nf.txt",
"cookies_txt": f"{dirPath}/configs/Cookies/cookies.txt",
"keys_file": f"{dirPath}/configs/KEYS/netflix.keys",
"token_file": f"{dirPath}/configs/Tokens/netflix_token.json",
"email": "Cfklop@max07.club",
"password": "1111",
"manifest_language": "en-US",
"metada_language": "en",
"manifestEsn": "NFCDIE-03-{}".format(utils().random_hex(30)),
"androidEsn": "NFANDROID1-PRV-P-GOOGLEPIXEL=4=XL-8162-" + utils_.random_hex(64),
}
#####################################(DIRS & FILES)##############################
def make_dirs():
FILES = []
DIRS = [
f"{dirPath}/configs/Cookies",
f"{dirPath}/configs/Tokens",
f"{dirPath}/bin/tools/aria2c",
]
for dirs in DIRS:
if not os.path.exists(dirs):
os.makedirs(dirs)
for files in FILES:
if not os.path.isfile(files):
with open(files, "w") as f:
f.write("\n")
make_dirs()
#####################################(tool)#####################################
class tool:
def config(self, service):
return Config[service]
def bin(self):
return BIN
def vpn(self):
return VPN
def paths(self):
return PATHS
def muxer(self):
return MUXER
def devices(self):
return DEVICES
def aria2c(self):
return ARIA2C
def video_settings(self):
return SETTINGS

629
helpers/Muxer.py 100644
View File

@ -0,0 +1,629 @@

import re, os, sys, subprocess, contextlib, json, glob
from configs.config import tool
from helpers.ripprocess import ripprocess
from pymediainfo import MediaInfo
import logging
class Muxer(object):
def __init__(self, **kwargs):
self.logger = logging.getLogger(__name__)
self.CurrentName_Original = kwargs.get("CurrentName", None)
self.CurrentName = kwargs.get("CurrentName", None)
self.SeasonFolder = kwargs.get("SeasonFolder", None)
self.CurrentHeigh = kwargs.get("CurrentHeigh", None)
self.CurrentWidth = kwargs.get("CurrentWidth", None)
self.source_tag = kwargs.get("Source", None)
self.AudioProfile = self.get_audio_id() # kwargs.get("AudioProfile", None)
self.VideoProfile = self.get_video_id() # kwargs.get("VideoProfile", None)
self.mkvmerge = tool().bin()["mkvmerge"]
self.merge = []
self.muxer_settings = tool().muxer()
##############################################################################
self.packer = kwargs.get("group", None)
self.extra_output_folder = self.packer["EXTRA_FOLDER"]
self.Group = (
self.packer["GROUP"]
if self.packer["GROUP"]
else self.muxer_settings["GROUP"]
)
self.muxer_scheme = (
self.packer["SCHEME"]
if self.packer["SCHEME"]
else self.muxer_settings["scheme"]
)
self.scheme = self.muxer_settings["schemeslist"][self.muxer_scheme]
self.Extras = self.muxer_settings["EXTRAS"]
self.fps24 = True if self.source_tag in self.muxer_settings["FPS24"] else False
self.default_mux = True if self.muxer_settings["DEFAULT"] else False
self.PrepareMuxer()
def is_extra_folder(self):
extra_folder = None
if self.extra_output_folder:
if not os.path.isabs(self.extra_output_folder):
raise ValueError("Error you should provide full path dir: {}.".format(self.extra_output_folder))
if not os.path.exists(self.extra_output_folder):
try:
os.makedirs(self.extra_output_folder)
except Exception as e:
raise ValueError("Error when create folder dir [{}]: {}.".format(e, self.extra_output_folder))
extra_folder = self.extra_output_folder
return extra_folder
if self.muxer_settings["mkv_folder"]:
if not os.path.isabs(self.muxer_settings["mkv_folder"]):
raise ValueError("Error you should provide full path dir: {}.".format(self.muxer_settings["mkv_folder"]))
if not os.path.exists(self.muxer_settings["mkv_folder"]):
try:
os.makedirs(self.muxer_settings["mkv_folder"])
except Exception as e:
raise ValueError("Error when create folder dir [{}]: {}.".format(e, self.muxer_settings["mkv_folder"]))
extra_folder = self.muxer_settings["mkv_folder"]
return extra_folder
return extra_folder
def PrepareMuxer(self):
if self.muxer_settings["noTitle"]:
self.CurrentName = self.noTitle()
extra_folder = self.is_extra_folder()
if extra_folder:
self.SeasonFolder = extra_folder
else:
if not self.default_mux:
if self.SeasonFolder:
self.SeasonFolder = self.setFolder()
return
def SortFilesBySize(self):
file_list = []
audio_tracks = (
glob.glob(f"{self.CurrentName_Original}*.eac3")
+ glob.glob(f"{self.CurrentName_Original}*.ac3")
+ glob.glob(f"{self.CurrentName_Original}*.aac")
+ glob.glob(f"{self.CurrentName_Original}*.m4a")
+ glob.glob(f"{self.CurrentName_Original}*.dts")
)
if audio_tracks == []:
raise FileNotFoundError("no audio files found")
for file in audio_tracks:
file_list.append({"file": file, "size": os.path.getsize(file)})
file_list = sorted(file_list, key=lambda k: int(k["size"]))
return file_list[-1]["file"]
def GetVideoFile(self):
videofiles = [
"{} [{}p]_Demuxed.mp4",
"{} [{}p]_Demuxed.mp4",
"{} [{}p] [UHD]_Demuxed.mp4",
"{} [{}p] [UHD]_Demuxed.mp4",
"{} [{}p] [VP9]_Demuxed.mp4",
"{} [{}p] [HIGH]_Demuxed.mp4",
"{} [{}p] [VP9]_Demuxed.mp4",
"{} [{}p] [HEVC]_Demuxed.mp4",
"{} [{}p] [HDR]_Demuxed.mp4",
"{} [{}p] [HDR-DV]_Demuxed.mp4",
]
for videofile in videofiles:
filename = videofile.format(self.CurrentName_Original, self.CurrentHeigh)
if os.path.isfile(filename):
return filename
return None
def get_video_id(self):
video_file = self.GetVideoFile()
if not video_file:
raise ValueError("No Video file in Dir...")
media_info = MediaInfo.parse(video_file)
track = [track for track in media_info.tracks if track.track_type == "Video"][0]
if track.format == "AVC":
if track.encoding_settings:
return "x264"
return "H.264"
elif track.format == "HEVC":
if track.commercial_name == "HDR10" and track.color_primaries:
return "HDR.HEVC"
if track.commercial_name == "HEVC" and track.color_primaries:
return "HEVC"
return "DV.HEVC"
return None
def get_audio_id(self):
audio_id = None
media_info = MediaInfo.parse(self.SortFilesBySize())
track = [track for track in media_info.tracks if track.track_type == "Audio"][0]
if track.format == "E-AC-3":
audioCodec = "DDP"
elif track.format == "AC-3":
audioCodec = "DD"
elif track.format == "AAC":
audioCodec = "AAC"
elif track.format == "DTS":
audioCodec = "DTS"
elif "DTS" in track.format:
audioCodec = "DTS"
else:
audioCodec = "DDP"
if track.channel_s == 8:
channels = "7.1"
elif track.channel_s == 6:
channels = "5.1"
elif track.channel_s == 2:
channels = "2.0"
elif track.channel_s == 1:
channels = "1.0"
else:
channels = "5.1"
audio_id = (
f"{audioCodec}{channels}.Atmos"
if "Atmos" in track.commercial_name
else f"{audioCodec}{channels}"
)
return audio_id
def Heigh(self):
try:
Width = int(self.CurrentWidth)
Heigh = int(self.CurrentHeigh)
except Exception:
return self.CurrentHeigh
res1080p = "1080p"
res720p = "720p"
sd = ""
if Width >= 3840:
return "2160p"
if Width >= 2560:
return "1440p"
if Width > 1920:
if Heigh > 1440:
return "2160p"
return "1440p"
if Width == 1920:
return res1080p
elif Width == 1280:
return res720p
if Width >= 1400:
return res1080p
if Width < 1400 and Width >= 1100:
return res720p
if Heigh == 1080:
return res1080p
elif Heigh == 720:
return res720p
if Heigh >= 900:
return res1080p
if Heigh < 900 and Heigh >= 700:
return res720p
return sd
def noTitle(self):
regex = re.compile("(.*) [S]([0-9]+)[E]([0-9]+)")
if regex.search(self.CurrentName):
return regex.search(self.CurrentName).group(0)
return self.CurrentName
def Run(self, command):
self.logger.debug("muxing command: {}".format(command))
def unbuffered(proc, stream="stdout"):
newlines = ["\n", "\r\n", "\r"]
stream = getattr(proc, stream)
with contextlib.closing(stream):
while True:
out = []
last = stream.read(1)
# Don't loop forever
if last == "" and proc.poll() is not None:
break
while last not in newlines:
# Don't loop forever
if last == "" and proc.poll() is not None:
break
out.append(last)
last = stream.read(1)
out = "".join(out)
yield out
proc = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
universal_newlines=True,
)
self.logger.info("\nStart Muxing...")
for line in unbuffered(proc):
if "Progress:" in line:
sys.stdout.write("\r%s" % (line))
sys.stdout.flush()
elif "Multiplexing" in line:
sys.stdout.write("\r%s" % (line.replace("Multiplexing", "Muxing")))
sys.stdout.flush()
elif "Error" in line:
sys.stdout.write("\r%s" % (line))
sys.stdout.flush()
self.logger.info("")
def setName(self):
outputVideo = (
self.scheme.replace(
"{t}", ripprocess().CleanMyFileNamePlease(self.CurrentName)
)
.replace("{r}", self.Heigh())
.replace("{s}", self.source_tag)
.replace("{ac}", self.AudioProfile)
.replace("{vc}", self.VideoProfile)
.replace("{gr}", self.Group)
)
for i in range(10):
outputVideo = re.sub(r"(\.\.)", ".", outputVideo)
if self.SeasonFolder:
outputVideo = os.path.join(os.path.abspath(self.SeasonFolder), outputVideo)
outputVideo = outputVideo.replace("\\", "/")
return f"{outputVideo}.mkv"
def setFolder(self):
folder = (
self.scheme.replace(
"{t}", ripprocess().CleanMyFileNamePlease(self.SeasonFolder)
)
.replace("{r}", self.Heigh())
.replace("{s}", self.source_tag)
.replace("{ac}", self.AudioProfile)
.replace("{vc}", self.VideoProfile)
.replace("{gr}", self.Group)
)
for i in range(10):
folder = re.sub(r"(\.\.)", ".", folder)
return folder
def LanguageList(self):
LanguageList = [
["Hindi", "hin", "hin", "Hindi"],
["Tamil", "tam", "tam", "Tamil"],
["Telugu", "tel", "tel", "Telugu"],
["English", "eng", "eng", "English"],
["Afrikaans", "af", "afr", "Afrikaans"],
["Arabic", "ara", "ara", "Arabic"],
["Arabic (Syria)", "araSy", "ara", "Arabic Syria"],
["Arabic (Egypt)", "araEG", "ara", "Arabic Egypt"],
["Arabic (Kuwait)", "araKW", "ara", "Arabic Kuwait"],
["Arabic (Lebanon)", "araLB", "ara", "Arabic Lebanon"],
["Arabic (Algeria)", "araDZ", "ara", "Arabic Algeria"],
["Arabic (Bahrain)", "araBH", "ara", "Arabic Bahrain"],
["Arabic (Iraq)", "araIQ", "ara", "Arabic Iraq"],
["Arabic (Jordan)", "araJO", "ara", "Arabic Jordan"],
["Arabic (Libya)", "araLY", "ara", "Arabic Libya"],
["Arabic (Morocco)", "araMA", "ara", "Arabic Morocco"],
["Arabic (Oman)", "araOM", "ara", "Arabic Oman"],
["Arabic (Saudi Arabia)", "araSA", "ara", "Arabic Saudi Arabia"],
["Arabic (Tunisia)", "araTN", "ara", "Arabic Tunisia"],
[
"Arabic (United Arab Emirates)",
"araAE",
"ara",
"Arabic United Arab Emirates",
],
["Arabic (Yemen)", "araYE", "ara", "Arabic Yemen"],
["Armenian", "hye", "arm", "Armenian"],
["Assamese", "asm", "asm", "Assamese"],
["Bengali", "ben", "ben", "Bengali"],
["Basque", "eus", "baq", "Basque"],
["British English", "enGB", "eng", "British English"],
["Bulgarian", "bul", "bul", "Bulgarian"],
["Cantonese", "None", "chi", "Cantonese"],
["Catalan", "cat", "cat", "Catalan"],
["Simplified Chinese", "zhoS", "chi", "Chinese Simplified"],
["Traditional Chinese", "zhoT", "chi", "Chinese Traditional"],
["Croatian", "hrv", "hrv", "Croatian"],
["Czech", "ces", "cze", "Czech"],
["Danish", "dan", "dan", "Danish"],
["Dutch", "nld", "dut", "Dutch"],
["Estonian", "est", "est", "Estonian"],
["Filipino", "fil", "fil", "Filipino"],
["Finnish", "fin", "fin", "Finnish"],
["Flemish", "nlBE", "dut", "Flemish"],
["French", "fra", "fre", "French"],
["French Canadian", "caFra", "fre", "French Canadian"],
["Canadian French", "caFra", "fre", "Canadian French"],
["German", "deu", "ger", "German"],
["Greek", "ell", "gre", "Greek"],
["Gujarati", "guj", "guj", "Gujarati"],
["Hebrew", "heb", "heb", "Hebrew"],
["Hungarian", "hun", "hun", "Hungarian"],
["Icelandic", "isl", "ice", "Icelandic"],
["Indonesian", "ind", "ind", "Indonesian"],
["Italian", "ita", "ita", "Italian"],
["Japanese", "jpn", "jpn", "Japanese"],
["Kannada (India)", "kan", "kan", "Kannada (India)"],
["Khmer", "khm", "khm", "Khmer"],
["Klingon", "tlh", "tlh", "Klingon"],
["Korean", "kor", "kor", "Korean"],
["Lithuanian", "lit", "lit", "Lithuanian"],
["Latvian", "lav", "lav", "Latvian"],
["Malay", "msa", "may", "Malay"],
["Malayalam", "mal", "mal", "Malayalam"],
["Mandarin", "None", "chi", "Mandarin"],
["Mandarin Chinese (Simplified)", "zh-Hans", "chi", "Simplified"],
["Mandarin Chinese (Traditional)", "zh-Hant", "chi", "Traditional"],
["Yue Chinese", "yue", "chi", "(Yue Chinese)"],
["Manipuri", "mni", "mni", "Manipuri"],
["Marathi", "mar", "mar", "Marathi"],
["No Dialogue", "zxx", "zxx", "No Dialogue"],
["Norwegian", "nor", "nor", "Norwegian"],
["Norwegian Bokmal", "nob", "nob", "Norwegian Bokmal"],
["Persian", "fas", "per", "Persian"],
["Polish", "pol", "pol", "Polish"],
["Portuguese", "por", "por", "Portuguese"],
["Brazilian Portuguese", "brPor", "por", "Brazilian Portuguese"],
["Punjabi", "pan", "pan", "Punjabi"],
["Panjabi", "pan", "pan", "Panjabi"],
["Romanian", "ron", "rum", "Romanian"],
["Russian", "rus", "rus", "Russian"],
["Serbian", "srp", "srp", "Serbian"],
["Sinhala", "sin", "sin", "Sinhala"],
["Slovak", "slk", "slo", "Slovak"],
["Slovenian", "slv", "slv", "Slovenian"],
["Spanish", "spa", "spa", "Spanish"],
["European Spanish", "euSpa", "spa", "European Spanish"],
["Swedish", "swe", "swe", "Swedish"],
["Thai", "tha", "tha", "Thai"],
["Tagalog", "tgl", "tgl", "Tagalog"],
["Turkish", "tur", "tur", "Turkish"],
["Ukrainian", "ukr", "ukr", "Ukrainian"],
["Urdu", "urd", "urd", "Urdu"],
["Vietnamese", "vie", "vie", "Vietnamese"],
]
return LanguageList
def ExtraLanguageList(self):
ExtraLanguageList = [
["Polish - Dubbing", "pol", "pol", "Polish - Dubbing"],
["Polish - Lektor", "pol", "pol", "Polish - Lektor"],
]
return ExtraLanguageList
def AddChapters(self):
if os.path.isfile(self.CurrentName_Original + " Chapters.txt"):
self.merge += [
"--chapter-charset",
"UTF-8",
"--chapters",
self.CurrentName_Original + " Chapters.txt",
]
return
def AddVideo(self):
inputVideo = None
videofiles = [
"{} [{}p]_Demuxed.mp4",
"{} [{}p]_Demuxed.mp4",
"{} [{}p] [UHD]_Demuxed.mp4",
"{} [{}p] [UHD]_Demuxed.mp4",
"{} [{}p] [VP9]_Demuxed.mp4",
"{} [{}p] [HIGH]_Demuxed.mp4",
"{} [{}p] [VP9]_Demuxed.mp4",
"{} [{}p] [HEVC]_Demuxed.mp4",
"{} [{}p] [HDR]_Demuxed.mp4",
"{} [{}p] [HDR-DV]_Demuxed.mp4",
]
for videofile in videofiles:
filename = videofile.format(self.CurrentName_Original, self.CurrentHeigh)
if os.path.isfile(filename):
inputVideo = filename
break
if not inputVideo:
self.logger.info("cannot found video file.")
exit(-1)
if self.default_mux:
outputVideo = (
re.compile("|".join([".h264", ".h265", ".vp9", ".mp4"])).sub("", inputVideo)
+ ".mkv"
)
if self.SeasonFolder:
outputVideo = os.path.join(
os.path.abspath(self.SeasonFolder), outputVideo
)
outputVideo = outputVideo.replace("\\", "/")
else:
outputVideo = self.setName()
self.outputVideo = outputVideo
if self.fps24:
self.merge += [
self.mkvmerge,
"--output",
outputVideo,
"--default-duration",
"0:24000/1001p",
"--language",
"0:und",
"--default-track",
"0:yes",
"(",
inputVideo,
")",
]
else:
self.merge += [
self.mkvmerge,
"--output",
outputVideo,
"--title",
'RAB',
"(",
inputVideo,
")",
]
return
def AddAudio(self):
audiofiles = [
"{} {}.ac3",
"{} {} - Audio Description.ac3",
"{} {}.eac3",
"{} {} - Audio Description.eac3",
"{} {}.aac",
"{} {} - Audio Description.aac",
]
for (audio_language, subs_language, language_id, language_name,) in (
self.LanguageList() + self.ExtraLanguageList()
):
for audiofile in audiofiles:
filename = audiofile.format(self.CurrentName_Original, audio_language)
if os.path.isfile(filename):
self.merge += [
"--language",
f"0:{language_id}",
"--track-name",
"0:Audio Description" if 'Audio Description' in filename
else f"0:{language_name}",
"--default-track",
"0:yes"
if subs_language == self.muxer_settings["AUDIO"]
else "0:no",
"(",
filename,
")",
]
return
def AddSubtitles(self):
srts = [
"{} {}.srt",
]
forceds = [
"{} forced-{}.srt",
]
sdhs = [
"{} sdh-{}.srt",
]
for (
audio_language,
subs_language,
language_id,
language_name,
) in self.LanguageList():
for subtitle in srts:
filename = subtitle.format(self.CurrentName_Original, subs_language)
if os.path.isfile(filename):
self.merge += [
"--language",
f"0:{language_id}",
"--track-name",
f"0:{language_name}",
"--forced-track",
"0:no",
"--default-track",
"0:yes"
if subs_language == self.muxer_settings["SUB"]
else "0:no",
"--compression",
"0:none",
"(",
filename,
")",
]
for subtitle in forceds:
filename = subtitle.format(self.CurrentName_Original, subs_language)
if os.path.isfile(filename):
self.merge += [
"--language",
f"0:{language_id}",
"--track-name",
f"0:Forced",
"--forced-track",
"0:yes",
"--default-track",
"0:no",
"--compression",
"0:none",
"(",
filename,
")",
]
for subtitle in sdhs:
filename = subtitle.format(self.CurrentName_Original, subs_language)
if os.path.isfile(filename):
self.merge += [
"--language",
f"0:{language_id}",
"--track-name",
f"0:SDH",
"--forced-track",
"0:no",
"--default-track",
"0:no",
"--compression",
"0:none",
"(",
filename,
")",
]
return
def startMux(self):
self.AddVideo()
self.AddAudio()
self.AddSubtitles()
self.AddChapters()
if not os.path.isfile(self.outputVideo):
self.Run(self.merge + self.Extras)
return self.outputVideo

View File

@ -0,0 +1,551 @@
import base64, binascii, json, os, re, random, requests, string, time, traceback, logging
from datetime import datetime
from Cryptodome.Cipher import AES, PKCS1_OAEP
from Cryptodome.Util import Padding
from Cryptodome.Hash import HMAC, SHA256
from Cryptodome.PublicKey import RSA
from pywidevine.cdm import cdm, deviceconfig
from configs.config import tool
class MSLClient:
def __init__(self, profiles=None, wv_keyexchange=True, proxies=None):
self.session = requests.session()
self.logger = logging.getLogger(__name__)
if proxies:
self.session.proxies.update(proxies)
self.nf_endpoints = {
"manifest": "https://www.netflix.com/nq/msl_v1/cadmium/pbo_manifests/^1.0.0/router",
"license": "https://www.netflix.com/nq/msl_v1/cadmium/pbo_licenses/^1.0.0/router",
}
######################################################################
self.config = tool().config("NETFLIX")
self.email = self.config["email"]
self.password = self.config["password"]
self.device = tool().devices()["NETFLIX-MANIFEST"]
self.save_rsa_location = self.config["token_file"]
self.languages = self.config["manifest_language"]
self.license_path = None
######################################################################
if os.path.isfile(self.save_rsa_location):
self.generatePrivateKey = RSA.importKey(
json.loads(open(self.save_rsa_location, "r").read())["RSA_KEY"]
)
else:
self.generatePrivateKey = RSA.generate(2048)
if wv_keyexchange:
self.wv_keyexchange = True
self.cdm = cdm.Cdm()
self.cdm_session = None
else:
self.wv_keyexchange = False
self.cdm = None
self.cdm_session = None
self.manifest_challenge = '' # set desired wv data to overide wvexchange data
self.profiles = profiles
self.logger.debug("Using profiles: {}".format(self.profiles))
esn = self.config["androidEsn"]
if esn is None:
self.logger.error(
'\nandroid esn not found, set esn with cdm systemID in config.py'
)
else:
self.esn = esn
self.logger.debug("Using esn: " + self.esn)
self.messageid = random.randint(0, 2 ** 52)
self.session_keys = {} #~
self.header = {
"sender": self.esn,
"handshake": True,
"nonreplayable": 2,
"capabilities": {"languages": [], "compressionalgos": []},
"recipient": "Netflix",
"renewable": True,
"messageid": self.messageid,
"timestamp": time.time(),
}
self.setRSA()
def get_header_extra(self):
if self.wv_keyexchange:
self.cdm_session = self.cdm.open_session(
None,
deviceconfig.DeviceConfig(self.device),
b"\x0A\x7A\x00\x6C\x38\x2B",
True,
)
wv_request = base64.b64encode(
self.cdm.get_license_request(self.cdm_session)
).decode("utf-8")
self.header["keyrequestdata"] = [
{
"scheme": "WIDEVINE",
"keydata": {
"keyrequest": wv_request
}
}
]
else:
self.header["keyrequestdata"] = [
{
"scheme": "ASYMMETRIC_WRAPPED",
"keydata": {
"publickey": base64.b64encode(
self.generatePrivateKey.publickey().exportKey("DER")
).decode("utf8"),
"mechanism": "JWK_RSA",
"keypairid": "rsaKeypairId",
},
}
]
return self.header
def setRSA(self):
if os.path.isfile(self.save_rsa_location):
master_token = self.load_tokens()
expires = master_token["expiration"]
valid_until = datetime.utcfromtimestamp(int(expires))
present_time = datetime.now()
difference = valid_until - present_time
difference = difference.total_seconds() / 60 / 60
if difference < 10:
self.logger.debug("rsa file found. expired soon")
self.session_keys["session_keys"] = self.generate_handshake()
else:
self.logger.debug("rsa file found")
self.session_keys["session_keys"] = {
"mastertoken": master_token["mastertoken"],
"sequence_number": master_token["sequence_number"],
"encryption_key": master_token["encryption_key"],
"sign_key": master_token["sign_key"],
}
else:
self.logger.debug("rsa file not found")
self.session_keys["session_keys"] = self.generate_handshake()
def load_playlist(self, viewable_id):
payload = {
"version": 2,
"url": "/manifest", #"/licensedManifest"
"id": int(time.time()),
"languages": self.languages,
"params": {
#"challenge": self.manifest_challenge,
"type": "standard",
"viewableId": viewable_id,
"profiles": self.profiles,
"flavor": "STANDARD", #'PRE_FETCH'
"drmType": "widevine",
"usePsshBox": True,
"useHttpsStreams": True,
"supportsPreReleasePin": True,
"supportsWatermark": True,
'supportsUnequalizedDownloadables': True,
'requestEligibleABTests': True,
"isBranching": False,
'isNonMember': False,
'isUIAutoPlay': False,
"imageSubtitleHeight": 1080,
"uiVersion": "shakti-v4bf615c3",
'uiPlatform': 'SHAKTI',
"clientVersion": "6.0026.291.011",
'desiredVmaf': 'plus_lts', # phone_plus_exp
"showAllSubDubTracks": True,
#"preferredTextLocale": "ar",
#"preferredAudioLocale": "ar",
#"maxSupportedLanguages": 2,
"preferAssistiveAudio": False,
"deviceSecurityLevel": "3000",
'licenseType': 'standard',
'titleSpecificData': {
str(viewable_id): {
'unletterboxed': True
}
},
"videoOutputInfo": [
{
"type": "DigitalVideoOutputDescriptor",
"outputType": "unknown",
"supportedHdcpVersions": ['2.2'],
"isHdcpEngaged": True,
}
],
},
}
request_data = self.msl_request(payload)
response = self.session.post(self.nf_endpoints["manifest"], data=request_data)
manifest = json.loads(json.dumps(self.decrypt_response(response.text)))
if manifest.get("result"):
#with open('videoTraks.json', 'w', encoding='utf-8') as d:
#["result"]["video_tracks"]
# d.write(json.dumps(manifest, indent=2))
self.license_path = manifest["result"]["links"]["license"]["href"]
return manifest
if manifest