diff --git a/lib/Db/TimelineWrite.php b/lib/Db/TimelineWrite.php index 4248aeaa..845f9f78 100644 --- a/lib/Db/TimelineWrite.php +++ b/lib/Db/TimelineWrite.php @@ -9,6 +9,7 @@ use OCA\Memories\Service\Index; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\File; use OCP\IDBConnection; +use OCP\Lock\ILockingProvider; require_once __DIR__.'/../ExifFields.php'; @@ -22,28 +23,54 @@ class TimelineWrite use TimelineWritePlaces; protected IDBConnection $connection; protected LivePhoto $livePhoto; + protected ILockingProvider $lockingProvider; public function __construct( IDBConnection $connection, - LivePhoto $livePhoto + LivePhoto $livePhoto, + ILockingProvider $lockingProvider ) { $this->connection = $connection; $this->livePhoto = $livePhoto; + $this->lockingProvider = $lockingProvider; } /** * Process a file to insert Exif data into the database. * * @param File $file File node to process + * @param bool $lock Lock the file before processing * @param bool $force Update the record even if the file has not changed + * + * @return bool True if the file was processed + * + * @throws \OCP\Lock\LockedException If the file is locked + * @throws \OCP\DB\Exception If the database query fails */ - public function processFile(File $file, bool $force = false): bool - { + public function processFile( + File $file, + bool $lock = true, + bool $force = false + ): bool { // Check if we want to process this file if (!Index::isSupported($file)) { return false; } + // Check if we need to lock the file + if ($lock) { + $lockKey = 'memories/'.$file->getId(); + $lockType = ILockingProvider::LOCK_EXCLUSIVE; + + try { + $this->lockingProvider->acquireLock($lockKey, $lockType); + + return $this->processFile($file, false, $force); + } finally { + $this->lockingProvider->releaseLock($lockKey, $lockType); + } + } + // Get parameters $mtime = $file->getMtime(); $fileId = $file->getId(); diff --git a/lib/Listeners/PostWriteListener.php b/lib/Listeners/PostWriteListener.php index 65f06560..cfabffdb 100644 --- a/lib/Listeners/PostWriteListener.php +++ b/lib/Listeners/PostWriteListener.php @@ -27,14 +27,19 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Events\Node\NodeTouchedEvent; use OCP\Files\Events\Node\NodeWrittenEvent; +use Psr\Log\LoggerInterface; class PostWriteListener implements IEventListener { private TimelineWrite $timelineWrite; + private LoggerInterface $logger; - public function __construct(TimelineWrite $timelineWrite) - { + public function __construct( + TimelineWrite $timelineWrite, + LoggerInterface $logger + ) { $this->timelineWrite = $timelineWrite; + $this->logger = $logger; } public function handle(Event $event): void @@ -64,6 +69,12 @@ class PostWriteListener implements IEventListener // and getParent() is called on it. } - $this->timelineWrite->processFile($node); + try { + $this->timelineWrite->processFile($node); + } catch (\Exception $e) { + $this->logger->error('Memories failed to process file: {message}', [ + 'message' => $e->getMessage(), + ]); + } } } diff --git a/lib/Service/Index.php b/lib/Service/Index.php index dec12cf3..f779f29c 100644 --- a/lib/Service/Index.php +++ b/lib/Service/Index.php @@ -185,11 +185,15 @@ class Index */ public function indexFile(File $file): void { + $path = $file->getPath(); + try { - $this->log("Indexing file {$file->getPath()}", true); + $this->log("Indexing file {$path}", true); $this->timelineWrite->processFile($file); + } catch (\OCP\Lock\LockedException $e) { + $this->log("Skipping file {$path} due to lock\n", true); } catch (\Exception $e) { - $this->error("Failed to index file {$file->getPath()}: {$e->getMessage()}"); + $this->error("Failed to index file {$path}: {$e->getMessage()}"); } finally { $this->tempManager->clean(); }