Skip to content

Commit

Permalink
Merge pull request #323 from nextcloud/enh/admin-view
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelklehr authored Sep 23, 2022
2 parents 1ace1da + 6f964f3 commit 4ffc473
Show file tree
Hide file tree
Showing 23 changed files with 1,213 additions and 1,827 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@
This app goes through your media collection and adds fitting tags, automatically categorizing your photos and music.

* 📷 👪 Recognizes faces from contact photos
* 📷 👪 Recognizes faces and groups photos by face
* 📷 🏔 Recognizes animals, landscapes, food, vehicles, buildings and other objects
* 📷 🗼 Recognizes landmarks and monuments
* 👂 🎵 Recognizes music genres
* ⚡ Tagging works via Nextcloud's Collaborative Tags, allowing access by any of your apps
* 👂 listen to your tagged music with the audioplayer app
* 📷 view your tagged photos with the photos app

Note: This project is quite young and thus still a bit rough around the edges.

### Examples

![](https://github.com/marcelklehr/recognize/raw/master/screenshots/imagenet_examples.jpg)
Expand Down Expand Up @@ -54,7 +52,7 @@ Recognize uses

### One click

Go to "Apps" in your nextcloud, search for "recognize" and click install (currently only works on Nextcloud v22+. If you're below that, you'll need to install manually).
Go to "Apps" in your nextcloud, search for "recognize" and click install.

### Configuration

Expand Down
4 changes: 3 additions & 1 deletion appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
['name' => 'admin#reset', 'url' => '/admin/reset', 'verb' => 'GET'],
['name' => 'admin#recrawl', 'url' => '/admin/recrawl', 'verb' => 'GET'],
['name' => 'admin#count', 'url' => '/admin/count', 'verb' => 'GET'],
['name' => 'admin#countQueued', 'url' => '/admin/countQueued', 'verb' => 'GET'],
['name' => 'admin#count_missed', 'url' => '/admin/countMissed', 'verb' => 'GET'],
['name' => 'admin#avx', 'url' => '/admin/avx', 'verb' => 'GET'],
['name' => 'admin#platform', 'url' => '/admin/platform', 'verb' => 'GET'],
['name' => 'admin#musl', 'url' => '/admin/musl', 'verb' => 'GET'],
['name' => 'user#update_cluster', 'url' => '/user/cluster/{id}', 'verb' => 'POST'],
['name' => 'admin#get_setting', 'url' => '/admin/settings/{setting}', 'verb' => 'GET'],
['name' => 'admin#set_setting', 'url' => '/admin/settings/{setting}', 'verb' => 'PUT'],
],
];
27 changes: 23 additions & 4 deletions lib/BackgroundJobs/ClassifierJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,43 @@
use OCP\DB\Exception;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
use OCP\IConfig;
use Psr\Log\LoggerInterface;

abstract class ClassifierJob extends Job {
private LoggerInterface $logger;
private QueueService $queue;
private IUserMountCache $userMountCache;
private IJobList $jobList;
/**
* @var \OCP\IConfig
*/
private IConfig $config;

public function __construct(ITimeFactory $time, LoggerInterface $logger, QueueService $queue, IUserMountCache $userMountCache, IJobList $jobList) {
public function __construct(ITimeFactory $time, LoggerInterface $logger, QueueService $queue, IUserMountCache $userMountCache, IJobList $jobList, IConfig $config) {
parent::__construct($time);
$this->logger = $logger;
$this->queue = $queue;
$this->userMountCache = $userMountCache;
$this->jobList = $jobList;
$this->config = $config;
}

protected function runClassifier(string $model, $argument) {
$storageId = $argument['storageId'];
$rootId = $argument['rootId'];
if ($this->config->getAppValue('recognize', $model.'.enabled', 'false') !== 'true') {
$this->logger->debug('Not classifying files of storage '.$storageId. ' using '.$model. ' because model is disabled');
// `static` to get extending subclass name
$this->jobList->remove(static::class, $argument);
return;
}
$this->logger->debug('Classifying files of storage '.$storageId. ' using '.$model);
try {
$files = $this->queue->getFromQueue($model, $storageId, $rootId, $this->getBatchSize());
} catch (Exception $e) {
$this->logger->error('Cannot retrieve items from imagenet queue', ['exception' => $e]);
$this->config->setAppValue('recognize', $model.'.status', 'false');
$this->logger->error('Cannot retrieve items from '.$model.' queue', ['exception' => $e]);
return;
}

Expand All @@ -44,7 +57,12 @@ protected function runClassifier(string $model, $argument) {
\OC_Util::setupFS($mounts[0]->getUser()->getUID());
}

$this->classify($files);
try {
$this->classify($files);
} catch(\Throwable $e) {
$this->config->setAppValue('recognize', $model.'.status', 'false');
throw $e;
}

try {
// If there is at least one file left in the queue, reschedule this job
Expand All @@ -54,7 +72,8 @@ protected function runClassifier(string $model, $argument) {
$this->jobList->remove(static::class, $argument);
}
} catch (Exception $e) {
$this->logger->error('Cannot retrieve items from imagenet queue', ['exception' => $e]);
$this->config->setAppValue('recognize', $model.'.status', 'false');
$this->logger->error('Cannot retrieve items from '.$model.' queue', ['exception' => $e]);
return;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyFacesJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ClassifyFacesJob extends ClassifierJob {
private ClusteringFaceClassifier $faces;

public function __construct(ITimeFactory $time, LoggerInterface $logger, QueueService $queue, IConfig $config, ClusteringFaceClassifier $faceClassifier, IUserMountCache $mountCache, IJobList $jobList) {
parent::__construct($time, $logger, $queue, $mountCache, $jobList);
parent::__construct($time, $logger, $queue, $mountCache, $jobList, $config);
$this->config = $config;
$this->faces = $faceClassifier;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyImagenetJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ClassifyImagenetJob extends ClassifierJob {
private ImagenetClassifier $imagenet;

public function __construct(ITimeFactory $time, LoggerInterface $logger, QueueService $queue, IConfig $config, ImagenetClassifier $imagenet, IUserMountCache $mountCache, IJobList $jobList) {
parent::__construct($time, $logger, $queue, $mountCache, $jobList);
parent::__construct($time, $logger, $queue, $mountCache, $jobList, $config);
$this->config = $config;
$this->imagenet = $imagenet;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyLandmarksJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ClassifyLandmarksJob extends ClassifierJob {
private LandmarksClassifier $landmarks;

public function __construct(ITimeFactory $time, LoggerInterface $logger, QueueService $queue, IConfig $config, LandmarksClassifier $landmarks, IUserMountCache $mountCache, IJobList $jobList) {
parent::__construct($time, $logger, $queue, $mountCache, $jobList);
parent::__construct($time, $logger, $queue, $mountCache, $jobList, $config);
$this->config = $config;
$this->landmarks = $landmarks;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyMovinetJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ClassifyMovinetJob extends ClassifierJob {
private MovinetClassifier $movinet;

public function __construct(ITimeFactory $time, LoggerInterface $logger, QueueService $queue, IConfig $config, MovinetClassifier $movinet, IUserMountCache $mountCache, IJobList $jobList) {
parent::__construct($time, $logger, $queue, $mountCache, $jobList);
parent::__construct($time, $logger, $queue, $mountCache, $jobList, $config);
$this->config = $config;
$this->movinet = $movinet;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/BackgroundJobs/ClassifyMusicnnJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ClassifyMusicnnJob extends ClassifierJob {
private MusicnnClassifier $musicnn;

public function __construct(ITimeFactory $time, LoggerInterface $logger, QueueService $queue, IConfig $config, MusicnnClassifier $musicnn, IUserMountCache $mountCache, IJobList $jobList) {
parent::__construct($time, $logger, $queue, $mountCache, $jobList);
parent::__construct($time, $logger, $queue, $mountCache, $jobList, $config);
$this->config = $config;
$this->musicnn = $musicnn;
}
Expand Down
14 changes: 14 additions & 0 deletions lib/BackgroundJobs/SchedulerJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

use OC\Files\Cache\CacheQueryBuilder;
use OC\SystemConfig;
use OCA\Recognize\Classifiers\Audio\MusicnnClassifier;
use OCA\Recognize\Classifiers\Images\ClusteringFaceClassifier;
use OCA\Recognize\Classifiers\Images\ImagenetClassifier;
use OCA\Recognize\Classifiers\Images\LandmarksClassifier;
use OCA\Recognize\Classifiers\Video\MovinetClassifier;
use OCA\Recognize\Service\Logger;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
Expand Down Expand Up @@ -49,6 +54,14 @@ public function __construct(ITimeFactory $timeFactory, Logger $logger, IDBConnec
}

protected function run($argument): void {
$models = $argument['models'] ?? [
ClusteringFaceClassifier::MODEL_NAME,
ImagenetClassifier::MODEL_NAME,
LandmarksClassifier::MODEL_NAME,
MovinetClassifier::MODEL_NAME,
MusicnnClassifier::MODEL_NAME,
];

$qb = $this->db->getQueryBuilder();
$qb->select('root_id', 'storage_id', 'mount_provider_class')
->from('mounts')
Expand Down Expand Up @@ -80,6 +93,7 @@ protected function run($argument): void {
'root_id' => $rootId,
'override_root' => $overrideRoot,
'last_file_id' => 0,
'models' => $models,
]);
}

Expand Down
56 changes: 51 additions & 5 deletions lib/BackgroundJobs/StorageCrawlJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
use OCA\Recognize\Classifiers\Audio\MusicnnClassifier;
use OCA\Recognize\Classifiers\Images\ClusteringFaceClassifier;
use OCA\Recognize\Classifiers\Images\ImagenetClassifier;
use OCA\Recognize\Classifiers\Images\LandmarksClassifier;
use OCA\Recognize\Classifiers\Video\MovinetClassifier;
use OCA\Recognize\Constants;
use OCA\Recognize\Db\QueueFile;
use OCA\Recognize\Service\Logger;
use OCA\Recognize\Service\QueueService;
use OCA\Recognize\Service\TagManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\QueuedJob;
Expand All @@ -33,22 +35,32 @@ class StorageCrawlJob extends QueuedJob {
private IJobList $jobList;
private IDBConnection $db;
private SystemConfig $systemConfig;
private TagManager $tagManager;

public function __construct(ITimeFactory $timeFactory, Logger $logger, IMimeTypeLoader $mimeTypes, QueueService $queue, IJobList $jobList, IDBConnection $db, SystemConfig $systemConfig) {
public function __construct(ITimeFactory $timeFactory, Logger $logger, IMimeTypeLoader $mimeTypes, QueueService $queue, IJobList $jobList, IDBConnection $db, SystemConfig $systemConfig, TagManager $tagManager) {
parent::__construct($timeFactory);
$this->logger = $logger;
$this->mimeTypes = $mimeTypes;
$this->queue = $queue;
$this->jobList = $jobList;
$this->db = $db;
$this->systemConfig = $systemConfig;
$this->tagManager = $tagManager;
}

protected function run($argument): void {
$storageId = $argument['storage_id'];
$rootId = $argument['root_id'];
$overrideRoot = $argument['override_root'];
$lastFileId = $argument['last_file_id'];
$models = $argument['models'] ?? [
ClusteringFaceClassifier::MODEL_NAME,
ImagenetClassifier::MODEL_NAME,
LandmarksClassifier::MODEL_NAME,
MovinetClassifier::MODEL_NAME,
MusicnnClassifier::MODEL_NAME,
];

$qb = new CacheQueryBuilder($this->db, $this->systemConfig, $this->logger);
try {
$root = $qb->selectFileCache()
Expand All @@ -63,13 +75,31 @@ protected function run($argument): void {
$videoTypes = array_map(fn ($mimeType) => $this->mimeTypes->getId($mimeType), Constants::VIDEO_FORMATS);
$audioTypes = array_map(fn ($mimeType) => $this->mimeTypes->getId($mimeType), Constants::AUDIO_FORMATS);

$mimeTypes = [];
if (in_array(ClusteringFaceClassifier::MODEL_NAME, $models) ||
in_array(ImagenetClassifier::MODEL_NAME, $models) ||
in_array(LandmarksClassifier::MODEL_NAME, $models)) {
$mimeTypes = array_merge($imageTypes, $mimeTypes);
}
if (in_array(MovinetClassifier::MODEL_NAME, $models)) {
$mimeTypes = array_merge($videoTypes, $mimeTypes);
}
if (in_array(MusicnnClassifier::MODEL_NAME, $models)) {
$mimeTypes = array_merge($audioTypes, $mimeTypes);
}
if (count($mimeTypes) === 0) {
// Remove current iteration
$this->jobList->remove(self::class, $argument);
return;
}

try {
$qb = new CacheQueryBuilder($this->db, $this->systemConfig, $this->logger);
$files = $qb->selectFileCache()
->whereStorageId($storageId)
->andWhere($qb->expr()->like('path', $qb->createNamedParameter($root['path'] . '/%')))
->andWhere($qb->expr()->eq('storage', $qb->createNamedParameter($storageId)))
->andWhere($qb->expr()->in('mimetype', $qb->createNamedParameter(array_merge($imageTypes, $videoTypes, $audioTypes), IQueryBuilder::PARAM_INT_ARRAY)))
->andWhere($qb->expr()->in('mimetype', $qb->createNamedParameter($mimeTypes, IQueryBuilder::PARAM_INT_ARRAY)))
->andWhere($qb->expr()->gt('filecache.fileid', $qb->createNamedParameter($lastFileId)))
->orderBy('filecache.fileid', 'ASC')
->setMaxResults(100)
Expand All @@ -92,8 +122,23 @@ protected function run($argument): void {
$queueFile->setUpdate(false);
try {
if (in_array($file['mimetype'], $imageTypes)) {
$this->queue->insertIntoQueue(ImagenetClassifier::MODEL_NAME, $queueFile);
$this->queue->insertIntoQueue(ClusteringFaceClassifier::MODEL_NAME, $queueFile);
if (in_array(ImagenetClassifier::MODEL_NAME, $models)) {
$this->queue->insertIntoQueue(ImagenetClassifier::MODEL_NAME, $queueFile);
}
if (!in_array(ImagenetClassifier::MODEL_NAME, $models) && in_array(LandmarksClassifier::MODEL_NAME, $models)) {
$tags = $this->tagManager->getTagsForFiles([$queueFile->getFileId()]);
/** @var \OCP\SystemTag\ISystemTag[] $fileTags */
$fileTags = $tags[$queueFile->getFileId()];
$landmarkTags = array_filter($fileTags, function ($tag) {
return in_array($tag->getName(), LandmarksClassifier::PRECONDITION_TAGS);
});
if (count($landmarkTags) > 0) {
$this->queue->insertIntoQueue(LandmarksClassifier::MODEL_NAME, $queueFile);
}
}
if (in_array(ClusteringFaceClassifier::MODEL_NAME, $models)) {
$this->queue->insertIntoQueue(ClusteringFaceClassifier::MODEL_NAME, $queueFile);
}
}
if (in_array($file['mimetype'], $videoTypes)) {
$this->queue->insertIntoQueue(MovinetClassifier::MODEL_NAME, $queueFile);
Expand All @@ -113,7 +158,8 @@ protected function run($argument): void {
'storage_id' => $storageId,
'root_id' => $rootId,
'override_root' => $overrideRoot,
'last_file_id' => $queueFile->getFileId()
'last_file_id' => $queueFile->getFileId(),
'models' => $models,
]);
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/Classifiers/Audio/MusicnnClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function classify(array $queueFiles): void {

foreach ($classifierProcess as $queueFile => $results) {
$this->tagManager->assignTags($queueFile->getFileId(), $results);
$this->config->setAppValue('recognize', self::MODEL_NAME.'.status', 'true');
}
}
}
1 change: 1 addition & 0 deletions lib/Classifiers/Images/ClusteringFaceClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public function classify(array $queueFiles): void {
}
$usersToCluster[] = $userId;
}
$this->config->setAppValue('recognize', self::MODEL_NAME.'.status', 'true');
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/Classifiers/Images/ImagenetClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public function classify(array $queueFiles): void {
$landmarkTags = array_filter($results, function ($tagName) {
return in_array($tagName, LandmarksClassifier::PRECONDITION_TAGS);
});
$this->config->setAppValue('recognize', self::MODEL_NAME.'.status', 'true');
if (count($landmarkTags) > 0) {
try {
$this->queue->insertIntoQueue(LandmarksClassifier::MODEL_NAME, $queueFile);
Expand Down
1 change: 1 addition & 0 deletions lib/Classifiers/Images/LandmarksClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function classify(array $queueFiles): void {

foreach ($classifierProcess as $queueFile => $results) {
$this->tagManager->assignTags($queueFile->getFileId(), $results);
$this->config->setAppValue('recognize', self::MODEL_NAME.'.status', 'true');
}
}
}
1 change: 1 addition & 0 deletions lib/Classifiers/Video/MovinetClassifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public function classify(array $queueFiles): void {

foreach ($classifierProcess as $queueFile => $results) {
$this->tagManager->assignTags($queueFile->getFileId(), $results);
$this->config->setAppValue('recognize', self::MODEL_NAME.'.status', 'false');
}
}
}
Loading

0 comments on commit 4ffc473

Please sign in to comment.