From 522e1f5e1e31ad6f6434dfaad3de7e1647208421 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Sat, 20 Aug 2022 02:53:21 +0000 Subject: [PATCH] Refactor to more files --- lib/Command/Index.php | 9 +- lib/Controller/ApiController.php | 33 +-- lib/Controller/PageController.php | 2 +- lib/Db/TimelineQuery.php | 133 +++++++++++ lib/Db/TimelineWrite.php | 102 +++++++++ lib/Db/Util.php | 315 --------------------------- lib/Exif.php | 106 +++++++++ lib/Listeners/PostDeleteListener.php | 6 +- lib/Listeners/PostWriteListener.php | 7 +- lib/Util.php | 22 ++ 10 files changed, 384 insertions(+), 351 deletions(-) create mode 100644 lib/Db/TimelineQuery.php create mode 100644 lib/Db/TimelineWrite.php delete mode 100644 lib/Db/Util.php create mode 100644 lib/Exif.php create mode 100644 lib/Util.php diff --git a/lib/Command/Index.php b/lib/Command/Index.php index b9de0168..d643da77 100644 --- a/lib/Command/Index.php +++ b/lib/Command/Index.php @@ -37,6 +37,7 @@ use OCP\IPreview; use OCP\IUser; use OCP\IUserManager; use OCA\Files_External\Service\GlobalStoragesService; +use OCA\Memories\Db\TimelineWrite; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; use Symfony\Component\Console\Command\Command; @@ -58,7 +59,7 @@ class Index extends Command { protected OutputInterface $output; protected IManager $encryptionManager; protected IDBConnection $connection; - protected \OCA\Memories\Db\Util $util; + protected TimelineWrite $timelineWrite; public function __construct(IRootFolder $rootFolder, IUserManager $userManager, @@ -75,7 +76,7 @@ class Index extends Command { $this->config = $config; $this->encryptionManager = $encryptionManager; $this->connection = $connection; - $this->util = new \OCA\Memories\Db\Util($connection); + $this->timelineWrite = new TimelineWrite($this->connection); try { $this->globalService = $container->get(GlobalStoragesService::class); @@ -98,7 +99,7 @@ class Index extends Command { return false; } - $exif = \OCA\Memories\Db\Util::getExifFromStream($stream); + $exif = \OCA\Memories\Exif::getExifFromStream($stream); fclose($stream); if (!$exif || $exif["DateTimeOriginal"] !== "2004:08:31 19:52:58") { @@ -159,6 +160,6 @@ class Index extends Command { private function parseFile(File &$file): void { // $this->output->writeln('Generating entry for ' . $file->getPath() . ' ' . $file->getId()); - $this->util->processFile($file); + $this->timelineWrite->processFile($file); } } \ No newline at end of file diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 37620353..469276a8 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -28,11 +28,10 @@ namespace OCA\Memories\Controller; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; use OCA\Memories\AppInfo\Application; +use OCA\Memories\Db\TimelineQuery; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\Http\StreamResponse; -use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\Files\IRootFolder; use OCP\IConfig; use OCP\IDBConnection; @@ -45,8 +44,8 @@ class ApiController extends Controller { private IConfig $config; private IUserSession $userSession; private IDBConnection $connection; - private \OCA\Memories\Db\Util $util; private IRootFolder $rootFolder; + private TimelineQuery $timelineQuery; public function __construct( IRequest $request, @@ -60,7 +59,7 @@ class ApiController extends Controller { $this->config = $config; $this->userSession = $userSession; $this->connection = $connection; - $this->util = new \OCA\Memories\Db\Util($this->connection); + $this->timelineQuery = new TimelineQuery($this->connection); $this->rootFolder = $rootFolder; } @@ -75,7 +74,7 @@ class ApiController extends Controller { return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED); } - $list = $this->util->getDays($this->config, $user->getUID()); + $list = $this->timelineQuery->getDays($this->config, $user->getUID()); return new JSONResponse($list, Http::STATUS_OK); } @@ -90,7 +89,7 @@ class ApiController extends Controller { return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED); } - $list = $this->util->getDay($this->config, $user->getUID(), intval($id)); + $list = $this->timelineQuery->getDay($this->config, $user->getUID(), intval($id)); return new JSONResponse($list, Http::STATUS_OK); } @@ -137,7 +136,7 @@ class ApiController extends Controller { } // Get response from db - $list = $this->util->getDaysFolder($node->getId()); + $list = $this->timelineQuery->getDaysFolder($node->getId()); // Get subdirectories $sub = $node->search(new SearchQuery( @@ -180,7 +179,7 @@ class ApiController extends Controller { return new JSONResponse([], Http::STATUS_FORBIDDEN); } - $list = $this->util->getDayFolder($node->getId(), intval($dayId)); + $list = $this->timelineQuery->getDayFolder($node->getId(), intval($dayId)); return new JSONResponse($list, Http::STATUS_OK); } @@ -204,22 +203,4 @@ class ApiController extends Controller { $this->config->setUserValue($userId, Application::APPNAME, $key, $value); return new JSONResponse([], Http::STATUS_OK); } - - /** - * @NoAdminRequired - * @NoCSRFRequired - */ - public function serviceWorker(): StreamResponse { - $response = new StreamResponse(__DIR__.'/../../js/photos-service-worker.js'); - $response->setHeaders([ - 'Content-Type' => 'application/javascript', - 'Service-Worker-Allowed' => '/' - ]); - $policy = new ContentSecurityPolicy(); - $policy->addAllowedWorkerSrcDomain("'self'"); - $policy->addAllowedScriptDomain("'self'"); - $policy->addAllowedConnectDomain("'self'"); - $response->setContentSecurityPolicy($policy); - return $response; - } } \ No newline at end of file diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 4a8e841c..36a068ca 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -55,7 +55,7 @@ class PageController extends Controller { $this->eventDispatcher->dispatchTyped(new LoadViewer()); - $timelinePath = \OCA\Memories\Db\Util::getPhotosPath($this->config, $user->getUid()); + $timelinePath = \OCA\Memories\Util::getPhotosPath($this->config, $user->getUid()); $this->initialState->provideInitialState('timelinePath', $timelinePath); $response = new TemplateResponse($this->appName, 'main'); diff --git a/lib/Db/TimelineQuery.php b/lib/Db/TimelineQuery.php new file mode 100644 index 00000000..a20bda8b --- /dev/null +++ b/lib/Db/TimelineQuery.php @@ -0,0 +1,133 @@ +connection = $connection; + } + + /** + * Process the days response + * @param array $days + */ + private function processDays(&$days) { + foreach($days as &$row) { + $row["day_id"] = intval($row["day_id"]); + $row["count"] = intval($row["count"]); + } + return $days; + } + + /** + * Get the days response from the database for the timeline + * @param IConfig $config + * @param string $userId + */ + public function getDays( + IConfig &$config, + string &$user, + ): array { + $sql = 'SELECT day_id, COUNT(file_id) AS count + FROM `*PREFIX*memories` + INNER JOIN `*PREFIX*filecache` + ON `*PREFIX*filecache`.`fileid` = `*PREFIX*memories`.`file_id` + AND `*PREFIX*filecache`.`path` LIKE ? + WHERE user_id=? + GROUP BY day_id + ORDER BY day_id DESC'; + + $path = "files" . Exif::getPhotosPath($config, $user) . "%"; + $rows = $this->connection->executeQuery($sql, [$path, $user], [ + \PDO::PARAM_STR, \PDO::PARAM_STR, + ])->fetchAll(); + return $this->processDays($rows); + } + + /** + * Get the days response from the database for one folder + * @param int $folderId + */ + public function getDaysFolder(int &$folderId) { + $sql = 'SELECT day_id, COUNT(file_id) AS count + FROM `*PREFIX*memories` + INNER JOIN `*PREFIX*filecache` + ON `*PREFIX*filecache`.`fileid` = `*PREFIX*memories`.`file_id` + AND (`*PREFIX*filecache`.`parent`=? OR `*PREFIX*filecache`.`fileid`=?) + GROUP BY day_id + ORDER BY day_id DESC'; + $rows = $this->connection->executeQuery($sql, [$folderId, $folderId], [ + \PDO::PARAM_INT, \PDO::PARAM_INT, + ])->fetchAll(); + return $this->processDays($rows); + } + + /** + * Process the single day response + * @param array $day + */ + private function processDay(&$day) { + foreach($day as &$row) { + $row["file_id"] = intval($row["file_id"]); + $row["is_video"] = intval($row["is_video"]); + if (!$row["is_video"]) { + unset($row["is_video"]); + } + } + return $day; + } + + /** + * Get a day response from the database for the timeline + * @param IConfig $config + * @param string $userId + * @param int $dayId + */ + public function getDay( + IConfig &$config, + string &$user, + int &$dayId, + ): array { + $sql = 'SELECT file_id, *PREFIX*filecache.etag, is_video + FROM *PREFIX*memories + INNER JOIN *PREFIX*filecache + ON *PREFIX*filecache.fileid = *PREFIX*memories.file_id + AND `*PREFIX*filecache`.`path` LIKE ? + WHERE user_id = ? AND day_id = ? + ORDER BY date_taken DESC'; + + $path = "files" . Exif::getPhotosPath($config, $user) . "%"; + $rows = $this->connection->executeQuery($sql, [$path, $user, $dayId], [ + \PDO::PARAM_STR, \PDO::PARAM_STR, \PDO::PARAM_INT, + ])->fetchAll(); + return $this->processDay($rows); + } + + /** + * Get a day response from the database for one folder + * @param int $folderId + * @param int $dayId + */ + public function getDayFolder( + int &$folderId, + int &$dayId, + ): array { + $sql = 'SELECT file_id, *PREFIX*filecache.etag, is_video + FROM `*PREFIX*memories` + INNER JOIN `*PREFIX*filecache` + ON `*PREFIX*filecache`.`fileid` = `*PREFIX*memories`.`file_id` + AND (`*PREFIX*filecache`.`parent`=? OR `*PREFIX*filecache`.`fileid`=?) + WHERE `*PREFIX*memories`.`day_id`=?'; + $rows = $this->connection->executeQuery($sql, [$folderId, $folderId, $dayId], [ + \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, + ])->fetchAll(); + return $this->processDay($rows); + } +} \ No newline at end of file diff --git a/lib/Db/TimelineWrite.php b/lib/Db/TimelineWrite.php new file mode 100644 index 00000000..698ba57e --- /dev/null +++ b/lib/Db/TimelineWrite.php @@ -0,0 +1,102 @@ +connection = $connection; + } + + /** + * Process a file to insert Exif data into the database + * @param File $file + */ + public function processFile(File &$file): void { + // There is no easy way to UPSERT in a standard SQL way, so just + // do multiple calls. The worst that can happen is more updates, + // but that's not a big deal. + // https://stackoverflow.com/questions/15252213/sql-standard-upsert-call + + // Check if we want to process this file + $mime = $file->getMimeType(); + $is_image = in_array($mime, Application::IMAGE_MIMES); + $is_video = in_array($mime, Application::VIDEO_MIMES); + if (!$is_image && !$is_video) { + return; + } + + // Get parameters + $mtime = $file->getMtime(); + $user = $file->getOwner()->getUID(); + $fileId = $file->getId(); + + // Check if need to update + $sql = 'SELECT `mtime` + FROM *PREFIX*memories + WHERE file_id = ? AND user_id = ?'; + $prevRow = $this->connection->executeQuery($sql, [ + $fileId, $user, + ], [ + \PDO::PARAM_INT, \PDO::PARAM_STR, + ])->fetch(); + if ($prevRow && intval($prevRow['mtime']) === $mtime) { + return; + } + + // Get exif data + $exif = []; + try { + $exif = Exif::getExifFromFile($file); + } catch (\Exception) {} + + // Get more parameters + $dateTaken = Exif::getDateTaken($file, $exif); + $dayId = floor($dateTaken / 86400); + $dateTaken = gmdate('Y-m-d H:i:s', $dateTaken); + + if ($prevRow) { + // Update existing row + $sql = 'UPDATE *PREFIX*memories + SET day_id = ?, date_taken = ?, is_video = ?, mtime = ? + WHERE user_id = ? AND file_id = ?'; + $this->connection->executeStatement($sql, [ + $dayId, $dateTaken, $is_video, $mtime, + $user, $fileId, + ], [ + \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_BOOL, \PDO::PARAM_INT, + \PDO::PARAM_STR, \PDO::PARAM_INT, + ]); + } else { + // Create new row + $sql = 'INSERT + INTO *PREFIX*memories (day_id, date_taken, is_video, mtime, user_id, file_id) + VALUES (?, ?, ?, ?, ?, ?)'; + $this->connection->executeStatement($sql, [ + $dayId, $dateTaken, $is_video, $mtime, + $user, $fileId, + ], [ + \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_BOOL, \PDO::PARAM_INT, + \PDO::PARAM_STR, \PDO::PARAM_INT, + ]); + } + } + + /** + * Remove a file from the exif database + * @param File $file + */ + public function deleteFile(File &$file) { + $sql = 'DELETE + FROM *PREFIX*memories + WHERE file_id = ?'; + $this->connection->executeStatement($sql, [$file->getId()], [\PDO::PARAM_INT]); + } +} \ No newline at end of file diff --git a/lib/Db/Util.php b/lib/Db/Util.php deleted file mode 100644 index acda8a4e..00000000 --- a/lib/Db/Util.php +++ /dev/null @@ -1,315 +0,0 @@ -connection = $connection; - } - - /** - * Get the path to the user's configured photos directory. - * @param IConfig $config - * @param string $userId - */ - public static function getPhotosPath(IConfig &$config, string &$userId) { - $p = $config->getUserValue($userId, Application::APPNAME, 'timelinePath', ''); - if (empty($p)) { - return '/Photos/'; - } - return $p; - } - - /** - * Get exif data as a JSON object from a Nextcloud file. - * @param File $file - */ - public static function getExifFromFile(File &$file) { - $handle = $file->fopen('rb'); - if (!$handle) { - throw new \Exception('Could not open file'); - } - - $exif = self::getExifFromStream($handle); - fclose($handle); - return $exif; - } - - /** - * Get exif data as a JSON object from a stream. - * @param resource $handle - */ - public static function getExifFromStream(&$handle) { - // Start exiftool and output to json - $pipes = []; - $proc = proc_open('exiftool -json -', [ - 0 => array('pipe', 'rb'), - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w'), - ], $pipes); - - // Write the file to exiftool's stdin - // Assume exif exists in the first 256 kb of the file - stream_copy_to_stream($handle, $pipes[0], 256 * 1024); - fclose($pipes[0]); - - // Get output from exiftool - $stdout = stream_get_contents($pipes[1]); - - // Clean up - fclose($pipes[1]); - fclose($pipes[2]); - proc_close($proc); - - // Parse the json - $json = json_decode($stdout, true); - if (!$json) { - throw new \Exception('Could not read exif data'); - } - return $json[0]; - } - - /** - * Get the date taken from either the file or exif data if available. - * @param File $file - * @param array $exif - */ - public static function getDateTaken(File &$file, array &$exif) { - $dt = $exif['DateTimeOriginal']; - if (!isset($dt) || empty($dt)) { - $dt = $exif['CreateDate']; - } - - // Check if found something - if (isset($dt) && !empty($dt)) { - $dt = \DateTime::createFromFormat('Y:m:d H:i:s', $dt); - if ($dt && $dt->getTimestamp() > -5364662400) { // 1800 A.D. - return $dt->getTimestamp(); - } - } - - // Fall back to creation time - $dateTaken = $file->getCreationTime(); - - // Fall back to upload time - if ($dateTaken == 0) { - $dateTaken = $file->getUploadTime(); - } - - // Fall back to modification time - if ($dateTaken == 0) { - $dateTaken = $file->getMtime(); - } - return $dateTaken; - } - - /** - * Process a file to insert Exif data into the database - * @param File $file - */ - public function processFile(File &$file): void { - // There is no easy way to UPSERT in a standard SQL way, so just - // do multiple calls. The worst that can happen is more updates, - // but that's not a big deal. - // https://stackoverflow.com/questions/15252213/sql-standard-upsert-call - - // Check if we want to process this file - $mime = $file->getMimeType(); - $is_image = in_array($mime, Application::IMAGE_MIMES); - $is_video = in_array($mime, Application::VIDEO_MIMES); - if (!$is_image && !$is_video) { - return; - } - - // Get parameters - $mtime = $file->getMtime(); - $user = $file->getOwner()->getUID(); - $fileId = $file->getId(); - - // Check if need to update - $sql = 'SELECT `mtime` - FROM *PREFIX*memories - WHERE file_id = ? AND user_id = ?'; - $prevRow = $this->connection->executeQuery($sql, [ - $fileId, $user, - ], [ - \PDO::PARAM_INT, \PDO::PARAM_STR, - ])->fetch(); - if ($prevRow && intval($prevRow['mtime']) === $mtime) { - return; - } - - // Get exif data - $exif = []; - try { - $exif = self::getExifFromFile($file); - } catch (\Exception) {} - - // Get more parameters - $dateTaken = $this->getDateTaken($file, $exif); - $dayId = floor($dateTaken / 86400); - $dateTaken = gmdate('Y-m-d H:i:s', $dateTaken); - - if ($prevRow) { - // Update existing row - $sql = 'UPDATE *PREFIX*memories - SET day_id = ?, date_taken = ?, is_video = ?, mtime = ? - WHERE user_id = ? AND file_id = ?'; - $this->connection->executeStatement($sql, [ - $dayId, $dateTaken, $is_video, $mtime, - $user, $fileId, - ], [ - \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_BOOL, \PDO::PARAM_INT, - \PDO::PARAM_STR, \PDO::PARAM_INT, - ]); - } else { - // Create new row - $sql = 'INSERT - INTO *PREFIX*memories (day_id, date_taken, is_video, mtime, user_id, file_id) - VALUES (?, ?, ?, ?, ?, ?)'; - $this->connection->executeStatement($sql, [ - $dayId, $dateTaken, $is_video, $mtime, - $user, $fileId, - ], [ - \PDO::PARAM_INT, \PDO::PARAM_STR, \PDO::PARAM_BOOL, \PDO::PARAM_INT, - \PDO::PARAM_STR, \PDO::PARAM_INT, - ]); - } - } - - /** - * Remove a file from the exif database - * @param File $file - */ - public function deleteFile(File &$file) { - $sql = 'DELETE - FROM *PREFIX*memories - WHERE file_id = ?'; - $this->connection->executeStatement($sql, [$file->getId()], [\PDO::PARAM_INT]); - } - - /** - * Process the days response - * @param array $days - */ - private function processDays(&$days) { - foreach($days as &$row) { - $row["day_id"] = intval($row["day_id"]); - $row["count"] = intval($row["count"]); - } - return $days; - } - - /** - * Get the days response from the database for the timeline - * @param IConfig $config - * @param string $userId - */ - public function getDays( - IConfig &$config, - string &$user, - ): array { - $sql = 'SELECT day_id, COUNT(file_id) AS count - FROM `*PREFIX*memories` - INNER JOIN `*PREFIX*filecache` - ON `*PREFIX*filecache`.`fileid` = `*PREFIX*memories`.`file_id` - AND `*PREFIX*filecache`.`path` LIKE ? - WHERE user_id=? - GROUP BY day_id - ORDER BY day_id DESC'; - - $path = "files" . self::getPhotosPath($config, $user) . "%"; - $rows = $this->connection->executeQuery($sql, [$path, $user], [ - \PDO::PARAM_STR, \PDO::PARAM_STR, - ])->fetchAll(); - return $this->processDays($rows); - } - - /** - * Get the days response from the database for one folder - * @param int $folderId - */ - public function getDaysFolder(int &$folderId) { - $sql = 'SELECT day_id, COUNT(file_id) AS count - FROM `*PREFIX*memories` - INNER JOIN `*PREFIX*filecache` - ON `*PREFIX*filecache`.`fileid` = `*PREFIX*memories`.`file_id` - AND (`*PREFIX*filecache`.`parent`=? OR `*PREFIX*filecache`.`fileid`=?) - GROUP BY day_id - ORDER BY day_id DESC'; - $rows = $this->connection->executeQuery($sql, [$folderId, $folderId], [ - \PDO::PARAM_INT, \PDO::PARAM_INT, - ])->fetchAll(); - return $this->processDays($rows); - } - - /** - * Process the single day response - * @param array $day - */ - private function processDay(&$day) { - foreach($day as &$row) { - $row["file_id"] = intval($row["file_id"]); - $row["is_video"] = intval($row["is_video"]); - if (!$row["is_video"]) { - unset($row["is_video"]); - } - } - return $day; - } - - /** - * Get a day response from the database for the timeline - * @param IConfig $config - * @param string $userId - * @param int $dayId - */ - public function getDay( - IConfig &$config, - string &$user, - int &$dayId, - ): array { - $sql = 'SELECT file_id, *PREFIX*filecache.etag, is_video - FROM *PREFIX*memories - INNER JOIN *PREFIX*filecache - ON *PREFIX*filecache.fileid = *PREFIX*memories.file_id - AND `*PREFIX*filecache`.`path` LIKE ? - WHERE user_id = ? AND day_id = ? - ORDER BY date_taken DESC'; - - $path = "files" . self::getPhotosPath($config, $user) . "%"; - $rows = $this->connection->executeQuery($sql, [$path, $user, $dayId], [ - \PDO::PARAM_STR, \PDO::PARAM_STR, \PDO::PARAM_INT, - ])->fetchAll(); - return $this->processDay($rows); - } - - /** - * Get a day response from the database for one folder - * @param int $folderId - * @param int $dayId - */ - public function getDayFolder( - int &$folderId, - int &$dayId, - ): array { - $sql = 'SELECT file_id, *PREFIX*filecache.etag, is_video - FROM `*PREFIX*memories` - INNER JOIN `*PREFIX*filecache` - ON `*PREFIX*filecache`.`fileid` = `*PREFIX*memories`.`file_id` - AND (`*PREFIX*filecache`.`parent`=? OR `*PREFIX*filecache`.`fileid`=?) - WHERE `*PREFIX*memories`.`day_id`=?'; - $rows = $this->connection->executeQuery($sql, [$folderId, $folderId, $dayId], [ - \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, - ])->fetchAll(); - return $this->processDay($rows); - } -} \ No newline at end of file diff --git a/lib/Exif.php b/lib/Exif.php new file mode 100644 index 00000000..a5879a1b --- /dev/null +++ b/lib/Exif.php @@ -0,0 +1,106 @@ +getUserValue($userId, Application::APPNAME, 'timelinePath', ''); + if (empty($p)) { + return '/Photos/'; + } + return $p; + } + + /** + * Get exif data as a JSON object from a Nextcloud file. + * @param File $file + */ + public static function getExifFromFile(File &$file) { + $handle = $file->fopen('rb'); + if (!$handle) { + throw new \Exception('Could not open file'); + } + + $exif = self::getExifFromStream($handle); + fclose($handle); + return $exif; + } + + /** + * Get exif data as a JSON object from a stream. + * @param resource $handle + */ + public static function getExifFromStream(&$handle) { + // Start exiftool and output to json + $pipes = []; + $proc = proc_open('exiftool -json -', [ + 0 => array('pipe', 'rb'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w'), + ], $pipes); + + // Write the file to exiftool's stdin + // Assume exif exists in the first 256 kb of the file + stream_copy_to_stream($handle, $pipes[0], 256 * 1024); + fclose($pipes[0]); + + // Get output from exiftool + $stdout = stream_get_contents($pipes[1]); + + // Clean up + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($proc); + + // Parse the json + $json = json_decode($stdout, true); + if (!$json) { + throw new \Exception('Could not read exif data'); + } + return $json[0]; + } + + /** + * Get the date taken from either the file or exif data if available. + * @param File $file + * @param array $exif + */ + public static function getDateTaken(File &$file, array &$exif) { + $dt = $exif['DateTimeOriginal']; + if (!isset($dt) || empty($dt)) { + $dt = $exif['CreateDate']; + } + + // Check if found something + if (isset($dt) && !empty($dt)) { + $dt = \DateTime::createFromFormat('Y:m:d H:i:s', $dt); + if ($dt && $dt->getTimestamp() > -5364662400) { // 1800 A.D. + return $dt->getTimestamp(); + } + } + + // Fall back to creation time + $dateTaken = $file->getCreationTime(); + + // Fall back to upload time + if ($dateTaken == 0) { + $dateTaken = $file->getUploadTime(); + } + + // Fall back to modification time + if ($dateTaken == 0) { + $dateTaken = $file->getMtime(); + } + return $dateTaken; + } +} \ No newline at end of file diff --git a/lib/Listeners/PostDeleteListener.php b/lib/Listeners/PostDeleteListener.php index a7e7ff87..b99c37d9 100644 --- a/lib/Listeners/PostDeleteListener.php +++ b/lib/Listeners/PostDeleteListener.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace OCA\Memories\Listeners; +use \OCA\Memories\Db\TimelineWrite; + use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Events\Node\NodeDeletedEvent; @@ -30,10 +32,10 @@ use OCP\Files\Folder; use OCP\IDBConnection; class PostDeleteListener implements IEventListener { - private \OCA\Memories\Db\Util $util; + private TimelineWrite $util; public function __construct(IDBConnection $connection) { - $this->util = new \OCA\Memories\Db\Util($connection); + $this->util = new TimelineWrite($connection); } public function handle(Event $event): void { diff --git a/lib/Listeners/PostWriteListener.php b/lib/Listeners/PostWriteListener.php index 495df15e..a3f46f41 100644 --- a/lib/Listeners/PostWriteListener.php +++ b/lib/Listeners/PostWriteListener.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace OCA\Memories\Listeners; +use \OCA\Memories\Db\TimelineWrite; + use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Events\Node\NodeRenamedEvent; @@ -33,13 +35,12 @@ use OCP\IDBConnection; use OCP\IUserManager; class PostWriteListener implements IEventListener { - private IUserManager $userManager; - private \OCA\Memories\Db\Util $util; + private TimelineWrite $util; public function __construct(IDBConnection $connection, IUserManager $userManager) { $this->userManager = $userManager; - $this->util = new \OCA\Memories\Db\Util($connection); + $this->util = new TimelineWrite($connection); } public function handle(Event $event): void { diff --git a/lib/Util.php b/lib/Util.php new file mode 100644 index 00000000..542d2c97 --- /dev/null +++ b/lib/Util.php @@ -0,0 +1,22 @@ +getUserValue($userId, Application::APPNAME, 'timelinePath', ''); + if (empty($p)) { + return '/Photos/'; + } + return $p; + } +} \ No newline at end of file