diff --git a/lib/BinExt.php b/lib/BinExt.php new file mode 100644 index 00000000..133dcca8 --- /dev/null +++ b/lib/BinExt.php @@ -0,0 +1,75 @@ + {$target}"); + } + + return true; + } + + /** Get path to exiftool binary for proc_open */ + public static function getExiftool(): array + { + if (Util::getSystemConfig('memories.exiftool_no_local')) { + return ['perl', __DIR__.'/../exiftool-bin/exiftool/exiftool']; + } + + return [Util::getSystemConfig('memories.exiftool')]; + } + + /** Detect the exiftool binary to use */ + public static function detectExiftool(): void + { + if (!empty($path = Util::getSystemConfig('memories.exiftool'))) { + if (file_exists($path) && !is_executable($path)) { + chmod($path, 0755); + } + + return; + } + + if (Util::getSystemConfig('memories.exiftool_no_local')) { + return; + } + + // Detect architecture + $arch = \OCA\Memories\Util::getArch(); + $libc = \OCA\Memories\Util::getLibc(); + + // Get static binary if available + if ($arch && $libc) { + // get target file path + $path = realpath(__DIR__."/../exiftool-bin/exiftool-{$arch}-{$libc}"); + + // Set config + Util::setSystemConfig('memories.exiftool', $path); + + // make sure it is executable + if (file_exists($path)) { + if (!is_executable($path)) { + chmod($path, 0755); + } + } else { + error_log("Exiftool binary not found: {$path}"); + Util::setSystemConfig('memories.exiftool_no_local', true); + } + } + } +} diff --git a/lib/Controller/OtherController.php b/lib/Controller/OtherController.php index 581333da..51707d32 100644 --- a/lib/Controller/OtherController.php +++ b/lib/Controller/OtherController.php @@ -24,8 +24,8 @@ declare(strict_types=1); namespace OCA\Memories\Controller; use OCA\Memories\AppInfo\Application; +use OCA\Memories\BinExt; use OCA\Memories\Exceptions; -use OCA\Memories\Exif; use OCA\Memories\Util; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; @@ -87,22 +87,7 @@ class OtherController extends GenericApiController throw Exceptions::Forbidden('Cannot change settings in readonly mode'); } - // Make sure the key is valid - $defaults = Util::systemConfigDefaults(); - if (!\array_key_exists($key, $defaults)) { - throw Exceptions::BadRequest('Invalid key'); - } - - // Make sure the value has the same type as the default value - if (\gettype($value) !== \gettype($defaults[$key])) { - throw Exceptions::BadRequest('Invalid value type'); - } - - if ($value === $defaults[$key]) { - $this->config->deleteSystemValue($key); - } else { - $this->config->setSystemValue($key, $value); - } + Util::setSystemConfig($key, $value); return new JSONResponse([], Http::STATUS_OK); }); @@ -118,8 +103,16 @@ class OtherController extends GenericApiController return Util::guardEx(function () { $status = []; - // Check exiftool + // Check exiftool version $status['exiftool'] = $this->getExecutableStatus(Util::getSystemConfig('memories.exiftool')); + if ('ok' === $status['exiftool'] || Util::getSystemConfig('memories.exiftool_no_local')) { + try { + BinExt::testExiftool(); + $status['exiftool'] = 'test_ok'; + } catch (\Exception $e) { + $status['exiftool'] = 'test_fail:'.$e->getMessage(); + } + } // Check for system perl $status['perl'] = $this->getExecutableStatus(exec('which perl')); diff --git a/lib/Exif.php b/lib/Exif.php index 1f2da684..d8b998e5 100644 --- a/lib/Exif.php +++ b/lib/Exif.php @@ -11,7 +11,6 @@ use OCP\IConfig; class Exif { private const FORBIDDEN_EDIT_MIMES = ['image/bmp', 'image/x-dcraw', 'video/MP2T']; - private const EXIFTOOL_VER = '12.58'; private const EXIFTOOL_TIMEOUT = 30000; private const EXIFTOOL_ARGS = ['-api', 'QuickTimeUTC=1', '-n', '-U', '-json', '--b']; @@ -357,66 +356,9 @@ class Exif return self::getExifFromLocalPathWithSeparateProc($path, ['-G4']); } - /** Get path to exiftool binary */ - private static function getExiftool() + private static function getExiftool(): array { - $configKey = 'memories.exiftool'; - $config = \OC::$server->get(IConfig::class); - $configPath = $config->getSystemValue($configKey); - $noLocal = $config->getSystemValue($configKey.'_no_local', false); - - // We know already where it is - if (!empty($configPath) && file_exists($configPath)) { - if (!is_executable($configPath)) { - chmod($configPath, 0755); - } - - return explode(' ', $configPath); - } - - // Detect architecture - $arch = $noLocal ? null : \OCA\Memories\Util::getArch(); - $libc = $noLocal ? null : \OCA\Memories\Util::getLibc(); - - // Get static binary if available - if ($arch && $libc && !$noLocal) { - // get target file path - $path = realpath(__DIR__."/../exiftool-bin/exiftool-{$arch}-{$libc}"); - - // check if file exists - if (file_exists($path)) { - // make executable before version check - if (!is_executable($path)) { - chmod($path, 0755); - } - - // check if the version prints correctly - $ver = self::EXIFTOOL_VER; - $vero = shell_exec("{$path} -ver"); - if ($vero && false !== stripos(trim($vero), $ver)) { - $out = trim($vero); - echo "Exiftool binary version check passed {$out} <==> {$ver}\n"; - $config->setSystemValue($configKey, $path); - - return [$path]; - } - error_log("Exiftool version check failed {$vero} <==> {$ver}"); - $config->setSystemValue($configKey.'_no_local', true); - } else { - error_log("Exiftool not found: {$path}"); - } - } - - // Fallback to perl script - $path = __DIR__.'/../exiftool-bin/exiftool/exiftool'; - if (file_exists($path)) { - return ['perl', $path]; - } - - error_log("Exiftool not found: {$path}"); - - // Fallback to system binary - return ['exiftool']; + return BinExt::getExiftool(); } /** Initialize static exiftool process for local reads */ diff --git a/lib/Migration/Repair.php b/lib/Migration/Repair.php index 8307e919..f3e301de 100644 --- a/lib/Migration/Repair.php +++ b/lib/Migration/Repair.php @@ -24,6 +24,9 @@ class Repair implements IRepairStep public function run(IOutput $output): void { + // detect exiftool binary + \OCA\Memories\BinExt::detectExiftool(); + // kill any instances of go-transcode and go-vod \OCA\Memories\Util::pkill('go-transcode'); \OCA\Memories\Util::pkill('go-vod'); diff --git a/lib/Util.php b/lib/Util.php index 1f4267c3..38525fa8 100644 --- a/lib/Util.php +++ b/lib/Util.php @@ -294,6 +294,38 @@ class Util return $config->getSystemValue($key, $default ?? self::systemConfigDefaults()[$key]); } + /** + * Set a system config key. + * + * @param mixed $value + * + * @throws \InvalidArgumentException + */ + public static function setSystemConfig(string $key, $value): void + { + $config = \OC::$server->get(\OCP\IConfig::class); + + // Check if the key is valid + $defaults = self::systemConfigDefaults(); + if (!\array_key_exists($key, $defaults)) { + throw new \InvalidArgumentException('Invalid system config key'); + } + + // Check if the value has the correct type + if (null !== $value && \gettype($value) !== \gettype($defaults[$key])) { + $expected = \gettype($defaults[$key]); + $got = \gettype($value); + + throw new \InvalidArgumentException("Invalid type for system config {$key}, expected {$expected}, got {$got}"); + } + + if ($value === $defaults[$key] || null === $value) { + $config->deleteSystemValue($key); + } else { + $config->setSystemValue($key, $value); + } + } + /** Get list of defaults for all system config keys. */ public static function systemConfigDefaults(): array { diff --git a/src/Admin.vue b/src/Admin.vue index d6404e7f..24730b5c 100644 --- a/src/Admin.vue +++ b/src/Admin.vue @@ -430,7 +430,7 @@ export default defineComponent({ } else if (status.startsWith("test_fail")) { return this.t( "memories", - "{name} binary failed test: {info}", + "{name} failed test: {info}", { name, info: status.substring(10),