Skip to content

Commit

Permalink
Update with ImageProxy extension
Browse files Browse the repository at this point in the history
  • Loading branch information
Victrid committed May 15, 2024
1 parent 66baaf9 commit d473c71
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 134 deletions.
37 changes: 24 additions & 13 deletions configure.phtml
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
<form action="<?php echo _url('extension', 'configure', 'e', urlencode($this->getName())); ?>" method="post">
<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
<?php
declare(strict_types=1);
/** @var ImageProxyExtension $this */
?>
<form action="<?= _url('extension', 'configure', 'e', urlencode($this->getName())) ?>" method="post">
<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" />
<h3>User fetch settings</h3>
<div class="form-group">
<label class="group-name" for="image_cache_url"><?php echo _t('ext.imagecache.cache_url'); ?></label>
<label class="group-name" for="image_cache_url"><?= _t('ext.imagecache.cache_url') ?></label>
<div class="group-controls">
<input type="url" name="image_cache_url" id="image_cache_url" value="<?php echo FreshRSS_Context::$user_conf->image_cache_url; ?>">
<input type="url" name="image_cache_url" id="image_cache_url" value="<?= FreshRSS_Context::userConf()->image_cache_url ?>">
</div>
</div>
<div class="form-group">
<label class="group-name" for="image_cache_url_encode"><?php echo _t('ext.imagecache.url_encode'); ?></label>
<label class="group-name" for="image_cache_url_encode"><?= _t('ext.imagecache.url_encode') ?></label>
<div class="group-controls">
<input type="checkbox" name="image_cache_url_encode" id="image_cache_url_encode" value="1" <?php echo (FreshRSS_Context::$user_conf->image_cache_url_encode ? 'checked' : ''); ?>>
<input type="checkbox" name="image_cache_url_encode" id="image_cache_url_encode" value="1" <?= FreshRSS_Context::userConf()->image_cache_url_encode ? 'checked' : '' ?>>
</div>
</div>
<h3><?= _t('ext.imagecache.proactive_cache') ?></h3>
<p><?= _t('ext.imagecache.proactive_cache_desc') ?></p>
<div class="form-group">
<label class="group-name" for="image_cache_post_enabled"><?= _t('ext.imagecache.proactive_cache_enabled') ?></label>
<div class="group-controls">
<input type="checkbox" name="image_cache_post_enabled" id="image_cache_post_enabled" value="1" <?= FreshRSS_Context::userConf()->image_cache_post_enabled ? 'checked' : '' ?>>
</div>
</div>
<h3>FreshRSS notification settings</h3>
<div class="form-group">
<label class="group-name" for="image_cache_post_url"><?php echo _t('ext.imagecache.post_url'); ?></label>
<label class="group-name" for="image_cache_post_url"><?= _t('ext.imagecache.post_url') ?></label>
<div class="group-controls">
<input type="url" name="image_cache_post_url" id="image_cache_post_url" value="<?php echo FreshRSS_Context::$user_conf->image_cache_post_url; ?>">
<input type="url" name="image_cache_post_url" id="image_cache_post_url" value="<?= FreshRSS_Context::userConf()->image_cache_post_url ?>">
</div>
</div>
<div class="form-group">
<label class="group-name" for="image_cache_access_token"><?php echo _t('ext.imagecache.access_token'); ?></label>
<label class="group-name" for="image_cache_access_token"><?= _t('ext.imagecache.access_token'); ?></label>
<div class="group-controls">
<input type="text" name="image_cache_access_token" id="image_cache_access_token" value="<?php echo FreshRSS_Context::$user_conf->image_cache_access_token; ?>">
<input type="text" name="image_cache_access_token" id="image_cache_access_token" value="<?= FreshRSS_Context::userConf()->image_cache_access_token ?>">
</div>
</div>
<div class="form-group form-actions">
<div class="group-controls">
<button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button>
<button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button>
<button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button>
<button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button>
</div>
</div>
</form>
259 changes: 141 additions & 118 deletions extension.php
Original file line number Diff line number Diff line change
@@ -1,142 +1,165 @@
<?php

class ImageCacheExtension extends Minz_Extension {
// Defaults
const CACHE_URL = 'https://example.com/pic?url=';
const CACHE_POST_URL = 'https://example.com/prepare';
const CACHE_ACCESS_TOKEN = '';
const URL_ENCODE = '1';
declare(strict_types=1);

public function init() {
$this->registerHook('entry_before_display',
array($this, 'content_modification_hook'));
$this->registerHook('entry_before_insert',
array($this, 'image_upload_hook'));
// Defaults
$save = false;
FreshRSS_Context::$user_conf->image_cache_url=html_entity_decode(FreshRSS_Context::$user_conf->image_cache_url);
if (is_null(FreshRSS_Context::$user_conf->image_cache_url)) {
FreshRSS_Context::$user_conf->image_cache_url = self::CACHE_URL;
$save = true;
}
if (is_null(FreshRSS_Context::$user_conf->image_cache_post_url)) {
FreshRSS_Context::$user_conf->image_cache_post_url = self::CACHE_POST_URL;
$save = true;
}
if (is_null(FreshRSS_Context::$user_conf->image_cache_post_url)) {
FreshRSS_Context::$user_conf->image_cache_access_token = self::CACHE_ACCESS_TOKEN;
$save = true;
}
if (is_null(FreshRSS_Context::$user_conf->image_cache_url_encode)) {
FreshRSS_Context::$user_conf->image_cache_url_encode = self::URL_ENCODE;
$save = true;
}
if ($save) {
FreshRSS_Context::$user_conf->save();
}
}
final class ImageCacheExtension extends Minz_Extension
{
// Defaults
private const CACHE_URL = 'https://wsrv.nl/?url=';
private const CACHE_POST_URL = 'https://example.com/prepare';
private const CACHE_ACCESS_TOKEN = '';
private const URL_ENCODE = '1';
private const CACHE_POST_ENABLED = '';

public function handleConfigureAction() {
$this->registerTranslates();
#[\Override]
public function init(): void
{
if (!FreshRSS_Context::hasSystemConf()) {
throw new FreshRSS_Context_Exception('System configuration not initialised!');
}
$this->registerHook('entry_before_display', [self::class, 'content_modification_hook']);
$this->registerHook('entry_before_insert', [self::class, 'image_upload_hook']);
// Defaults
$save = false;
if (is_null(FreshRSS_Context::userConf()->image_cache_url)) {
FreshRSS_Context::userConf()->image_cache_url = self::CACHE_URL;
$save = true;
}
if (is_null(FreshRSS_Context::userConf()->image_cache_post_url)) {
FreshRSS_Context::userConf()->image_cache_post_url = self::CACHE_POST_URL;
$save = true;
}
if (is_null(FreshRSS_Context::userConf()->image_cache_post_url)) {
FreshRSS_Context::userConf()->image_cache_access_token = self::CACHE_ACCESS_TOKEN;
$save = true;
}
if (is_null(FreshRSS_Context::userConf()->image_cache_url_encode)) {
FreshRSS_Context::userConf()->image_cache_url_encode = self::URL_ENCODE;
$save = true;
}
if (is_null(FreshRSS_Context::userConf()->image_cache_post_enabled)) {
FreshRSS_Context::userConf()->image_cache_post_enabled = self::CACHE_POST_ENABLED;
$save = true;
}
if ($save) {
FreshRSS_Context::userConf()->save();
}
}

#[\Override]
public function handleConfigureAction(): void
{
$this->registerTranslates();

if (Minz_Request::isPost()) {
FreshRSS_Context::userConf()->image_cache_url = Minz_Request::paramString('image_cache_url');
FreshRSS_Context::userConf()->image_cache_post_url = Minz_Request::paramString('image_cache_post_url');
FreshRSS_Context::userConf()->image_cache_access_token = Minz_Request::paramString('image_cache_access_token');
FreshRSS_Context::userConf()->image_cache_url_encode = Minz_Request::paramString('image_cache_url_encode');
FreshRSS_Context::userConf()->image_cache_post_enabled = Minz_Request::paramString('image_cache_post_enabled');
FreshRSS_Context::userConf()->save();
}
}

if (Minz_Request::isPost()) {
FreshRSS_Context::$user_conf->image_cache_url = Minz_Request::param('image_cache_url', self::CACHE_URL);
FreshRSS_Context::$user_conf->image_cache_post_url = Minz_Request::param('image_cache_post_url', self::CACHE_POST_URL);
FreshRSS_Context::$user_conf->image_cache_access_token = Minz_Request::param('image_cache_access_token', self::CACHE_ACCESS_TOKEN);
FreshRSS_Context::$user_conf->image_cache_url_encode = Minz_Request::param('image_cache_url_encode', '');
FreshRSS_Context::$user_conf->save();
}
}

public static function posturl($url,$data){
$data = json_encode($data);
public static function curlPostRequest(string $url, array $data): mixed
{
$data = json_encode($data);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json;charset='utf-8'",
'Content-Length: ' . strlen($data),
"Accept: application/json")
);
"Content-Type: application/json;charset='utf-8'",
'Content-Length: ' . strlen($data),
"Accept: application/json")
);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
$output = curl_exec($curl);
curl_close($curl);
return json_decode($output, true);
return json_decode($output, true);
}

public static function post_request($url, $post_url) {
return self::posturl($post_url, array("access_token" => FreshRSS_Context::$user_conf->image_cache_access_token, "url" => $url));

public static function send_proactive_cache_request(string $url): mixed
{
if (FreshRSS_Context::userConf()->image_cache_post_enabled) {
$post_url = FreshRSS_Context::userConf()->image_cache_post_url;
return self::curlPostRequest($post_url, array("access_token" => FreshRSS_Context::userConf()->image_cache_access_token, "url" => $url));
}
return false;
}

public static function getCacheImageUri($url) {
public static function getCacheImageUri(string $url): string
{
$url = rawurlencode($url);
return FreshRSS_Context::$user_conf->image_cache_url . $url;
}


# Used for srcset
public static function getSrcSetUris($matches) {
return str_replace($matches[1], self::getCacheImageUri($matches[1]), $matches[0]);
}

public static function uploadSrcSetUris($matches) {
return str_replace($matches[1], self::post_request($matches[1], FreshRSS_Context::$user_conf->image_cache_post_url), $matches[0]);
}

public static function uploadUris($content) {
if (empty($content)) {
return $content;
}
$doc = new DOMDocument();
libxml_use_internal_errors(true); // prevent tag soup errors from showing
$doc->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
$imgs = $doc->getElementsByTagName('img');
foreach ($imgs as $img) {
if ($img->hasAttribute('src')) {
self::post_request($img->getAttribute('src'), FreshRSS_Context::$user_conf->image_cache_post_url);
}
if ($img->hasAttribute('srcset')) {
$newSrcSet = preg_replace_callback('/(?:([^\s,]+)(\s*(?:\s+\d+[wx])(?:,\s*)?))/', 'self::uploadSrcSetUris', $img->getAttribute('srcset'));
}
}
}
return FreshRSS_Context::userConf()->image_cache_url . $url;
}

public static function swapUris($content) {
if (empty($content)) {
return $content;
}
$doc = new DOMDocument();
libxml_use_internal_errors(true); // prevent tag soup errors from showing
$doc->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
$imgs = $doc->getElementsByTagName('img');
foreach ($imgs as $img) {
if ($img->hasAttribute('src')) {
$newSrc = self::getCacheImageUri($img->getAttribute('src'));
$img->setAttribute('src', $newSrc);
}
if ($img->hasAttribute('srcset')) {
$newSrcSet = preg_replace_callback('/(?:([^\s,]+)(\s*(?:\s+\d+[wx])(?:,\s*)?))/', 'self::getSrcSetUris', $img->getAttribute('srcset'));
$img->setAttribute('srcset', $newSrcSet);
}
}
return $doc->saveHTML();
}
public static function cache_images(string $content): string
{
if (empty($content)) {
return $content;
}
$doc = new DOMDocument();
libxml_use_internal_errors(true); // prevent tag soup errors from showing
$doc->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
$imgs = $doc->getElementsByTagName('img');
foreach ($imgs as $img) {
if ($img->hasAttribute('src')) {
self::send_proactive_cache_request($img->getAttribute('src'));
}
if ($img->hasAttribute('srcset')) {
preg_replace_callback('/(?:([^\s,]+)(\s*(?:\s+\d+[wx])(?:,\s*)?))/',
function (array $matches): string {
self::send_proactive_cache_request($matches[1]);
return '';
},
$img->getAttribute('srcset'));
}
}
return $content;
}

public static function content_modification_hook($entry) {
$entry->_content(
self::swapUris($entry->content())
);
public static function swapUris(string $content): string
{
if (empty($content)) {
return $content;
}
$doc = new DOMDocument();
libxml_use_internal_errors(true); // prevent tag soup errors from showing
$doc->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
$imgs = $doc->getElementsByTagName('img');
foreach ($imgs as $img) {
if ($img->hasAttribute('src')) {
$newSrc = self::getCacheImageUri($img->getAttribute('src'));
$img->setAttribute('src', $newSrc);
}
if ($img->hasAttribute('srcset')) {
$newSrcSet = preg_replace_callback('/(?:([^\s,]+)(\s*(?:\s+\d+[wx])(?:,\s*)?))/',
function (array $matches): string {
return str_replace($matches[1], self::getCacheImageUri($matches[1]), $matches[0]);
}
, $img->getAttribute('srcset'));
$img->setAttribute('srcset', $newSrcSet);
}
}
return $doc->saveHTML();
}

return $entry;
}
public static function image_upload_hook($entry) {
self::uploadUris($entry->content());
public static function content_modification_hook($entry)
{
$entry->_content(
self::swapUris($entry->content())
);

return $entry;
}
return $entry;
}

public static function image_upload_hook($entry)
{
self::cache_images($entry->content());
return $entry;
}
}
8 changes: 6 additions & 2 deletions i18n/en/ext.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
return array(
'imagecache' => array(
'cache_url' => 'Cache URL (for user to fetch)',
'url_encode' => 'Encode the URL',
'url_encode' => 'Encode the URL',
'post_url' => 'Post URL (for freshRSS to inform)',
'access_token' => 'Access Token (for freshRSS to inform)',
'proactive_cache' => 'FreshRSS Proactive Cache Settings',
'proactive_cache_enabled' => 'Enable proactive cache',
'proactive_cache_desc' => 'The proactive cache allows FreshRSS to notify cache server to download pictures ' .
'when new article is fetched. This will improve picture loading speed.',
),
);
);
3 changes: 3 additions & 0 deletions i18n/zh-CN/ext.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
'url_encode' => '对URL进行转义',
'post_url' => '通知URL(用于建立缓存)',
'access_token' => '访问token(用于建立缓存)',
'proactive_cache' => '主动缓存设置',
'proactive_cache_enabled' => '启用主动缓存',
'proactive_cache_desc' => '主动缓存允许FreshRSS在获取到新文章时就通知缓存服务器下载图片。这样可以加快看到图片的速度。',
),
);
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Image Cache",
"author": "Victrid",
"description": "Cache feed images on your own facility or Cloudflare cache.",
"version": 0.3,
"version": "0.4.0",
"entrypoint": "ImageCache",
"type": "user"
}

0 comments on commit d473c71

Please sign in to comment.