mirror of
https://github.com/Terranom674/Piwigo_Bratonien_Tools.git
synced 2026-09-19 23:24:36 +00:00
435 lines
11 KiB
PHP
435 lines
11 KiB
PHP
<?php
|
|
if (!defined('PHPWG_ROOT_PATH'))
|
|
{
|
|
die('Hacking attempt!');
|
|
}
|
|
|
|
require_once(BRATONIEN_TOOLS_PATH.'include/cache_worker_settings.inc.php');
|
|
|
|
function bratonien_tools_main_cache_status_file()
|
|
{
|
|
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-tools-main-cache.status.json';
|
|
}
|
|
|
|
function bratonien_tools_main_cache_cancel_file()
|
|
{
|
|
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-tools-main-cache.cancel';
|
|
}
|
|
|
|
function bratonien_tools_main_cache_lock_file()
|
|
{
|
|
return PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-tools-main-cache.lock';
|
|
}
|
|
|
|
function bratonien_tools_write_main_cache_status(array $status)
|
|
{
|
|
$file = bratonien_tools_main_cache_status_file();
|
|
$directory = dirname($file);
|
|
if (!is_dir($directory) && !mkdir($directory, 0755, true) && !is_dir($directory))
|
|
{
|
|
throw new RuntimeException('Cache-Status konnte nicht vorbereitet werden.');
|
|
}
|
|
|
|
$payload = array_merge(array(
|
|
'state'=>'idle',
|
|
'message'=>'',
|
|
'total'=>0,
|
|
'completed'=>0,
|
|
'generated'=>0,
|
|
'cached'=>0,
|
|
'skipped'=>0,
|
|
'errors'=>0,
|
|
'current'=>'',
|
|
'worker_count'=>0,
|
|
'cpu_count'=>0,
|
|
'updated_at'=>time(),
|
|
), $status);
|
|
$payload['updated_at'] = time();
|
|
|
|
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
if ($json === false || @file_put_contents($file, $json."\n", LOCK_EX) === false)
|
|
{
|
|
throw new RuntimeException('Cache-Status konnte nicht gespeichert werden.');
|
|
}
|
|
@chmod($file, 0664);
|
|
}
|
|
|
|
function bratonien_tools_read_main_cache_status()
|
|
{
|
|
$file = bratonien_tools_main_cache_status_file();
|
|
if (!is_file($file) || !is_readable($file))
|
|
{
|
|
return null;
|
|
}
|
|
$raw = @file_get_contents($file);
|
|
$status = $raw !== false ? json_decode($raw, true) : null;
|
|
return is_array($status) ? $status : null;
|
|
}
|
|
|
|
function bratonien_tools_main_cache_is_running()
|
|
{
|
|
$status = bratonien_tools_read_main_cache_status();
|
|
if (!is_array($status) || !in_array(($status['state'] ?? ''), array('queued','running','cancelling'), true))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$updated = (int)($status['updated_at'] ?? 0);
|
|
return $updated > 0 && (time() - $updated) < 45;
|
|
}
|
|
|
|
function bratonien_tools_main_cache_process_active()
|
|
{
|
|
$lock_path = bratonien_tools_main_cache_lock_file();
|
|
$lock = @fopen($lock_path, 'c');
|
|
if (!$lock)
|
|
{
|
|
return bratonien_tools_main_cache_is_running();
|
|
}
|
|
|
|
$free = @flock($lock, LOCK_EX | LOCK_NB);
|
|
if ($free)
|
|
{
|
|
@flock($lock, LOCK_UN);
|
|
}
|
|
fclose($lock);
|
|
return !$free;
|
|
}
|
|
|
|
function bratonien_tools_request_main_cache_cancel()
|
|
{
|
|
$cancel = bratonien_tools_main_cache_cancel_file();
|
|
$directory = dirname($cancel);
|
|
if (!is_dir($directory) && !mkdir($directory, 0755, true) && !is_dir($directory))
|
|
{
|
|
throw new RuntimeException('Cache-Abbruch konnte nicht vorbereitet werden.');
|
|
}
|
|
|
|
if (@file_put_contents($cancel, (string)time()."\n", LOCK_EX) === false)
|
|
{
|
|
throw new RuntimeException('Cache-Abbruchsignal konnte nicht geschrieben werden.');
|
|
}
|
|
@chmod($cancel, 0664);
|
|
|
|
bratonien_tools_write_main_cache_status(array(
|
|
'state'=>'cancelling',
|
|
'message'=>'Cache-Aufbau wird abgebrochen.',
|
|
));
|
|
}
|
|
|
|
function bratonien_tools_wait_main_cache_stopped($timeout_seconds=10.0)
|
|
{
|
|
$deadline = microtime(true) + max(0.1, (float)$timeout_seconds);
|
|
do
|
|
{
|
|
if (!bratonien_tools_main_cache_process_active())
|
|
{
|
|
return true;
|
|
}
|
|
usleep(100000);
|
|
}
|
|
while (microtime(true) < $deadline);
|
|
|
|
return !bratonien_tools_main_cache_process_active();
|
|
}
|
|
|
|
function bratonien_tools_cancel_main_cache_build()
|
|
{
|
|
if (!bratonien_tools_main_cache_process_active() && !bratonien_tools_main_cache_is_running())
|
|
{
|
|
@unlink(bratonien_tools_main_cache_cancel_file());
|
|
bratonien_tools_write_main_cache_status(array(
|
|
'state'=>'idle',
|
|
'message'=>'Kein Cache-Aufbau aktiv.',
|
|
));
|
|
return array('message'=>'Es läuft kein Piwigo-Bildcache-Aufbau.');
|
|
}
|
|
|
|
bratonien_tools_request_main_cache_cancel();
|
|
|
|
if (!bratonien_tools_wait_main_cache_stopped(10.0))
|
|
{
|
|
return array('message'=>'Abbruch wurde angefordert. Ein Worker beendet noch die aktuell laufende Bildoperation.');
|
|
}
|
|
|
|
@unlink(bratonien_tools_main_cache_cancel_file());
|
|
bratonien_tools_write_main_cache_status(array(
|
|
'state'=>'cancelled',
|
|
'message'=>'Piwigo-Bildcache-Aufbau wurde abgebrochen.',
|
|
));
|
|
|
|
return array('message'=>'Piwigo-Bildcache-Aufbau wurde abgebrochen.');
|
|
}
|
|
|
|
function bratonien_tools_clear_image_cache()
|
|
{
|
|
if (!defined('PWG_DERIVATIVE_DIR'))
|
|
{
|
|
throw new RuntimeException('PWG_DERIVATIVE_DIR ist nicht definiert.');
|
|
}
|
|
|
|
if (bratonien_tools_main_cache_process_active() || bratonien_tools_main_cache_is_running())
|
|
{
|
|
bratonien_tools_request_main_cache_cancel();
|
|
if (!bratonien_tools_wait_main_cache_stopped(10.0))
|
|
{
|
|
throw new RuntimeException('Der laufende Cache-Aufbau konnte noch nicht beendet werden. Bitte den Abbruch kurz abschließen lassen und erneut leeren.');
|
|
}
|
|
}
|
|
|
|
@unlink(bratonien_tools_main_cache_cancel_file());
|
|
|
|
$cache_root = PHPWG_ROOT_PATH . PWG_DERIVATIVE_DIR;
|
|
|
|
if (!is_dir($cache_root))
|
|
{
|
|
throw new RuntimeException('Bildcache-Verzeichnis wurde nicht gefunden: ' . $cache_root);
|
|
}
|
|
|
|
$real_cache_root = realpath($cache_root);
|
|
$real_piwigo_root = realpath(PHPWG_ROOT_PATH);
|
|
|
|
if ($real_cache_root === false || $real_piwigo_root === false)
|
|
{
|
|
throw new RuntimeException('Sicherheitspruefung des Bildcache-Pfads fehlgeschlagen.');
|
|
}
|
|
|
|
$root_prefix = rtrim($real_piwigo_root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
|
if (strpos($real_cache_root . DIRECTORY_SEPARATOR, $root_prefix) !== 0)
|
|
{
|
|
throw new RuntimeException('Bildcache liegt ausserhalb der Piwigo-Installation. Abbruch.');
|
|
}
|
|
|
|
$before = bratonien_tools_scan_image_cache($real_cache_root);
|
|
|
|
if (!function_exists('clear_derivative_cache'))
|
|
{
|
|
require_once(PHPWG_ROOT_PATH . 'admin/include/functions.php');
|
|
}
|
|
|
|
if (!function_exists('clear_derivative_cache'))
|
|
{
|
|
throw new RuntimeException('Piwigos Funktion clear_derivative_cache() ist nicht verfuegbar.');
|
|
}
|
|
|
|
clear_derivative_cache('all');
|
|
|
|
$failed_files = array();
|
|
$residual_deleted = 0;
|
|
|
|
if (is_dir($real_cache_root))
|
|
{
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($real_cache_root, FilesystemIterator::SKIP_DOTS),
|
|
RecursiveIteratorIterator::CHILD_FIRST
|
|
);
|
|
|
|
foreach ($iterator as $item)
|
|
{
|
|
$path = $item->getPathname();
|
|
|
|
if ($item->isDir())
|
|
{
|
|
@rmdir($path);
|
|
continue;
|
|
}
|
|
|
|
if (strtolower($item->getFilename()) === 'index.htm')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (@unlink($path))
|
|
{
|
|
$residual_deleted++;
|
|
}
|
|
else
|
|
{
|
|
$failed_files[] = $path;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($failed_files))
|
|
{
|
|
throw new RuntimeException(
|
|
sprintf(
|
|
'%d Dateien konnten nicht geloescht werden. Erste problematische Datei: %s',
|
|
count($failed_files),
|
|
$failed_files[0]
|
|
)
|
|
);
|
|
}
|
|
|
|
$after = bratonien_tools_scan_image_cache($real_cache_root);
|
|
|
|
if ($after['files'] > 0)
|
|
{
|
|
throw new RuntimeException(
|
|
sprintf(
|
|
'Bildcache wurde nicht vollstaendig geleert. Noch %d Datei(en) vorhanden. Erste Datei: %s',
|
|
$after['files'],
|
|
$after['first_file']
|
|
)
|
|
);
|
|
}
|
|
|
|
bratonien_tools_write_main_cache_status(array(
|
|
'state'=>'idle',
|
|
'message'=>'Bildcache wurde geleert. Kein manueller Cache-Aufbau aktiv.',
|
|
));
|
|
|
|
return array(
|
|
'message' => sprintf(
|
|
'Bildcache vollstaendig geleert: %d Dateien (%s) entfernt, davon %d Custom-Derivate. %d Restdateien wurden zusaetzlich ausserhalb von Piwigos Standard-Loeschmustern entfernt. Ein laufender manueller Cache-Aufbau wurde zuvor beendet.',
|
|
$before['files'],
|
|
bratonien_tools_format_bytes($before['bytes']),
|
|
$before['custom'],
|
|
$residual_deleted
|
|
),
|
|
);
|
|
}
|
|
|
|
function bratonien_tools_scan_image_cache($cache_root)
|
|
{
|
|
$result = array(
|
|
'files' => 0,
|
|
'bytes' => 0,
|
|
'custom' => 0,
|
|
'first_file' => '',
|
|
);
|
|
|
|
if (!is_dir($cache_root))
|
|
{
|
|
return $result;
|
|
}
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($cache_root, FilesystemIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $item)
|
|
{
|
|
if (!$item->isFile() && !$item->isLink())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (strtolower($item->getFilename()) === 'index.htm')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$result['files']++;
|
|
if ($item->isFile())
|
|
{
|
|
$result['bytes'] += $item->getSize();
|
|
}
|
|
|
|
if (strpos($item->getFilename(), '-cu_') !== false)
|
|
{
|
|
$result['custom']++;
|
|
}
|
|
|
|
if ($result['first_file'] === '')
|
|
{
|
|
$result['first_file'] = $item->getPathname();
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
function bratonien_tools_find_php_cli()
|
|
{
|
|
foreach (array('/usr/bin/php', '/usr/bin/php8.4', '/usr/local/bin/php') as $candidate)
|
|
{
|
|
if (is_file($candidate) && is_executable($candidate))
|
|
{
|
|
return $candidate;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function bratonien_tools_start_main_cache_build()
|
|
{
|
|
if (bratonien_tools_main_cache_process_active() || bratonien_tools_main_cache_is_running())
|
|
{
|
|
return array('started'=>false, 'running'=>true, 'message'=>'Der Piwigo-Bildcache wird bereits aufgebaut.');
|
|
}
|
|
|
|
@unlink(bratonien_tools_main_cache_cancel_file());
|
|
|
|
$worker = realpath(BRATONIEN_TOOLS_PATH.'main-cache-build.php');
|
|
$php = bratonien_tools_find_php_cli();
|
|
if (!$worker || !is_file($worker))
|
|
{
|
|
throw new RuntimeException('Cache-Worker wurde nicht gefunden.');
|
|
}
|
|
if (!$php)
|
|
{
|
|
throw new RuntimeException('PHP-CLI wurde nicht gefunden.');
|
|
}
|
|
if (!function_exists('exec'))
|
|
{
|
|
throw new RuntimeException('PHP exec() ist deaktiviert; Cache-Worker kann nicht gestartet werden.');
|
|
}
|
|
|
|
$settings = bratonien_tools_get_cache_worker_settings();
|
|
$worker_count = max(1, (int)$settings['worker_count']);
|
|
$cpu_count = max(1, (int)$settings['cpu_count']);
|
|
|
|
bratonien_tools_write_main_cache_status(array(
|
|
'state'=>'queued',
|
|
'message'=>sprintf('Piwigo-Bildcache wird mit %d Worker(n) vorbereitet.', $worker_count),
|
|
'worker_count'=>$worker_count,
|
|
'cpu_count'=>$cpu_count,
|
|
));
|
|
|
|
$log = PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'bratonien-tools-main-cache.log';
|
|
$command = 'nohup '.escapeshellarg($php).' '.escapeshellarg($worker).' --workers='.$worker_count.' >> '.escapeshellarg($log).' 2>&1 < /dev/null & echo $!';
|
|
$output = array();
|
|
$exit = 1;
|
|
@exec($command, $output, $exit);
|
|
$pid = isset($output[0]) ? (int)$output[0] : 0;
|
|
|
|
if ($exit !== 0 || $pid <= 0)
|
|
{
|
|
bratonien_tools_write_main_cache_status(array(
|
|
'state'=>'error',
|
|
'message'=>'Piwigo-Bildcache konnte nicht gestartet werden.',
|
|
'errors'=>1,
|
|
'worker_count'=>$worker_count,
|
|
'cpu_count'=>$cpu_count,
|
|
));
|
|
throw new RuntimeException('Piwigo-Bildcache konnte nicht gestartet werden.');
|
|
}
|
|
|
|
return array(
|
|
'started'=>true,
|
|
'pid'=>$pid,
|
|
'message'=>sprintf(
|
|
'Piwigo-Bildcache wurde mit %d Worker(n) gestartet (%d CPU(s), %s).',
|
|
$worker_count,
|
|
$cpu_count,
|
|
$settings['auto'] ? 'Automatik 1:1' : 'manuell'
|
|
),
|
|
);
|
|
}
|
|
|
|
function bratonien_tools_format_bytes($bytes)
|
|
{
|
|
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
|
$value = (float) $bytes;
|
|
$unit = 0;
|
|
|
|
while ($value >= 1024 && $unit < count($units) - 1)
|
|
{
|
|
$value /= 1024;
|
|
$unit++;
|
|
}
|
|
|
|
return number_format($value, $unit === 0 ? 0 : 2, ',', '.') . ' ' . $units[$unit];
|
|
}
|