Skip to content

Commit

Permalink
copy live photos
Browse files Browse the repository at this point in the history
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
  • Loading branch information
ArtificialOwl committed Dec 12, 2023
1 parent 3c704d4 commit bb1aaad
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 7 deletions.
4 changes: 4 additions & 0 deletions apps/files/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@
use OCP\Collaboration\Resources\IProviderManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Cache\CacheEntryRemovedEvent;
use OCP\Files\Events\Node\BeforeNodeCopiedEvent;
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
use OCP\Files\Events\Node\NodeCopiedEvent;
use OCP\IConfig;
use OCP\IPreview;
use OCP\IRequest;
Expand Down Expand Up @@ -129,6 +131,8 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(BeforeNodeDeletedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(BeforeNodeRestoredEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(CacheEntryRemovedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(BeforeNodeCopiedEvent::class, SyncLivePhotosListener::class);
$context->registerEventListener(NodeCopiedEvent::class, SyncLivePhotosListener::class);

$context->registerSearchProvider(FilesSearchProvider::class);

Expand Down
50 changes: 44 additions & 6 deletions apps/files/lib/Listener/SyncLivePhotosListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Cache\CacheEntryRemovedEvent;
use OCP\Files\Events\Node\BeforeNodeCopiedEvent;
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
use OCP\Files\Events\Node\NodeCopiedEvent;
use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
Expand Down Expand Up @@ -65,7 +67,6 @@ public function handle(Event $event): void {
}

$peerFile = null;

if ($event instanceof BeforeNodeRenamedEvent) {
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
} elseif ($event instanceof BeforeNodeRestoredEvent) {
Expand All @@ -74,20 +75,26 @@ public function handle(Event $event): void {
$peerFile = $this->getLivePhotoPeer($event->getNode()->getId());
} elseif ($event instanceof CacheEntryRemovedEvent) {
$peerFile = $this->getLivePhotoPeer($event->getFileId());
} elseif ($event instanceof BeforeNodeCopiedEvent || $event instanceof NodeCopiedEvent) {
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
}

if ($peerFile === null) {
return; // not a Live Photo
}

if ($event instanceof BeforeNodeRenamedEvent) {
$this->handleMove($event, $peerFile);
$this->handleMove($event, $peerFile, false);
} elseif ($event instanceof BeforeNodeDeletedEvent) {
$this->handleDeletion($event, $peerFile);
} elseif ($event instanceof CacheEntryRemovedEvent) {
$peerFile->delete();
} elseif ($event instanceof BeforeNodeRestoredEvent) {
$this->handleRestore($event, $peerFile);
} elseif ($event instanceof BeforeNodeCopiedEvent) {
$this->handleMove($event, $peerFile, true);
} elseif ($event instanceof NodeCopiedEvent) {
$this->handleCopy($event, $peerFile);
}
}

Expand All @@ -98,14 +105,13 @@ public function handle(Event $event): void {
* of pending renames inside the 'pendingRenames' property,
* to prevent infinite recursive.
*/
private function handleMove(BeforeNodeRenamedEvent $event, Node $peerFile): void {
private function handleMove(Event $event, Node $peerFile, bool $prepForCopyOnly = false): void {
$sourceFile = $event->getSource();

Check failure on line 109 in apps/files/lib/Listener/SyncLivePhotosListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedMethod

apps/files/lib/Listener/SyncLivePhotosListener.php:109:25: UndefinedMethod: Method OCP\EventDispatcher\Event::getSource does not exist (see https://psalm.dev/022)
$targetFile = $event->getTarget();

Check failure on line 110 in apps/files/lib/Listener/SyncLivePhotosListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedMethod

apps/files/lib/Listener/SyncLivePhotosListener.php:110:25: UndefinedMethod: Method OCP\EventDispatcher\Event::getTarget does not exist (see https://psalm.dev/022)
$targetParent = $targetFile->getParent();
$sourceExtension = $sourceFile->getExtension();
$peerFileExtension = $peerFile->getExtension();
$targetName = $targetFile->getName();
$targetPath = $targetFile->getPath();

if (!str_ends_with($targetName, ".".$sourceExtension)) {
$event->abortOperation(new NotPermittedException("Cannot change the extension of a Live Photo"));

Check failure on line 117 in apps/files/lib/Listener/SyncLivePhotosListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedMethod

apps/files/lib/Listener/SyncLivePhotosListener.php:117:12: UndefinedMethod: Method OCP\EventDispatcher\Event::abortOperation does not exist (see https://psalm.dev/022)
Expand All @@ -129,15 +135,47 @@ private function handleMove(BeforeNodeRenamedEvent $event, Node $peerFile): void
return;
}

$this->pendingRenames[$sourceFile->getId()] = $targetPath;
$this->pendingRenames[$sourceFile->getId()] = $peerFile->getId();

Check failure on line 138 in apps/files/lib/Listener/SyncLivePhotosListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidPropertyAssignmentValue

apps/files/lib/Listener/SyncLivePhotosListener.php:138:3: InvalidPropertyAssignmentValue: $this->pendingRenames with declared type 'array<int, string>' cannot be assigned type 'non-empty-array<array-key, int|string>' (see https://psalm.dev/145)

Check failure

Code scanning / Psalm

InvalidPropertyAssignmentValue Error

$this->pendingRenames with declared type 'array<int, string>' cannot be assigned type 'non-empty-array<array-key, int|string>'
try {
$peerFile->move($targetParent->getPath() . '/' . $peerTargetName);
if (!$prepForCopyOnly) {
$peerFile->move($targetParent->getPath() . '/' . $peerTargetName);
}
} catch (\Throwable $ex) {
$event->abortOperation($ex);

Check failure on line 144 in apps/files/lib/Listener/SyncLivePhotosListener.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedMethod

apps/files/lib/Listener/SyncLivePhotosListener.php:144:12: UndefinedMethod: Method OCP\EventDispatcher\Event::abortOperation does not exist (see https://psalm.dev/022)
}
unset($this->pendingRenames[$sourceFile->getId()]);
}


/**
* handle copy, we already know if it is doable from BeforeNodeCopiedEvent, so we just copy the linked file
*
* @param NodeCopiedEvent $event
* @param Node $peerFile
*/
private function handleCopy(NodeCopiedEvent $event, Node $peerFile): void {
$sourceFile = $event->getSource();
$sourceExtension = $sourceFile->getExtension();
$peerFileExtension = $peerFile->getExtension();
$targetFile = $event->getTarget();
$targetParent = $targetFile->getParent();
$targetName = $targetFile->getName();
$peerTargetName = substr($targetName, 0, -strlen($sourceExtension)) . $peerFileExtension;

/**
* let's use freshly set variable.
* we copy the file and get its id. We already have the id of the current copy
* We have everything to update metadata and keep the link between the 2 copies.
*/
$newPeerFile = $peerFile->copy($targetParent->getPath() . '/' . $peerTargetName);
$targetMetadata = $this->filesMetadataManager->getMetadata($targetFile->getId(), true);
$targetMetadata->setString('files-live-photo', (string)$newPeerFile->getId());
$this->filesMetadataManager->saveMetadata($targetMetadata);
$peerMetadata = $this->filesMetadataManager->getMetadata($newPeerFile->getId(), true);
$peerMetadata->setString('files-live-photo', (string)$targetFile->getId());
$this->filesMetadataManager->saveMetadata($peerMetadata);
}

/**
* During deletion event, we trigger another recursive delete on the peer file.
* Delete operations on the .mov file directly are currently blocked.
Expand Down
2 changes: 1 addition & 1 deletion lib/private/Files/Node/HookConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public function copy($arguments) {
$this->root->emit('\OC\Files', 'preCopy', [$source, $target]);
$this->dispatcher->dispatch('\OCP\Files::preCopy', new GenericEvent([$source, $target]));

$event = new BeforeNodeCopiedEvent($source, $target);
$event = new BeforeNodeCopiedEvent($source, $target, $arguments['run']);
$this->dispatcher->dispatchTyped($event);
}

Expand Down
23 changes: 23 additions & 0 deletions lib/public/Files/Events/Node/BeforeNodeCopiedEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,31 @@
*/
namespace OCP\Files\Events\Node;

use OCP\DB\Exception;
use OCP\Files\Node;

/**
* @since 20.0.0
*/
class BeforeNodeCopiedEvent extends AbstractNodesEvent {
/**
* @since 20.0.0
*/
public function __construct(Node $source, Node $target, private bool &$run) {
parent::__construct($source, $target);
}

/**
* @since 28.0.0
* @return never
*/
public function abortOperation(\Throwable $ex = null) {
$this->stopPropagation();
$this->run = false;
if ($ex !== null) {
throw $ex;
} else {
throw new Exception('Operation aborted');
}
}
}

0 comments on commit bb1aaad

Please sign in to comment.