Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
47 / 47
SearchMediaService
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
10 / 10
23
100.00% covered (success)
100.00%
47 / 47
 getMediaFiles
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 searchFolder
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
14 / 14
 addMediaFile
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 haveEnoughPictures
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 searchSubFolders
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
7 / 7
 folderNeedsToBeSearched
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
1 / 1
 abortSearch
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 isPreviewAvailable
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
7 / 7
 addFolderToAlbumsArray
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 addFileToImagesArray
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
<?php
/**
 * Gallery
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Olivier Paroz <galleryapps@oparoz.com>
 *
 * @copyright Olivier Paroz 2014-2016
 */
namespace OCA\Gallery\Service;
use OCP\Files\Folder;
use OCP\Files\File;
/**
 * Searches the instance for media files which can be shown
 *
 * @package OCA\Gallery\Service
 */
class SearchMediaService extends FilesService {
    /** @var null|array<string,string|int> */
    private $images = [];
    /** @var null|array<string,string|int> */
    private $albums = [];
    /** @var string[] */
    private $supportedMediaTypes;
    /**
     * This returns the list of all media files which can be shown starting from the given folder
     *
     * @param Folder $folderNode the current album
     * @param string[] $supportedMediaTypes the list of supported media types
     * @param array $features the list of supported features
     *
     * @return array<null|array<string,string|int>> all the images we could find
     */
    public function getMediaFiles($folderNode, $supportedMediaTypes, $features) {
        $this->supportedMediaTypes = $supportedMediaTypes;
        $this->features = $features;
        $this->searchFolder($folderNode);
        return [$this->images, $this->albums];
    }
    /**
     * Look for media files and folders in the given folder
     *
     * @param Folder $folder
     * @param int $subDepth
     *
     * @return int
     */
    private function searchFolder($folder, $subDepth = 0) {
        $albumImageCounter = 0;
        $subFolders = [];
        $this->addFolderToAlbumsArray($folder);
        $nodes = $this->getNodes($folder, $subDepth);
        foreach ($nodes as $node) {
            if (!$this->isAllowedAndAvailable($node)) {
                continue;
            }
            $nodeType = $this->getNodeType($node);
            $subFolders = array_merge($subFolders, $this->getAllowedSubFolder($node, $nodeType));
            $albumImageCounter = $this->addMediaFile($node, $nodeType, $albumImageCounter);
            if ($this->haveEnoughPictures($albumImageCounter, $subDepth)) {
                break;
            }
        }
        $albumImageCounter = $this->searchSubFolders($subFolders, $subDepth, $albumImageCounter);
        return $albumImageCounter;
    }
    /**
     * Adds the node to the list of images if it's a file and we can generate a preview of it
     *
     * @param File|Folder $node
     * @param string $nodeType
     * @param int $albumImageCounter
     *
     * @return int
     */
    private function addMediaFile($node, $nodeType, $albumImageCounter) {
        if ($nodeType === 'file') {
            $albumImageCounter = $albumImageCounter + (int)$this->isPreviewAvailable($node);
        }
        return $albumImageCounter;
    }
    /**
     * Checks if we've collected enough pictures to be able to build the view
     *
     * An album is full when we find max 4 pictures at the same level
     *
     * @param int $albumImageCounter
     * @param int $subDepth
     *
     * @return bool
     */
    private function haveEnoughPictures($albumImageCounter, $subDepth) {
        if ($subDepth === 0) {
            return false;
        }
        return $albumImageCounter === 4;
    }
    /**
     * Looks for pictures in sub-folders
     *
     * If we're at level 0, we need to look for pictures in sub-folders no matter what
     * If we're at deeper levels, we only need to go further if we haven't managed to find one
     * picture in the current folder
     *
     * @param array <Folder> $subFolders
     * @param int $subDepth
     * @param int $albumImageCounter
     *
     * @return int
     */
    private function searchSubFolders($subFolders, $subDepth, $albumImageCounter) {
        if ($this->folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter)) {
            $subDepth++;
            foreach ($subFolders as $subFolder) {
                //$this->logger->debug("Sub-Node path : {path}", ['path' => $subFolder->getPath()]);
                $albumImageCounter = $this->searchFolder($subFolder, $subDepth);
                if ($this->abortSearch($subDepth, $albumImageCounter)) {
                    break;
                }
            }
        }
        return $albumImageCounter;
    }
    /**
     * Checks if we need to look for media files in the specified folder
     *
     * @param array <Folder> $subFolders
     * @param int $subDepth
     * @param int $albumImageCounter
     *
     * @return bool
     */
    private function folderNeedsToBeSearched($subFolders, $subDepth, $albumImageCounter) {
        return !empty($subFolders) && ($subDepth === 0 || $albumImageCounter === 0);
    }
    /**
     * Returns true if there is no need to check any other sub-folder at the same depth level
     *
     * @param int $subDepth
     * @param int $count
     *
     * @return bool
     */
    private function abortSearch($subDepth, $count) {
        return $subDepth > 1 && $count > 0;
    }
    /**
     * Returns true if the file is of a supported media type and adds it to the array of items to
     * return
     *
     * @todo We could potentially check if the file is readable ($file->stat() maybe) in order to
     *     only return valid files, but this may slow down operations
     *
     * @param File $file the file to test
     *
     * @return bool
     */
    private function isPreviewAvailable($file) {
        try {
            $mimeType = $file->getMimeType();
            if (in_array($mimeType, $this->supportedMediaTypes)) {
                $this->addFileToImagesArray($mimeType, $file);
                return true;
            }
        } catch (\Exception $exception) {
            return false;
        }
        return false;
    }
    /**
     * Adds a folder to the albums array
     *
     * @param Folder $folder the folder to add to the albums array
     */
    private function addFolderToAlbumsArray($folder) {
        $albumData = $this->getFolderData($folder);
        $this->albums[$albumData['path']] = $albumData;
    }
    /**
     * Adds a file to the images array
     *
     * @param string $mimeType the media type of the file to add to the images array
     * @param File $file the file to add to the images array
     */
    private function addFileToImagesArray($mimeType, $file) {
        $imageData = $this->getNodeData($file);
        $imageData['mimetype'] = $mimeType;
        $this->images[] = $imageData;
    }
}