mirror of
https://github.com/X1a0He/Adobe-Downloader.git
synced 2025-11-25 03:14:57 +08:00
365 lines
17 KiB
Swift
365 lines
17 KiB
Swift
//
|
||
// NewJSONParser.swift
|
||
// Adobe Downloader
|
||
//
|
||
// Created by X1a0He on 2/26/25.
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/**
|
||
v6: https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v6/products/all?channel=ccm&channel=sti&platform=macarm64,macuniversal,osx10-64,osx10&_type=json&productType=Desktop
|
||
v5: https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v5/products/all?channel=ccm&channel=sti&platform=macarm64,macuniversal,osx10-64,osx10&_type=json&productType=Desktop
|
||
v4: https://prod-rel-ffc-ccm.oobesaas.adobe.com/adobe-ffc-external/core/v4/products/all?channel=ccm&channel=sti&platform=macarm64,macuniversal,osx10-64,osx10&_type=json&productType=Desktop
|
||
*/
|
||
|
||
class NewJSONParser {
|
||
static func parse(jsonString: String) throws {
|
||
guard let jsonData = jsonString.data(using: .utf8),
|
||
let jsonObject = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
||
throw ParserError.invalidJSON
|
||
}
|
||
let apiVersion = Int(StorageData.shared.apiVersion) ?? 6
|
||
globalStiResult = try parseSti(jsonObject: jsonObject, apiVersion: apiVersion)
|
||
globalCcmResult = try parseCcm(jsonObject: jsonObject, apiVersion: apiVersion)
|
||
|
||
// 更新全局 CDN
|
||
if !globalCcmResult.cdn.isEmpty {
|
||
globalCdn = globalCcmResult.cdn
|
||
} else if !globalStiResult.cdn.isEmpty {
|
||
globalCdn = globalStiResult.cdn
|
||
}
|
||
}
|
||
|
||
static func parseStiProducts(jsonString: String) throws {
|
||
guard let jsonData = jsonString.data(using: .utf8),
|
||
let jsonObject = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
||
throw ParserError.invalidJSON
|
||
}
|
||
let apiVersion = Int(StorageData.shared.apiVersion) ?? 6
|
||
let result = try parseSti(jsonObject: jsonObject, apiVersion: apiVersion)
|
||
globalStiResult = result
|
||
|
||
// 更新全局 CDN
|
||
globalCdn = result.cdn
|
||
}
|
||
|
||
static func parseCcmProducts(jsonString: String) throws {
|
||
guard let jsonData = jsonString.data(using: .utf8),
|
||
let jsonObject = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
||
throw ParserError.invalidJSON
|
||
}
|
||
let apiVersion = Int(StorageData.shared.apiVersion) ?? 6
|
||
let result = try parseCcm(jsonObject: jsonObject, apiVersion: apiVersion)
|
||
globalCcmResult = result
|
||
|
||
// 更新全局 CDN
|
||
globalCdn = result.cdn
|
||
}
|
||
|
||
private static func parseSti(jsonObject: [String: Any], apiVersion: Int) throws -> NewParseResult {
|
||
let cdnPath: [String]
|
||
if apiVersion == 6 {
|
||
cdnPath = ["channels", "channel"]
|
||
} else {
|
||
cdnPath = ["channel"]
|
||
}
|
||
|
||
func getValue(from dict: [String: Any], path: [String]) -> Any? {
|
||
var current: Any = dict
|
||
for key in path {
|
||
guard let dict = current as? [String: Any],
|
||
let value = dict[key] else {
|
||
return nil
|
||
}
|
||
current = value
|
||
}
|
||
return current
|
||
}
|
||
|
||
var channelArray: [[String: Any]] = []
|
||
if let channels = getValue(from: jsonObject, path: cdnPath) {
|
||
if let array = channels as? [[String: Any]] {
|
||
channelArray = array
|
||
} else if let dict = channels as? [String: Any],
|
||
let array = dict["channel"] as? [[String: Any]] {
|
||
channelArray = array
|
||
}
|
||
}
|
||
|
||
guard let firstChannel = channelArray.first,
|
||
let cdn = (firstChannel["cdn"] as? [String: Any])?["secure"] as? String else {
|
||
throw ParserError.missingCDN
|
||
}
|
||
|
||
var products = [Product]()
|
||
|
||
for channel in channelArray {
|
||
let channelName = channel["name"] as? String ?? ""
|
||
if(channelName != "sti") { continue }
|
||
|
||
guard let productsContainer = channel["products"] as? [String: Any],
|
||
let productArray = productsContainer["product"] as? [[String: Any]] else {
|
||
continue
|
||
}
|
||
|
||
for product in productArray {
|
||
|
||
guard let productId = product["id"] as? String,
|
||
let productDisplayName = product["displayName"] as? String,
|
||
let productVersion = product["version"] as? String else {
|
||
continue
|
||
}
|
||
|
||
/**
|
||
sti 的 referencedProducts 就是空的,不需要
|
||
同时也不需要 icon,因为是隐藏的,所以这个不要也罢
|
||
*/
|
||
var productObject = Product(
|
||
type: product["type"] as? String ?? "",
|
||
displayName: productDisplayName,
|
||
family: product["family"] as? String ?? "",
|
||
appLineage: product["appLineage"] as? String ?? "",
|
||
familyName: product["familyName"] as? String ?? "",
|
||
productIcons: [],
|
||
platforms: [],
|
||
referencedProducts: [],
|
||
version: productVersion,
|
||
id: productId,
|
||
hidden: true
|
||
)
|
||
|
||
if let platforms = product["platforms"] as? [String: Any],
|
||
let platformArray = platforms["platform"] as? [[String: Any]] {
|
||
for platform in platformArray {
|
||
guard let platformId = platform["id"] as? String,
|
||
let languageSets = platform["languageSet"] as? [[String: Any]],
|
||
let languageSet = languageSets.first else {
|
||
continue
|
||
}
|
||
|
||
// sti 的 dependencies 就是空的
|
||
let newLanguageSet = Product.Platform.LanguageSet(
|
||
manifestURL: (languageSet["urls"] as? [String: Any])?["manifestURL"] as? String ?? "",
|
||
dependencies: [],
|
||
productCode: languageSet["productCode"] as? String ?? "",
|
||
name: languageSet["name"] as? String ?? "",
|
||
installSize: languageSet["installSize"] as? Int ?? 0,
|
||
buildGuid: languageSet["buildGuid"] as? String ?? "", // 将 buildGuid 赋值给 LanguageSet
|
||
baseVersion: languageSet["baseVersion"] as? String ?? "",
|
||
productVersion: languageSet["productVersion"] as? String ?? ""
|
||
)
|
||
|
||
// sti 的 module 也是空的,不需要
|
||
var newPlatform = Product.Platform(
|
||
languageSet: [newLanguageSet],
|
||
modules: [],
|
||
range: [],
|
||
id: platformId
|
||
)
|
||
|
||
if let range = platform["systemCompatibility"] as? [String: Any],
|
||
let operatingSystem = range["operatingSystem"] as? [String: Any],
|
||
let rangeArray = operatingSystem["range"] as? [String] {
|
||
let min = rangeArray.first ?? ""
|
||
let max = rangeArray.count > 1 ? rangeArray[1] : ""
|
||
let newRange = Product.Platform.Range(min: min, max: max)
|
||
newPlatform.range = [newRange]
|
||
}
|
||
|
||
productObject.platforms.append(newPlatform)
|
||
}
|
||
}
|
||
products.append(productObject)
|
||
|
||
}
|
||
}
|
||
|
||
return NewParseResult(products: products, cdn: cdn)
|
||
}
|
||
|
||
private static func parseCcm(jsonObject: [String: Any], apiVersion: Int) throws -> NewParseResult {
|
||
let cdnPath: [String]
|
||
if apiVersion == 6 {
|
||
cdnPath = ["channels", "channel"]
|
||
} else {
|
||
cdnPath = ["channel"]
|
||
}
|
||
|
||
func getValue(from dict: [String: Any], path: [String]) -> Any? {
|
||
var current: Any = dict
|
||
for key in path {
|
||
guard let dict = current as? [String: Any],
|
||
let value = dict[key] else {
|
||
return nil
|
||
}
|
||
current = value
|
||
}
|
||
return current
|
||
}
|
||
|
||
var channelArray: [[String: Any]] = []
|
||
if let channels = getValue(from: jsonObject, path: cdnPath) {
|
||
if let array = channels as? [[String: Any]] {
|
||
channelArray = array
|
||
} else if let dict = channels as? [String: Any],
|
||
let array = dict["channel"] as? [[String: Any]] {
|
||
channelArray = array
|
||
}
|
||
}
|
||
|
||
guard let firstChannel = channelArray.first,
|
||
let cdn = (firstChannel["cdn"] as? [String: Any])?["secure"] as? String else {
|
||
throw ParserError.missingCDN
|
||
}
|
||
|
||
var products = [Product]()
|
||
|
||
for channel in channelArray {
|
||
let channelName = channel["name"] as? String ?? ""
|
||
if(channelName != "ccm") { continue }
|
||
|
||
guard let productsContainer = channel["products"] as? [String: Any],
|
||
let productArray = productsContainer["product"] as? [[String: Any]] else {
|
||
continue
|
||
}
|
||
|
||
for product in productArray {
|
||
guard let productId = product["id"] as? String,
|
||
let productDisplayName = product["displayName"] as? String,
|
||
let productVersion = product["version"] as? String else {
|
||
continue
|
||
}
|
||
|
||
let icons = (product["productIcons"] as? [String: Any])?["icon"] as? [[String: Any]] ?? []
|
||
let productIcons = icons.compactMap { icon -> Product.ProductIcon? in
|
||
guard let size = icon["size"] as? String,
|
||
let value = icon["value"] as? String else {
|
||
return nil
|
||
}
|
||
return Product.ProductIcon(value: value, size: size)
|
||
}
|
||
|
||
var productObject = Product(
|
||
type: product["type"] as? String ?? "",
|
||
displayName: productDisplayName,
|
||
family: product["family"] as? String ?? "",
|
||
appLineage: product["appLineage"] as? String ?? "",
|
||
familyName: product["familyName"] as? String ?? "",
|
||
productIcons: productIcons,
|
||
platforms: [],
|
||
referencedProducts: [],
|
||
version: productVersion,
|
||
id: productId,
|
||
hidden: false
|
||
)
|
||
|
||
if let platforms = product["platforms"] as? [String: Any],
|
||
let platformArray = platforms["platform"] as? [[String: Any]] {
|
||
for platform in platformArray {
|
||
guard let platformId = platform["id"] as? String,
|
||
let languageSets = platform["languageSet"] as? [[String: Any]],
|
||
let languageSet = languageSets.first else {
|
||
continue
|
||
}
|
||
|
||
var newLanguageSet = Product.Platform.LanguageSet(
|
||
manifestURL: (languageSet["urls"] as? [String: Any])?["manifestURL"] as? String ?? "",
|
||
dependencies: [],
|
||
productCode: languageSet["productCode"] as? String ?? "",
|
||
name: languageSet["name"] as? String ?? "",
|
||
installSize: languageSet["installSize"] as? Int ?? 0,
|
||
buildGuid: languageSet["buildGuid"] as? String ?? "",
|
||
baseVersion: languageSet["baseVersion"] as? String ?? "",
|
||
productVersion: languageSet["productVersion"] as? String ?? ""
|
||
)
|
||
|
||
var dependencies: [Product.Platform.LanguageSet.Dependency] = []
|
||
if let deps = languageSet["dependencies"] as? [String: Any],
|
||
let depArray = deps["dependency"] as? [[String: Any]] {
|
||
dependencies = depArray.compactMap { dep in
|
||
guard let sapCode = dep["sapCode"] as? String,
|
||
let baseVersion = dep["baseVersion"] as? String else {
|
||
return Product.Platform.LanguageSet.Dependency(sapCode: "",baseVersion: "",productVersion: "",buildGuid: "")
|
||
}
|
||
|
||
var productVersion = ""
|
||
var buildGuid = ""
|
||
|
||
if !globalStiResult.products.isEmpty {
|
||
let matchingProducts = globalStiResult.products.filter { $0.id == sapCode }
|
||
|
||
if let latestProduct = matchingProducts.sorted(by: {
|
||
return AppStatics.compareVersions($0.version, $1.version) < 0
|
||
}).last {
|
||
let targetPlatformId = AppStatics.isAppleSilicon ? "macarm64" : "osx10-64"
|
||
let _ = latestProduct.platforms.map { $0.id }
|
||
if let matchingPlatform = latestProduct.platforms.first(where: { $0.id == targetPlatformId }),
|
||
let firstLanguageSet = matchingPlatform.languageSet.first {
|
||
productVersion = firstLanguageSet.productVersion
|
||
buildGuid = firstLanguageSet.buildGuid
|
||
}
|
||
}
|
||
}
|
||
|
||
return Product.Platform.LanguageSet.Dependency(
|
||
sapCode: sapCode,
|
||
baseVersion: baseVersion,
|
||
productVersion: productVersion,
|
||
buildGuid: buildGuid
|
||
)
|
||
}
|
||
newLanguageSet.dependencies.append(contentsOf: dependencies)
|
||
}
|
||
|
||
var newPlatform = Product.Platform(
|
||
languageSet: [newLanguageSet],
|
||
modules: [],
|
||
range: [],
|
||
id: platformId
|
||
)
|
||
|
||
if let modules = platform["modules"] as? [String: Any],
|
||
let moduleArray = modules["module"] as? [[String: Any]] {
|
||
let newModules: [Product.Platform.Module] = moduleArray.compactMap { (module: [String: Any]) -> Product.Platform.Module? in
|
||
guard let displayName = module["displayName"] as? String,
|
||
let deploymentType = module["deploymentType"] as? String,
|
||
let id = module["id"] as? String else {
|
||
return nil
|
||
}
|
||
return Product.Platform.Module(displayName: displayName, deploymentType: deploymentType, id: id)
|
||
}
|
||
newPlatform.modules = newModules
|
||
}
|
||
|
||
if let range = platform["systemCompatibility"] as? [String: Any],
|
||
let operatingSystem = range["operatingSystem"] as? [String: Any],
|
||
let rangeArray = operatingSystem["range"] as? [String] {
|
||
let min = rangeArray.first ?? ""
|
||
let max = rangeArray.count > 1 ? rangeArray[1] : ""
|
||
let newRange = Product.Platform.Range(min: min, max: max)
|
||
newPlatform.range = [newRange]
|
||
}
|
||
|
||
productObject.platforms.append(newPlatform)
|
||
}
|
||
}
|
||
|
||
if let referencedProductsArray = product["referencedProducts"] as? [[String: Any]] {
|
||
let referencedProducts: [Product.ReferencedProduct] = referencedProductsArray.compactMap { (refProduct: [String: Any]) -> Product.ReferencedProduct? in
|
||
guard let sapCode = refProduct["sapCode"] as? String,
|
||
let version = refProduct["version"] as? String else {
|
||
return nil
|
||
}
|
||
return Product.ReferencedProduct(sapCode: sapCode, version: version)
|
||
}
|
||
productObject.referencedProducts = referencedProducts
|
||
}
|
||
products.append(productObject)
|
||
}
|
||
}
|
||
|
||
return NewParseResult(products: products, cdn: cdn)
|
||
}
|
||
}
|