Exclude mounts in nomedia

Signed-off-by: Varun Patil <varunpatil@ucla.edu>
pull/579/head
Varun Patil 2023-04-14 15:26:20 -07:00
parent 8ec21747f3
commit 3ada3d6510
3 changed files with 82 additions and 14 deletions

View File

@ -23,12 +23,17 @@ declare(strict_types=1);
namespace OCA\Memories\Db;
use OC\Files\Search\SearchComparison;
use OC\Files\Search\SearchQuery;
use OCA\Memories\Exceptions;
use OCA\Memories\Exif;
use OCA\Memories\Util;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Search\ISearchComparison;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IUserManager;
@ -42,19 +47,22 @@ class FsManager
protected IRootFolder $rootFolder;
protected AlbumsQuery $albumsQuery;
protected IRequest $request;
protected ICache $nomediaCache;
public function __construct(
IConfig $config,
IUserSession $userSession,
IRootFolder $rootFolder,
AlbumsQuery $albumsQuery,
IRequest $request
IRequest $request,
ICacheFactory $cacheFactory
) {
$this->config = $config;
$this->userSession = $userSession;
$this->rootFolder = $rootFolder;
$this->albumsQuery = $albumsQuery;
$this->request = $request;
$this->nomediaCache = $cacheFactory->createLocal('memories:nomedia');
}
/** Get the TimelineRoot object relevant to the request */
@ -98,16 +106,30 @@ class FsManager
$folder = $userFolder->get(Exif::removeExtraSlash($folderPath));
$root->addFolder($folder);
} else {
// Get timeline paths
$paths = Exif::getTimelinePaths($uid);
if ($path = $this->request->getParam('timelinePath', null)) {
$paths = [Exif::removeExtraSlash($path)];
}
// Combined etag, for cache invalidation.
// This is cheaper and more sensible than the root etag.
// The only time this breaks down is if the user places a .nomedia
// outside the timeline path; rely on expiration for that.
$cEtag = $uid;
// Multiple timeline path support
foreach ($paths as $path) {
$root->addFolder($userFolder->get($path));
$node = $userFolder->get($path);
$root->addFolder($node);
$cEtag .= $node->getEtag();
}
// Add shares or external stores inside the current folders
$root->addMountPoints();
// Exclude .nomedia folders
$root->excludePaths($this->getNoMediaFolders($userFolder, md5($cEtag)));
}
} catch (\OCP\Files\NotFoundException $e) {
$msg = $e->getMessage();
@ -118,6 +140,27 @@ class FsManager
return $root;
}
/**
* Get list of folders with .nomedia file.
*
* @param Folder $root root folder
* @param string $key etag for cache key
*/
public function getNoMediaFolders(Folder $root, string $key): array
{
if (null !== ($paths = $this->nomediaCache->get($key))) {
return $paths;
}
$comp = new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', '.nomedia');
$search = $root->search(new SearchQuery($comp, 0, 0, [], Util::getUser()));
$paths = array_map(fn (File $node) => \dirname($node->getPath()), $search);
$this->nomediaCache->set($key, $paths, 60 * 60); // 1 hour
return $paths;
}
/**
* Get a file with ID for the current user.
*

View File

@ -154,6 +154,11 @@ trait TimelineQueryDays
$root = $this->root();
}
// Never join against an empty root
if (!$root || $root->isEmpty()) {
throw new \InvalidArgumentException('Timeline root is empty');
}
// Join with memories
$baseOp = $query->expr()->eq('f.fileid', 'm.fileid');
if ($root->isEmpty()) {

View File

@ -14,6 +14,8 @@ class TimelineRoot
/** Initialize */
public function __construct()
{
$this->folders = [];
$this->folderPaths = [];
}
/**
@ -31,35 +33,42 @@ class TimelineRoot
*/
public function addFolder(FileInfo $info)
{
$folderPath = $info->getPath();
$path = $info->getPath();
if (FileInfo::MIMETYPE_FOLDER !== $info->getMimetype()) {
throw new \Exception("Not a folder: {$folderPath}");
throw new \Exception("Not a folder: {$path}");
}
if (!$info->isReadable()) {
throw new \Exception("Folder not readable: {$folderPath}");
throw new \Exception("Folder not readable: {$path}");
}
// Add top level folder
$id = $info->getId();
$this->folders[$id] = $info;
$this->folderPaths[$id] = $folderPath;
$this->setFolder($info->getId(), $info, $path);
}
// Add mountpoints recursively
public function addMountPoints()
{
$mp = [];
$folders = [];
foreach ($this->folderPaths as $id => $folderPath) {
$mounts = \OC\Files\Filesystem::getMountManager()->findIn($folderPath);
foreach ($mounts as &$mount) {
$id = $mount->getStorageRootId();
$path = $mount->getMountPoint();
$mp[$id] = $path;
foreach ($mounts as $mount) {
$this->setFolder($mount->getStorageRootId(), null, $mount->getMountPoint());
}
}
$this->folderPaths += $folders;
}
public function excludePaths(array $paths)
{
foreach ($paths as $path) {
foreach ($this->folderPaths as $id => $folderPath) {
if (str_starts_with($folderPath, $path)) {
unset($this->folderPaths[$id], $this->folders[$id]);
}
}
}
$this->folderPaths += $mp;
}
public function getFolderPath(int $id)
@ -86,4 +95,15 @@ class TimelineRoot
{
return empty($this->folderPaths);
}
private function setFolder(int $id, ?FileInfo $fileInfo, ?string $path)
{
if (null !== $path) {
$this->folderPaths[$id] = $path;
}
if (null !== $fileInfo) {
$this->folders[$id] = $fileInfo;
}
}
}