array( 'url' => array( 'name' => 'Repository URL', 'title' => 'Usually ends with /repo/', 'required' => true, 'exampleValue' => 'https://srv.tt-rss.org/fdroid/repo' ) ), 'Latest Updates' => array( 'sorting' => array( 'name' => 'Sort By', 'type' => 'list', 'values' => array( 'Latest added apps' => 'added', 'Latest updated apps' => 'lastUpdated' ) ), 'locale' => array( 'name' => 'Locale', 'defaultValue' => 'en-US' ) ), 'Follow Package' => array( 'package' => array( 'name' => 'Package Identifier', 'required' => true, 'exampleValue' => 'org.fox.ttrss' ) ) ); // Stores repo information private $repo; public function getURI() { if (empty($this->queriedContext)) return parent::getURI(); $url = rtrim($this->GetInput('url'), '/'); return strstr($url, '?', true) ?: $url; } public function getName() { if (empty($this->queriedContext)) return parent::getName(); $name = $this->repo['repo']['name']; switch($this->queriedContext) { case 'Latest Updates': return $name; case 'Follow Package': return $this->getInput('package') . ' - ' . $name; default: returnServerError('Unimplemented Context (getName)'); } } public function collectData() { $this->repo = $this->getRepo(); switch($this->queriedContext) { case 'Latest Updates': $this->getAllUpdates(); break; case 'Follow Package': $this->getPackage($this->getInput('package')); break; default: returnServerError('Unimplemented Context (collectData)'); } } private function getRepo() { $url = $this->getURI(); // Get repo information (only available as JAR) $jar = getContents($url . '/index-v1.jar'); $jar_loc = tempnam(sys_get_temp_dir(), ''); file_put_contents($jar_loc, $jar); // JAR files are specially formatted ZIP files $jar = new ZipArchive; if ($jar->open($jar_loc) !== true) { returnServerError('Failed to extract archive'); } // Get file pointer to the relevant JSON inside $fp = $jar->getStream('index-v1.json'); if (!$fp) { returnServerError('Failed to get file pointer'); } $data = json_decode(stream_get_contents($fp), true); fclose($fp); $jar->close(); return $data; } private function getAllUpdates() { $apps = $this->repo['apps']; usort($apps, function($a, $b) { return $b[$this->getInput('sorting')] <=> $a[$this->getInput('sorting')]; }); $apps = array_slice($apps, 0, self::ITEM_LIMIT); foreach($apps as $app) { $latest = reset($this->repo['packages'][$app['packageName']]); if (isset($app['localized'])) { // Try provided locale, then en-US, then any $lang = $app['localized']; $lang = $lang[$this->getInput('locale')] ?? $lang['en-US'] ?? reset($lang); } else $lang = array(); $item = array(); $item['uri'] = $this->getURI() . '/' . $latest['apkName']; $item['title'] = $lang['name'] ?? $app['packageName']; $item['title'] .= ' ' . $latest['versionName']; $item['timestamp'] = date(DateTime::ISO8601, (int) ($app['lastUpdated'] / 1000)); if (isset($app['authorName'])) $item['author'] = $app['authorName']; if (isset($app['categories'])) $item['categories'] = $app['categories']; // Adding Content $icon = $app['icon'] ?? ''; if (!empty($icon)) { $icon = $this->getURI() . '/icons-320/' . $icon; $item['enclosures'] = array($icon); $icon = ''; } $summary = $lang['summary'] ?? $app['summary'] ?? ''; $description = markdownToHtml(trim($lang['description'] ?? $app['description'] ?? 'None')); $whatsNew = markdownToHtml(trim($lang['whatsNew'] ?? 'None')); $website = $this->link($lang['webSite'] ?? $app['webSite'] ?? $app['authorWebSite'] ?? null); $source = $this->link($app['sourceCode'] ?? null); $issueTracker = $this->link($app['issueTracker'] ?? null); $license = $app['license'] ?? 'None'; $item['content'] = <<{$summary}

Description

{$description}

What's New

{$whatsNew}

Information

Website: {$website}

Source Code: {$source}

Issue Tracker: {$issueTracker}

license: {$app['license']}

EOD; $this->items[] = $item; } } private function getPackage($package) { if (!isset($this->repo['packages'][$package])) { returnClientError('Invalid Package Name'); } $package = $this->repo['packages'][$package]; $count = self::ITEM_LIMIT; foreach($package as $version) { $item = array(); $item['uri'] = $this->getURI() . '/' . $version['apkName']; $item['title'] = $version['versionName']; $item['timestamp'] = date(DateTime::ISO8601, (int) ($version['added'] / 1000)); $item['uid'] = $version['versionCode']; $size = round($version['size'] / 1048576, 1); // Bytes -> MB $sdk_link = 'https://developer.android.com/studio/releases/platforms'; $item['content'] = <<size: {$size}MB

Minimum SDK: {$version['minSdkVersion']} (SDK to Android Version List)

hash ({$version['hashType']}): {$version['hash']}

EOD; $this->items[] = $item; if (--$count <= 0) break; } } private function link($url) { if (empty($url)) return null; return '' . $url . ''; } }