-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FDroidRepoBridge] Add F-Droid Repo Bridge
- Loading branch information
Showing
1 changed file
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
<?php | ||
class FDroidRepoBridge extends BridgeAbstract { | ||
const NAME = 'F-Droid Repository Bridge'; | ||
const URI = 'https://f-droid.org/'; | ||
const DESCRIPTION = 'Query any F-Droid Repository for its latest updates.'; | ||
const MAINTAINER = 'Yaman Qalieh'; | ||
|
||
const ITEM_LIMIT = 50; | ||
|
||
const PARAMETERS = array( | ||
'global' => 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 = '<img src="' . $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'] = <<<EOD | ||
{$icon} | ||
<p>{$summary}</p> | ||
<h1>Description</h1> | ||
{$description} | ||
<h1>What's New</h1> | ||
{$whatsNew} | ||
<h1>Information</h1> | ||
<p>Website: {$website}</p> | ||
<p>Source Code: {$source}</p> | ||
<p>Issue Tracker: {$issueTracker}</p> | ||
<p>license: {$app['license']}</p> | ||
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'] = <<<EOD | ||
<p>size: {$size}MB</p> | ||
<p>Minimum SDK: {$version['minSdkVersion']} | ||
(<a href="{$sdk_link}">SDK to Android Version List</a>)</p> | ||
<p>hash ({$version['hashType']}): {$version['hash']}</p> | ||
EOD; | ||
$this->items[] = $item; | ||
if (--$count <= 0) | ||
break; | ||
} | ||
} | ||
|
||
private function link($url) { | ||
if (empty($url)) | ||
return null; | ||
return '<a href="' . $url . '">' . $url . '</a>'; | ||
} | ||
} |