This commit is contained in:
widevinedump 2021-12-24 09:35:09 +05:30
parent 0bfa3f62e9
commit 4966f5355a
162 changed files with 250891 additions and 0 deletions

185
pydisney/disneyplus_api.py Normal file
View file

@ -0,0 +1,185 @@
import uuid, requests, json
class DSNP(object):
def __init__(self, DsnyID, Token, Type, Season=False, ishdr=False, isuhd=False, ishevc=False):
self.uhd = isuhd
self.hdr = ishdr
self.hevc = ishevc
self.isAmovie = True
self.DsnyID = DsnyID
self.contentTransactionId = uuid.uuid4()
self.Token = Token
if Type == 'show':
self.isAmovie = False
self.Season = Season
self.api = {
'DmcSeriesBundle': 'https://disney.content.edge.bamgrid.com/svc/content/DmcSeriesBundle/version/5.1/region/US/audience/false/maturity/1850/language/en/encodedSeriesId/{video_id}',
'DmcEpisodes': 'https://disney.content.edge.bamgrid.com/svc/content/DmcEpisodes/version/5.1/region/US/audience/false/maturity/1850/language/en/seasonId/{season_id}/pageSize/30/page/1',
'DmcVideo': 'https://disney.content.edge.bamgrid.com/svc/content/DmcVideoBundle/version/5.1/region/US/audience/false/maturity/1850/language/en/encodedFamilyId/{family_id}',
'LicenseServer': 'https://edge.bamgrid.com/widevine/v1/obtain-license',
'manifest': 'https://us.edge.bamgrid.com/media/{mediaid}/scenarios/{scenarios}'
}
self.scenarios = {
"default": "restricted-drm-ctr-sw",
"default_hevc": "handset-drm-ctr-h265",
"SD": "handset-drm-ctr",
"HD": "tv-drm-ctr",
"atmos": "tv-drm-ctr-h265-hdr10-atmos",
"uhd_sdr": "tv-drm-ctr-h265-atmos",
"uhd_hdr": "tv-drm-ctr-h265-hdr10-atmos",
"uhd_dv": "tv-drm-ctr-h265-dovi-atmos",
}
def load_info_m3u8(self, mediaId, mediaFormat, quality, isAtmos=False):
headers = {
"accept": "application/vnd.media-service+json; version=2",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36",
"Sec-Fetch-Mode": "cors",
"x-bamsdk-platform": "windows",
"x-bamsdk-version": '3.10',
"Origin": 'https://www.disneyplus.com',
"authorization": self.Token
}
if isAtmos:
atmosurl = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['uhd_hdr'])
resp = requests.get(atmosurl, headers=headers)
if resp.status_code != 200:
print('M3U8 - Error:' + str(resp.text))
return False
data = json.loads(resp.text)
atmos_url = data['stream']['complete']
if mediaFormat == 'SD':
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['SD'])
else:
if self.uhd:
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['uhd_sdr'])
elif self.hdr:
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['uhd_hdr'])
elif self.hevc:
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['uhd_sdr'])
elif int(quality) == 720:
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['default'])
elif int(quality) < 720:
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['SD'])
elif int(quality) == 720 and self.hevc:
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['default_hevc'])
else:
url = self.api['manifest'].format(mediaid=mediaId, scenarios=self.scenarios['HD'])
resp = requests.get(url=url, headers=headers)
if resp.status_code != 200:
print('M3U8 - Error:' + str(resp.text))
return False
data = json.loads(resp.text)
m3u8_url = data['stream']['complete']
if isAtmos:
return m3u8_url, atmos_url
return m3u8_url, None
def load_playlist(self):
headers = {
"accept": "application/json",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36",
"Sec-Fetch-Mode": "cors",
"x-bamsdk-platform": "windows",
"x-bamsdk-version": '3.10',
"authorization": f'Bearer {self.Token}'
}
if self.isAmovie:
url = self.api['DmcVideo'].format(family_id=self.DsnyID)
else:
url = self.api['DmcSeriesBundle'].format(video_id=self.DsnyID)
resp = requests.get(url=url, headers=headers)
if resp.status_code != 200:
print('DATA - Error:' + str(resp.text))
return False
data = resp.json()
if self.isAmovie:
data_info = data['data']['DmcVideoBundle']['video']
title = data_info['text']['title']['full']['program']['default']['content']
description = data_info['text']['description']['medium']['program']['default']['content']
js_data = {
'Title': title,
'Year': data_info['releases'][0]['releaseYear'],
'Description': description,
'mediaFormat': data_info['mediaMetadata']['format'],
'id': {
'contentId': data_info['contentId'],
'mediaId': data_info['mediaMetadata']['mediaId']
}
}
return js_data
else:
EpisodeList = []
data_info = data['data']['DmcSeriesBundle']
SeasonTitle = data_info['series']['text']['title']['full']['series']['default']['content']
for season in data_info['seasons']['seasons']:
if int(season['seasonSequenceNumber']) == int(self.Season):
SeascontentId = season['seasonId']
headers = {
"accept": "application/json",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36",
"Sec-Fetch-Mode": "cors",
"x-bamsdk-platform": "windows",
"x-bamsdk-version": '3.10',
"authorization": f'Bearer {self.Token}'
}
url = self.api['DmcEpisodes'].format(season_id=SeascontentId)
resp = requests.get(url=url, headers=headers)
if resp.status_code != 200:
print('DATA - Error:' + str(resp.text))
return False
JS = resp.json()
JS = JS['data']['DmcEpisodes']['videos']
for eps in JS:
EpisodesDict = {'contentId': eps['contentId'],
'mediaId': eps['mediaMetadata']['mediaId'],
'seasonNumber': eps['seasonSequenceNumber'],
'episodeNumber': eps['episodeSequenceNumber'],
'Title': SeasonTitle,
'mediaFormat': eps['mediaMetadata']['format']}
EpisodeList.append(EpisodesDict)
return EpisodeList
return

View file

@ -0,0 +1,162 @@
import re
import requests, json
class LOGIN(object):
def __init__(self, email, password, proxies=False):
self.email = email
self.password = password
self.web_page = 'https://www.disneyplus.com/login'
self.devices_url = "https://global.edge.bamgrid.com/devices"
self.login_url = 'https://global.edge.bamgrid.com/idp/login'
self.token_url = "https://global.edge.bamgrid.com/token"
self.grant_url = 'https://global.edge.bamgrid.com/accounts/grant'
self.SESSION = requests.Session()
if proxies:
self.SESSION.proxies.update(proxies)
def clientapikey(self):
r = self.SESSION.get(self.web_page)
match = re.search("window.server_path = ({.*});", r.text)
janson = json.loads(match.group(1))
clientapikey = janson["sdk"]["clientApiKey"]
return clientapikey
def assertion(self, client_apikey):
postdata = {
"applicationRuntime": "firefox",
"attributes": {},
"deviceFamily": "browser",
"deviceProfile": "macosx"
}
header = {"authorization": "Bearer {}".format(client_apikey), "Origin": "https://www.disneyplus.com"}
res = self.SESSION.post(url=self.devices_url, headers=header, json=postdata)
assertion = res.json()["assertion"]
return assertion
def access_token(self, client_apikey, assertion_):
header = {"authorization": "Bearer {}".format(client_apikey), "Origin": "https://www.disneyplus.com"}
postdata = {
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"latitude": "0",
"longitude": "0",
"platform": "browser",
"subject_token": assertion_,
"subject_token_type": "urn:bamtech:params:oauth:token-type:device"
}
res = self.SESSION.post(url=self.token_url, headers=header, data=postdata)
if res.status_code == 200:
access_token = res.json()["access_token"]
return access_token
if 'unreliable-location' in str(res.text):
print('Make sure you use NL proxy/vpn, or your proxy/vpn is blacklisted.')
exit()
else:
try:
print('Error: ' + str(res.json()["errors"]['error_description']))
exit()
except Exception:
print('Error: ' + str(res.text))
exit()
return None
def login(self, access_token):
headers = {
'accept': 'application/json; charset=utf-8',
'authorization': "Bearer {}".format(access_token),
'content-type': 'application/json; charset=UTF-8',
'Origin': 'https://www.disneyplus.com',
'Referer': 'https://www.disneyplus.com/login/password',
'Sec-Fetch-Mode': 'cors',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
'x-bamsdk-platform': 'windows',
'x-bamsdk-version': '3.10',
}
data = {'email': self.email, 'password': self.password}
res = self.SESSION.post(url=self.login_url, data=json.dumps(data), headers=headers)
if res.status_code == 200:
id_token = res.json()["id_token"]
return id_token
try:
print('Error: ' + str(res.json()["errors"]))
exit()
except Exception:
print('Error: ' + str(res.text))
exit()
return None
def grant(self, id_token, access_token):
headers = {
'accept': 'application/json; charset=utf-8',
'authorization': "Bearer {}".format(access_token),
'content-type': 'application/json; charset=UTF-8',
'Origin': 'https://www.disneyplus.com',
'Referer': 'https://www.disneyplus.com/login/password',
'Sec-Fetch-Mode': 'cors',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
'x-bamsdk-platform': 'windows',
'x-bamsdk-version': '3.10',
}
data = {'id_token': id_token}
res = self.SESSION.post(url=self.grant_url, data=json.dumps(data), headers=headers)
assertion = res.json()["assertion"]
return assertion
def FinalToken(self, subject_token, client_apikey):
header = {"authorization": "Bearer {}".format(client_apikey), "Origin": "https://www.disneyplus.com"}
postdata = {
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"latitude": "0",
"longitude": "0",
"platform": "browser",
"subject_token": subject_token,
"subject_token_type": "urn:bamtech:params:oauth:token-type:account"
}
res = self.SESSION.post(url=self.token_url, headers=header, data=postdata)
if res.status_code == 200:
access_token = res.json()["access_token"]
expires_in = res.json()["expires_in"]
return access_token, expires_in
try:
print('Error: ' + str(res.json()["errors"]))
exit()
except Exception:
print('Error: ' + str(res.text))
exit()
return None, None
def GetAuthToken(self):
clientapikey_ = self.clientapikey()
assertion_ = self.assertion(clientapikey_)
access_token_ = self.access_token(clientapikey_, assertion_)
id_token_ = self.login(access_token_)
user_assertion = self.grant(id_token_, access_token_)
TOKEN, EXPIRE = self.FinalToken(user_assertion, clientapikey_)
return TOKEN, EXPIRE

View file

@ -0,0 +1,251 @@
import os, subprocess, sys, contextlib
class Muxer(object):
def __init__(self, CurrentName, SeasonFolder, CurrentHeigh, Type, defaults, mkvmergeexe):
self.CurrentName = CurrentName
self.SeasonFolder = SeasonFolder
self.CurrentHeigh = CurrentHeigh
self.Type = Type
self.defaults = defaults
self.mkvmergeexe = mkvmergeexe
def mux(self, command):
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
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,
)
for line in unbuffered(proc):
if 'Progress:' in line:
sys.stdout.write("\r%s" % (line.replace('Progress:', 'Progress:')))
sys.stdout.flush()
print('')
return
def DPMuxer(self):
VideoInputNoExist = False
if os.path.isfile('.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p].h264'):
VideoInputName = '.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p].h264'
if self.Type == "show":
VideoOutputName = '.\\'+self.SeasonFolder+'\\'+self.CurrentName + '.mkv'
else:
VideoOutputName = '.\\'+self.CurrentName + '.mkv'
elif os.path.isfile('.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p].h265'):
VideoInputName = '.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p].h265'
if self.Type == "show":
VideoOutputName = '.\\'+self.SeasonFolder+'\\'+self.CurrentName + '.mkv'
else:
VideoOutputName = '.\\'+self.CurrentName + '.mkv'
elif os.path.isfile('.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p] [HEVC].h265'):
VideoInputName = '.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p] [HEVC].h265'
if self.Type == "show":
VideoOutputName = '.\\'+self.SeasonFolder+'\\'+self.CurrentName + '.mkv'
else:
VideoOutputName = '.\\'+self.CurrentName + '.mkv'
elif os.path.isfile('.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p] [HDR].h265'):
VideoInputName = '.\\'+self.CurrentName + ' [' + self.CurrentHeigh + 'p] [HDR].h265'
if self.Type == "show":
VideoOutputName = '.\\'+self.SeasonFolder+'\\'+self.CurrentName + '.mkv'
else:
VideoOutputName = '.\\'+self.CurrentName + '.mkv'
else:
VideoInputNoExist = True
if VideoInputNoExist == False:
AudioExtensionsList=[
".ac3",
".eac3",
".m4a",
".dts",
".mp3",
".aac",
]
SubsExtensionsList= [
".srt",
".ass",
]
language_tag = "English"
if language_tag == "English":
subs_forced = '[Forced]'
subs_full = ''
subs_sdh = '[SDH]'
LanguageList = [
["English", "eng", "eng", "English", "yes", "no"],
["Afrikaans", "af", "afr", "Afrikaans", "no", "no"],
["Arabic", "ara", "ara", "Arabic", "no", "no"],
["Arabic (Syria)", "araSy", "ara", "Arabic Syria", "no", "no"],
["Arabic (Egypt)", "araEG", "ara", "Arabic Egypt", "no", "no"],
["Arabic (Kuwait)", "araKW", "ara", "Arabic Kuwait", "no", "no"],
["Arabic (Lebanon)", "araLB", "ara", "Arabic Lebanon", "no", "no"],
["Arabic (Algeria)", "araDZ", "ara", "Arabic Algeria", "no", "no"],
["Arabic (Bahrain)", "araBH", "ara", "Arabic Bahrain", "no", "no"],
["Arabic (Iraq)", "araIQ", "ara", "Arabic Iraq", "no", "no"],
["Arabic (Jordan)", "araJO", "ara", "Arabic Jordan", "no", "no"],
["Arabic (Libya)", "araLY", "ara", "Arabic Libya", "no", "no"],
["Arabic (Morocco)", "araMA", "ara", "Arabic Morocco", "no", "no"],
["Arabic (Oman)", "araOM", "ara", "Arabic Oman", "no", "no"],
["Arabic (Saudi Arabia)", "araSA", "ara", "Arabic Saudi Arabia", "no", "no"],
["Arabic (Tunisia)", "araTN", "ara", "Arabic Tunisia", "no", "no"],
["Arabic (United Arab Emirates)", "araAE", "ara", "Arabic United Arab Emirates", "no", "no"],
["Arabic (Yemen)", "araYE", "ara", "Arabic Yemen", "no", "no"],
["Armenian", "hye", "arm", "Armenian", "no", "no"],
["Assamese", "asm", "asm", "Assamese", "no", "no"],
["Bangla", "ben", "ben", "Bengali", "no", "no"],
["Basque", "eus", "baq", "Basque", "no", "no"],
["Bulgarian", "bul", "bul", "Bulgarian", "no", "no"],
["Cantonese", "None", "chi", "Cantonese", "no", "no"],
["Catalan", "cat", "cat", "Catalan", "no", "no"],
["Simplified Chinese", "zhoS", "chi", "Chinese Simplified", "no", "no"],
["Traditional Chinese", "zhoT", "chi", "Chinese Traditional", "no", "no"],
["Croatian", "hrv", "hrv", "Croatian", "no", "no"],
["Czech", "ces", "cze", "Czech", "no", "no"],
["Danish", "dan", "dan", "Danish", "no", "no"],
["Dutch", "nld", "dut", "Dutch", "no", "no"],
["Estonian", "est", "est", "Estonian", "no", "no"],
["Filipino", "fil", "fil", "Filipino", "no", "no"],
["Finnish", "fin", "fin", "Finnish", "no", "no"],
["Flemish", "nlBE", "dut", "Flemish", "no", "no"],
["French", "fra", "fra", "French", "no", "no"],
["French Canadian", "caFra", "fre", "French Canadian", "no", "no"],
["German", "deu", "ger", "German", "no", "no"],
["Greek", "ell", "gre", "Greek", "no", "no"],
["Gujarati", "guj", "guj", "Gujarati", "no", "no"],
["Hebrew", "heb", "heb", "Hebrew", "no", "no"],
["Hindi", "hin", "hin", "Hindi", "no", "no"],
["Hungarian", "hun", "hun", "Hungarian", "no", "no"],
["Icelandic", "isl", "ice", "Icelandic", "no", "no"],
["Indonesian", "ind", "ind", "Indonesian", "no", "no"],
["Italian", "ita", "ita", "Italian", "no", "no"],
["Japanese", "jpn", "jpn", "Japanese", "no", "no"],
["Kannada (India)", "kan", "kan", "Kannada (India)", "no", "no"],
["Khmer", "khm", "khm", "Khmer", "no", "no"],
["Klingon", "tlh", "tlh", "Klingon", "no", "no"],
["Korean", "kor", "kor", "Korean", "no", "no"],
["Lithuanian", "lit", "lit", "Lithuanian", "no", "no"],
["Latvian", "lav", "lav", "Latvian", "no", "no"],
["Malay", "msa", "may", "Malay", "no", "no"],
["Malayalam", "mal", "mal", "Malayalam", "no", "no"],
["Mandarin", "None", "chi", "Mandarin", "no", "no"],
["Mandarin Chinese (Simplified)", "zh-Hans", "chi", "Simplified", "no", "no"],
["Mandarin Chinese (Traditional)", "zh-Hant", "chi", "Traditional", "no", "no"],
["Yue Chinese", "yue", "chi", "(Yue Chinese)", "no", "no"],
["Manipuri", "mni", "mni", "Manipuri", "no", "no"],
["Marathi", "mar", "mar", "Marathi", "no", "no"],
["No Dialogue", "zxx", "zxx", "No Dialogue", "no", "no"],
["Norwegian", "nor", "nor", "Norwegian", "no", "no"],
["Persian", "fas", "per", "Persian", "no", "no"],
["Polish", "pol", "pol", "Polish", "no", "no"],
["Portuguese", "por", "por", "Portuguese", "no", "no"],
["Brazilian Portuguese", "brPor", "por", "Brazilian Portuguese", "no", "no"],
["Punjabi", "pan", "pan", "Punjabi", "no", "no"],
["Romanian", "ron", "rum", "Romanian", "no", "no"],
["Russian", "rus", "rus", "Russian", "no", "no"],
["Serbian", "srp", "srp", "Serbian", "no", "no"],
["Sinhala", "sin", "sin", "Sinhala", "no", "no"],
["Slovak", "slk", "slo", "Slovak", "no", "no"],
["Slovenian", "slv", "slv", "Slovenian", "no", "no"],
["Spanish", "spa", "spa", "Spanish", "no", "no"],
["European Spanish", "euSpa", "spa", "European Spanish", "no", "no"],
["Swedish", "swe", "swe", "Swedish", "no", "no"],
["Tagalog", "tgl", "tgl", "Tagalog", "no", "no"],
["Tamil", "tam", "tam", "Tamil", "no", "no"],
["Telugu", "tel", "tel", "Telugu", "no", "no"],
["Thai", "tha", "tha", "Thai", "no", "no"],
["Turkish", "tur", "tur", "Turkish", "no", "no"],
["Ukrainian", "ukr", "ukr", "Ukrainian", "no", "no"],
["Urdu", "urd", "urd", "Urdu", "no", "no"],
["Vietnamese", "vie", "vie", "Vietnamese", "no", "no"], ]
ALLAUDIOS = []
for audio_language, subs_language, language_id, language_name, audio_default, subs_default in LanguageList:
for AudioExtension in AudioExtensionsList:
if os.path.isfile('.\\' + self.CurrentName + ' ' + language_id + AudioExtension):
if language_id == self.defaults['audio']:
ALLAUDIOS = ALLAUDIOS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name, '--default-track', '0:yes', '(', '.\\' + self.CurrentName + ' ' + language_id + AudioExtension, ')']
else:
ALLAUDIOS = ALLAUDIOS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name, '--default-track', '0:no', '(', '.\\' + self.CurrentName + ' ' + language_id + AudioExtension, ')']
OnlyOneLanguage = False
if len(ALLAUDIOS) == 9:
OnlyOneLanguage = True
elif len(ALLAUDIOS) == 18:
if ALLAUDIOS[1] == ALLAUDIOS[10]:
if ' - Audio Description' in ALLAUDIOS[7] or ' - Audio Description' in ALLAUDIOS[16]:
OnlyOneLanguage = True
else:
OnlyOneLanguage = False
ALLSUBS = []
for audio_language, subs_language, language_id, language_name, audio_default, subs_default in LanguageList:
if subs_language == self.defaults['sub']:
subs_default == 'yes'
for SubsExtension in SubsExtensionsList:
if os.path.isfile('.\\' + self.CurrentName + ' ' + 'forced-' + subs_language + SubsExtension):
if subs_language == self.defaults['sub']:
ALLSUBS = ALLSUBS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name + ' ' + subs_forced, '--forced-track', '0:yes', '--default-track', '0:no', '--compression', '0:none', '(', '.\\' + self.CurrentName + ' ' + 'forced-' + subs_language + SubsExtension, ')']
else:
ALLSUBS = ALLSUBS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name + ' ' + subs_forced, '--forced-track', '0:yes', '--default-track', '0:' + subs_default, '--compression', '0:none', '(', '.\\' + self.CurrentName + ' ' + 'forced-' + subs_language + SubsExtension, ')']
if OnlyOneLanguage == True:
pass
if os.path.isfile('.\\' + self.CurrentName + ' ' + subs_language + SubsExtension):
if subs_language == self.defaults['sub']:
ALLSUBS = ALLSUBS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name + ' ' + subs_full, '--forced-track', '0:no', '--default-track', '0:yes', '--compression', '0:none', '(', '.\\' + self.CurrentName + ' ' + subs_language + SubsExtension, ')']
else:
ALLSUBS = ALLSUBS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name + ' ' + subs_full, '--forced-track', '0:no', '--default-track', '0:' + subs_default, '--compression', '0:none', '(', '.\\' + self.CurrentName + ' ' + subs_language + SubsExtension, ')']
elif os.path.isfile('.\\' + self.CurrentName + ' ' + subs_language + SubsExtension):
if subs_language == self.defaults['sub']:
ALLSUBS = ALLSUBS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name + ' ' + subs_full, '--forced-track', '0:no', '--default-track', '0:yes', '--compression', '0:none', '(', '.\\' + self.CurrentName + ' ' + subs_language + SubsExtension, ')']
else:
ALLSUBS = ALLSUBS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name + ' ' + subs_full, '--forced-track', '0:no', '--default-track', '0:no', '--compression', '0:none', '(', '.\\' + self.CurrentName + ' ' + subs_language + SubsExtension, ')']
if os.path.isfile('.\\' + self.CurrentName + ' ' + 'sdh-' + subs_language + SubsExtension):
ALLSUBS = ALLSUBS + ['--language', '0:' + language_id, '--track-name', '0:' + language_name + ' ' + subs_sdh, '--forced-track', '0:no', '--default-track', '0:no', '--compression', '0:none', '(', '.\\' + self.CurrentName + ' ' + 'sdh-' + subs_language + SubsExtension, ')']
#MUX
mkvmerge_command_video = [self.mkvmergeexe,
'--output',
VideoOutputName,
'--language',
'0:und',
'--default-track',
'0:yes',
'(',
VideoInputName,
')']
mkvmerge_command = mkvmerge_command_video + ALLAUDIOS + ALLSUBS
self.mux(mkvmerge_command)

View file

@ -0,0 +1,231 @@
import os, clipboard, uuid, requests, re, sys, pycountry
from pydisney.m3u8_formater import M3U8
languageCodes = {
"zh-Hans": "zhoS",
"zh-Hant": "zhoT",
"pt-BR": "brPor",
"es-ES": "euSpa",
"en-GB": "enGB",
"en-PH": "enPH",
"nl-BE": "nlBE",
"fil": "enPH",
"yue": "zhoS",
'fr-CA':'caFra'
}
class Parser(object):
def __init__(self, m3u8, atmos_m3u8, is2ch=False):
self.m3u8 = m3u8
self.base = self.m3u8.rsplit('/', 1)[0] + '/'
if atmos_m3u8 is not None:
self.isAtmos = True
self.atmos_m3u8 = atmos_m3u8
self.atmos_base = self.atmos_m3u8.rsplit('/', 1)[0] + '/'
else:
self.isAtmos=False
self.is2ch = is2ch
def countrycode(self, code):
if code == 'cmn-Hans':
return 'Mandarin Chinese (Simplified)', 'zh-Hans'
elif code == 'cmn-Hant':
return 'Mandarin Chinese (Traditional)', 'zh-Hant'
elif code == 'es-419':
return 'Spanish', 'spa'
elif code == 'es-ES':
return 'European Spanish', 'euSpa'
elif code == 'pt-BR':
return 'Brazilian Portuguese', 'brPor'
elif code == 'pt-PT':
return 'Portuguese', 'por'
elif code == 'fr-CA':
return 'French Canadian', 'caFra'
elif code == 'fr-FR':
return 'French', 'fra'
lang_code = code[:code.index('-')] if '-' in code else code
lang = pycountry.languages.get(alpha_2=lang_code)
if lang is None:
lang = pycountry.languages.get(alpha_3=lang_code)
try:
languagecode = languageCodes[code]
except KeyError:
languagecode = lang.alpha_3
return lang.name, languagecode
def getCodec(self, codecs):
if ishevc or ishdr or ishdrdv:
search = 'hvc'
else:
search = 'avc'
l = []
for c in codecs.split(','):
if search in c:
l.append(c)
return l[-1]
def Parser(self):
AudioCodecs = None
AudioExtension = None
AudioList = []
subtitleList = []
forcedlist = []
videoList = []
added = set()
manifest_req = requests.get(self.m3u8)
video_manifest = M3U8(manifest_req.text)
if self.isAtmos:
atmos_req = requests.get(self.atmos_m3u8)
try:
audio_manifest = M3U8(atmos_req.text)
audio_base = self.atmos_base
audio_text = atmos_req.text
except ValueError:
audio_manifest = video_manifest
audio_base = self.base
audio_text = manifest_req.text
else:
audio_manifest = video_manifest
audio_base = self.base
audio_text = manifest_req.text
if self.isAtmos:
if 'atmos' in str(audio_text):
AudioCodecs = 'atmos'
AudioExtension = '.eac3'
else:
print('this item has no atmos.')
print('trying ac3 6ch...')
if 'eac-3' in str(audio_text):
AudioCodecs = 'eac-3'
AudioExtension = '.eac3'
else:
print('this item has no ac3 6ch, trying aac 2ch')
if 'aac-128k' in str(audio_text):
AudioCodecs = 'aac-128k'
AudioExtension = '.aac'
else:
sys.exit(1)
else:
if self.is2ch:
if 'aac-128k' in str(audio_text):
AudioCodecs = 'aac-128k'
AudioExtension = '.aac'
else:
print('this item has no aac 2ch')
sys.exit(1)
else:
if 'eac-3' in str(audio_text):
AudioCodecs = 'eac-3'
AudioExtension = '.eac3'
else:
print('this item has no ac3 6ch, trying aac 2ch')
if 'aac-128k' in str(audio_text):
AudioCodecs = 'aac-128k'
AudioExtension = '.aac'
else:
print('this item has no aac 2ch')
sys.exit(1)
if AudioCodecs is None or AudioExtension is None:
print('error while search for audio codec in m3u8 streams.')
sys.exit(1)
video_streams = [x for x in video_manifest.master_playlist if x['TAG'] == 'EXT-X-STREAM-INF']
audio_streams = [x for x in audio_manifest.master_playlist if x['TAG'] == 'EXT-X-MEDIA']
subs_streams = [x for x in video_manifest.master_playlist if x['TAG'] == 'EXT-X-MEDIA']
for video in video_streams:
if not video["URI"] in added:
bitrate = 'None'
if re.search('([0-9]*)k_', video["URI"]):
bitrate = str(re.search('([0-9]*)k_', video["URI"])[1])
else:
if re.search('([0-9]*)_complete', video["URI"]):
bitrate = str(re.search('([0-9]*)_complete', video["URI"])[1])
videoList.append(
{
'resolution': video["RESOLUTION"],
'codec': str(video["CODECS"]),
'bandwidth': str(video["BANDWIDTH"]),
'bitrate': bitrate,
'height': video["RESOLUTION"].rsplit('x', 1)[1],
'url': self.base+video["URI"]
}
)
added.add(video["URI"])
for m in audio_streams:
if m['TYPE'] == 'AUDIO' and m['GROUP-ID'] == AudioCodecs and m.get('CHARACTERISTICS') is None:
bitrate = 'None'
if re.search('([0-9]*)k_', m["URI"]):
bitrate = str(re.search('([0-9]*)k_', m["URI"])[1])
else:
if re.search('([0-9]*)_complete', m["URI"]):
bitrate = str(re.search('([0-9]*)_complete', m["URI"])[1])
bitrate = '768' if str(m['CHANNELS']) == '16/JOC' and int(bitrate) > 768 else bitrate
language, code = self.countrycode(m['LANGUAGE'])
Profile = m['GROUP-ID']
Profile = "aac" if "aac" in m['GROUP-ID'].lower() else Profile
Profile = "eac-3" if "eac-3" in m['GROUP-ID'].lower() else Profile
Profile = "atmos" if "joc" in m['GROUP-ID'].lower() else Profile
AudioList.append(
{
'language': str(language),
'code': str(code),
'bitrate': bitrate,
'codec': Profile,
'channels': str(m['CHANNELS'].replace('"', "").replace("/JOC", "")),
'url': audio_base+m['URI']
}
)
for m in subs_streams:
if m['TYPE'] == 'SUBTITLES' and m['FORCED'] == 'NO':
language, code = self.countrycode(m['LANGUAGE'])
subtitleList.append(
{
'language': str(language),
'code': str(code),
'url': self.base+m['URI']
}
)
if m['TYPE'] == 'SUBTITLES' and m['FORCED'] == 'NO' and m['LANGUAGE'] == 'en':
language, code = self.countrycode(m['LANGUAGE'])
subtitleList.append(
{
'language': str(language),
'code': 'sdh-' + str(code),
'url': self.base+m['URI']
}
)
if m['TYPE'] == 'SUBTITLES' and m['FORCED'] == 'YES':
language, code = self.countrycode(m['LANGUAGE'])
forcedlist.append(
{
'language': str(language),
'code': str(code),
'url': self.base+m['URI']
}
)
videoList = sorted(videoList, key=lambda k: int(k['bandwidth']))
print(videoList)
return videoList, AudioList, subtitleList, forcedlist, AudioExtension

472
pydisney/m3u8_formater.py Normal file
View file

@ -0,0 +1,472 @@
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
from __future__ import absolute_import
import binascii
import copy
import os
import random
import re
import time
from datetime import datetime
from datetime import timedelta
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers import modes
from svtplay_dl.error import ServiceError
from svtplay_dl.error import UIException
from svtplay_dl.fetcher import VideoRetriever
from svtplay_dl.subtitle import subtitle
from svtplay_dl.utils.http import get_full_url
from svtplay_dl.utils.output import ETA
from svtplay_dl.utils.output import output
from svtplay_dl.utils.output import progress_stream
from svtplay_dl.utils.output import progressbar
class HLSException(UIException):
def __init__(self, url, message):
self.url = url
super().__init__(message)
class LiveHLSException(HLSException):
def __init__(self, url):
super().__init__(url, "This is a live HLS stream, and they are not supported.")
def hlsparse(config, res, url, **kwargs):
streams = {}
if not res:
return streams
if res.status_code > 400:
streams[0] = ServiceError("Can't read HLS playlist. {}".format(res.status_code))
return streams
m3u8 = M3U8(res.text)
keycookie = kwargs.pop("keycookie", None)
authorization = kwargs.pop("authorization", None)
httpobject = kwargs.pop("httpobject", None)
output = kwargs.pop("output", None)
media = {}
subtitles = {}
segments = None
if m3u8.master_playlist:
for i in m3u8.master_playlist:
audio_url = None
if i["TAG"] == "EXT-X-MEDIA":
if "AUTOSELECT" in i and (i["AUTOSELECT"].upper() == "YES"):
if i["TYPE"] and i["TYPE"] != "SUBTITLES":
if "URI" in i:
if segments is None:
segments = True
if i["GROUP-ID"] not in media:
media[i["GROUP-ID"]] = []
media[i["GROUP-ID"]].append(i["URI"])
else:
segments = False
if i["TYPE"] == "SUBTITLES":
if "URI" in i:
if i["GROUP-ID"] not in subtitles:
subtitles[i["GROUP-ID"]] = []
item = [i["URI"], i["LANGUAGE"]]
if item not in subtitles[i["GROUP-ID"]]:
subtitles[i["GROUP-ID"]].append(item)
continue
elif i["TAG"] == "EXT-X-STREAM-INF":
bit_rate = float(i["BANDWIDTH"]) / 1000
if "AUDIO" in i and (i["AUDIO"] in media):
audio_url = get_full_url(media[i["AUDIO"]][0], url)
urls = get_full_url(i["URI"], url)
else:
continue # Needs to be changed to utilise other tags.
streams[int(bit_rate)] = HLS(
copy.copy(config),
urls,
bit_rate,
cookies=res.cookies,
keycookie=keycookie,
authorization=authorization,
audio=audio_url,
output=output,
segments=bool(segments),
kwargs=kwargs,
)
if subtitles and httpobject:
for sub in list(subtitles.keys()):
for n in subtitles[sub]:
m3u8s = M3U8(httpobject.request("get", get_full_url(n[0], url), cookies=res.cookies).text)
if "cmore" in url:
subtype = "wrstsegment" # this have been seen in tv4play
else:
subtype = "wrst"
streams[int(random.randint(1, 40))] = subtitle(
copy.copy(config),
subtype,
get_full_url(m3u8s.media_segment[0]["URI"], url),
subfix=n[1],
output=copy.copy(output),
m3u8=m3u8s,
)
elif m3u8.media_segment:
config.set("segments", False)
streams[0] = HLS(
copy.copy(config), url, 0, cookies=res.cookies, keycookie=keycookie, authorization=authorization, output=output, segments=False
)
else:
streams[0] = ServiceError("Can't find HLS playlist in m3u8 file.")
return streams
class HLS(VideoRetriever):
@property
def name(self):
return "hls"
def download(self):
self.output_extention = "ts"
if self.segments:
if self.audio:
self._download(self.audio, file_name=(copy.copy(self.output), "audio.ts"))
self._download(self.url, file_name=(self.output, "ts"))
else:
# Ignore audio
self.audio = None
self._download(self.url, file_name=(self.output, "ts"))
def _download(self, url, file_name):
cookies = self.kwargs.get("cookies", None)
start_time = time.time()
m3u8 = M3U8(self.http.request("get", url, cookies=cookies).text)
key = None
def random_iv():
return os.urandom(16)
file_d = output(file_name[0], self.config, file_name[1])
if file_d is None:
return
hls_time_stamp = self.kwargs.pop("hls_time_stamp", False)
decryptor = None
size_media = len(m3u8.media_segment)
eta = ETA(size_media)
total_duration = 0
duration = 0
max_duration = 0
for index, i in enumerate(m3u8.media_segment):
if "duration" in i["EXTINF"]:
duration = i["EXTINF"]["duration"]
max_duration = max(max_duration, duration)
total_duration += duration
item = get_full_url(i["URI"], url)
if not self.config.get("silent"):
if self.config.get("live"):
progressbar(size_media, index + 1, "".join(["DU: ", str(timedelta(seconds=int(total_duration)))]))
else:
eta.increment()
progressbar(size_media, index + 1, "".join(["ETA: ", str(eta)]))
data = self.http.request("get", item, cookies=cookies)
if data.status_code == 404:
break
data = data.content
if m3u8.encrypted:
headers = {}
if self.keycookie:
keycookies = self.keycookie
else:
keycookies = cookies
if self.authorization:
headers["authorization"] = self.authorization
# Update key/decryptor
if "EXT-X-KEY" in i:
keyurl = get_full_url(i["EXT-X-KEY"]["URI"], url)
if keyurl and keyurl[:4] == "skd:":
raise HLSException(keyurl, "Can't decrypt beacuse of DRM")
key = self.http.request("get", keyurl, cookies=keycookies, headers=headers).content
iv = binascii.unhexlify(i["EXT-X-KEY"]["IV"][2:].zfill(32)) if "IV" in i["EXT-X-KEY"] else random_iv()
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
decryptor = cipher.decryptor()
if decryptor:
data = decryptor.update(data)
else:
raise ValueError("No decryptor found for encrypted hls steam.")
file_d.write(data)
if self.config.get("capture_time") > 0 and total_duration >= self.config.get("capture_time") * 60:
break
if (size_media == (index + 1)) and self.config.get("live"):
sleep_int = (start_time + max_duration * 2) - time.time()
if sleep_int > 0:
time.sleep(sleep_int)
size_media_old = size_media
while size_media_old == size_media:
start_time = time.time()
if hls_time_stamp:
end_time_stamp = (datetime.utcnow() - timedelta(minutes=1, seconds=max_duration * 2)).replace(microsecond=0)
start_time_stamp = end_time_stamp - timedelta(minutes=1)
base_url = url.split(".m3u8")[0]
url = "{}.m3u8?in={}&out={}?".format(base_url, start_time_stamp.isoformat(), end_time_stamp.isoformat())
new_m3u8 = M3U8(self.http.request("get", url, cookies=cookies).text)
for n_m3u in new_m3u8.media_segment:
if not any(d["URI"] == n_m3u["URI"] for d in m3u8.media_segment):
m3u8.media_segment.append(n_m3u)
size_media = len(m3u8.media_segment)
if size_media_old == size_media:
time.sleep(max_duration)
file_d.close()
if not self.config.get("silent"):
progress_stream.write("\n")
self.finished = True
class M3U8:
# Created for hls version <=7
# https://tools.ietf.org/html/rfc8216
MEDIA_SEGMENT_TAGS = ("EXTINF", "EXT-X-BYTERANGE", "EXT-X-DISCONTINUITY", "EXT-X-KEY", "EXT-X-MAP", "EXT-X-PROGRAM-DATE-TIME", "EXT-X-DATERANGE")
MEDIA_PLAYLIST_TAGS = (
"EXT-X-TARGETDURATION",
"EXT-X-MEDIA-SEQUENCE",
"EXT-X-DISCONTINUITY-SEQUENCE",
"EXT-X-ENDLIST",
"EXT-X-PLAYLIST-TYPE",
"EXT-X-I-FRAMES-ONLY",
)
MASTER_PLAYLIST_TAGS = ("EXT-X-MEDIA", "EXT-X-STREAM-INF", "EXT-X-I-FRAME-STREAM-INF", "EXT-X-SESSION-DATA", "EXT-X-SESSION-KEY")
MEDIA_OR_MASTER_PLAYLIST_TAGS = ("EXT-X-INDEPENDENT-SEGMENTS", "EXT-X-START")
TAG_TYPES = {"MEDIA_SEGMENT": 0, "MEDIA_PLAYLIST": 1, "MASTER_PLAYLIST": 2}
def __init__(self, data):
self.version = None
self.media_segment = []
self.media_playlist = {}
self.master_playlist = []
self.encrypted = False
self.independent_segments = False
self.parse_m3u(data)
def __str__(self):
return "Version: {}\nMedia Segment: {}\nMedia Playlist: {}\nMaster Playlist: {}\nEncrypted: {}\tIndependent_segments: {}".format(
self.version, self.media_segment, self.media_playlist, self.master_playlist, self.encrypted, self.independent_segments
)
def parse_m3u(self, data):
if not data.startswith("#EXTM3U"):
raise ValueError("Does not appear to be an 'EXTM3U' file.")
data = data.replace("\r\n", "\n")
lines = data.split("\n")[1:]
last_tag_type = None
tag_type = None
media_segment_info = {}
for index, l in enumerate(lines):
if not l:
continue
elif l.startswith("#EXT"):
info = {}
tag, attr = _get_tag_attribute(l)
if tag == "EXT-X-VERSION":
self.version = int(attr)
# 4.3.2. Media Segment Tags
elif tag in M3U8.MEDIA_SEGMENT_TAGS:
tag_type = M3U8.TAG_TYPES["MEDIA_SEGMENT"]
# 4.3.2.1. EXTINF
if tag == "EXTINF":
if "," in attr:
dur, title = attr.split(",", 1)
else:
dur = attr
title = None
info["duration"] = float(dur)
info["title"] = title
# 4.3.2.2. EXT-X-BYTERANGE
elif tag == "EXT-X-BYTERANGE":
if "@" in attr:
n, o = attr.split("@", 1)
info["n"], info["o"] = (int(n), int(o))
else:
info["n"] = int(attr)
info["o"] = 0
# 4.3.2.3. EXT-X-DISCONTINUITY
elif tag == "EXT-X-DISCONTINUITY":
pass
# 4.3.2.4. EXT-X-KEY
elif tag == "EXT-X-KEY":
self.encrypted = True
info = _get_tuple_attribute(attr)
# 4.3.2.5. EXT-X-MAP
elif tag == "EXT-X-MAP":
info = _get_tuple_attribute(attr)
# 4.3.2.6. EXT-X-PROGRAM-DATE-TIME"
elif tag == "EXT-X-PROGRAM-DATE-TIME":
info = attr
# 4.3.2.7. EXT-X-DATERANGE
elif tag == "EXT-X-DATERANGE":
info = _get_tuple_attribute(attr)
media_segment_info[tag] = info
# 4.3.3. Media Playlist Tags
elif tag in M3U8.MEDIA_PLAYLIST_TAGS:
tag_type = M3U8.TAG_TYPES["MEDIA_PLAYLIST"]
# 4.3.3.1. EXT-X-TARGETDURATION
if tag == "EXT-X-TARGETDURATION":
info = int(attr)
# 4.3.3.2. EXT-X-MEDIA-SEQUENCE
elif tag == "EXT-X-MEDIA-SEQUENCE":
info = int(attr)
# 4.3.3.3. EXT-X-DISCONTINUITY-SEQUENCE
elif tag == "EXT-X-DISCONTINUITY-SEQUENCE":
info = int(attr)
# 4.3.3.4. EXT-X-ENDLIST
elif tag == "EXT-X-ENDLIST":
break
# 4.3.3.5. EXT-X-PLAYLIST-TYPE
elif tag == "EXT-X-PLAYLIST-TYPE":
info = attr
# 4.3.3.6. EXT-X-I-FRAMES-ONLY
elif tag == "EXT-X-I-FRAMES-ONLY":
pass
self.media_playlist[tag] = info
# 4.3.4. Master Playlist Tags
elif tag in M3U8.MASTER_PLAYLIST_TAGS:
tag_type = M3U8.TAG_TYPES["MASTER_PLAYLIST"]
# 4.3.4.1. EXT-X-MEDIA
if tag == "EXT-X-MEDIA":
info = _get_tuple_attribute(attr)
# 4.3.4.2. EXT-X-STREAM-INF
elif tag == "EXT-X-STREAM-INF":
info = _get_tuple_attribute(attr)
if "BANDWIDTH" not in info:
raise ValueError("Can't find 'BANDWIDTH' in 'EXT-X-STREAM-INF'")
info["URI"] = lines[index + 1]
# 4.3.4.3. EXT-X-I-FRAME-STREAM-INF
elif tag == "EXT-X-I-FRAME-STREAM-INF":
info = _get_tuple_attribute(attr)
# 4.3.4.4. EXT-X-SESSION-DATA
elif tag == "EXT-X-SESSION-DATA":
info = _get_tuple_attribute(attr)
# 4.3.4.5. EXT-X-SESSION-KEY
elif tag == "EXT-X-SESSION-KEY":
self.encrypted = True
info = _get_tuple_attribute(attr)
info["TAG"] = tag
self.master_playlist.append(info)
# 4.3.5. Media or Master Playlist Tags
elif tag in M3U8.MEDIA_OR_MASTER_PLAYLIST_TAGS:
tag_type = M3U8.TAG_TYPES["MEDIA_PLAYLIST"]
# 4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS
if tag == "EXT-X-INDEPENDENT-SEGMENTS":
self.independent_segments = True
# 4.3.5.2. EXT-X-START
elif tag == "EXT-X-START":
info = _get_tuple_attribute(attr)
self.media_playlist[tag] = info
# Unused tags
else:
pass
# This is a comment
elif l.startswith("#"):
pass
# This must be a url/uri
else:
tag_type = None
if last_tag_type is M3U8.TAG_TYPES["MEDIA_SEGMENT"]:
media_segment_info["URI"] = l
self.media_segment.append(media_segment_info)
media_segment_info = {}
last_tag_type = tag_type
if self.media_segment and self.master_playlist:
raise ValueError("This 'M3U8' file contains data for both 'Media Segment' and 'Master Playlist'. This is not allowed.")
def _get_tag_attribute(line):
line = line[1:]
try:
search_line = re.search(r"^([A-Z\-]*):(.*)", line)
return search_line.group(1), search_line.group(2)
except Exception:
return line, None
def _get_tuple_attribute(attribute):
attr_tuple = {}
for art_l in re.split(""",(?=(?:[^'"]|'[^']*'|"[^"]*")*$)""", attribute):
if art_l:
name, value = art_l.split("=", 1)
name = name.strip()
# Checks for attribute name
if not re.match(r"^[A-Z0-9\-]*$", name):
raise ValueError("Not a valid attribute name.")
# Remove extra quotes of string
if value.startswith('"') and value.endswith('"'):
value = value[1:-1]
attr_tuple[name] = value
return attr_tuple

80
pydisney/namehelper.py Normal file
View file

@ -0,0 +1,80 @@
import logging, os, re, subprocess, sys
from pymediainfo import MediaInfo
__LOGGER__ = logging.getLogger(__name__)
def rename(file, source, group):
base_name = file
name = os.path.splitext(os.path.basename(file))[0]
directory_name = os.path.dirname(file)
media_info = MediaInfo.parse(file)
for track in media_info.tracks:
if track.track_type == 'Video':
if int(track.width) == 1280 or int(track.height) == 720: resolution = '720p'
elif int(track.width) == 1920 or int(track.height) == 1080: resolution = '1080p'
else:
if int(track.height) > 600:
resolution = '{}p'.format(track.height)
else:
resolution = None
if track.format == "AVC":
if track.encoding_settings: codec = "x264"
else:codec = "H.264"
elif track.format == "HEVC":
if track.encoding_settings: codec = "x265"
else:codec = "H.265"
if 'Main 10@L5' in track.format_profile:
hdr = True
else:
hdr = None
try:
track = [track for track in media_info.tracks if track.track_type == "Audio"][0]
except IndexError:
track = track
if track.track_type == 'Audio':
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:
print("No Audio Root Found: {}".format(track.format))
audioCodec = None
if track.channel_s == 6:
if "Atmos" in track.commercial_name:
channels = '5.1.Atmos'
else:
channels = "5.1"
elif track.channel_s == 2: channels = "2.0"
elif track.channel_s == 1: channels = "1.0"
else:
print("No Audio Channel Found: {}".format(track.channel_s))
channels = None
name = name.replace(" ", ".").replace("'", "").replace(',', '')
if hdr is not None:
name = '{}.{}.{}.WEB-DL.HDR.{}{}.{}-{}'.format(
name, resolution, source, audioCodec, channels, codec, group).replace('.-.', '.')
else:
if resolution is None:
name = '{}.{}.WEB-DL.{}{}.{}-{}'.format(
name, source, audioCodec, channels, codec, group).replace('.-.', '.')
else:
name = '{}.{}.{}.WEB-DL.{}{}.{}-{}'.format(
name, resolution, source, audioCodec, channels, codec, group).replace('.-.', '.')
name = re.sub(r'(\.\.)', '.', name)
filename = '{}.mkv'.format(os.path.join(directory_name, name))
if os.path.exists(filename): os.remove(filename)
os.rename(base_name, filename)