Skip to content

Commit

Permalink
Add CLI and hooks to reverse geocode pictures' coordinates
Browse files Browse the repository at this point in the history
Signed-off-by: Louis Chemineau <louis@chmn.me>
  • Loading branch information
artonge committed Oct 17, 2022
1 parent abc4f5f commit 0063898
Show file tree
Hide file tree
Showing 16 changed files with 1,055 additions and 63 deletions.
18 changes: 11 additions & 7 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
<?xml version="1.0"?>
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>photos</id>
<name>Photos</name>
<summary>Your memories under your control</summary>
<description>Your memories under your control</description>
<version>1.8.0</version>
<version>1.9.0</version>
<licence>agpl</licence>
<author mail="skjnldsv@protonmail.com">John Molakvoæ</author>
<author mail="skjnldsv@protonmail.com">John Molakvoæ</author>
<namespace>Photos</namespace>
<category>multimedia</category>
<types>
<dav />
<authentication />
</types>

<website>https://github.com/nextcloud/photos</website>
<bugs>https://github.com/nextcloud/photos/issues</bugs>
<website>https://github.com/nextcloud/photos</website>
<bugs>https://github.com/nextcloud/photos/issues</bugs>
<repository>https://github.com/nextcloud/photos.git</repository>
<default_enable />
<dependencies>
Expand All @@ -30,6 +29,11 @@
</navigation>
</navigations>

<commands>
<command>OCA\Photos\Command\UpdateReverseGeocodingFiles</command>
<command>OCA\Photos\Command\MapMediaToLocation</command>
</commands>

<sabre>
<collections>
<collection>OCA\Photos\Sabre\RootCollection</collection>
Expand All @@ -39,4 +43,4 @@
<plugin>OCA\Photos\Sabre\Album\PropFindPlugin</plugin>
</plugins>
</sabre>
</info>
</info>
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@
"vimeo/psalm": "^4.22",
"sabre/dav": "^4.2.1",
"nextcloud/ocp": "dev-master"
},
"require": {
"hexogen/kdtree": "^0.2.0"
}
}
66 changes: 64 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 11 additions & 52 deletions lib/Album/AlbumFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,11 @@

namespace OCA\Photos\Album;

use OC\Metadata\FileMetadata;
use OCA\Photos\DB\PhotosFile;

class AlbumFile {
private int $fileId;
private string $name;
private string $mimeType;
private int $size;
private int $mtime;
private string $etag;
class AlbumFile extends PhotosFile{
private int $added;
private string $owner;
/** @var array<string, FileMetadata> */
private array $metaData = [];

public function __construct(
int $fileId,
Expand All @@ -47,52 +39,19 @@ public function __construct(
int $added,
string $owner
) {
$this->fileId = $fileId;
$this->name = $name;
$this->mimeType = $mimeType;
$this->size = $size;
$this->mtime = $mtime;
$this->etag = $etag;
parent::__construct(
$fileId,
$name,
$mimeType,
$size,
$mtime,
$etag
);

$this->added = $added;
$this->owner = $owner;
}

public function getFileId(): int {
return $this->fileId;
}

public function getName(): string {
return $this->name;
}

public function getMimeType(): string {
return $this->mimeType;
}

public function getSize(): int {
return $this->size;
}

public function getMTime(): int {
return $this->mtime;
}

public function getEtag(): string {
return $this->etag;
}

public function setMetadata(string $key, FileMetadata $value): void {
$this->metaData[$key] = $value;
}

public function hasMetadata(string $key): bool {
return isset($this->metaData[$key]);
}

public function getMetadata(string $key): FileMetadata {
return $this->metaData[$key];
}

public function getAdded(): int {
return $this->added;
}
Expand Down
18 changes: 18 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@
use OCA\Photos\Listener\SabrePluginAuthInitListener;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\Photos\Listener\CacheEntryRemovedListener;
use OCA\Photos\Listener\LocationManagerNodeEventListener;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Files\Cache\CacheEntryInsertedEvent;
use OCP\Files\Cache\CacheEntryRemovedEvent;
use OCP\Files\Cache\CacheEntryUpdatedEvent;
use OCP\Share\Events\ShareCreatedEvent;
use OCP\Share\Events\ShareDeletedEvent;
use OCP\User\Events\UserDeletedEvent;

class Application extends App implements IBootstrap {
public const APP_ID = 'photos';
Expand Down Expand Up @@ -65,8 +71,20 @@ public function __construct() {
public function register(IRegistrationContext $context): void {
/** Register $principalBackend for the DAV collection */
$context->registerServiceAlias('principalBackend', Principal::class);

$context->registerEventListener(CacheEntryRemovedEvent::class, CacheEntryRemovedListener::class);

$context->registerEventListener(CacheEntryRemovedEvent::class, LocationManagerNodeEventListener::class);
// Priority of -1 to be triggered after event listeners populating metadata.
$context->registerEventListener(CacheEntryInsertedEvent::class, LocationManagerNodeEventListener::class, -1);
$context->registerEventListener(CacheEntryUpdatedEvent::class, LocationManagerNodeEventListener::class, -1);
$context->registerEventListener(UserDeletedEvent::class, LocationManagerNodeEventListener::class);
$context->registerEventListener(ShareCreatedEvent::class, LocationManagerNodeEventListener::class);
$context->registerEventListener(ShareDeletedEvent::class, LocationManagerNodeEventListener::class);

$context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class);

require_once __DIR__ . '/../../vendor/autoload.php';
}

public function boot(IBootContext $context): void {
Expand Down
120 changes: 120 additions & 0 deletions lib/Command/MapMediaToLocation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
*
* @author Louis Chemineau <louis@chmn.me>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Photos\Command;

use OCP\IConfig;
use OCP\IUserManager;
use OCP\Files\IRootFolder;
use OCP\Files\Folder;
use OCP\BackgroundJob\IJobList;
use OCA\Photos\Service\MediaLocationManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class MapMediaToLocation extends Command {
private IRootFolder $rootFolder;
private MediaLocationManager $mediaLocationManager;
private IConfig $config;
private IUserManager $userManager;

public function __construct(
IJobList $jobList,
IRootFolder $rootFolder,
MediaLocationManager $mediaLocationManager,
IConfig $config,
IUserManager $userManager
) {
parent::__construct();
$this->config = $config;
$this->jobList = $jobList;
$this->rootFolder = $rootFolder;
$this->mediaLocationManager = $mediaLocationManager;
$this->userManager = $userManager;
}

/**
* Configure the command
*
* @return void
*/
protected function configure() {
$this->setName('photos:map-media-to-location')
->setDescription('Reverse geocode media coordinates.')
->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Limit the mapping to a user.', null);
}

/**
* Execute the command
*
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
if (!$this->config->getSystemValueBool('enable_file_metadata', true)) {
throw new \Exception('File metadata is not enabled.');
}

$userId = $input->getOption('user');
if ($userId === null) {
$this->scanForAllUsers();
} else {
$this->scanFilesForUser($userId);
}

return 0;
}

private function scanForAllUsers() {
$users = $this->userManager->search('');

foreach ($users as $user) {
$this->scanFilesForUser($user->getUID());
}
}

private function scanFilesForUser(string $userId) {
$userFolder = $this->rootFolder->getUserFolder($userId);
$this->scanFolder($userFolder);
}

private function scanFolder(Folder $folder) {
foreach ($folder->getDirectoryListing() as $node) {
if ($node instanceof Folder) {
$this->scanFolder($node);
continue;
}

if (!str_starts_with($node->getMimeType(), 'image')) {
continue;
}

$this->mediaLocationManager->addLocationForFileAndUser($node->getId(), $node->getOwner()->getUID());
}
}
}
Loading

0 comments on commit 0063898

Please sign in to comment.