diff --git a/lib/Command/RescanPhotos.php b/lib/Command/RescanPhotos.php index 6dac6b03d..e7d70f842 100644 --- a/lib/Command/RescanPhotos.php +++ b/lib/Command/RescanPhotos.php @@ -26,84 +26,94 @@ class RescanPhotos extends Command { - protected IUserManager $userManager; - protected OutputInterface $output; - protected IManager $encryptionManager; - protected PhotofilesService $photofilesService; - protected IConfig $config; + protected IUserManager $userManager; + protected OutputInterface $output; + protected IManager $encryptionManager; + protected PhotofilesService $photofilesService; + protected IConfig $config; - public function __construct(IUserManager $userManager, - IManager $encryptionManager, - PhotofilesService $photofilesService, - IConfig $config) { - parent::__construct(); - $this->userManager = $userManager; - $this->encryptionManager = $encryptionManager; - $this->photofilesService = $photofilesService; - $this->config = $config; - } + public function __construct( + IUserManager $userManager, + IManager $encryptionManager, + PhotofilesService $photofilesService, + IConfig $config) { + parent::__construct(); + $this->userManager = $userManager; + $this->encryptionManager = $encryptionManager; + $this->photofilesService = $photofilesService; + $this->config = $config; + } - /** - * @return void - */ - protected function configure() { - $this->setName('maps:scan-photos') - ->setDescription('Rescan photos GPS exif data') - ->addArgument( - 'user_id', - InputArgument::OPTIONAL, - 'Rescan photos GPS exif data for the given user' - ) - ->addOption( - 'now', - null, - InputOption::VALUE_NONE, - 'Dot the rescan now and not as background jobs. Doing it now might run out of memory.' - ); - } + /** + * @return void + */ + protected function configure() { + $this->setName('maps:scan-photos') + ->setDescription('Rescan photos GPS exif data') + ->addArgument( + 'user_id', + InputArgument::OPTIONAL, + 'Rescan photos GPS exif data for the given user' + ) + ->addArgument( + 'path', + InputArgument::OPTIONAL, + 'Scan photos GPS exif data for the given path under user\'s files without wiping the database.' + ) + ->addOption( + 'now', + null, + InputOption::VALUE_NONE, + 'Dot the rescan now and not as background jobs. Doing it now might run out of memory.' + ); + } - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output): int { - if ($this->encryptionManager->isEnabled()) { - $output->writeln('Encryption is enabled. Aborted.'); - return 1; - } - $this->output = $output; - $userId = $input->getArgument('user_id'); - $inBackground = !($input->getOption('now') ?? true); - if ($inBackground) { - echo "Extracting coordinates from photo is performed in a BackgroundJob \n"; - } - if ($userId === null) { - $this->userManager->callForSeenUsers(function (IUser $user) use ($inBackground) { - $this->rescanUserPhotos($user->getUID(), $inBackground); - }); - } else { - $user = $this->userManager->get($userId); - if ($user !== null) { - $this->rescanUserPhotos($userId, $inBackground); - } - } - return 0; - } + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int { + if ($this->encryptionManager->isEnabled()) { + $output->writeln('Encryption is enabled. Aborted.'); + return 1; + } + $this->output = $output; + $userId = $input->getArgument('user_id'); + $pathToScan = $input->getArgument('path'); + $inBackground = !($input->getOption('now') ?? true); + if ($inBackground) { + echo "Extracting coordinates from photo is performed in a BackgroundJob \n"; + } + if ($userId === null) { + $this->userManager->callForSeenUsers(function (IUser $user, string $pathToScan) use ($inBackground) { + $this->rescanUserPhotos($user->getUID(), $inBackground, $pathToScan); + }); + } else { + $user = $this->userManager->get($userId); + if ($user !== null) { + $this->rescanUserPhotos($userId, $inBackground, $pathToScan); + } + } + return 0; + } - /** - * @param string $userId - * @param bool $inBackground - * @return void - * @throws \OCP\PreConditionNotMetException - */ - private function rescanUserPhotos(string $userId, bool $inBackground = true) { - echo '======== User '.$userId.' ========'."\n"; - $c = 1; - foreach ($this->photofilesService->rescan($userId, $inBackground) as $path) { - echo '['.$c.'] Photo "'.$path.'" added'."\n"; - $c++; - } - $this->config->setUserValue($userId, 'maps', 'installScanDone', 'yes'); - } + /** + * @param string $userId + * @param bool $inBackground + * @param string $pathToScan + * @return void + * @throws \OCP\PreConditionNotMetException + */ + private function rescanUserPhotos(string $userId, bool $inBackground = true, string $pathToScan = null) { + echo '======== User ' . $userId . ' ========' . "\n"; + $c = 1; + foreach ($this->photofilesService->rescan($userId, $inBackground, $pathToScan) as $path) { + echo '[' . $c . '] Photo "' . $path . '" added' . "\n"; + $c++; + } + if ($pathToScan === null) { + $this->config->setUserValue($userId, 'maps', 'installScanDone', 'yes'); + } + } } diff --git a/lib/Service/PhotofilesService.php b/lib/Service/PhotofilesService.php index 461b0c3ca..c87a2ee0d 100644 --- a/lib/Service/PhotofilesService.php +++ b/lib/Service/PhotofilesService.php @@ -31,6 +31,7 @@ use Psr\Log\LoggerInterface; require_once __DIR__ . '/../../vendor/autoload.php'; + use lsolesen\pel\PelDataWindow; use lsolesen\pel\PelEntryAscii; use lsolesen\pel\PelEntryRational; @@ -71,11 +72,16 @@ public function __construct( $this->backgroundJobCache = $this->cacheFactory->createDistributed('maps:background-jobs'); } - public function rescan($userId, $inBackground = true) { + public function rescan($userId, $inBackground = true, $pathToScan = null) { $this->photosCache->clear($userId); $userFolder = $this->root->getUserFolder($userId); - $photos = $this->gatherPhotoFiles($userFolder, true); - $this->photoMapper->deleteAll($userId); + if ($pathToScan === null) { + $folder = $userFolder; + $this->photoMapper->deleteAll($userId); + } else { + $folder = $userFolder->get($pathToScan); + } + $photos = $this->gatherPhotoFiles($folder, true); foreach ($photos as $photo) { if ($inBackground) { $this->addPhoto($photo, $userId); @@ -404,7 +410,7 @@ private function gatherPhotoFiles($folder, $recursive) { } try { $notes = array_merge($notes, $this->gatherPhotoFiles($node, $recursive)); - } catch (\OCP\Files\StorageNotAvailableException|\Exception $e) { + } catch (\OCP\Files\StorageNotAvailableException | \Exception $e) { $msg = 'WARNING: Could not access ' . $node->getName(); echo($msg . "\n"); $this->logger->error($msg); @@ -442,12 +448,12 @@ private function getExif($file) : ?ExifGeoData { $exif_geo_data->validate(true); } catch (ExifDataInvalidException $e) { $exif_geo_data = null; - $this->logger->notice($e->getMessage(), ['code' => $e->getCode(),'path' => $path]); + $this->logger->notice($e->getMessage(), ['code' => $e->getCode(), 'path' => $path]); } catch (ExifDataNoLocationException $e) { - $this->logger->notice($e->getMessage(), ['code' => $e->getCode(),'path' => $path]); + $this->logger->notice($e->getMessage(), ['code' => $e->getCode(), 'path' => $path]); } catch (\Throwable $f) { $exif_geo_data = null; - $this->logger->error($f->getMessage(), ['code' => $f->getCode(),'path' => $path]); + $this->logger->error($f->getMessage(), ['code' => $f->getCode(), 'path' => $path]); } return $exif_geo_data; } @@ -519,19 +525,25 @@ private function setGeolocation($pelSubIfdGps, $latitudeDegreeDecimal, $longitud = $this->degreeDecimalToDegreeMinuteSecond(abs($longitudeDegreeDecimal)); $pelSubIfdGps->addEntry(new PelEntryAscii( - PelTag::GPS_LATITUDE_REF, $latitudeRef)); + PelTag::GPS_LATITUDE_REF, + $latitudeRef + )); $pelSubIfdGps->addEntry(new PelEntryRational( PelTag::GPS_LATITUDE, [$latitudeDegreeMinuteSecond['degree'], 1], [$latitudeDegreeMinuteSecond['minute'], 1], - [round($latitudeDegreeMinuteSecond['second'] * 1000), 1000])); + [round($latitudeDegreeMinuteSecond['second'] * 1000), 1000] + )); $pelSubIfdGps->addEntry(new PelEntryAscii( - PelTag::GPS_LONGITUDE_REF, $longitudeRef)); + PelTag::GPS_LONGITUDE_REF, + $longitudeRef + )); $pelSubIfdGps->addEntry(new PelEntryRational( PelTag::GPS_LONGITUDE, [$longitudeDegreeMinuteSecond['degree'], 1], [$longitudeDegreeMinuteSecond['minute'], 1], - [round($longitudeDegreeMinuteSecond['second'] * 1000), 1000])); + [round($longitudeDegreeMinuteSecond['second'] * 1000), 1000] + )); } private function degreeDecimalToDegreeMinuteSecond($degreeDecimal) { @@ -542,5 +554,4 @@ private function degreeDecimalToDegreeMinuteSecond($degreeDecimal) { $second = $remainder * 60; return ['degree' => $degree, 'minute' => $minute, 'second' => $second]; } - }