Last active
August 17, 2025 17:55
-
Star
(226)
You must be signed in to star a gist -
Fork
(45)
You must be signed in to fork a gist
-
-
Save ayyybe/a5f01c6f40020f9a7bc4939beeb2df1d to your computer and use it in GitHub Desktop.
Revisions
-
ayyybe revised this gist
Dec 20, 2021 . No changes.There are no files selected for viewing
-
ayyybe revised this gist
May 17, 2021 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -386,7 +386,7 @@ def get_application_json(buildGuid): with Popen(['/usr/bin/osacompile', '-l', 'JavaScript', '-o', os.path.join(dest, install_app_path)], stdin=PIPE) as p: p.communicate(INSTALL_APP_APPLE_SCRIPT.encode('utf-8')) icon_path = '/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Install.app/Contents/Resources/CreativeCloudInstaller.icns' shutil.copyfile(icon_path, os.path.join(install_app_path, 'Contents', 'Resources', 'applet.icns')) products_dir = os.path.join(install_app_path, 'Contents', 'Resources', 'products') -
ayyybe revised this gist
May 17, 2021 . No changes.There are no files selected for viewing
-
ayyybe revised this gist
Sep 30, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -115,7 +115,7 @@ cancelButton: 'Cancel' }) const cmd = shellescape([ 'sudo', hyperDrivePath, '--install=1', '--driverXML=' + driverPath ]) app.displayDialog('Run this command in Terminal to install (press \\'OK\\' to copy to clipboard)', { defaultAnswer: cmd }) app.setTheClipboardTo(cmd) return } -
ayyybe revised this gist
Sep 30, 2020 . 1 changed file with 67 additions and 29 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,10 @@ #!/usr/bin/env python3 # CHANGELOG (0.1.2-hotfix1) # + updated script to work with new api (newer downloads work now) # + added workaround for broken installer on big sur # + made everything even more messy and disgusting import os import json import argparse @@ -9,8 +15,9 @@ from collections import OrderedDict from subprocess import Popen, PIPE VERSION = 4 VERSION_STR = '0.1.2-hotfix1' CODE_QUALITY = 'FUCKING_AWFUL' INSTALL_APP_APPLE_SCRIPT = ''' const app = Application.currentApplication() @@ -77,8 +84,42 @@ return false } function shellescape(a) { var ret = []; a.forEach(function(s) { if (/[^A-Za-z0-9_\\/:=-]/.test(s)) { s = "'"+s.replace(/'/g,"'\\\\''")+"'"; s = s.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning .replace(/\\\\\'''/g, "\\\\'" ); // remove non-escaped single-quote if there are enclosed between 2 escaped } ret.push(s); }); return ret.join(' '); } function run() { const appPath = app.pathTo(this).toString() //const driverPath = appPath.substring(0, appPath.lastIndexOf('/')) + '/products/driver.xml' const driverPath = appPath + '/Contents/Resources/products/driver.xml' const hyperDrivePath = '/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup' // The JXA Objective-C bridge is completely broken in Big Sur if (!$.NSProcessInfo && parseFloat(app.doShellScript('sw_vers -productVersion')) >= 11.0) { app.displayAlert('GUI unavailable in Big Sur', { message: 'JXA is currently broken in Big Sur.\\nInstall in Terminal instead?', buttons: ['Cancel', 'Install in Terminal'], defaultButton: 'Install in Terminal', cancelButton: 'Cancel' }) const cmd = shellescape([ 'sudo', hyperDrivePath, '--install=1', '--driverXML=' + driverPath ]) app.displayDialog('Run this command in Terminal to install (press \\'Okay\\' to copy to clipboard)', { defaultAnswer: cmd }) app.setTheClipboardTo(cmd) return } const args = $.NSProcessInfo.processInfo.arguments const argv = [] const argc = args.count @@ -89,11 +130,6 @@ const installFlag = argv.indexOf('-y') > -1 if (!installFlag) { app.displayAlert('Adobe Package Installer', { message: 'Start installation now?', @@ -140,10 +176,8 @@ } ''' ADOBE_PRODUCTS_XML_URL = 'https://cdn-ffc.oobesaas.adobe.com/core/v5/products/all?_type=xml&channel=ccm,sti&platform=osx10,osx10-64&productType=Desktop' ADOBE_APPLICATION_JSON_URL = 'https://cdn-ffc.oobesaas.adobe.com/core/v3/applications' DRIVER_XML = '''<DriverInfo> <ProductInfo> @@ -180,8 +214,8 @@ def dl(filename, url): with open(filename, 'wb') as f: shutil.copyfileobj(r.raw, f) def r(url, headers=ADOBE_REQ_HEADERS): req = session.get(url, headers=headers) req.encoding = 'utf-8' return req.text @@ -191,14 +225,18 @@ def get_products_xml(): def parse_products_xml(products_xml): cdn = products_xml.find('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('channel/products/product'): displayName = p.find('displayName').text sap = p.get('id') version = p.get('version') dependencies = list(p.find('platforms/platform/languageSet/dependencies')) if not products.get(sap): if p.find('productInfoPage'): print(p.find('productInfoPage').text) products[sap] = { 'hidden': p.find('platforms/platform').get('id') != 'osx10-64' or parent_map[parent_map[p]].get('name') != 'ccm', 'displayName': displayName, 'sapCode': sap, 'versions': OrderedDict() @@ -209,16 +247,16 @@ def parse_products_xml(products_xml): 'version': version, 'dependencies': [{ 'sapCode': d.find('sapCode').text, 'version': d.find('baseVersion').text } for d in dependencies], 'buildGuid': p.find('platforms/platform/languageSet').get('buildGuid') } return products, cdn def get_application_json(buildGuid): headers = ADOBE_REQ_HEADERS.copy() headers['x-adobe-build-guid'] = buildGuid return json.loads(r(ADOBE_APPLICATION_JSON_URL, headers)) if __name__ == '__main__': @@ -229,25 +267,23 @@ def get_application_json(sapCode, version): parser.add_argument('-d', '--destination', help='Directory to download installation files to', action='store') args = parser.parse_args() ye = int((32 - len(VERSION_STR)) / 2) print('=================================') print('= Adobe macOS Package Generator =') print('{} {} {}\n'.format('='*ye, VERSION_STR, '='*(31-len(VERSION_STR)-ye))) if (not os.path.isfile('/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup')): print('Adobe HyperDrive installer not found.\nPlease make sure the Creative Cloud app is installed.') exit(1) print('Downloading products.xml\n') products_xml = get_products_xml() print('Parsing products.xml') products, cdn = parse_products_xml(products_xml) print('CDN: ' + cdn) print(str(len([p for p in products if not products[p]['hidden']])) + ' products found:') sapCode = None if (args.sapCode): @@ -261,7 +297,8 @@ def get_application_json(sapCode, version): if not sapCode: for p in products.values(): if not p['hidden']: print('[{}] {}'.format(p['sapCode'], p['displayName'])) while sapCode is None: val = input('\nPlease enter the SAP Code of the desired product (eg. PHSP for Photoshop): ') @@ -289,7 +326,7 @@ def get_application_json(sapCode, version): print('{} {}'.format(product['displayName'], v['version'])) while version is None: val = input('\nPlease enter the desired version (eg. 21.2.3): ') if versions.get(val): version = val else: @@ -339,8 +376,9 @@ def get_application_json(sapCode, version): print('dest: ' + install_app_path) prodInfo = versions[version] prods_to_download = [{ 'sapCode': d['sapCode'], 'version': d['version'], 'buildGuid': products[d['sapCode']]['versions'][d['version']]['buildGuid'] } for d in prodInfo['dependencies']] prods_to_download.insert(0, { 'sapCode': prodInfo['sapCode'], 'version': prodInfo['version'], 'buildGuid': prodInfo['buildGuid'] }) print(prods_to_download) print('\nCreating {}'.format(install_app_name)) @@ -361,7 +399,7 @@ def get_application_json(sapCode, version): app_json_path = os.path.join(product_dir, 'application.json') print('[{}_{}] Downloading application.json'.format(s, v)) app_json = get_application_json(p['buildGuid']) p['application_json'] = app_json print('[{}_{}] Creating folder for product'.format(s, v)) -
ayyybe revised this gist
Mar 31, 2020 . No changes.There are no files selected for viewing
-
ayyybe revised this gist
Mar 31, 2020 . 1 changed file with 10 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,13 +3,14 @@ import json import argparse import requests session = requests.Session() import shutil from xml.etree import ElementTree as ET from collections import OrderedDict from subprocess import Popen, PIPE VERSION = 3 VERSION_STR = '0.1.2' INSTALL_APP_APPLE_SCRIPT = ''' const app = Application.currentApplication() @@ -140,7 +141,8 @@ ''' #ADOBE_PRODUCTS_XML_URL = 'https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v4/products/all?_type=xml&channel=ccm,sti&platform=osx10,osx10-64&productType=Desktop' POPULATE_ADOBE_COOKIES_URL = 'https://adobeid-na1.services.adobe.com/ims/check/v4/token?client_id=CreativeCloud_v5_1' ADOBE_PRODUCTS_XML_URL = 'https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v4/products/all?_type=xml&channel=ccm&platform=osx10-64&productType=Desktop' ADOBE_APPLICATION_JSON_URL = 'https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v2/applications?name={name}&version={version}&platform={platform}' DRIVER_XML = '''<DriverInfo> @@ -174,12 +176,12 @@ } def dl(filename, url): with session.get(url, stream=True, headers=ADOBE_REQ_HEADERS) as r: with open(filename, 'wb') as f: shutil.copyfileobj(r.raw, f) def r(url): req = session.get(url, headers=ADOBE_REQ_HEADERS) req.encoding = 'utf-8' return req.text @@ -235,6 +237,9 @@ def get_application_json(sapCode, version): print('Adobe HyperDrive installer not found.\nPlease make sure the Creative Cloud app is installed.') exit(1) print('Populating .adobe.com cookies') session.post(POPULATE_ADOBE_COOKIES_URL, headers=ADOBE_REQ_HEADERS) print('Downloading products.xml\n') products_xml = get_products_xml() -
ayyybe revised this gist
Mar 27, 2020 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -260,7 +260,9 @@ def get_application_json(sapCode, version): while sapCode is None: val = input('\nPlease enter the SAP Code of the desired product (eg. PHSP for Photoshop): ') if val == 'APRO': print('\033[1;31mAcrobat is currently broken, please sit tight while I try to find a solution.\nAll other products are functional.\033[0m') elif products.get(val): sapCode = val else: print('{} is not a valid SAP Code. Please use a value from the list above.'.format(val)) -
ayyybe revised this gist
Mar 24, 2020 . 2 changed files with 2 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -38,5 +38,4 @@ fi clear echo "${CYAN}starting ccdl${RESET}" python3 <(curl -s https://gist.github.com/ayyybe/a5f01c6f40020f9a7bc4939beeb2df1d/raw/ccdl.py) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,8 +3,7 @@ CYAN="$(tput bold; tput setaf 6)" RESET="$(tput sgr0)" curl https://gist.github.com/ayyybe/a5f01c6f40020f9a7bc4939beeb2df1d/raw/ccdl.command -o "/Applications/Adobe Packager.command" chmod +x "/Applications/Adobe Packager.command" clear -
ayyybe revised this gist
Mar 24, 2020 . 3 changed files with 135 additions and 44 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,42 @@ #!/bin/bash CYAN="$(tput bold; tput setaf 6)" RESET="$(tput sgr0)" clear if command -v python3 > /dev/null 2>&1; then if [ $(python3 -c "print('ye')") = "ye" ]; then clear echo "${CYAN}python3 found!${RESET}" else clear echo "python3 found but non-functional" # probably xcode-select stub on Catalina+ echo "${CYAN}If you received a popup asking to install some tools, please accept.${RESET}" read -n1 -r -p "Press [SPACE] when installation is complete, or any other key to abort." key echo "" if [ "$key" != '' ]; then exit fi fi else echo "${CYAN}installing python3...${RESET}" if ! command -v brew > /dev/null 2>&1; then echo | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" fi brew install python fi python3 -c 'import requests' > /dev/null 2>&1 if [ $? == 0 ]; then echo "${CYAN}requests found!${RESET}" else echo "${CYAN}installing requests...${RESET}" python3 -m pip install requests --user fi clear echo "${CYAN}starting ccdl${RESET}" #python3 <(curl -s https://gist.github.com/ayyybe/a5f01c6f40020f9a7bc4939beeb2df1d/raw/ccdl.py) python3 <(curl -s https://pastebin.com/raw/dzYTCSHw) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,12 +2,15 @@ import os import json import argparse import requests import shutil from xml.etree import ElementTree as ET from collections import OrderedDict from subprocess import Popen, PIPE VERSION = 2 VERSION_STR = '0.1.1' INSTALL_APP_APPLE_SCRIPT = ''' const app = Application.currentApplication() app.includeStandardAdditions = true @@ -86,7 +89,8 @@ const installFlag = argv.indexOf('-y') > -1 const appPath = app.pathTo(this).toString() //const driverPath = appPath.substring(0, appPath.lastIndexOf('/')) + '/products/driver.xml' const driverPath = appPath + '/Contents/Resources/products/driver.xml' const hyperDrivePath = '/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup' if (!installFlag) { @@ -97,7 +101,7 @@ cancelButton: 'Cancel' }) const output = app.doShellScript(`"${appPath}/Contents/MacOS/applet" -y`, { administratorPrivileges: true }) const alert = JSON.parse(output) alert.params ? app.displayAlert(alert.title, alert.params) : app.displayAlert(alert.title) return @@ -163,25 +167,21 @@ <EsdDirectory>./{sapCode}</EsdDirectory> </Dependency>''' ADOBE_REQ_HEADERS = { 'X-Adobe-App-Id': 'accc-hdcore-desktop', 'User-Agent': 'Adobe Application Manager 2.0', 'X-Api-Key': 'CC_HD_ESD_1_0' } def dl(filename, url): with requests.get(url, stream=True, headers=ADOBE_REQ_HEADERS) as r: with open(filename, 'wb') as f: shutil.copyfileobj(r.raw, f) def r(url): req = requests.get(url, headers=ADOBE_REQ_HEADERS) req.encoding = 'utf-8' return req.text def get_products_xml(): return ET.fromstring(r(ADOBE_PRODUCTS_XML_URL)) @@ -221,15 +221,19 @@ def get_application_json(sapCode, version): if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-l', '--installLanguage', help='Language code (eg. en_US)', 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') args = parser.parse_args() print('=================================') print('= Adobe macOS Package Generator =') print('============= {} =============\n'.format(VERSION_STR)) if (not os.path.isfile('/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup')): print('Adobe HyperDrive installer not found.\nPlease make sure the Creative Cloud app is installed.') exit(1) print('Downloading products.xml\n') products_xml = get_products_xml() @@ -243,7 +247,7 @@ def get_application_json(sapCode, version): sapCode = None if (args.sapCode): if products.get(args.sapCode): print('\nUsing provided SAP Code: ' + args.sapCode) sapCode = args.sapCode else: print('\nProvided SAP Code not found in products: ' + args.sapCode) @@ -266,7 +270,7 @@ def get_application_json(sapCode, version): 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) @@ -286,27 +290,67 @@ def get_application_json(sapCode, version): print('') langs = [ 'en_US', 'en_GB', 'en_IL', 'en_AE', 'es_ES', 'es_MX', 'pt_BR', 'fr_FR', 'fr_CA', 'fr_MA', 'it_IT', 'de_DE', 'nl_NL', 'ru_RU', 'uk_UA', 'zh_TW', 'zh_CN', 'ja_JP', 'ko_KR', 'pl_PL', 'hu_HU', 'cs_CZ', 'tr_TR', 'sv_SE', 'nb_NO', 'fi_FI', 'da_DK' ] installLanguage = None if (args.installLanguage): if (args.installLanguage in langs): print('\nUsing provided language: ' + args.installLanguage) installLanguage = args.installLanguage else: print('\nProvided language not available: ' + args.installLanguage) if not installLanguage: print('Available languages: {}'.format(', '.join(langs))) while installLanguage is None: val = input('\nPlease enter the desired install language, or nothing for [en_US]: ') or 'en_US' if (val in langs): installLanguage = val else: print('{} is not available. Please use a value from the list above.'.format(val)) dest = None if (args.destination): print('\nUsing provided destination: ' + args.destination) dest = args.destination else: print('\nPlease navigate to the desired downloads folder, or cancel to abort.') p = Popen(['/usr/bin/osascript', '-e', 'tell application (path to frontmost application as text)\nset _path to choose folder\nPOSIX path of _path\nend'], stdout=PIPE) dest = p.communicate()[0].decode('utf-8').strip() if (p.returncode != 0): print('Exiting...') exit() print('') install_app_name = 'Install {}_{}-{}.app'.format(sapCode, version, installLanguage) install_app_path = os.path.join(dest, install_app_name) print('sapCode: ' + sapCode) print('version: ' + version) print('installLanguage: ' + installLanguage) print('dest: ' + install_app_path) prodInfo = versions[version] prods_to_download = [{ 'sapCode': d['sapCode'], 'version': d['version'] } for d in prodInfo['dependencies']] prods_to_download.insert(0, { 'sapCode': prodInfo['sapCode'], 'version': prodInfo['version'] }) print('\nCreating {}'.format(install_app_name)) install_app_path = os.path.join(dest, 'Install {}_{}-{}.app'.format(sapCode, version, installLanguage)) with Popen(['/usr/bin/osacompile', '-l', 'JavaScript', '-o', os.path.join(dest, install_app_path)], stdin=PIPE) as p: p.communicate(INSTALL_APP_APPLE_SCRIPT.encode('utf-8')) icon_path = '/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Install.app/Contents/Resources/app.icns' shutil.copyfile(icon_path, os.path.join(install_app_path, 'Contents', 'Resources', 'applet.icns')) products_dir = os.path.join(install_app_path, 'Contents', 'Resources', 'products') print('\nPreparing...\n') for p in prods_to_download: s, v = p['sapCode'], p['version'] product_dir = os.path.join(products_dir, s) app_json_path = os.path.join(product_dir, 'application.json') print('[{}_{}] Downloading application.json'.format(s, v)) @@ -327,7 +371,7 @@ def get_application_json(sapCode, version): for p in prods_to_download: s, v = p['sapCode'], p['version'] app_json = p['application_json'] product_dir = os.path.join(products_dir, s) print('[{}_{}] Parsing available packages'.format(s, v)) core_pkg_count = 0 @@ -364,16 +408,8 @@ def get_application_json(sapCode, version): language = installLanguage ) with open(os.path.join(products_dir, 'driver.xml'), 'w') as f: f.write(driver) f.close() print('\nPackage successfully created. Run {} to install.'.format(install_app_path)) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,13 @@ #!/bin/bash CYAN="$(tput bold; tput setaf 6)" RESET="$(tput sgr0)" #curl https://gist.github.com/ayyybe/a5f01c6f40020f9a7bc4939beeb2df1d/raw/ccdl.command -o "/Applications/Adobe Packager.command" curl https://pastebin.com/raw/0KnPC6yn -o "/Applications/Adobe Packager.command" chmod +x "/Applications/Adobe Packager.command" clear echo "${CYAN}Done! You can now start /Applications/Adobe Packager.command to begin${RESET}" exit -
ayyybe revised this gist
Mar 23, 2020 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -152,6 +152,7 @@ </ProductInfo> <RequestInfo> <InstallDir>/Applications</InstallDir> <InstallLanguage>{language}</InstallLanguage> </RequestInfo> </DriverInfo> ''' @@ -359,7 +360,8 @@ def get_application_json(sapCode, version): dependencies = '\n'.join([DRIVER_XML_DEPENDENCY.format( sapCode = d['sapCode'], version = d['version'] ) for d in prodInfo['dependencies']]), language = installLanguage ) with open(os.path.join(dest, 'products', 'driver.xml'), 'w') as f: -
ayyybe created this gist
Mar 22, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,377 @@ #!/usr/bin/env python3 import os import json import argparse import urllib3 import shutil from xml.etree import ElementTree as ET from collections import OrderedDict from subprocess import Popen, PIPE INSTALL_APP_APPLE_SCRIPT = ''' const app = Application.currentApplication() app.includeStandardAdditions = true ObjC.import('Cocoa') ObjC.import('stdio') ObjC.import('stdlib') ObjC.registerSubclass({ name: 'HandleDataAction', methods: { 'outData:': { types: ['void', ['id']], implementation: function(sender) { const data = sender.object.availableData if (data.length !== '0') { const output = $.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding).js const res = parseOutput(output) if (res) { switch (res.type) { case 'progress': Progress.additionalDescription = `Progress: ${res.data}%` Progress.completedUnitCount = res.data break case 'exit': if (res.data === 0) { $.puts(JSON.stringify({ title: 'Installation succeeded' })) } else { $.puts(JSON.stringify({ title: `Failed with error code ${res.data}` })) } $.exit(0) break } } sender.object.waitForDataInBackgroundAndNotify } else { $.NSNotificationCenter.defaultCenter.removeObserver(this) } } } } }) function parseOutput(output) { let matches matches = output.match(/Progress: ([0-9]{1,3})%/) if (matches) { return { type: 'progress', data: parseInt(matches[1], 10) } } matches = output.match(/Exit Code: ([0-9]{1,3})/) if (matches) { return { type: 'exit', data: parseInt(matches[1], 10) } } return false } function run() { const args = $.NSProcessInfo.processInfo.arguments const argv = [] const argc = args.count for (var i = 0; i < argc; i++) { argv.push(ObjC.unwrap(args.objectAtIndex(i))) } delete args const installFlag = argv.indexOf('-y') > -1 const appPath = app.pathTo(this).toString() const driverPath = appPath.substring(0, appPath.lastIndexOf('/')) + '/products/driver.xml' const hyperDrivePath = '/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Setup' if (!installFlag) { app.displayAlert('Adobe Package Installer', { message: 'Start installation now?', buttons: ['Cancel', 'Install'], defaultButton: 'Install', cancelButton: 'Cancel' }) const output = app.doShellScript(`${appPath}/Contents/MacOS/applet -y`, { administratorPrivileges: true }) const alert = JSON.parse(output) alert.params ? app.displayAlert(alert.title, alert.params) : app.displayAlert(alert.title) return } const stdout = $.NSPipe.pipe const task = $.NSTask.alloc.init task.executableURL = $.NSURL.alloc.initFileURLWithPath(hyperDrivePath) task.arguments = $(['--install=1', '--driverXML=' + driverPath]) task.standardOutput = stdout const dataAction = $.HandleDataAction.alloc.init $.NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(dataAction, 'outData:', $.NSFileHandleDataAvailableNotification, $.initialOutputFile) stdout.fileHandleForReading.waitForDataInBackgroundAndNotify let err = $.NSError.alloc.initWithDomainCodeUserInfo('', 0, '') const ret = task.launchAndReturnError(err) if (!ret) { $.puts(JSON.stringify({ title: 'Error', params: { message: 'Failed to launch task: ' + err.localizedDescription.UTF8String } })) $.exit(0) } Progress.description = "Installing packages..." Progress.additionalDescription = "Preparing…" Progress.totalUnitCount = 100 task.waitUntilExit } ''' #ADOBE_PRODUCTS_XML_URL = 'https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v4/products/all?_type=xml&channel=ccm,sti&platform=osx10,osx10-64&productType=Desktop' ADOBE_PRODUCTS_XML_URL = 'https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v4/products/all?_type=xml&channel=ccm&platform=osx10,osx10-64&productType=Desktop' ADOBE_APPLICATION_JSON_URL = 'https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v2/applications?name={name}&version={version}&platform={platform}' DRIVER_XML = '''<DriverInfo> <ProductInfo> <Name>Adobe {name}</Name> <SAPCode>{sapCode}</SAPCode> <CodexVersion>{version}</CodexVersion> <Platform>osx10-64</Platform> <EsdDirectory>./{sapCode}</EsdDirectory> <Dependencies> {dependencies} </Dependencies> </ProductInfo> <RequestInfo> <InstallDir>/Applications</InstallDir> </RequestInfo> </DriverInfo> ''' DRIVER_XML_DEPENDENCY = ''' <Dependency> <SAPCode>{sapCode}</SAPCode> <BaseVersion>{version}</BaseVersion> <EsdDirectory>./{sapCode}</EsdDirectory> </Dependency>''' urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) http = urllib3.PoolManager() def dl(filename, url): r = http.request('GET', url, preload_content=False, headers={ 'X-Adobe-App-Id': 'accc-hdcore-desktop', 'User-Agent': 'Adobe Application Manager 2.0', 'X-Api-Key': 'CC_HD_ESD_1_0' }) with open(filename, 'wb') as out_file: shutil.copyfileobj(r, out_file) def r(url): return http.request('GET', url, headers={ 'X-Adobe-App-Id': 'accc-hdcore-desktop', 'User-Agent': 'Adobe Application Manager 2.0', 'X-Api-Key': 'CC_HD_ESD_1_0' }).data.decode('utf-8') def get_products_xml(): return ET.fromstring(r(ADOBE_PRODUCTS_XML_URL)) def parse_products_xml(products_xml): cdn = products_xml.find('channel/cdn/secure').text products = {} for p in products_xml.findall('channel/products/product'): displayName = p.find('displayName').text sap = p.get('id') version = p.get('version') dependencies = list(p.find('platforms/platform/languageSet/dependencies')) if not products.get(sap): products[sap] = { 'displayName': displayName, 'sapCode': sap, 'versions': OrderedDict() } products[sap]['versions'][version] = { 'sapCode': sap, 'version': version, 'dependencies': [{ 'sapCode': d.find('sapCode').text, 'version': d.find('baseVersion').text } for d in dependencies] } return products, cdn def get_application_json(sapCode, version): try: return json.loads(r(ADOBE_APPLICATION_JSON_URL.format(name=sapCode, version=version, platform='osx10-64'))) except json.decoder.JSONDecodeError: return json.loads(r(ADOBE_APPLICATION_JSON_URL.format(name=sapCode, version=version, platform='osx10'))) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-l', '--installLanguage', help='Language code (defaults to \'en_US\'', action='store', default='en_US') 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') args = parser.parse_args() print('=================================') print('= Adobe macOS Package Generator =') print('============= 0.1.0 =============\n') print('Downloading products.xml\n') products_xml = get_products_xml() print('Parsing products.xml') products, cdn = parse_products_xml(products_xml) print('CDN: ' + cdn) print(str(len(products)) + ' products found:') sapCode = None if (args.sapCode): if products.get(args.sapCode): print('\nUsing provided SAP Code: ' + args.sapCode) sapCode = args.sapCode else: print('\nProvided SAP Code not found in products: ' + args.sapCode) print('') if not sapCode: for p in products.values(): print('[{}] {}'.format(p['sapCode'], p['displayName'])) while sapCode is None: val = input('\nPlease enter the SAP Code of the desired product (eg. PHSP for Photoshop): ') if products.get(val): sapCode = val else: print('{} is not a valid SAP Code. Please use a value from the list above.'.format(val)) product = products.get(sapCode) versions = 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: for v in reversed(versions.values()): print('{} {}'.format(product['displayName'], v['version'])) while version is None: val = input('\nPlease enter the desired version (eg. 21.0.3): ') if versions.get(val): version = val else: print('{} is not a valid version. Please use a value from the list above.'.format(val)) print('') prodInfo = versions[version] installLanguage = args.installLanguage dest = args.destination or '{}_{}-{}'.format(sapCode, version, installLanguage) print('installLanguage: ' + installLanguage) print('sapCode: ' + sapCode) print('version: ' + version) print('dest: ' + dest) #print(get_application_json('CORE', '1.0')) prods_to_download = [{ 'sapCode': d['sapCode'], 'version': d['version'] } for d in prodInfo['dependencies']] prods_to_download.insert(0, { 'sapCode': prodInfo['sapCode'], 'version': prodInfo['version'] }) #print(prods_to_download) print ('\nPreparing...\n') for p in prods_to_download: s, v = p['sapCode'], p['version'] product_dir = os.path.join(dest, 'products', s) app_json_path = os.path.join(product_dir, 'application.json') print('[{}_{}] Downloading application.json'.format(s, v)) app_json = get_application_json(s, v) p['application_json'] = app_json print('[{}_{}] Creating folder for product'.format(s, v)) os.makedirs(product_dir, exist_ok=True) print('[{}_{}] Saving application.json'.format(s, v)) with open(app_json_path, 'w') as file: json.dump(app_json, file, separators=(',', ':')) print('') print ('Downloading...\n') for p in prods_to_download: s, v = p['sapCode'], p['version'] app_json = p['application_json'] product_dir = os.path.join(dest, 'products', s) print('[{}_{}] Parsing available packages'.format(s, v)) core_pkg_count = 0 noncore_pkg_count = 0 packages = app_json['Packages']['Package'] download_urls = [] for pkg in packages: if pkg.get('Type') and pkg['Type'] == 'core': core_pkg_count += 1 download_urls.append(cdn + pkg['Path']) else: if ((not pkg.get('Condition')) or installLanguage in pkg['Condition']): # TODO: actually parse `Condition` and check it properly (and maybe look for & add support for conditions other than installLanguage) noncore_pkg_count += 1 download_urls.append(cdn + pkg['Path']) print('[{}_{}] Selected {} core packages and {} non-core packages'.format(s, v, core_pkg_count, noncore_pkg_count)) for url in download_urls: name = url.split('/')[-1].split('?')[0] print('[{}_{}] Downloading {}'.format(s, v, name)) dl(os.path.join(product_dir, name), url) print('\nGenerating driver.xml') driver = DRIVER_XML.format( name = product['displayName'], sapCode = prodInfo['sapCode'], version = prodInfo['version'], dependencies = '\n'.join([DRIVER_XML_DEPENDENCY.format( sapCode = d['sapCode'], version = d['version'] ) for d in prodInfo['dependencies']]) ) with open(os.path.join(dest, 'products', 'driver.xml'), 'w') as f: f.write(driver) f.close() print('\nCreating Install.app') with Popen(['/usr/bin/osacompile', '-l', 'JavaScript', '-o', os.path.join(dest, 'Install.app')], stdin=PIPE) as p: p.communicate(INSTALL_APP_APPLE_SCRIPT.encode('utf-8')) iconPath = '/Library/Application Support/Adobe/Adobe Desktop Common/HDBox/Install.app/Contents/Resources/app.icns' shutil.copyfile(iconPath, os.path.join(dest, 'Install.app', 'Contents', 'Resources', 'applet.icns')) print('\nPackage successfully created. Run Install.app inside the destination folder to install.')