Files
adobe-packager-windows/ccdl-win.py
Kamte e70d579ec1 Update ccdl-win.py
add platform switch
2024-03-08 19:47:57 +06:30

687 lines
24 KiB
Python

"""
This is the Adobe Offline Package downloader for Windows.
Download package only! Installer not included
FYI - If cannot down APRO, please re-run the script :)
Check line number 593
Based on https://github.com/Drovosek01/adobe-packager
"""
import os
import string
import random
import argparse
import json
import locale
import ctypes
import sys
from collections import OrderedDict
from xml.etree import ElementTree as ET
try:
import requests
except ImportError:
sys.exit("""You need requests module!
install it from https://pypi.org/project/requests/
or run: pip3 install requests.""")
try:
from tqdm.auto import tqdm
except ImportError:
sys.exit("""You need tqdm module!
install it from https://pypi.org/project/tqdm/
or run: pip3 install tqdm.""")
VERSION = 1
VERSION_STR = '0.0.1'
CODE_QUALITY = 'Really_AWFUL'
ADOBE_PRODUCTS_XML_URL = 'https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v{urlVersion}/products/all?_type=xml&channel=ccm&channel=sti&platform={installPlatform}&productType=Desktop'
ADOBE_APPLICATION_JSON_URL = 'https://cdn-ffc.oobesaas.adobe.com/core/v3/applications'
ADOBE_REQ_HEADERS = {
'X-Adobe-App-Id': 'accc-apps-panel-desktop',
'User-Agent': 'Adobe Application Manager 2.0',
'X-Api-Key': 'CC_HD_ESD_1_0',
'Cookie': 'fg=' + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(26)) + '======'
}
ADOBE_DL_HEADERS = {
'User-Agent': 'Creative Cloud'
}
session = requests.sessions.Session()
try:
os.environ["PROGRAMFILES(X86)"]
osArch = "win64"
except:
osArch = "win32"
def show_version():
ye = int((32 - len(VERSION_STR)) / 2)
print('=================================')
print('= Adobe CC Package Downloader =')
print('{} {} {}\n'.format('=' * ye, VERSION_STR,
'=' * (31 - len(VERSION_STR) - ye)))
def get_install_dir():
return os.path.join(os.environ["PROGRAMFILES"], "Adobe")
def questiony(question: str) -> bool:
"""Question prompt default Y."""
reply = None
while reply not in ("", "y", "n"):
reply = input(f"{question} (Y/n): ").lower()
return (reply in ("", "y"))
def url_version():
urlVersion = None
if args.urlVersion:
if args.urlVersion.lower() == "v4" or args.urlVersion == "4":
urlVersion = 4
elif args.urlVersion.lower() == "v5" or args.urlVersion == "5":
urlVersion = 5
elif args.urlVersion.lower() == "v6" or args.urlVersion == "6":
urlVersion = 6
else:
print('Invalid argument "{}" for {}'.format(args.urlVersion, 'URL version'))
exit(1)
while not urlVersion:
val = input('\nPlease enter the URL version(v4/v5/v6) for downloading products.xml, or nothing for v6: ') or 'v6'
if val == 'v4' or val == '4':
urlVersion = 4
elif val == 'v5' or val == '5':
urlVersion = 5
elif val == 'v6' or val == '6':
urlVersion = 6
else:
print('Invalid URL version: {}'.format(val))
print('\nUsing URL version {}\n'.format(urlVersion))
return urlVersion
def app_platform():
appPlatform = None
if args.appPlatform:
if "64" in args.appPlatform:
appPlatform = "win64"
print('\nUsing provided version: ' + appPlatform)
elif "32" in args.appPlatform:
appPlatform = "win32"
print('\nUsing provided version: ' + appPlatform)
else:
print('Invalid argument "{}" for {}'.format(args.appPlatform, 'application platform'))
exit(1)
while not appPlatform:
val = input('Please enter application platform (eg win32), or nothing for win64: ') or 'win64'
if val == 'win32':
appPlatform = 'win32'
elif val == 'win64':
appPlatform = 'win64'
else:
print('Invalid platform: {}'.format(val))
print('\nGetting {} products\n'.format(appPlatform))
return appPlatform
def product_code(sapCodes):
selectedCode = args.sapCode
if not selectedCode:
for s, d in sapCodes.items():
print('[{}]{}{}'.format(s, (10 - len(s)) * ' ', d))
while selectedCode is None:
val = input('\nPlease enter the SAP Code of the desired product from the list above: ').upper()
if products.get(val):
selectedCode = val
elif val == '':
print('No product selected! Please use a value from the list above.')
else:
print('{} is not available! Please use a value from the list above.'.format(val))
return selectedCode
def product_version(product, versions):
version = None
if (args.version):
if versions.get(args.version):
print('\nUsing provided version: ' + args.version)
version = args.version
else:
print('\nProvided version not found: ' + args.version)
print('')
if not version:
lastVersion = None
for v in reversed(versions.values()):
if v['buildGuid'] and v['apPlatform'] in allowedPlatforms:
print('{} Platform: {} - {}'.format(product['displayName'], v['apPlatform'], v['productVersion']))
lastVersion = v['productVersion']
while version is None:
val = input('\nPlease enter the desired version. Nothing for ' + lastVersion + ': ') or lastVersion
if versions.get(val):
version = val
else:
print('{} is not a valid version. Please use a value from the list above.'.format(val))
print('\nPrepare to download Adobe {}, version {}'.format(product['displayName'], version))
return version
def install_language(supportedLangs):
if 'mul' in supportedLangs:
supportedLangs[supportedLangs.index('mul')] = 'All'
else:
if 'All' not in supportedLangs:
supportedLangs.append('All')
supportedLangs.sort()
# Detecting Current set default Os language. Fixed.
windll = ctypes.windll.kernel32
osLang = locale.windows_locale[ windll.GetUserDefaultUILanguage() ]
if args.osLanguage:
osLang = args.osLanguage
defLang = 'All'
if osLang in supportedLangs:
defLang = osLang
installLanguage = None
if args.installLanguage:
if args.installLanguage in supportedLangs:
print('\nUsing provided language: ' + args.installLanguage)
installLanguage = args.installLanguage
else:
print('\nProvided language not available: ' + args.installLanguage)
if not installLanguage:
print('\nAvailable languages: {}'.format(', '.join(supportedLangs)))
while installLanguage is None:
val = input(
f'\nPlease enter the desired install language, or nothing for [{defLang}]: ') or defLang
if len(val) == 5:
val = val[0:2].lower() + val[2] + val[3:5].upper()
elif len(val) == 3:
val = val[0].upper() + val[1:].lower()
if val in supportedLangs:
installLanguage = val
else:
print(
'{} is not available. Please use a value from the list above.'.format(val))
if osLang != installLanguage:
if installLanguage != 'All':
while osLang not in supportedLangs:
print('Could not detect your default Language.')
osLang = input(
f'\nPlease enter the your OS Language, or nothing for [{installLanguage}]: ') or installLanguage
if osLang not in supportedLangs:
print(
'{} is not available. Please use a value from the list above.'.format(osLang))
print('\nDownloading packages with {} language'.format(installLanguage))
return installLanguage
def parse_products_xml(products_url, url_version, allowed_platform, selected_platform):
# get xml data
#print('Using existing products.xml\n')
#products_xml = ET.parse('products.xml')
print('Downloading product data\n')
response = session.get(products_url, stream=True, headers=ADOBE_REQ_HEADERS)
response.encoding = 'utf-8'
products_xml = ET.fromstring(response.content)
# to study
#with open('products.xml', 'wb+') as f:
# f.write(response.content)
"""2nd stage of parsing the XML."""
if url_version == 6:
prefix = 'channels/'
else:
prefix = ''
cdn = products_xml.find(prefix + 'channel/cdn/secure').text
products = {}
parent_map = {c: p for p in products_xml.iter() for c in p}
for p in products_xml.findall(prefix + 'channel/products/product'):
sap = p.get('id')
hidden = parent_map[parent_map[p]].get('name') != 'ccm'
displayName = p.find('displayName').text
productVersion = p.get('version')
if not products.get(sap):
products[sap] = {
'hidden': hidden,
'displayName': displayName,
'sapCode': sap,
'versions': OrderedDict()
}
#icon for main application
icons = []
if len(p.find('productIcons')):
for icon in p.findall('productIcons/icon'):
icons.append(icon.text)
for pf in p.findall('platforms/platform'):
baseVersion = pf.find('languageSet').get('baseVersion')
buildGuid = pf.find('languageSet').get('buildGuid')
appPlatform = pf.get('id')
dependencies = list(pf.findall('languageSet/dependencies/dependency'))
# filter by platform
if sap == 'APRO' and appPlatform != selected_platform:
break
if sap == 'APRO':
baseVersion = productVersion
if url_version == 4 or url_version == 5:
productVersion = pf.find('languageSet/nglLicensingInfo/appVersion').text
if url_version == 6:
for b in products_xml.findall('builds/build'):
if b.get("id") == sap and b.get("version") == baseVersion:
productVersion = b.find('nglLicensingInfo/appVersion').text
break
buildGuid = pf.find('languageSet/urls/manifestURL').text
# This is actually manifest URL
languageSet = pf.findall('languageSet/locales/locale')
supportedLang = []
for locale in languageSet:
supportedLang.append(locale.get('name'))
# filter products for allowed platform
if appPlatform in allowed_platform:
products[sap]['versions'][productVersion] = {
'sapCode': sap,
'displayName': displayName,
'baseVersion': baseVersion,
'productVersion': productVersion,
'productIcons': icons,
'supportedLanguages': supportedLang,
'apPlatform': appPlatform,
'dependencies': [{
'sapCode': d.find('sapCode').text, 'version': d.find('baseVersion').text
} for d in dependencies],
'buildGuid': buildGuid
}
return products, cdn
def get_products():
selectedVersion = url_version()
selectedPlatform = app_platform()
if args.Auth:
ADOBE_REQ_HEADERS['Authorization'] = args.Auth
allowedPlatforms = [selectedPlatform]
productsPlatform = selectedPlatform
if selectedPlatform == 'win64':
allowedPlatforms.append('win32')
productsPlatform += ',win32'
products_xml_url = ADOBE_PRODUCTS_XML_URL.format(urlVersion=selectedVersion, installPlatform=productsPlatform)
# download and parse product xml
products, cdn = parse_products_xml(products_xml_url, selectedVersion, allowedPlatforms, selectedPlatform)
sapCodes = {}
for p in products.values():
if not p['hidden']:
versions = p['versions']
lastVersion = None
for v in reversed(versions.values()):
if v['buildGuid'] and v['apPlatform'] in allowedPlatforms:
lastVersion = v['productVersion']
if lastVersion:
sapCodes[p['sapCode']] = p['displayName']
print(str(len(sapCodes)) + ' products found:')
if args.sapCode and products.get(args.sapCode.upper()) is None:
print('\nProvided SAP Code not found in products: ' + args.sapCode)
exit(1)
return products, cdn, sapCodes, allowedPlatforms
def get_download_path():
"""Ask for desired download folder"""
if (args.destination):
print('\nUsing provided destination: ' + args.destination + '\n')
dest = args.destination
else:
print('\nDownloaded file will be put on current location.\n')
dest = os.path.dirname(os.path.realpath(__name__))
return dest
def download_progress(url, dest_dir, prefix=''):
response = session.get(
url, stream=True, headers=ADOBE_REQ_HEADERS)
total_size_in_bytes = int(
response.headers.get('content-length', 0))
if (total_size_in_bytes > 0):
block_size = 1024 # 1 Kibibyte
progress_bar = tqdm(total=total_size_in_bytes,
unit='iB', unit_scale=True)
if prefix:
name = prefix + os.path.basename(url)
dest_dir = dest_dir + '\\' + name
with open(dest_dir, 'wb') as file:
for data in response.iter_content(block_size):
progress_bar.update(len(data))
file.write(data)
progress_bar.close()
if total_size_in_bytes != 0 and progress_bar.n != total_size_in_bytes:
print("ERROR, something went wrong")
def icons_download(product, dest_dir):
"""Download product icons"""
print('Downloading product icons\n')
icon_dir = os.path.join(dest_dir, 'icons')
os.makedirs(icon_dir, exist_ok=True)
icons = product['productIcons']
prefix = product['sapCode'].lower()
for url in icons:
download_progress(url, icon_dir, prefix)
def file_download(url, dest_dir, s, v, name=None):
"""Download a product package"""
if not name:
name = os.path.basename(url)
print('[{}_{}] Downloading {}'.format(s, v, name))
file_path = os.path.join(dest_dir, name)
response = session.head(url, stream=True, headers=ADOBE_DL_HEADERS)
total_size_in_bytes = int(
response.headers.get('content-length', 0))
if (args.skipExisting and os.path.isfile(file_path) and os.path.getsize(file_path) == total_size_in_bytes):
print('[{}_{}] {} already exists, skipping\n'.format(s, v, name))
else:
download_progress(url, file_path)
def download_APRO(appInfo, cdn):
"""Download APRO"""
# download manifest.xml
response = session.get(cdn + appInfo['buildGuid'], stream=True, headers=ADOBE_REQ_HEADERS)
response.encoding = 'utf-8'
# to study
#with open('manifest.xml', 'wb+') as f:
# f.write(response.content)
manifest = ET.fromstring(response.text)
# for full app
assetList = manifest.find('app_modes/app_mode/asset_list')
for asset in assetList:
if asset.find('asset_type').text == 'EXE':
downloadURL = asset.find('asset_path').text
# for update
#downloadURL = manifest.find('asset_list/asset/asset_path').text
dest = get_download_path()
sapCode = appInfo['sapCode']
version = appInfo['productVersion']
name = os.path.basename(downloadURL)
print('')
print('dest: ' + os.path.join(dest, name))
print('\nDownloading...\n')
print('[{}_{}] Selected 1 package'.format(sapCode, version))
file_download(downloadURL, dest, sapCode, version, name)
print('\nInstaller successfully downloaded.')
return
def get_application_json(buildGuid):
"""Retrieve JSON."""
headers = ADOBE_REQ_HEADERS.copy()
headers['x-adobe-build-guid'] = buildGuid
response = session.get(ADOBE_APPLICATION_JSON_URL, headers=headers)
response.encoding = 'utf-8'
return json.loads(response.content)
def save_application_json(package_dir, json_data):
app_json_path = os.path.join(package_dir, 'application.json')
with open(app_json_path, 'w+') as file:
json.dump(json_data, file, separators=(',', ':'))
def write_driver_xml(jsonData, products_dir, prefix=""):
driver = ET.Element('DriverInfo')
product = ET.SubElement(driver, 'ProductInfo')
prd_name = ET.SubElement(product, 'Name')
prd_name.text = 'Adobe ' + jsonData['Name']
sap_code = ET.SubElement(product, 'SAPCode')
sap_code.text = jsonData['SAPCode']
cod_ver = ET.SubElement(product, 'CodexVersion')
cod_ver.text = jsonData['CodexVersion']
base_ver = ET.SubElement(product, 'BaseVersion')
base_ver.text = jsonData['BaseVersion']
platform = ET.SubElement(product, 'Platform')
platform.text = jsonData['Platform']
esd_dir = ET.SubElement(product, 'EsdDirectory')
esd_dir.text = './' + jsonData['SAPCode']
if 'Dependencies' in jsonData:
dep_top = ET.SubElement(product, 'Dependencies')
for dependency in jsonData['Dependencies']['Dependency']:
dep_sub = ET.SubElement(dep_top, 'Dependency')
dep_code = ET.SubElement(dep_sub, 'SAPCode')
dep_code.text = dependency['SAPCode']
dep_base = ET.SubElement(dep_sub, 'BaseVersion')
dep_base.text = dependency['BaseVersion']
dep_dir = ET.SubElement(dep_sub, 'EsdDirectory')
dep_dir.text = './' + dependency['SAPCode']
tree = ET.ElementTree(driver)
ET.indent(tree, space=" ", level=0)
xml_file = os.path.join(products_dir, prefix + 'driver.xml')
tree.write(xml_file, encoding='utf-8', xml_declaration=True)
def condition_check(condition, language):
if 'installLanguage' in condition:
if language == 'All':
return True
else:
if language in condition:
return True
if 'OSProcessorFamily' in condition:
# lock on 64 bit os
processor = '64-bit'
if processor in condition:
return True
return False
def language_for_premiere(language):
if '_' in language:
main, locale = language.split('_')
return '-esl_lp_' + main
return language
def package_filter(package, language):
coreCount = 0
noneCoreCount = 0
newPackage = []
urlPath = []
for pkg in package:
if pkg.get('Type') and pkg['Type'] == 'core':
if 'Condition' in pkg:
condition = pkg['Condition']
if condition_check(condition, language):
newPackage.append(pkg)
urlPath.append(pkg['Path'])
coreCount += 1
else:
newPackage.append(pkg)
urlPath.append(pkg['Path'])
coreCount += 1
else:
if 'Condition' in pkg:
condition = pkg['Condition']
if condition_check(condition, language):
newPackage.append(pkg)
urlPath.append(pkg['Path'])
noneCoreCount += 1
else:
# for premiere pro
if '-esl_lp_' in pkg['PackageName']:
if language_for_premiere(language) in pkg['PackageName']:
newPackage.append(pkg)
urlPath.append(pkg['Path'])
noneCoreCount += 1
else:
newPackage.append(pkg)
urlPath.append(pkg['Path'])
noneCoreCount += 1
print('Selected {} core packages and {} non-core packages'.format(coreCount, noneCoreCount))
return urlPath, newPackage
def dependencies_download(dep_data, products_dir):
dep_dir = os.path.join(products_dir, dep_data['sapCode'])
os.makedirs(dep_dir, exist_ok=True)
dep_data = list(dep_data['versions'].values())
firstItem = dep_data[0]
dep_json = get_application_json(firstItem['buildGuid'])
package_download(dep_json, dep_dir)
def package_download(app_json, package_dir, language='All'):
allPackages = app_json['Packages']['Package']
cdn = app_json['Cdn']['Secure']
sapCode = app_json['SAPCode']
version = app_json['ProductVersion']
urls, package = package_filter(allPackages, language)
# filtered json data
app_json['Packages']['Package'] = package
print('\nCreate application.json\n')
save_application_json(package_dir, app_json)
for url in urls:
file_download(cdn + url, package_dir, sapCode, version)
def run_ccdl(products, cdn, sapCodes, allowedPlatforms):
"""Run Main execution."""
# get product list
sapCode = product_code(sapCodes)
product = products.get(sapCode)
# version select
versions = product['versions']
selectedVersion = product_version(product, versions)
# product to download
prodInfo = versions[selectedVersion]
# language select
supportedLangs = prodInfo['supportedLanguages']
installLanguage = install_language(supportedLangs)
# acrobat download
if sapCode == 'APRO':
download_APRO(prodInfo, cdn)
return
# main product
print('\nPrepare to download Adobe {}-{}-{}-{}'.format(prodInfo['displayName'], prodInfo['productVersion'], installLanguage, prodInfo['apPlatform']))
dest = get_download_path()
#download icons
icons_download(prodInfo, dest)
# create products directory
products_dir = os.path.join(dest, 'products')
os.makedirs(products_dir, exist_ok=True)
package_dir = os.path.join(products_dir, sapCode)
os.makedirs(package_dir, exist_ok=True)
app_json = get_application_json(prodInfo['buildGuid'])
package_download(app_json, package_dir, installLanguage)
if 'Dependencies' in app_json:
for dependency in app_json['Dependencies']['Dependency']:
depSap = dependency['SAPCode']
depPackage = products.get(depSap)
dependencies_download(depPackage, products_dir)
print('\nGenerating driver.xml')
prefix = app_json['SAPCode'] + '-'
write_driver_xml(app_json, products_dir, prefix)
return
if __name__ == '__main__':
show_version()
parser = argparse.ArgumentParser()
parser.add_argument('-l', '--installLanguage',
help='Language code (eg. en_US)', action='store')
parser.add_argument('-o', '--osLanguage',
help='OS Language code (eg. en_US)', action='store')
parser.add_argument('-p', '--appPlatform',
help='Application platform (eg. win64)', action='store')
parser.add_argument('-s', '--sapCode',
help='SAP code for desired product (eg. PHSP)', action='store')
parser.add_argument('-v', '--version',
help='Version of desired product (eg. 21.0.3)', action='store')
parser.add_argument('-d', '--destination',
help='Directory to download installation files to', action='store')
parser.add_argument('-u', '--urlVersion',
help="Get app info from v4/v5/v6 url (eg. v6)", action='store')
parser.add_argument('-A', '--Auth',
help='Add a bearer_token to to authenticate your account, e.g. downloading Xd', action='store')
parser.add_argument('--noRepeatPrompt',
help="Don't prompt for additional downloads", action='store_true')
parser.add_argument('-x', '--skipExisting',
help="Skip existing files, e.g. resuming failed downloads", action='store_true')
args = parser.parse_args()
products, cdn, sapCodes, allowedPlatforms = get_products()
while True:
run_ccdl(products, cdn, sapCodes, allowedPlatforms)
if args.noRepeatPrompt or not questiony('\n\nDo you want to download another package'):
break