-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Apple App Store bridge fix #4516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,21 +52,37 @@ class AppleAppStoreBridge extends BridgeAbstract | |
], | ||
'defaultValue' => 'US', | ||
], | ||
'debug' => [ | ||
'name' => 'Debug Mode', | ||
'type' => 'checkbox', | ||
'defaultValue' => false | ||
] | ||
]]; | ||
|
||
const PLATFORM_MAPPING = [ | ||
'iphone' => 'ios', | ||
'ipad' => 'ios', | ||
'iphone' => 'ios', | ||
'ipad' => 'ios', | ||
'mac' => 'osx' | ||
]; | ||
|
||
private function makeHtmlUrl($id, $country) | ||
private $name; | ||
|
||
private function makeHtmlUrl() | ||
{ | ||
return 'https://apps.apple.com/' . $country . '/app/id' . $id; | ||
$id = $this->getInput('id'); | ||
$country = $this->getInput('country'); | ||
return "https://apps.apple.com/{$country}/app/id{$id}"; | ||
} | ||
|
||
private function makeJsonUrl($id, $platform, $country) | ||
private function makeJsonUrl() | ||
{ | ||
return "https://amp-api.apps.apple.com/v1/catalog/$country/apps/$id?platform=$platform&extend=versionHistory"; | ||
$id = $this->getInput('id'); | ||
$country = $this->getInput('country'); | ||
$platform = $this->getInput('p'); | ||
|
||
$platform_param = ($platform === 'mac') ? 'mac' : $platform; | ||
|
||
return "https://amp-api.apps.apple.com/v1/catalog/{$country}/apps/{$id}?platform={$platform_param}&extend=versionHistory"; | ||
} | ||
|
||
public function getName() | ||
|
@@ -78,94 +94,157 @@ public function getName() | |
return parent::getName(); | ||
} | ||
|
||
/** | ||
* In case of some platforms, the data is present in the initial response | ||
*/ | ||
private function getDataFromShoebox($id, $platform, $country) | ||
private function debugLog($message) | ||
{ | ||
$uri = $this->makeHtmlUrl($id, $country); | ||
$html = getSimpleHTMLDOMCached($uri, 3600); | ||
$script = $html->find('script[id="shoebox-ember-data-store"]', 0); | ||
|
||
$json = json_decode($script->innertext, true); | ||
return $json['data']; | ||
if ($this->getInput('debug')) { | ||
error_log("[AppleAppStoreBridge] $message"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pls dont use use:
|
||
} | ||
} | ||
|
||
private function getJWTToken($id, $platform, $country) | ||
private function getHtml() | ||
{ | ||
$uri = $this->makeHtmlUrl($id, $country); | ||
|
||
$html = getSimpleHTMLDOMCached($uri, 3600); | ||
$url = $this->makeHtmlUrl(); | ||
$this->debugLog("Fetching HTML from: $url"); | ||
|
||
$html = getSimpleHTMLDOM($url); | ||
|
||
if (!$html) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will never happen because return value is always |
||
throw new \Exception("Failed to retrieve HTML from App Store"); | ||
} | ||
|
||
$this->debugLog("HTML fetch successful"); | ||
return $html; | ||
} | ||
|
||
private function getJWTToken() | ||
{ | ||
$html = $this->getHtml(); | ||
$meta = $html->find('meta[name="web-experience-app/config/environment"]', 0); | ||
|
||
$json = urldecode($meta->content); | ||
|
||
$json = json_decode($json); | ||
|
||
return $json->MEDIA_API->token; | ||
|
||
if (!$meta || !isset($meta->content)) { | ||
throw new \Exception("JWT token not found in page content"); | ||
} | ||
|
||
try { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. put as little code as possible in the try clause |
||
$decoded_content = urldecode($meta->content); | ||
$this->debugLog("Found meta tag content"); | ||
$decoded_json = json_decode($decoded_content, true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See |
||
|
||
if (!isset($decoded_json['MEDIA_API']['token'])) { | ||
throw new \Exception("Token field not found in JSON structure"); | ||
} | ||
|
||
$token = $decoded_json['MEDIA_API']['token']; | ||
$this->debugLog("Successfully extracted JWT token"); | ||
return $token; | ||
} catch (\Exception $e) { | ||
throw new \Exception("Failed to extract JWT token: " . $e->getMessage()); | ||
} | ||
} | ||
|
||
private function getAppData($id, $platform, $country, $token) | ||
private function getAppData() | ||
{ | ||
$uri = $this->makeJsonUrl($id, $platform, $country); | ||
|
||
$token = $this->getJWTToken(); | ||
|
||
$url = $this->makeJsonUrl(); | ||
$this->debugLog("Fetching data from API: $url"); | ||
|
||
$headers = [ | ||
"Authorization: Bearer $token", | ||
'Authorization: Bearer ' . $token, | ||
'Origin: https://apps.apple.com', | ||
'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', | ||
]; | ||
|
||
$json = json_decode(getContents($uri, $headers), true); | ||
|
||
|
||
$content = getContents($url, $headers); | ||
if (!$content) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
throw new \Exception("Failed to get content from API"); | ||
} | ||
|
||
$json = json_decode($content, true); | ||
|
||
if (!isset($json['data']) || empty($json['data'])) { | ||
throw new \Exception("No app data found in API response"); | ||
} | ||
|
||
$this->debugLog("Successfully retrieved app data from API"); | ||
return $json['data'][0]; | ||
} | ||
|
||
/** | ||
* Parses the version history from the data received | ||
* @return array list of versions with details on each element | ||
*/ | ||
private function getVersionHistory($data, $platform) | ||
private function extractAppDetails($data) | ||
{ | ||
switch ($platform) { | ||
case 'mac': | ||
return $data['relationships']['platforms']['data'][0]['attributes']['versionHistory']; | ||
default: | ||
$os = self::PLATFORM_MAPPING[$platform]; | ||
return $data['attributes']['platformAttributes'][$os]['versionHistory']; | ||
try { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code does not need to be wrapped in a try catch |
||
if (isset($data['attributes'])) { | ||
$this->name = $data['attributes']['name'] ?? null; | ||
$author = $data['attributes']['artistName'] ?? null; | ||
$this->debugLog("Found app details in attributes: {$this->name} by {$author}"); | ||
return [$this->name, $author]; | ||
} | ||
|
||
$this->name = "App " . $this->getInput('id'); | ||
$this->debugLog("App details not found, using default: {$this->name}"); | ||
return [$this->name, "Unknown Developer"]; | ||
} catch (\Exception $e) { | ||
$this->debugLog("Error extracting app details: " . $e->getMessage()); | ||
$this->name = "App " . $this->getInput('id'); | ||
return [$this->name, "Unknown Developer"]; | ||
} | ||
} | ||
|
||
public function collectData() | ||
private function getVersionHistory($data) | ||
{ | ||
$id = $this->getInput('id'); | ||
$country = $this->getInput('country'); | ||
$platform = $this->getInput('p'); | ||
|
||
switch ($platform) { | ||
case 'mac': | ||
$data = $this->getDataFromShoebox($id, $platform, $country); | ||
break; | ||
|
||
default: | ||
$token = $this->getJWTToken($id, $platform, $country); | ||
$data = $this->getAppData($id, $platform, $country, $token); | ||
$this->debugLog("Extracting version history for platform: {$platform}"); | ||
|
||
try { | ||
$platform_key = self::PLATFORM_MAPPING[$platform] ?? $platform; | ||
|
||
if (isset($data['attributes']['platformAttributes'][$platform_key]['versionHistory'])) { | ||
return $data['attributes']['platformAttributes'][$platform_key]['versionHistory']; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should work:
|
||
|
||
$this->debugLog("No version history found for {$platform}"); | ||
return []; | ||
|
||
} catch (\Exception $e) { | ||
$this->debugLog("Error extracting version history: " . $e->getMessage()); | ||
return []; | ||
} | ||
} | ||
|
||
$versionHistory = $this->getVersionHistory($data, $platform); | ||
$name = $this->name = $data['attributes']['name']; | ||
$author = $data['attributes']['artistName']; | ||
|
||
foreach ($versionHistory as $row) { | ||
$item = []; | ||
|
||
$item['content'] = nl2br($row['releaseNotes']); | ||
$item['title'] = $name . ' - ' . $row['versionDisplay']; | ||
$item['timestamp'] = $row['releaseDate']; | ||
$item['author'] = $author; | ||
|
||
$item['uri'] = $this->makeHtmlUrl($id, $country); | ||
|
||
$this->items[] = $item; | ||
public function collectData() | ||
{ | ||
try { | ||
$this->debugLog("Getting data for " . $this->getInput('p') . " app"); | ||
$data = $this->getAppData(); | ||
|
||
list($name, $author) = $this->extractAppDetails($data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pls dont use Something like:
|
||
|
||
$version_history = $this->getVersionHistory($data); | ||
$this->debugLog("Found " . count($version_history) . " versions for {$name}"); | ||
|
||
foreach ($version_history as $entry) { | ||
try { | ||
$version = $entry['versionDisplay'] ?? 'Unknown Version'; | ||
$release_notes = $entry['releaseNotes'] ?? 'No release notes available'; | ||
$release_date = $entry['releaseDate'] ?? 'Unknown Date'; | ||
|
||
$item = []; | ||
$item['title'] = "{$name} - {$version}"; | ||
$item['content'] = nl2br($release_notes) ?: 'No release notes available'; | ||
$item['timestamp'] = $release_date; | ||
$item['author'] = $author; | ||
$item['uri'] = $this->makeHtmlUrl(); | ||
|
||
$this->items[] = $item; | ||
} catch (\Exception $e) { | ||
$this->debugLog("Error processing version entry: " . $e->getMessage()); | ||
} | ||
} | ||
|
||
$this->debugLog("Successfully collected " . count($this->items) . " items"); | ||
} catch (\Exception $e) { | ||
$this->debugLog("Error collecting data: " . $e->getMessage()); | ||
throw $e; | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls choose an alternative way to interpolate string, e.g.
sprintf