Skip to content

Commit 6527080

Browse files
CarlSchwanartonge
authored andcommitted
Allow scanning for metadata with occ scan:file --generate-metadata
Signed-off-by: Carl Schwan <carl@carlschwan.eu> Signed-off-by: Louis Chemineau <louis@chmn.me>
1 parent dcccf07 commit 6527080

File tree

4 files changed

+68
-61
lines changed

4 files changed

+68
-61
lines changed

apps/files/lib/Command/Scan.php

+33-16
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@
3737
use OC\Core\Command\InterruptedException;
3838
use OC\DB\Connection;
3939
use OC\DB\ConnectionAdapter;
40+
use OCP\Files\File;
4041
use OC\ForbiddenException;
42+
use OC\Metadata\MetadataManager;
4143
use OCP\EventDispatcher\IEventDispatcher;
44+
use OCP\Files\IRootFolder;
4245
use OCP\Files\Mount\IMountPoint;
4346
use OCP\Files\NotFoundException;
4447
use OCP\Files\StorageNotAvailableException;
@@ -51,19 +54,22 @@
5154
use Symfony\Component\Console\Output\OutputInterface;
5255

5356
class Scan extends Base {
57+
private IUserManager $userManager;
58+
protected float $execTime = 0;
59+
protected int $foldersCounter = 0;
60+
protected int $filesCounter = 0;
61+
private IRootFolder $root;
62+
private MetadataManager $metadataManager;
5463

55-
/** @var IUserManager $userManager */
56-
private $userManager;
57-
/** @var float */
58-
protected $execTime = 0;
59-
/** @var int */
60-
protected $foldersCounter = 0;
61-
/** @var int */
62-
protected $filesCounter = 0;
63-
64-
public function __construct(IUserManager $userManager) {
64+
public function __construct(
65+
IUserManager $userManager,
66+
IRootFolder $rootFolder,
67+
MetadataManager $metadataManager
68+
) {
6569
$this->userManager = $userManager;
6670
parent::__construct();
71+
$this->root = $rootFolder;
72+
$this->metadataManager = $metadataManager;
6773
}
6874

6975
protected function configure() {
@@ -83,6 +89,12 @@ protected function configure() {
8389
InputArgument::OPTIONAL,
8490
'limit rescan to this path, eg. --path="/alice/files/Music", the user_id is determined by the path and the user_id parameter and --all are ignored'
8591
)
92+
->addOption(
93+
'generate-metadata',
94+
null,
95+
InputOption::VALUE_NONE,
96+
'Generate metadata for all scanned files'
97+
)
8698
->addOption(
8799
'all',
88100
null,
@@ -106,21 +118,26 @@ protected function configure() {
106118
);
107119
}
108120

109-
protected function scanFiles($user, $path, OutputInterface $output, $backgroundScan = false, $recursive = true, $homeOnly = false) {
121+
protected function scanFiles(string $user, string $path, bool $scanMetadata, OutputInterface $output, bool $backgroundScan = false, bool $recursive = true, bool $homeOnly = false): void {
110122
$connection = $this->reconnectToDatabase($output);
111123
$scanner = new \OC\Files\Utils\Scanner(
112124
$user,
113125
new ConnectionAdapter($connection),
114-
\OC::$server->query(IEventDispatcher::class),
126+
\OC::$server->get(IEventDispatcher::class),
115127
\OC::$server->get(LoggerInterface::class)
116128
);
117129

118130
# check on each file/folder if there was a user interrupt (ctrl-c) and throw an exception
119-
120-
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) {
131+
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function (string $path) use ($output, $scanMetadata) {
121132
$output->writeln("\tFile\t<info>$path</info>", OutputInterface::VERBOSITY_VERBOSE);
122133
++$this->filesCounter;
123134
$this->abortIfInterrupted();
135+
if ($scanMetadata) {
136+
$node = $this->root->get($path);
137+
if ($node instanceof File) {
138+
$this->metadataManager->generateMetadata($node, false);
139+
}
140+
}
124141
});
125142

126143
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) {
@@ -197,7 +214,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
197214
++$user_count;
198215
if ($this->userManager->userExists($user)) {
199216
$output->writeln("Starting scan for user $user_count out of $users_total ($user)");
200-
$this->scanFiles($user, $path, $output, $input->getOption('unscanned'), !$input->getOption('shallow'), $input->getOption('home-only'));
217+
$this->scanFiles($user, $path, $input->getOption('generate-metadata'), $output, $input->getOption('unscanned'), !$input->getOption('shallow'), $input->getOption('home-only'));
201218
$output->writeln('', OutputInterface::VERBOSITY_VERBOSE);
202219
} else {
203220
$output->writeln("<error>Unknown user $user_count $user</error>");
@@ -291,7 +308,7 @@ protected function showSummary($headers, $rows, OutputInterface $output) {
291308
protected function formatExecTime() {
292309
$secs = round($this->execTime);
293310
# convert seconds into HH:MM:SS form
294-
return sprintf('%02d:%02d:%02d', (int)($secs / 3600), ( (int)($secs / 60) % 60), $secs % 60);
311+
return sprintf('%02d:%02d:%02d', (int)($secs / 3600), ((int)($secs / 60) % 60), $secs % 60);
295312
}
296313

297314
protected function reconnectToDatabase(OutputInterface $output): Connection {

lib/private/Metadata/FileMetadataMapper.php

+13-36
Original file line numberDiff line numberDiff line change
@@ -112,59 +112,36 @@ public function clear(int $fileId): void {
112112
* @return Entity the saved entity with the set id
113113
* @throws Exception
114114
* @throws \InvalidArgumentException if entity has no id
115-
* @since 14.0.0
116115
*/
117116
public function update(Entity $entity): Entity {
118-
// if entity wasn't changed it makes no sense to run a db query
119-
$properties = $entity->getUpdatedFields();
120-
if (\count($properties) === 0) {
121-
return $entity;
117+
if (!($entity instanceof FileMetadata)) {
118+
throw new \Exception("Entity should be a FileMetadata entity");
122119
}
123120

124121
// entity needs an id
125122
$id = $entity->getId();
126123
if ($id === null) {
127-
throw new \InvalidArgumentException(
128-
'Entity which should be updated has no id');
129-
}
130-
131-
if (!($entity instanceof FileMetadata)) {
132-
throw new \Exception("Entity should be a FileMetadata entity");
124+
throw new \InvalidArgumentException('Entity which should be updated has no id');
133125
}
134126

135127
// entity needs an group_name
136128
$groupName = $entity->getGroupName();
137-
if ($id === null) {
138-
throw new \InvalidArgumentException(
139-
'Entity which should be updated has no group_name');
140-
}
141-
142-
// get updated fields to save, fields have to be set using a setter to
143-
// be saved
144-
// do not update the id and group_name field
145-
unset($properties['id']);
146-
unset($properties['group_name']);
147-
148-
$qb = $this->db->getQueryBuilder();
149-
$qb->update($this->tableName);
150-
151-
// build the fields
152-
foreach ($properties as $property => $updated) {
153-
$column = $entity->propertyToColumn($property);
154-
$getter = 'get' . ucfirst($property);
155-
$value = $entity->$getter();
156-
157-
$type = $this->getParameterTypeForProperty($entity, $property);
158-
$qb->set($column, $qb->createNamedParameter($value, $type));
129+
if ($groupName === null) {
130+
throw new \InvalidArgumentException('Entity which should be updated has no group_name');
159131
}
160132

161133
$idType = $this->getParameterTypeForProperty($entity, 'id');
162134
$groupNameType = $this->getParameterTypeForProperty($entity, 'groupName');
135+
$metadataValue = $entity->getMetadata();
136+
$metadataType = $this->getParameterTypeForProperty($entity, 'metadata');
163137

164-
$qb->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $idType)))
165-
->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, $groupNameType)));
138+
$qb = $this->db->getQueryBuilder();
166139

167-
$qb->executeStatement();
140+
$qb->update($this->tableName)
141+
->set('metadata', $qb->createNamedParameter($metadataValue, $metadataType))
142+
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $idType)))
143+
->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, $groupNameType)))
144+
->executeStatement();
168145

169146
return $entity;
170147
}

lib/private/Metadata/MetadataManager.php

+1-9
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,19 @@
2121

2222
use OC\Metadata\Provider\ExifProvider;
2323
use OCP\Files\File;
24-
use OCP\IConfig;
25-
use Psr\Log\LoggerInterface;
2624

2725
class MetadataManager implements IMetadataManager {
2826
/** @var array<string, IMetadataProvider> */
2927
private array $providers;
3028
private array $providerClasses;
3129
private FileMetadataMapper $fileMetadataMapper;
32-
private IConfig $config;
33-
private LoggerInterface $logger;
3430

3531
public function __construct(
36-
FileMetadataMapper $fileMetadataMapper,
37-
IConfig $config,
38-
LoggerInterface $logger
32+
FileMetadataMapper $fileMetadataMapper
3933
) {
4034
$this->providers = [];
4135
$this->providerClasses = [];
4236
$this->fileMetadataMapper = $fileMetadataMapper;
43-
$this->config = $config;
44-
$this->logger = $logger;
4537

4638
// TODO move to another place, where?
4739
$this->registerProvider(ExifProvider::class);

lib/private/Metadata/Provider/ExifProvider.php

+21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
<?php
22

3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu>
6+
* @copyright Copyright 2022 Louis Chmn <louis@chmn.me>
7+
* @license AGPL-3.0-or-later
8+
*
9+
* This code is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License, version 3,
11+
* as published by the Free Software Foundation.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License, version 3,
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>
20+
*
21+
*/
22+
323
namespace OC\Metadata\Provider;
424

525
use OC\Metadata\FileMetadata;
@@ -24,6 +44,7 @@ public static function isAvailable(): bool {
2444
return extension_loaded('exif');
2545
}
2646

47+
/** @return array{'gps': FileMetadata, 'size': FileMetadata} */
2748
public function execute(File $file): array {
2849
$exifData = [];
2950
$fileDescriptor = $file->fopen('rb');

0 commit comments

Comments
 (0)