This commit is contained in:
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 Normal file
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 Normal file
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 Normal file
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.

BIN
bin/tools/MediaInfo.exe Normal file

Binary file not shown.

BIN
bin/tools/aria2c.exe Normal file

Binary file not shown.

BIN
bin/tools/ffmpeg.exe Normal file

Binary file not shown.

BIN
bin/tools/ffplay.exe Normal file

Binary file not shown.

BIN
bin/tools/ffprobe.exe Normal file

Binary file not shown.

BIN
bin/tools/mkvmerge.exe Normal file

Binary file not shown.

BIN
bin/tools/mp4decrypt.exe Normal file

Binary file not shown.

BIN
bin/tools/mp4dump.exe Normal file

Binary file not shown.

13
checker.bat Normal file
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
]
}
}

2025
configs/KEYS/netflix.keys Normal file

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
}

0
configs/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

163
configs/config.py Normal file
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 Normal file
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