refactor: rework controller (1)
Signed-off-by: Varun Patil <varunpatil@ucla.edu>pull/563/head
parent
bd6aaeee3a
commit
0e385d2283
|
@ -24,8 +24,7 @@ declare(strict_types=1);
|
|||
namespace OCA\Memories\ClustersBackend;
|
||||
|
||||
use OCA\Memories\Db\TimelineQuery;
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\HttpResponseException;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IUserSession;
|
||||
|
||||
|
@ -95,7 +94,7 @@ class AlbumsBackend extends Backend
|
|||
// Get album
|
||||
$album = $this->timelineQuery->getAlbumIfAllowed($this->userId, $name);
|
||||
if (null === $album) {
|
||||
throw new HttpResponseException(Errors::NotFound("album {$name}"));
|
||||
throw Exceptions::NotFound("album {$name}");
|
||||
}
|
||||
|
||||
// Get files
|
||||
|
|
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Exif;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\Files\Folder;
|
||||
|
@ -40,142 +41,130 @@ class ArchiveController extends GenericApiController
|
|||
*/
|
||||
public function archive(string $id): Http\Response
|
||||
{
|
||||
$user = $this->userSession->getUser();
|
||||
if (null === $user) {
|
||||
return Errors::NotLoggedIn();
|
||||
}
|
||||
$uid = $user->getUID();
|
||||
$userFolder = $this->rootFolder->getUserFolder($uid);
|
||||
return Util::guardEx(function () use ($id) {
|
||||
$uid = Util::getUID();
|
||||
$userFolder = Util::getUserFolder();
|
||||
|
||||
// Check for permissions and get numeric Id
|
||||
$file = $userFolder->getById((int) $id);
|
||||
if (0 === \count($file)) {
|
||||
return Errors::NotFound("file id {$id}");
|
||||
}
|
||||
$file = $file[0];
|
||||
|
||||
// Check if user has permissions
|
||||
if (!$file->isUpdateable()) {
|
||||
return Errors::ForbiddenFileUpdate($file->getName());
|
||||
}
|
||||
|
||||
// Create archive folder in the root of the user's configured timeline
|
||||
$configPath = Exif::removeExtraSlash(Exif::getPhotosPath($this->config, $uid));
|
||||
$configPaths = explode(';', $configPath);
|
||||
$timelineFolders = [];
|
||||
$timelinePaths = [];
|
||||
|
||||
// Get all timeline paths
|
||||
foreach ($configPaths as $path) {
|
||||
try {
|
||||
$f = $userFolder->get($path);
|
||||
$timelineFolders[] = $f;
|
||||
$timelinePaths[] = $f->getPath();
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
return new JSONResponse(['message' => 'Timeline folder not found'], Http::STATUS_NOT_FOUND);
|
||||
// Check for permissions and get numeric Id
|
||||
$file = $userFolder->getById((int) $id);
|
||||
if (0 === \count($file)) {
|
||||
throw Exceptions::NotFound("file id {$id}");
|
||||
}
|
||||
}
|
||||
$file = $file[0];
|
||||
|
||||
// Bubble up from file until we reach the correct folder
|
||||
$fileStorageId = $file->getStorage()->getId();
|
||||
$parent = $file->getParent();
|
||||
$isArchived = false;
|
||||
while (true) {
|
||||
if (null === $parent) {
|
||||
throw new \Exception('Cannot get correct parent of file');
|
||||
// Check if user has permissions
|
||||
if (!$file->isUpdateable()) {
|
||||
throw Exceptions::ForbiddenFileUpdate($file->getName());
|
||||
}
|
||||
|
||||
// Hit a timeline folder
|
||||
if (\in_array($parent->getPath(), $timelinePaths, true)) {
|
||||
break;
|
||||
// Create archive folder in the root of the user's configured timeline
|
||||
$configPath = Exif::removeExtraSlash(Exif::getPhotosPath($this->config, $uid));
|
||||
$configPaths = explode(';', $configPath);
|
||||
$timelineFolders = [];
|
||||
$timelinePaths = [];
|
||||
|
||||
// Get all timeline paths
|
||||
foreach ($configPaths as $path) {
|
||||
try {
|
||||
$f = $userFolder->get($path);
|
||||
$timelineFolders[] = $f;
|
||||
$timelinePaths[] = $f->getPath();
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
throw Exceptions::NotFound("timeline folder {$path}");
|
||||
}
|
||||
}
|
||||
|
||||
// Hit the user's root folder
|
||||
if ($parent->getPath() === $userFolder->getPath()) {
|
||||
break;
|
||||
}
|
||||
// Bubble up from file until we reach the correct folder
|
||||
$fileStorageId = $file->getStorage()->getId();
|
||||
$parent = $file->getParent();
|
||||
$isArchived = false;
|
||||
while (true) {
|
||||
if (null === $parent) {
|
||||
throw new \Exception('Cannot get correct parent of file');
|
||||
}
|
||||
|
||||
// Hit a storage root
|
||||
try {
|
||||
if ($parent->getParent()->getStorage()->getId() !== $fileStorageId) {
|
||||
// Hit a timeline folder
|
||||
if (\in_array($parent->getPath(), $timelinePaths, true)) {
|
||||
break;
|
||||
}
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Hit an archive folder root
|
||||
if ($parent->getName() === \OCA\Memories\Util::$ARCHIVE_FOLDER) {
|
||||
$isArchived = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$parent = $parent->getParent();
|
||||
}
|
||||
|
||||
// Get path of current file relative to the parent folder
|
||||
$relativeFilePath = $parent->getRelativePath($file->getPath());
|
||||
|
||||
// Check if we want to archive or unarchive
|
||||
$body = $this->request->getParams();
|
||||
$unarchive = isset($body['archive']) && false === $body['archive'];
|
||||
if ($isArchived && !$unarchive) {
|
||||
return new JSONResponse(['message' => 'File already archived'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
if (!$isArchived && $unarchive) {
|
||||
return new JSONResponse(['message' => 'File not archived'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Final path of the file including the file name
|
||||
$destinationPath = '';
|
||||
|
||||
// Get if the file is already in the archive (relativePath starts with archive)
|
||||
if ($isArchived) {
|
||||
// file already in archive, remove it
|
||||
$destinationPath = $relativeFilePath;
|
||||
$parent = $parent->getParent();
|
||||
} else {
|
||||
// file not in archive, put it in there
|
||||
$af = \OCA\Memories\Util::$ARCHIVE_FOLDER;
|
||||
$destinationPath = Exif::removeExtraSlash($af.$relativeFilePath);
|
||||
}
|
||||
|
||||
// Remove the filename
|
||||
$destinationFolders = array_filter(explode('/', $destinationPath));
|
||||
array_pop($destinationFolders);
|
||||
|
||||
// Create folder tree
|
||||
$folder = $parent;
|
||||
foreach ($destinationFolders as $folderName) {
|
||||
try {
|
||||
$existingFolder = $folder->get($folderName.'/');
|
||||
if (!$existingFolder instanceof Folder) {
|
||||
throw new \OCP\Files\NotFoundException('Not a folder');
|
||||
// Hit the user's root folder
|
||||
if ($parent->getPath() === $userFolder->getPath()) {
|
||||
break;
|
||||
}
|
||||
$folder = $existingFolder;
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
|
||||
// Hit a storage root
|
||||
try {
|
||||
$folder = $folder->newFolder($folderName);
|
||||
} catch (\OCP\Files\NotPermittedException $e) {
|
||||
return new JSONResponse(['message' => 'Failed to create folder'], Http::STATUS_FORBIDDEN);
|
||||
if ($parent->getParent()->getStorage()->getId() !== $fileStorageId) {
|
||||
break;
|
||||
}
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Hit an archive folder root
|
||||
if ($parent->getName() === \OCA\Memories\Util::$ARCHIVE_FOLDER) {
|
||||
$isArchived = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$parent = $parent->getParent();
|
||||
}
|
||||
|
||||
// Get path of current file relative to the parent folder
|
||||
$relativeFilePath = $parent->getRelativePath($file->getPath());
|
||||
|
||||
// Check if we want to archive or unarchive
|
||||
$body = $this->request->getParams();
|
||||
$unarchive = isset($body['archive']) && false === $body['archive'];
|
||||
if ($isArchived && !$unarchive) {
|
||||
throw Exceptions::BadRequest('File already archived');
|
||||
}
|
||||
if (!$isArchived && $unarchive) {
|
||||
throw Exceptions::BadRequest('File not archived');
|
||||
}
|
||||
|
||||
// Final path of the file including the file name
|
||||
$destinationPath = '';
|
||||
|
||||
// Get if the file is already in the archive (relativePath starts with archive)
|
||||
if ($isArchived) {
|
||||
// file already in archive, remove it
|
||||
$destinationPath = $relativeFilePath;
|
||||
$parent = $parent->getParent();
|
||||
} else {
|
||||
// file not in archive, put it in there
|
||||
$af = \OCA\Memories\Util::$ARCHIVE_FOLDER;
|
||||
$destinationPath = Exif::removeExtraSlash($af.$relativeFilePath);
|
||||
}
|
||||
|
||||
// Remove the filename
|
||||
$destinationFolders = array_filter(explode('/', $destinationPath));
|
||||
array_pop($destinationFolders);
|
||||
|
||||
// Create folder tree
|
||||
$folder = $parent;
|
||||
foreach ($destinationFolders as $folderName) {
|
||||
try {
|
||||
$existingFolder = $folder->get($folderName.'/');
|
||||
if (!$existingFolder instanceof Folder) {
|
||||
throw Exceptions::NotFound('Not a folder: '.$existingFolder->getPath());
|
||||
}
|
||||
$folder = $existingFolder;
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
try {
|
||||
$folder = $folder->newFolder($folderName);
|
||||
} catch (\OCP\Files\NotPermittedException $e) {
|
||||
throw Exceptions::ForbiddenFileUpdate($folder->getPath().' [create]');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move file to archive folder
|
||||
try {
|
||||
// Move file to archive folder
|
||||
$file->move($folder->getPath().'/'.$file->getName());
|
||||
} catch (\OCP\Files\NotPermittedException $e) {
|
||||
return new JSONResponse(['message' => 'Failed to move file'], Http::STATUS_FORBIDDEN);
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
return new JSONResponse(['message' => 'File not found'], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
} catch (\OCP\Files\InvalidPathException $e) {
|
||||
return new JSONResponse(['message' => 'Invalid path'], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
} catch (\OCP\Lock\LockedException $e) {
|
||||
return new JSONResponse(['message' => 'File is locked'], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ declare(strict_types=1);
|
|||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\ClustersBackend\Backend;
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\HttpResponseException;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
|
@ -42,7 +42,7 @@ class ClustersController extends GenericApiController
|
|||
*/
|
||||
public function list(string $backend): Http\Response
|
||||
{
|
||||
return $this->guardEx(function () use ($backend) {
|
||||
return Util::guardEx(function () use ($backend) {
|
||||
$this->init($backend);
|
||||
|
||||
$list = $this->backend->getClusters();
|
||||
|
@ -60,7 +60,7 @@ class ClustersController extends GenericApiController
|
|||
*/
|
||||
public function preview(string $backend, string $name): Http\Response
|
||||
{
|
||||
return $this->guardEx(function () use ($backend, $name) {
|
||||
return Util::guardEx(function () use ($backend, $name) {
|
||||
$this->init($backend);
|
||||
|
||||
// Get list of some photos in this cluster
|
||||
|
@ -88,7 +88,7 @@ class ClustersController extends GenericApiController
|
|||
*/
|
||||
public function download(string $backend, string $name): Http\Response
|
||||
{
|
||||
return $this->guardEx(function () use ($backend, $name) {
|
||||
return Util::guardEx(function () use ($backend, $name) {
|
||||
$this->init($backend);
|
||||
|
||||
// Get list of all files in this cluster
|
||||
|
@ -111,7 +111,7 @@ class ClustersController extends GenericApiController
|
|||
{
|
||||
$user = $this->userSession->getUser();
|
||||
if (null === $user) {
|
||||
throw new HttpResponseException(Errors::NotLoggedIn());
|
||||
throw Exceptions::NotLoggedIn();
|
||||
}
|
||||
|
||||
if (\array_key_exists($backend, Backend::$backends)) {
|
||||
|
@ -121,13 +121,13 @@ class ClustersController extends GenericApiController
|
|||
}
|
||||
|
||||
if (!$this->backend->isEnabled()) {
|
||||
throw new HttpResponseException(Errors::NotEnabled($this->backend->appName()));
|
||||
throw Exceptions::NotEnabled($this->backend->appName());
|
||||
}
|
||||
|
||||
if (property_exists($this->backend, 'root')) {
|
||||
$this->backend->root = $this->getRequestRoot();
|
||||
if ($this->backend->root->isEmpty()) {
|
||||
throw new HttpResponseException(Errors::NoRequestRoot());
|
||||
throw Exceptions::NoRequestRoot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ class ClustersController extends GenericApiController
|
|||
$previewManager = \OC::$server->get(\OCP\IPreview::class);
|
||||
|
||||
// Try to get a preview
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->getUID());
|
||||
$userFolder = Util::getUserFolder();
|
||||
foreach ($photos as $img) {
|
||||
// Get the file
|
||||
$files = $userFolder->getById($this->backend->getFileId($img));
|
||||
|
@ -172,6 +172,6 @@ class ClustersController extends GenericApiController
|
|||
}
|
||||
}
|
||||
|
||||
return Errors::NotFound('preview from photos list');
|
||||
throw Exceptions::NotFound('preview from photos list');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ declare(strict_types=1);
|
|||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Db\TimelineRoot;
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
|
||||
|
@ -39,24 +40,10 @@ class DaysController extends GenericApiController
|
|||
*/
|
||||
public function days(): Http\Response
|
||||
{
|
||||
// Get the folder to show
|
||||
try {
|
||||
$uid = $this->getUID();
|
||||
} catch (\Exception $e) {
|
||||
return Errors::NotLoggedIn();
|
||||
}
|
||||
|
||||
// Get the folder to show
|
||||
$root = null;
|
||||
|
||||
try {
|
||||
return Util::guardEx(function () {
|
||||
$uid = $this->getShareToken() ? '' : Util::getUID();
|
||||
$root = $this->getRequestRoot();
|
||||
} catch (\Exception $e) {
|
||||
return Errors::Generic($e);
|
||||
}
|
||||
|
||||
// Run actual query
|
||||
try {
|
||||
$list = $this->timelineQuery->getDays(
|
||||
$root,
|
||||
$uid,
|
||||
|
@ -84,9 +71,7 @@ class DaysController extends GenericApiController
|
|||
}
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
} catch (\Exception $e) {
|
||||
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,41 +81,32 @@ class DaysController extends GenericApiController
|
|||
*/
|
||||
public function day(string $id): Http\Response
|
||||
{
|
||||
// Get user
|
||||
$uid = $this->getUID();
|
||||
return Util::guardEx(function () use ($id) {
|
||||
$uid = $this->getShareToken() ? '' : Util::getUID();
|
||||
|
||||
// Check for wildcard
|
||||
$dayIds = [];
|
||||
if ('*' === $id) {
|
||||
$dayIds = null;
|
||||
} else {
|
||||
// Split at commas and convert all parts to int
|
||||
$dayIds = array_map(function ($part) {
|
||||
return (int) $part;
|
||||
}, explode(',', $id));
|
||||
}
|
||||
// Check for wildcard
|
||||
$dayIds = [];
|
||||
if ('*' === $id) {
|
||||
$dayIds = null;
|
||||
} else {
|
||||
// Split at commas and convert all parts to int
|
||||
$dayIds = array_map(fn ($p) => (int) $p, explode(',', $id));
|
||||
}
|
||||
|
||||
// Check if $dayIds is empty
|
||||
if (null !== $dayIds && 0 === \count($dayIds)) {
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
}
|
||||
// Check if $dayIds is empty
|
||||
if (null !== $dayIds && 0 === \count($dayIds)) {
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
}
|
||||
|
||||
// Get the folder to show
|
||||
$root = null;
|
||||
|
||||
try {
|
||||
// Get the folder to show
|
||||
$root = $this->getRequestRoot();
|
||||
} catch (\Exception $e) {
|
||||
return Errors::Generic($e);
|
||||
}
|
||||
|
||||
// Convert to actual dayIds if month view
|
||||
if ($this->isMonthView()) {
|
||||
$dayIds = $this->timelineQuery->monthIdToDayIds((int) $dayIds[0]);
|
||||
}
|
||||
// Convert to actual dayIds if month view
|
||||
if ($this->isMonthView()) {
|
||||
$dayIds = $this->timelineQuery->monthIdToDayIds((int) $dayIds[0]);
|
||||
}
|
||||
|
||||
// Run actual query
|
||||
try {
|
||||
// Run actual query
|
||||
$list = $this->timelineQuery->getDay(
|
||||
$root,
|
||||
$uid,
|
||||
|
@ -153,9 +129,7 @@ class DaysController extends GenericApiController
|
|||
}
|
||||
|
||||
return new JSONResponse($list, Http::STATUS_OK);
|
||||
} catch (\Exception $e) {
|
||||
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,12 +139,14 @@ class DaysController extends GenericApiController
|
|||
*/
|
||||
public function dayPost(): Http\Response
|
||||
{
|
||||
$id = $this->request->getParam('body_ids');
|
||||
if (null === $id) {
|
||||
return Errors::MissingParameter('body_ids');
|
||||
}
|
||||
return Util::guardEx(function () {
|
||||
$id = $this->request->getParam('body_ids');
|
||||
if (null === $id) {
|
||||
throw Exceptions::MissingParameter('body_ids');
|
||||
}
|
||||
|
||||
return $this->day($id);
|
||||
return $this->day($id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,7 +24,8 @@ declare(strict_types=1);
|
|||
namespace OCA\Memories\Controller;
|
||||
|
||||
use bantu\IniGetWrapper\IniGetWrapper;
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\ISession;
|
||||
|
@ -44,16 +45,18 @@ class DownloadController extends GenericApiController
|
|||
*/
|
||||
public function request(): Http\Response
|
||||
{
|
||||
// Get ids from body
|
||||
$files = $this->request->getParam('files');
|
||||
if (null === $files || !\is_array($files)) {
|
||||
return Errors::MissingParameter('files');
|
||||
}
|
||||
return Util::guardEx(function () {
|
||||
// Get ids from body
|
||||
$files = $this->request->getParam('files');
|
||||
if (null === $files || !\is_array($files)) {
|
||||
throw Exceptions::MissingParameter('files');
|
||||
}
|
||||
|
||||
// Return id
|
||||
$handle = self::createHandle('memories', $files);
|
||||
// Return id
|
||||
$handle = self::createHandle('memories', $files);
|
||||
|
||||
return new JSONResponse(['handle' => $handle]);
|
||||
return new JSONResponse(['handle' => $handle]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,36 +86,36 @@ class DownloadController extends GenericApiController
|
|||
*/
|
||||
public function file(string $handle): Http\Response
|
||||
{
|
||||
// Get ids from request
|
||||
$session = \OC::$server->get(ISession::class);
|
||||
$key = "memories_download_{$handle}";
|
||||
$info = $session->get($key);
|
||||
$session->remove($key);
|
||||
return Util::guardEx(function () use ($handle) {
|
||||
// Get ids from request
|
||||
$session = \OC::$server->get(ISession::class);
|
||||
$key = "memories_download_{$handle}";
|
||||
$info = $session->get($key);
|
||||
$session->remove($key);
|
||||
|
||||
if (null === $info) {
|
||||
return Errors::NotFound('handle');
|
||||
}
|
||||
if (null === $info) {
|
||||
return Exceptions::NotFound('handle');
|
||||
}
|
||||
|
||||
$name = $info[0].'-'.date('YmdHis');
|
||||
$fileIds = $info[1];
|
||||
$name = $info[0].'-'.date('YmdHis');
|
||||
$fileIds = $info[1];
|
||||
|
||||
/** @var int[] $fileIds */
|
||||
$fileIds = array_filter(array_map('intval', $fileIds), function (int $id): bool {
|
||||
return $id > 0;
|
||||
/** @var int[] $fileIds */
|
||||
$fileIds = array_filter(array_map('intval', $fileIds), fn ($id) => $id > 0);
|
||||
|
||||
// Check if we have any valid ids
|
||||
if (0 === \count($fileIds)) {
|
||||
return Exceptions::NotFound('file IDs');
|
||||
}
|
||||
|
||||
// Download single file
|
||||
if (1 === \count($fileIds)) {
|
||||
return $this->one($fileIds[0]);
|
||||
}
|
||||
|
||||
// Download multiple files
|
||||
$this->multiple($name, $fileIds); // exits
|
||||
});
|
||||
|
||||
// Check if we have any valid ids
|
||||
if (0 === \count($fileIds)) {
|
||||
return Errors::NotFound('file IDs');
|
||||
}
|
||||
|
||||
// Download single file
|
||||
if (1 === \count($fileIds)) {
|
||||
return $this->one($fileIds[0]);
|
||||
}
|
||||
|
||||
// Download multiple files
|
||||
$this->multiple($name, $fileIds); // exits
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,47 +127,45 @@ class DownloadController extends GenericApiController
|
|||
*/
|
||||
public function one(int $fileid): Http\Response
|
||||
{
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (null === $file) {
|
||||
return Errors::NotFoundFile($fileid);
|
||||
}
|
||||
return Util::guardEx(function () use ($fileid) {
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (null === $file) {
|
||||
return Exceptions::NotFoundFile($fileid);
|
||||
}
|
||||
|
||||
// Get the owner's root folder
|
||||
$owner = $file->getOwner()->getUID();
|
||||
$userFolder = $this->rootFolder->getUserFolder($owner);
|
||||
// Get the owner's root folder
|
||||
$owner = $file->getOwner()->getUID();
|
||||
$userFolder = Util::getUserFolder($owner);
|
||||
|
||||
// Get the file in the context of the owner
|
||||
$ownerFile = $userFolder->getById($fileid);
|
||||
if (0 === \count($ownerFile)) {
|
||||
// This should never happen, since the file was already found earlier
|
||||
// Except if it was deleted in the meantime ...
|
||||
return new JSONResponse([
|
||||
'message' => 'File not found in owner\'s root folder',
|
||||
], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
// Get the file in the context of the owner
|
||||
$ownerFile = $userFolder->getById($fileid);
|
||||
if (0 === \count($ownerFile)) {
|
||||
// This should never happen, since the file was already found earlier
|
||||
// Except if it was deleted in the meantime ...
|
||||
throw new \Exception('File not found in owner\'s root folder');
|
||||
}
|
||||
|
||||
// Get DAV path of file relative to owner's root folder
|
||||
$path = $userFolder->getRelativePath($ownerFile[0]->getPath());
|
||||
if (null === $path) {
|
||||
return new JSONResponse([
|
||||
'message' => 'File path not found in owner\'s root folder',
|
||||
], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
// Get DAV path of file relative to owner's root folder
|
||||
$path = $userFolder->getRelativePath($ownerFile[0]->getPath());
|
||||
if (null === $path) {
|
||||
throw new \Exception('File path not found in owner\'s root folder');
|
||||
}
|
||||
|
||||
// Setup filesystem for owner
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_Util::setupFS($owner);
|
||||
// Setup filesystem for owner
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_Util::setupFS($owner);
|
||||
|
||||
// HEAD and RANGE support
|
||||
$server_params = ['head' => 'HEAD' === $this->request->getMethod()];
|
||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||
$server_params['range'] = $this->request->getHeader('Range');
|
||||
}
|
||||
// HEAD and RANGE support
|
||||
$server_params = ['head' => 'HEAD' === $this->request->getMethod()];
|
||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||
$server_params['range'] = $this->request->getHeader('Range');
|
||||
}
|
||||
|
||||
// Write file to output and exit
|
||||
\OC_Files::get(\dirname($path), basename($path), $server_params);
|
||||
// Write file to output and exit
|
||||
\OC_Files::get(\dirname($path), basename($path), $server_params);
|
||||
|
||||
exit;
|
||||
exit;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,7 +174,7 @@ class DownloadController extends GenericApiController
|
|||
* @param string $name Name of zip file
|
||||
* @param int[] $fileIds
|
||||
*/
|
||||
private function multiple(string $name, array &$fileIds)
|
||||
private function multiple(string $name, array $fileIds)
|
||||
{
|
||||
// Disable time limit
|
||||
$executionTime = (int) \OC::$server->get(IniGetWrapper::class)->getNumeric('max_execution_time');
|
||||
|
|
|
@ -26,9 +26,7 @@ trait FoldersTrait
|
|||
$folders = $view->getDirectoryContent($folder->getPath(), FileInfo::MIMETYPE_FOLDER, $folder);
|
||||
|
||||
// Sort by name
|
||||
usort($folders, function ($a, $b) {
|
||||
return strnatcmp($a->getName(), $b->getName());
|
||||
});
|
||||
usort($folders, fn ($a, $b) => strnatcmp($a->getName(), $b->getName()));
|
||||
|
||||
// Process to response type
|
||||
return [
|
||||
|
|
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Util;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IConfig;
|
||||
|
||||
|
@ -31,41 +32,12 @@ trait GenericApiControllerUtils
|
|||
protected IAppManager $appManager;
|
||||
protected IConfig $config;
|
||||
|
||||
/** Get logged in user's UID or throw exception */
|
||||
protected function getUID(): string
|
||||
{
|
||||
$user = $this->userSession->getUser();
|
||||
if ($this->getShareToken()) {
|
||||
$user = null;
|
||||
} elseif (null === $user) {
|
||||
throw new \Exception('User not logged in');
|
||||
}
|
||||
|
||||
return $user ? $user->getUID() : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Runa function and catch exceptions to return HTTP response.
|
||||
*
|
||||
* @param mixed $function
|
||||
*/
|
||||
protected function guardEx($function): \OCP\AppFramework\Http\Response
|
||||
{
|
||||
try {
|
||||
return $function();
|
||||
} catch (\OCA\Memories\HttpResponseException $e) {
|
||||
return $e->response;
|
||||
} catch (\Exception $e) {
|
||||
return \OCA\Memories\Errors::Generic($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if albums are enabled for this user.
|
||||
*/
|
||||
protected function albumsIsEnabled(): bool
|
||||
{
|
||||
return \OCA\Memories\Util::albumsIsEnabled($this->appManager);
|
||||
return Util::albumsIsEnabled($this->appManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +45,7 @@ trait GenericApiControllerUtils
|
|||
*/
|
||||
protected function tagsIsEnabled(): bool
|
||||
{
|
||||
return \OCA\Memories\Util::tagsIsEnabled($this->appManager);
|
||||
return Util::tagsIsEnabled($this->appManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,13 +53,13 @@ trait GenericApiControllerUtils
|
|||
*/
|
||||
protected function recognizeIsEnabled(): bool
|
||||
{
|
||||
return \OCA\Memories\Util::recognizeIsEnabled($this->appManager);
|
||||
return Util::recognizeIsEnabled($this->appManager);
|
||||
}
|
||||
|
||||
// Check if facerecognition is installed and enabled for this user.
|
||||
protected function facerecognitionIsInstalled(): bool
|
||||
{
|
||||
return \OCA\Memories\Util::facerecognitionIsInstalled($this->appManager);
|
||||
return Util::facerecognitionIsInstalled($this->appManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,7 +67,7 @@ trait GenericApiControllerUtils
|
|||
*/
|
||||
protected function facerecognitionIsEnabled(): bool
|
||||
{
|
||||
return \OCA\Memories\Util::facerecognitionIsEnabled($this->config, $this->getUID());
|
||||
return Util::facerecognitionIsEnabled($this->config, Util::getUID());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,6 +75,6 @@ trait GenericApiControllerUtils
|
|||
*/
|
||||
protected function placesIsEnabled(): bool
|
||||
{
|
||||
return \OCA\Memories\Util::placesGISType() > 0;
|
||||
return Util::placesGISType() > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,9 @@ declare(strict_types=1);
|
|||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\AppInfo\Application;
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Exif;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
|
@ -49,16 +50,16 @@ class ImageController extends GenericApiController
|
|||
bool $a = false,
|
||||
string $mode = 'fill'
|
||||
) {
|
||||
if (-1 === $id || 0 === $x || 0 === $y) {
|
||||
return Errors::MissingParameter('id, x, y');
|
||||
}
|
||||
return Util::guardEx(function () use ($id, $x, $y, $a, $mode) {
|
||||
if (-1 === $id || 0 === $x || 0 === $y) {
|
||||
throw Exceptions::MissingParameter('id, x, y');
|
||||
}
|
||||
|
||||
$file = $this->getUserFile($id);
|
||||
if (!$file) {
|
||||
return Errors::NotFoundFile($id);
|
||||
}
|
||||
$file = $this->getUserFile($id);
|
||||
if (!$file) {
|
||||
throw Exceptions::NotFoundFile($id);
|
||||
}
|
||||
|
||||
try {
|
||||
$preview = \OC::$server->get(\OCP\IPreview::class)->getPreview($file, $x, $y, !$a, $mode);
|
||||
$response = new FileDisplayResponse($preview, Http::STATUS_OK, [
|
||||
'Content-Type' => $preview->getMimeType(),
|
||||
|
@ -66,11 +67,7 @@ class ImageController extends GenericApiController
|
|||
$response->cacheFor(3600 * 24, false, true);
|
||||
|
||||
return $response;
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
return Errors::NotFound('preview');
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return Errors::Generic($e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,87 +81,85 @@ class ImageController extends GenericApiController
|
|||
*/
|
||||
public function multipreview()
|
||||
{
|
||||
// read body to array
|
||||
try {
|
||||
return Util::guardEx(function () {
|
||||
// read body to array
|
||||
$body = file_get_contents('php://input');
|
||||
$files = json_decode($body, true);
|
||||
} catch (\Exception $e) {
|
||||
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/** @var \OCP\IPreview $previewManager */
|
||||
$previewManager = \OC::$server->get(\OCP\IPreview::class);
|
||||
/** @var \OCP\IPreview $previewManager */
|
||||
$previewManager = \OC::$server->get(\OCP\IPreview::class);
|
||||
|
||||
// For checking max previews
|
||||
$previewRoot = new \OC\Preview\Storage\Root(
|
||||
\OC::$server->get(IRootFolder::class),
|
||||
\OC::$server->get(\OC\SystemConfig::class),
|
||||
);
|
||||
// For checking max previews
|
||||
$previewRoot = new \OC\Preview\Storage\Root(
|
||||
\OC::$server->get(IRootFolder::class),
|
||||
\OC::$server->get(\OC\SystemConfig::class),
|
||||
);
|
||||
|
||||
// stream the response
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + 7 * 3600 * 24));
|
||||
header('Cache-Control: max-age='. 7 * 3600 * 24 .', private');
|
||||
// stream the response
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + 7 * 3600 * 24));
|
||||
header('Cache-Control: max-age='. 7 * 3600 * 24 .', private');
|
||||
|
||||
foreach ($files as $bodyFile) {
|
||||
if (!isset($bodyFile['reqid']) || !isset($bodyFile['fileid']) || !isset($bodyFile['x']) || !isset($bodyFile['y']) || !isset($bodyFile['a'])) {
|
||||
continue;
|
||||
}
|
||||
$reqid = $bodyFile['reqid'];
|
||||
$fileid = (int) $bodyFile['fileid'];
|
||||
$x = (int) $bodyFile['x'];
|
||||
$y = (int) $bodyFile['y'];
|
||||
$a = '1' === $bodyFile['a'];
|
||||
if ($fileid <= 0 || $x <= 0 || $y <= 0) {
|
||||
continue;
|
||||
}
|
||||
foreach ($files as $bodyFile) {
|
||||
if (!isset($bodyFile['reqid']) || !isset($bodyFile['fileid']) || !isset($bodyFile['x']) || !isset($bodyFile['y']) || !isset($bodyFile['a'])) {
|
||||
continue;
|
||||
}
|
||||
$reqid = $bodyFile['reqid'];
|
||||
$fileid = (int) $bodyFile['fileid'];
|
||||
$x = (int) $bodyFile['x'];
|
||||
$y = (int) $bodyFile['y'];
|
||||
$a = '1' === $bodyFile['a'];
|
||||
if ($fileid <= 0 || $x <= 0 || $y <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (!$file) {
|
||||
continue;
|
||||
}
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (!$file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Make sure max preview exists
|
||||
$fileId = (string) $file->getId();
|
||||
$folder = $previewRoot->getFolder($fileId);
|
||||
$hasMax = false;
|
||||
foreach ($folder->getDirectoryListing() as $preview) {
|
||||
$name = $preview->getName();
|
||||
if (str_contains($name, '-max')) {
|
||||
$hasMax = true;
|
||||
try {
|
||||
// Make sure max preview exists
|
||||
$fileId = (string) $file->getId();
|
||||
$folder = $previewRoot->getFolder($fileId);
|
||||
$hasMax = false;
|
||||
foreach ($folder->getDirectoryListing() as $preview) {
|
||||
$name = $preview->getName();
|
||||
if (str_contains($name, '-max')) {
|
||||
$hasMax = true;
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$hasMax) {
|
||||
if (!$hasMax) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add this preview to the response
|
||||
$preview = $previewManager->getPreview($file, $x, $y, !$a, \OCP\IPreview::MODE_FILL);
|
||||
$content = $preview->getContent();
|
||||
if (empty($content)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
echo json_encode([
|
||||
'reqid' => $reqid,
|
||||
'Content-Length' => \strlen($content),
|
||||
'Content-Type' => $preview->getMimeType(),
|
||||
]);
|
||||
echo "\n";
|
||||
echo $content;
|
||||
ob_end_flush();
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
continue;
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add this preview to the response
|
||||
$preview = $previewManager->getPreview($file, $x, $y, !$a, \OCP\IPreview::MODE_FILL);
|
||||
$content = $preview->getContent();
|
||||
if (empty($content)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
echo json_encode([
|
||||
'reqid' => $reqid,
|
||||
'Content-Length' => \strlen($content),
|
||||
'Content-Type' => $preview->getMimeType(),
|
||||
]);
|
||||
echo "\n";
|
||||
echo $content;
|
||||
ob_end_flush();
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
continue;
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
exit;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -182,36 +177,38 @@ class ImageController extends GenericApiController
|
|||
bool $current = false,
|
||||
bool $tags = false
|
||||
): Http\Response {
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
return Errors::NotFoundFile($id);
|
||||
}
|
||||
|
||||
// Get the image info
|
||||
$info = $this->timelineQuery->getInfoById($file->getId(), $basic);
|
||||
|
||||
// Allow these ony for logged in users
|
||||
if (null !== $this->userSession->getUser()) {
|
||||
// Get list of tags for this file
|
||||
if ($tags) {
|
||||
$info['tags'] = $this->getTags($file->getId());
|
||||
return Util::guardEx(function () use ($id, $basic, $current, $tags) {
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
throw Exceptions::NotFoundFile($id);
|
||||
}
|
||||
|
||||
// Get latest exif data if requested
|
||||
if ($current) {
|
||||
$info['current'] = Exif::getExifFromFile($file);
|
||||
// Get the image info
|
||||
$info = $this->timelineQuery->getInfoById($file->getId(), $basic);
|
||||
|
||||
// Allow these ony for logged in users
|
||||
if (null !== $this->userSession->getUser()) {
|
||||
// Get list of tags for this file
|
||||
if ($tags) {
|
||||
$info['tags'] = $this->getTags($file->getId());
|
||||
}
|
||||
|
||||
// Get latest exif data if requested
|
||||
if ($current) {
|
||||
$info['current'] = Exif::getExifFromFile($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inject permissions and convert to string
|
||||
$info['permissions'] = \OCA\Memories\Util::permissionsToStr($file->getPermissions());
|
||||
// Inject permissions and convert to string
|
||||
$info['permissions'] = \OCA\Memories\Util::permissionsToStr($file->getPermissions());
|
||||
|
||||
// Inject other file parameters that are cheap to get now
|
||||
$info['mimetype'] = $file->getMimeType();
|
||||
$info['size'] = $file->getSize();
|
||||
$info['basename'] = $file->getName();
|
||||
// Inject other file parameters that are cheap to get now
|
||||
$info['mimetype'] = $file->getMimeType();
|
||||
$info['size'] = $file->getSize();
|
||||
$info['basename'] = $file->getName();
|
||||
|
||||
return new JSONResponse($info, Http::STATUS_OK);
|
||||
return new JSONResponse($info, Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,36 +221,30 @@ class ImageController extends GenericApiController
|
|||
*/
|
||||
public function setExif(string $id, array $raw): Http\Response
|
||||
{
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
return Errors::NotFoundFile($id);
|
||||
}
|
||||
return Util::guardEx(function () use ($id, $raw) {
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
throw Exceptions::NotFoundFile($id);
|
||||
}
|
||||
|
||||
// Check if user has permissions
|
||||
if (!$file->isUpdateable()) {
|
||||
return Errors::ForbiddenFileUpdate($file->getName());
|
||||
}
|
||||
// Check if user has permissions
|
||||
if (!$file->isUpdateable() || Util::isEncryptionEnabled()) {
|
||||
throw Exceptions::ForbiddenFileUpdate($file->getName());
|
||||
}
|
||||
|
||||
// Check for end-to-end encryption
|
||||
if (\OCA\Memories\Util::isEncryptionEnabled()) {
|
||||
return new JSONResponse(['message' => 'Cannot change encrypted file'], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
// Check if allowed to edit file
|
||||
$mime = $file->getMimeType();
|
||||
if (!\in_array($mime, Exif::allowedEditMimetypes(), true)) {
|
||||
$name = $file->getName();
|
||||
|
||||
// Check if allowed to edit file
|
||||
$mime = $file->getMimeType();
|
||||
if (!\in_array($mime, Exif::allowedEditMimetypes(), true)) {
|
||||
$name = $file->getName();
|
||||
throw Exceptions::Forbidden("Cannot edit file {$name} (blacklisted type {$mime})");
|
||||
}
|
||||
|
||||
return new JSONResponse(['message' => "Cannot edit file {$name} (blacklisted type {$mime})"], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
try {
|
||||
// Set the exif data
|
||||
Exif::setFileExif($file, $raw);
|
||||
} catch (\Exception $e) {
|
||||
return Errors::Generic($e);
|
||||
}
|
||||
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,35 +260,37 @@ class ImageController extends GenericApiController
|
|||
*/
|
||||
public function decodable(string $id): Http\Response
|
||||
{
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
return Errors::NotFoundFile($id);
|
||||
}
|
||||
return Util::guardEx(function () use ($id) {
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
throw Exceptions::NotFoundFile($id);
|
||||
}
|
||||
|
||||
// Check if valid image
|
||||
$mimetype = $file->getMimeType();
|
||||
if (!\in_array($mimetype, Application::IMAGE_MIMES, true)) {
|
||||
return Errors::ForbiddenFileUpdate($file->getName());
|
||||
}
|
||||
// Check if valid image
|
||||
$mimetype = $file->getMimeType();
|
||||
if (!\in_array($mimetype, Application::IMAGE_MIMES, true)) {
|
||||
throw Exceptions::Forbidden('Not an image');
|
||||
}
|
||||
|
||||
/** @var string Blob of image */
|
||||
$blob = $file->getContent();
|
||||
/** @var string Blob of image */
|
||||
$blob = $file->getContent();
|
||||
|
||||
// Convert image to JPEG if required
|
||||
if (!\in_array($mimetype, ['image/png', 'image/webp', 'image/jpeg', 'image/gif'], true)) {
|
||||
$image = new \Imagick();
|
||||
$image->readImageBlob($blob);
|
||||
$image->setImageFormat('jpeg');
|
||||
$image->setImageCompressionQuality(95);
|
||||
$blob = $image->getImageBlob();
|
||||
$mimetype = $image->getImageMimeType();
|
||||
}
|
||||
// Convert image to JPEG if required
|
||||
if (!\in_array($mimetype, ['image/png', 'image/webp', 'image/jpeg', 'image/gif'], true)) {
|
||||
$image = new \Imagick();
|
||||
$image->readImageBlob($blob);
|
||||
$image->setImageFormat('jpeg');
|
||||
$image->setImageCompressionQuality(95);
|
||||
$blob = $image->getImageBlob();
|
||||
$mimetype = $image->getImageMimeType();
|
||||
}
|
||||
|
||||
// Return the image
|
||||
$response = new Http\DataDisplayResponse($blob, Http::STATUS_OK, ['Content-Type' => $mimetype]);
|
||||
$response->cacheFor(3600 * 24, false, false);
|
||||
// Return the image
|
||||
$response = new Http\DataDisplayResponse($blob, Http::STATUS_OK, ['Content-Type' => $mimetype]);
|
||||
$response->cacheFor(3600 * 24, false, false);
|
||||
|
||||
return $response;
|
||||
return $response;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,7 +299,7 @@ class ImageController extends GenericApiController
|
|||
private function getTags(int $fileId): array
|
||||
{
|
||||
// Make sure tags are enabled
|
||||
if (!\OCA\Memories\Util::tagsIsEnabled($this->appManager)) {
|
||||
if (!Util::tagsIsEnabled($this->appManager)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -320,10 +313,9 @@ class ImageController extends GenericApiController
|
|||
/** @var \OCP\SystemTag\ISystemTag[] */
|
||||
$tags = $tagManager->getTagsByIds($tagIds);
|
||||
|
||||
return array_map(function ($tag) {
|
||||
return $tag->getName();
|
||||
}, array_filter($tags, function ($tag) {
|
||||
return $tag->isUserVisible();
|
||||
}));
|
||||
$visible = array_filter($tags, fn ($t) => $t->isUserVisible());
|
||||
|
||||
// Get the tag names
|
||||
return array_map(fn ($t) => $t->getName(), $visible);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
|
||||
|
@ -34,35 +35,27 @@ class MapController extends GenericApiController
|
|||
*/
|
||||
public function clusters(): Http\Response
|
||||
{
|
||||
// Get the folder to show
|
||||
$root = null;
|
||||
|
||||
try {
|
||||
return Util::guardEx(function () {
|
||||
// Get the folder to show
|
||||
$root = $this->getRequestRoot();
|
||||
} catch (\Exception $e) {
|
||||
return Errors::NoRequestRoot();
|
||||
}
|
||||
|
||||
// Make sure we have bounds and zoom level
|
||||
// Zoom level is used to determine the grid length
|
||||
$bounds = $this->request->getParam('bounds');
|
||||
$zoomLevel = $this->request->getParam('zoom');
|
||||
if (!$bounds || !$zoomLevel || !is_numeric($zoomLevel)) {
|
||||
return Errors::MissingParameter('bounds or zoom');
|
||||
}
|
||||
// Make sure we have bounds and zoom level
|
||||
// Zoom level is used to determine the grid length
|
||||
$bounds = $this->request->getParam('bounds');
|
||||
$zoomLevel = $this->request->getParam('zoom');
|
||||
if (!$bounds || !$zoomLevel || !is_numeric($zoomLevel)) {
|
||||
throw Exceptions::MissingParameter('bounds or zoom');
|
||||
}
|
||||
|
||||
// A tweakable parameter to determine the number of boxes in the map
|
||||
// Note: these parameters need to be changed in MapSplitMatter.vue as well
|
||||
$clusterDensity = 1;
|
||||
$gridLen = 180.0 / (2 ** $zoomLevel * $clusterDensity);
|
||||
// A tweakable parameter to determine the number of boxes in the map
|
||||
// Note: these parameters need to be changed in MapSplitMatter.vue as well
|
||||
$clusterDensity = 1;
|
||||
$gridLen = 180.0 / (2 ** $zoomLevel * $clusterDensity);
|
||||
|
||||
try {
|
||||
$clusters = $this->timelineQuery->getMapClusters($gridLen, $bounds, $root);
|
||||
|
||||
// Get previews for each cluster
|
||||
$clusterIds = array_map(function ($cluster) {
|
||||
return (int) $cluster['id'];
|
||||
}, $clusters);
|
||||
$clusterIds = array_map(fn ($cluster) => (int) $cluster['id'], $clusters);
|
||||
$previews = $this->timelineQuery->getMapClusterPreviews($clusterIds, $root);
|
||||
|
||||
// Merge the responses
|
||||
|
@ -75,8 +68,6 @@ class MapController extends GenericApiController
|
|||
}
|
||||
|
||||
return new JSONResponse($clusters);
|
||||
} catch (\Exception $e) {
|
||||
return Errors::Generic($e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ declare(strict_types=1);
|
|||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\AppInfo\Application;
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Http\StreamResponse;
|
||||
|
@ -43,20 +44,18 @@ class OtherController extends GenericApiController
|
|||
*/
|
||||
public function setUserConfig(string $key, string $value): Http\Response
|
||||
{
|
||||
$user = $this->userSession->getUser();
|
||||
if (null === $user) {
|
||||
return Errors::NotLoggedIn();
|
||||
}
|
||||
return Util::guardEx(function () use ($key, $value) {
|
||||
$uid = Util::getUID();
|
||||
|
||||
// Make sure not running in read-only mode
|
||||
if ($this->config->getSystemValue('memories.readonly', false)) {
|
||||
return new JSONResponse(['message' => 'Cannot change settings in readonly mode'], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
// Make sure not running in read-only mode
|
||||
if ($this->config->getSystemValue('memories.readonly', false)) {
|
||||
throw Exceptions::Forbidden('Cannot change settings in readonly mode');
|
||||
}
|
||||
|
||||
$userId = $user->getUID();
|
||||
$this->config->setUserValue($userId, Application::APPNAME, $key, $value);
|
||||
$this->config->setUserValue($uid, Application::APPNAME, $key, $value);
|
||||
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,7 +23,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
|
||||
|
@ -39,22 +40,21 @@ class ShareController extends GenericApiController
|
|||
*/
|
||||
public function links($id, $path): Http\Response
|
||||
{
|
||||
$file = $this->getNodeByIdOrPath($id, $path);
|
||||
if (!$file) {
|
||||
return Errors::Forbidden('file');
|
||||
}
|
||||
return Util::guardEx(function () use ($id, $path) {
|
||||
$file = $this->getNodeByIdOrPath($id, $path);
|
||||
|
||||
/** @var \OCP\Share\IManager $shareManager */
|
||||
$shareManager = \OC::$server->get(\OCP\Share\IManager::class);
|
||||
/** @var \OCP\Share\IManager $shareManager */
|
||||
$shareManager = \OC::$server->get(\OCP\Share\IManager::class);
|
||||
|
||||
$shares = $shareManager->getSharesBy($this->getUID(), \OCP\Share\IShare::TYPE_LINK, $file, true, 50, 0);
|
||||
if (empty($shares)) {
|
||||
return Errors::NotFound('external links');
|
||||
}
|
||||
$shares = $shareManager->getSharesBy(Util::getUID(), \OCP\Share\IShare::TYPE_LINK, $file, true, 50, 0);
|
||||
if (empty($shares)) {
|
||||
throw Exceptions::NotFound('external links');
|
||||
}
|
||||
|
||||
$links = array_map([$this, 'makeShareResponse'], $shares);
|
||||
$links = array_map(fn ($s) => $this->makeShareResponse($s), $shares);
|
||||
|
||||
return new JSONResponse($links, Http::STATUS_OK);
|
||||
return new JSONResponse($links, Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,24 +67,23 @@ class ShareController extends GenericApiController
|
|||
*/
|
||||
public function createNode($id, $path): Http\Response
|
||||
{
|
||||
$file = $this->getNodeByIdOrPath($id, $path);
|
||||
if (!$file) {
|
||||
return Errors::Forbidden('You are not allowed to share this file');
|
||||
}
|
||||
return Util::guardEx(function () use ($id, $path) {
|
||||
$file = $this->getNodeByIdOrPath($id, $path);
|
||||
|
||||
/** @var \OCP\Share\IManager $shareManager */
|
||||
$shareManager = \OC::$server->get(\OCP\Share\IManager::class);
|
||||
/** @var \OCP\Share\IManager $shareManager */
|
||||
$shareManager = \OC::$server->get(\OCP\Share\IManager::class);
|
||||
|
||||
/** @var \OCP\Share\IShare $share */
|
||||
$share = $shareManager->newShare();
|
||||
$share->setNode($file);
|
||||
$share->setShareType(\OCP\Share\IShare::TYPE_LINK);
|
||||
$share->setSharedBy($this->userSession->getUser()->getUID());
|
||||
$share->setPermissions(\OCP\Constants::PERMISSION_READ);
|
||||
/** @var \OCP\Share\IShare $share */
|
||||
$share = $shareManager->newShare();
|
||||
$share->setNode($file);
|
||||
$share->setShareType(\OCP\Share\IShare::TYPE_LINK);
|
||||
$share->setSharedBy($this->userSession->getUser()->getUID());
|
||||
$share->setPermissions(\OCP\Constants::PERMISSION_READ);
|
||||
|
||||
$share = $shareManager->createShare($share);
|
||||
$share = $shareManager->createShare($share);
|
||||
|
||||
return new JSONResponse($this->makeShareResponse($share), Http::STATUS_OK);
|
||||
return new JSONResponse($this->makeShareResponse($share), Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,46 +93,41 @@ class ShareController extends GenericApiController
|
|||
*/
|
||||
public function deleteShare(string $id): Http\Response
|
||||
{
|
||||
$uid = $this->getUID();
|
||||
if (!$uid) {
|
||||
return Errors::NotLoggedIn();
|
||||
}
|
||||
return Util::guardEx(function () use ($id) {
|
||||
$uid = Util::getUID();
|
||||
|
||||
/** @var \OCP\Share\IManager $shareManager */
|
||||
$shareManager = \OC::$server->get(\OCP\Share\IManager::class);
|
||||
/** @var \OCP\Share\IManager $shareManager */
|
||||
$shareManager = \OC::$server->get(\OCP\Share\IManager::class);
|
||||
|
||||
$share = $shareManager->getShareById($id);
|
||||
$share = $shareManager->getShareById($id);
|
||||
|
||||
if ($share->getSharedBy() !== $uid) {
|
||||
return Errors::Forbidden('You are not the owner of this share');
|
||||
}
|
||||
if ($share->getSharedBy() !== $uid) {
|
||||
throw Exceptions::Forbidden('You are not the owner of this share');
|
||||
}
|
||||
|
||||
$shareManager->deleteShare($share);
|
||||
$shareManager->deleteShare($share);
|
||||
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
|
||||
private function getNodeByIdOrPath($id, $path)
|
||||
{
|
||||
$uid = $this->getUID();
|
||||
if (!$uid) {
|
||||
return null;
|
||||
}
|
||||
$uid = Util::getUID();
|
||||
|
||||
$file = null;
|
||||
if ($id) {
|
||||
$file = $this->getUserFile($id);
|
||||
} elseif ($path) {
|
||||
try {
|
||||
$userFolder = $this->rootFolder->getUserFolder($uid);
|
||||
$file = $userFolder->get($path);
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
return null;
|
||||
try {
|
||||
$file = null;
|
||||
if ($id) {
|
||||
$file = $this->getUserFile($id);
|
||||
} elseif ($path) {
|
||||
$file = Util::getUserFolder($uid)->get($path);
|
||||
}
|
||||
} catch (\OCP\Files\NotFoundException $e) {
|
||||
throw Exceptions::NotFoundFile($path ?? $id);
|
||||
}
|
||||
|
||||
if (!$file || !$file->isShareable()) {
|
||||
return null;
|
||||
throw Exceptions::Forbidden('File not sharable');
|
||||
}
|
||||
|
||||
return $file;
|
||||
|
|
|
@ -23,7 +23,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
|
||||
|
@ -36,29 +37,31 @@ class TagsController extends GenericApiController
|
|||
*/
|
||||
public function set(int $id, array $add, array $remove): Http\Response
|
||||
{
|
||||
// Check tags enabled for this user
|
||||
if (!$this->tagsIsEnabled()) {
|
||||
return Errors::NotEnabled('Tags');
|
||||
}
|
||||
return Util::guardEx(function () use ($id, $add, $remove) {
|
||||
// Check tags enabled for this user
|
||||
if (!$this->tagsIsEnabled()) {
|
||||
throw Exceptions::NotEnabled('Tags');
|
||||
}
|
||||
|
||||
// Check the user is allowed to edit the file
|
||||
$file = $this->getUserFile($id);
|
||||
if (null === $file) {
|
||||
return Errors::NotFoundFile($id);
|
||||
}
|
||||
// Check the user is allowed to edit the file
|
||||
$file = $this->getUserFile($id);
|
||||
if (null === $file) {
|
||||
throw Exceptions::NotFoundFile($id);
|
||||
}
|
||||
|
||||
// Check the user is allowed to edit the file
|
||||
if (!$file->isUpdateable() || !($file->getPermissions() & \OCP\Constants::PERMISSION_UPDATE)) {
|
||||
return Errors::ForbiddenFileUpdate($file->getName());
|
||||
}
|
||||
// Check the user is allowed to edit the file
|
||||
if (!$file->isUpdateable() || !($file->getPermissions() & \OCP\Constants::PERMISSION_UPDATE)) {
|
||||
throw Exceptions::ForbiddenFileUpdate($file->getName());
|
||||
}
|
||||
|
||||
// Get mapper from tags to objects
|
||||
$om = \OC::$server->get(\OCP\SystemTag\ISystemTagObjectMapper::class);
|
||||
// Get mapper from tags to objects
|
||||
$om = \OC::$server->get(\OCP\SystemTag\ISystemTagObjectMapper::class);
|
||||
|
||||
// Add and remove tags
|
||||
$om->assignTags((string) $id, 'files', $add);
|
||||
$om->unassignTags((string) $id, 'files', $remove);
|
||||
// Add and remove tags
|
||||
$om->assignTags((string) $id, 'files', $add);
|
||||
$om->unassignTags((string) $id, 'files', $remove);
|
||||
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Controller;
|
||||
|
||||
use OCA\Memories\Errors;
|
||||
use OCA\Memories\Exceptions;
|
||||
use OCA\Memories\Exif;
|
||||
use OCA\Memories\Util;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataDisplayResponse;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
|
@ -43,58 +44,60 @@ class VideoController extends GenericApiController
|
|||
*/
|
||||
public function transcode(string $client, int $fileid, string $profile): Http\Response
|
||||
{
|
||||
// Make sure not running in read-only mode
|
||||
if (false !== $this->config->getSystemValue('memories.vod.disable', 'UNSET')) {
|
||||
return Errors::Forbidden('Transcoding disabled');
|
||||
}
|
||||
|
||||
// Check client identifier is 8 characters or more
|
||||
if (\strlen($client) < 8) {
|
||||
return Errors::MissingParameter('client (invalid)');
|
||||
}
|
||||
|
||||
// Get file
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (!$file || !$file->isReadable()) {
|
||||
return Errors::NotFoundFile($fileid);
|
||||
}
|
||||
|
||||
// Local files only for now
|
||||
if (!$file->getStorage()->isLocal()) {
|
||||
return Errors::Forbidden('External storage not supported');
|
||||
}
|
||||
|
||||
// Get file path
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
if (!$path || !file_exists($path)) {
|
||||
return Errors::NotFound('local file path');
|
||||
}
|
||||
|
||||
// Check if file starts with temp dir
|
||||
$tmpDir = sys_get_temp_dir();
|
||||
if (0 === strpos($path, $tmpDir)) {
|
||||
return Errors::Forbidden('files in temp directory not supported');
|
||||
}
|
||||
|
||||
// Request and check data was received
|
||||
try {
|
||||
$status = $this->getUpstream($client, $path, $profile);
|
||||
if (409 === $status || -1 === $status) {
|
||||
// Just a conflict (transcoding process changed)
|
||||
return new JSONResponse(['message' => 'Conflict'], Http::STATUS_CONFLICT);
|
||||
return Util::guardEx(function () use ($client, $fileid, $profile) {
|
||||
// Make sure not running in read-only mode
|
||||
if (false !== $this->config->getSystemValue('memories.vod.disable', 'UNSET')) {
|
||||
throw Exceptions::Forbidden('Transcoding disabled');
|
||||
}
|
||||
if (200 !== $status) {
|
||||
throw new \Exception("Transcoder returned {$status}");
|
||||
|
||||
// Check client identifier is 8 characters or more
|
||||
if (\strlen($client) < 8) {
|
||||
throw Exceptions::MissingParameter('client (invalid)');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$msg = 'Transcode failed: '.$e->getMessage();
|
||||
$this->logger->error($msg, ['app' => 'memories']);
|
||||
|
||||
return Errors::Generic($e);
|
||||
}
|
||||
// Get file
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (!$file || !$file->isReadable()) {
|
||||
throw Exceptions::NotFoundFile($fileid);
|
||||
}
|
||||
|
||||
// The response was already streamed, so we have nothing to do here
|
||||
exit;
|
||||
// Local files only for now
|
||||
if (!$file->getStorage()->isLocal()) {
|
||||
throw Exceptions::Forbidden('External storage not supported');
|
||||
}
|
||||
|
||||
// Get file path
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
if (!$path || !file_exists($path)) {
|
||||
throw Exceptions::NotFound('local file path');
|
||||
}
|
||||
|
||||
// Check if file starts with temp dir
|
||||
$tmpDir = sys_get_temp_dir();
|
||||
if (0 === strpos($path, $tmpDir)) {
|
||||
throw Exceptions::Forbidden('files in temp directory not supported');
|
||||
}
|
||||
|
||||
// Request and check data was received
|
||||
try {
|
||||
$status = $this->getUpstream($client, $path, $profile);
|
||||
if (409 === $status || -1 === $status) {
|
||||
// Just a conflict (transcoding process changed)
|
||||
return new JSONResponse(['message' => 'Conflict'], Http::STATUS_CONFLICT);
|
||||
}
|
||||
if (200 !== $status) {
|
||||
throw new \Exception("Transcoder returned {$status}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$msg = 'Transcode failed: '.$e->getMessage();
|
||||
$this->logger->error($msg, ['app' => 'memories']);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// The response was already streamed, so we have nothing to do here
|
||||
exit;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,111 +115,113 @@ class VideoController extends GenericApiController
|
|||
string $format = '',
|
||||
string $transcode = ''
|
||||
) {
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (null === $file) {
|
||||
return Errors::NotFoundFile($fileid);
|
||||
}
|
||||
|
||||
// Check file liveid
|
||||
if (!$liveid) {
|
||||
return Errors::MissingParameter('liveid');
|
||||
}
|
||||
|
||||
// Response data
|
||||
$name = '';
|
||||
$mime = '';
|
||||
$blob = null;
|
||||
$liveVideoPath = null;
|
||||
|
||||
// Video is inside the file
|
||||
$path = null;
|
||||
if (str_starts_with($liveid, 'self__')) {
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
$mime = 'video/mp4';
|
||||
$name = $file->getName().'.mp4';
|
||||
}
|
||||
|
||||
// Different manufacurers have different formats
|
||||
if ('self__trailer' === $liveid) {
|
||||
try { // Get trailer
|
||||
$blob = Exif::getBinaryExifProp($path, '-trailer');
|
||||
} catch (\Exception $e) {
|
||||
return Errors::NotFound('file trailer');
|
||||
}
|
||||
} elseif ('self__embeddedvideo' === $liveid) {
|
||||
try { // Get embedded video file
|
||||
$blob = Exif::getBinaryExifProp($path, '-EmbeddedVideoFile');
|
||||
} catch (\Exception $e) {
|
||||
return Errors::NotFound('embedded video');
|
||||
}
|
||||
} elseif (str_starts_with($liveid, 'self__traileroffset=')) {
|
||||
// Remove prefix
|
||||
$offset = (int) substr($liveid, \strlen('self__traileroffset='));
|
||||
if ($offset <= 0) {
|
||||
return new JSONResponse(['message' => 'Invalid offset'], Http::STATUS_BAD_REQUEST);
|
||||
return Util::guardEx(function () use ($fileid, $liveid, $format, $transcode) {
|
||||
$file = $this->getUserFile($fileid);
|
||||
if (null === $file) {
|
||||
throw Exceptions::NotFoundFile($fileid);
|
||||
}
|
||||
|
||||
// Read file from offset to end
|
||||
$blob = file_get_contents($path, false, null, $offset);
|
||||
} else {
|
||||
// Get stored video file (Apple MOV)
|
||||
$lp = $this->timelineQuery->getLivePhoto($fileid);
|
||||
if (!$lp || $lp['liveid'] !== $liveid) {
|
||||
return Errors::NotFound('live video entry');
|
||||
// Check file liveid
|
||||
if (!$liveid) {
|
||||
throw Exceptions::MissingParameter('liveid');
|
||||
}
|
||||
|
||||
// Get and return file
|
||||
$liveFileId = (int) $lp['fileid'];
|
||||
$files = $this->rootFolder->getById($liveFileId);
|
||||
if (0 === \count($files)) {
|
||||
return Errors::NotFound('live video file');
|
||||
}
|
||||
$liveFile = $files[0];
|
||||
// Response data
|
||||
$name = '';
|
||||
$mime = '';
|
||||
$blob = null;
|
||||
$liveVideoPath = null;
|
||||
|
||||
if ($liveFile instanceof File) {
|
||||
// Requested only JSON info
|
||||
if ('json' === $format) {
|
||||
return new JSONResponse($lp);
|
||||
// Video is inside the file
|
||||
$path = null;
|
||||
if (str_starts_with($liveid, 'self__')) {
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
$mime = 'video/mp4';
|
||||
$name = $file->getName().'.mp4';
|
||||
}
|
||||
|
||||
// Different manufacurers have different formats
|
||||
if ('self__trailer' === $liveid) {
|
||||
try { // Get trailer
|
||||
$blob = Exif::getBinaryExifProp($path, '-trailer');
|
||||
} catch (\Exception $e) {
|
||||
throw Exceptions::NotFound('file trailer');
|
||||
}
|
||||
} elseif ('self__embeddedvideo' === $liveid) {
|
||||
try { // Get embedded video file
|
||||
$blob = Exif::getBinaryExifProp($path, '-EmbeddedVideoFile');
|
||||
} catch (\Exception $e) {
|
||||
throw Exceptions::NotFound('embedded video');
|
||||
}
|
||||
} elseif (str_starts_with($liveid, 'self__traileroffset=')) {
|
||||
// Remove prefix
|
||||
$offset = (int) substr($liveid, \strlen('self__traileroffset='));
|
||||
if ($offset <= 0) {
|
||||
throw Exceptions::BadRequest('Invalid offset');
|
||||
}
|
||||
|
||||
$name = $liveFile->getName();
|
||||
$blob = $liveFile->getContent();
|
||||
$mime = $liveFile->getMimeType();
|
||||
$liveVideoPath = $liveFile->getStorage()->getLocalFile($liveFile->getInternalPath());
|
||||
}
|
||||
}
|
||||
|
||||
// Data not found
|
||||
if (!$blob) {
|
||||
return Errors::NotFound('live video data');
|
||||
}
|
||||
|
||||
// Transcode video if allowed
|
||||
if ($transcode && !$this->config->getSystemValue('memories.vod.disable', true)) {
|
||||
try {
|
||||
// If video path not given, write to temp file
|
||||
if (!$liveVideoPath) {
|
||||
$liveVideoPath = self::postFile($transcode, $blob)['path'];
|
||||
// Read file from offset to end
|
||||
$blob = file_get_contents($path, false, null, $offset);
|
||||
} else {
|
||||
// Get stored video file (Apple MOV)
|
||||
$lp = $this->timelineQuery->getLivePhoto($fileid);
|
||||
if (!$lp || $lp['liveid'] !== $liveid) {
|
||||
throw Exceptions::NotFound('live video entry');
|
||||
}
|
||||
|
||||
// If this is H.264 it won't get transcoded anyway
|
||||
if ($liveVideoPath && 200 === $this->getUpstream($transcode, $liveVideoPath, 'max.mov')) {
|
||||
exit;
|
||||
// Get and return file
|
||||
$liveFileId = (int) $lp['fileid'];
|
||||
$files = $this->rootFolder->getById($liveFileId);
|
||||
if (0 === \count($files)) {
|
||||
throw Exceptions::NotFound('live video file');
|
||||
}
|
||||
$liveFile = $files[0];
|
||||
|
||||
if ($liveFile instanceof File) {
|
||||
// Requested only JSON info
|
||||
if ('json' === $format) {
|
||||
return new JSONResponse($lp);
|
||||
}
|
||||
|
||||
$name = $liveFile->getName();
|
||||
$blob = $liveFile->getContent();
|
||||
$mime = $liveFile->getMimeType();
|
||||
$liveVideoPath = $liveFile->getStorage()->getLocalFile($liveFile->getInternalPath());
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Transcoding failed, just return the original video
|
||||
}
|
||||
}
|
||||
|
||||
// Make and send response
|
||||
$response = new DataDisplayResponse($blob, Http::STATUS_OK, []);
|
||||
$response->setHeaders([
|
||||
'Content-Type' => $mime,
|
||||
'Content-Disposition' => "attachment; filename=\"{$name}\"",
|
||||
]);
|
||||
$response->cacheFor(3600 * 24, false, false);
|
||||
// Data not found
|
||||
if (!$blob) {
|
||||
throw Exceptions::NotFound('live video data');
|
||||
}
|
||||
|
||||
return $response;
|
||||
// Transcode video if allowed
|
||||
if ($transcode && !$this->config->getSystemValue('memories.vod.disable', true)) {
|
||||
try {
|
||||
// If video path not given, write to temp file
|
||||
if (!$liveVideoPath) {
|
||||
$liveVideoPath = self::postFile($transcode, $blob)['path'];
|
||||
}
|
||||
|
||||
// If this is H.264 it won't get transcoded anyway
|
||||
if ($liveVideoPath && 200 === $this->getUpstream($transcode, $liveVideoPath, 'max.mov')) {
|
||||
exit;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Transcoding failed, just return the original video
|
||||
}
|
||||
}
|
||||
|
||||
// Make and send response
|
||||
$response = new DataDisplayResponse($blob, Http::STATUS_OK, []);
|
||||
$response->setHeaders([
|
||||
'Content-Type' => $mime,
|
||||
'Content-Disposition' => "attachment; filename=\"{$name}\"",
|
||||
]);
|
||||
$response->cacheFor(3600 * 24, false, false);
|
||||
|
||||
return $response;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Memories;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
class Errors
|
||||
{
|
||||
public static function Generic(\Exception $e, int $status = Http::STATUS_INTERNAL_SERVER_ERROR): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => $e->getMessage(),
|
||||
], $status);
|
||||
}
|
||||
|
||||
public static function NotLoggedIn(): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => 'User not logged in',
|
||||
], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
public static function NotEnabled(string $app): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => "{$app} app not enabled or not the required version.",
|
||||
], Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
|
||||
public static function NoRequestRoot(): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => 'Request root could not be determined',
|
||||
], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
public static function NotFound(string $ctx): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => "Not found ({$ctx})",
|
||||
], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
public static function NotFoundFile($identifier): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => "File not found ({$identifier})",
|
||||
], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
public static function Forbidden(string $ctx): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => "Forbidden ({$ctx})",
|
||||
], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
public static function ForbiddenFileUpdate(string $name): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => "Forbidden ({$name} cannot be updated)",
|
||||
], Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
public static function MissingParameter(string $name): Http\Response
|
||||
{
|
||||
return new DataResponse([
|
||||
'message' => "Missing parameter ({$name})",
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Memories;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
class Exceptions
|
||||
{
|
||||
public static function Generic(\Exception $e, int $status = Http::STATUS_INTERNAL_SERVER_ERROR): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => $e->getMessage(),
|
||||
], $status));
|
||||
}
|
||||
|
||||
public static function NotLoggedIn(): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => 'User not logged in',
|
||||
], Http::STATUS_PRECONDITION_FAILED));
|
||||
}
|
||||
|
||||
public static function NotEnabled(string $app): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => "{$app} app not enabled or not the required version.",
|
||||
], Http::STATUS_PRECONDITION_FAILED));
|
||||
}
|
||||
|
||||
public static function NoRequestRoot(): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => 'Request root could not be determined',
|
||||
], Http::STATUS_NOT_FOUND));
|
||||
}
|
||||
|
||||
public static function NotFound(string $ctx): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => "Not found ({$ctx})",
|
||||
], Http::STATUS_NOT_FOUND));
|
||||
}
|
||||
|
||||
public static function NotFoundFile($identifier): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => "File not found ({$identifier})",
|
||||
], Http::STATUS_NOT_FOUND));
|
||||
}
|
||||
|
||||
public static function Forbidden(string $ctx): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => "Forbidden ({$ctx})",
|
||||
], Http::STATUS_FORBIDDEN));
|
||||
}
|
||||
|
||||
public static function ForbiddenFileUpdate(string $name): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => "Forbidden ({$name} cannot be updated)",
|
||||
], Http::STATUS_FORBIDDEN));
|
||||
}
|
||||
|
||||
public static function MissingParameter(string $name): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => "Missing parameter ({$name})",
|
||||
], Http::STATUS_BAD_REQUEST));
|
||||
}
|
||||
|
||||
public static function BadRequest(string $ctx): HttpResponseException
|
||||
{
|
||||
return new HttpResponseException(new DataResponse([
|
||||
'message' => "Bad Request ({$ctx})",
|
||||
], Http::STATUS_BAD_REQUEST));
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@ use OCP\IConfig;
|
|||
|
||||
class Util
|
||||
{
|
||||
use UtilController;
|
||||
|
||||
public static $TAG_DAYID_START = -(1 << 30); // the world surely didn't exist
|
||||
public static $TAG_DAYID_FOLDERS = -(1 << 30) + 1;
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Memories;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
trait UtilController
|
||||
{
|
||||
/**
|
||||
* Run a function and catch exceptions to return HTTP response.
|
||||
*
|
||||
* @param Function $function
|
||||
*/
|
||||
public static function guardEx($function): Http\Response
|
||||
{
|
||||
try {
|
||||
return $function();
|
||||
} catch (\OCA\Memories\HttpResponseException $e) {
|
||||
return $e->response;
|
||||
} catch (\Exception $e) {
|
||||
return new DataResponse([
|
||||
'message' => $e->getMessage(),
|
||||
], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user.
|
||||
*
|
||||
* @throws \OCA\Memories\HttpResponseException if the user is not logged in
|
||||
*/
|
||||
public static function getUser(): \OCP\IUser
|
||||
{
|
||||
$user = \OC::$server->get(\OCP\IUserSession::class)->getUser();
|
||||
if (null === $user) {
|
||||
throw Exceptions::NotLoggedIn();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user ID.
|
||||
*
|
||||
* @throws \OCA\Memories\HttpResponseException if the user is not logged in
|
||||
*/
|
||||
public static function getUID(): string
|
||||
{
|
||||
return self::getUser()->getUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user's home folder.
|
||||
*
|
||||
* @param null|string $uid User ID, or null for current user
|
||||
*
|
||||
* @throws \OCA\Memories\HttpResponseException if the user is not logged in
|
||||
*/
|
||||
public static function getUserFolder(?string $uid = null): \OCP\Files\Folder
|
||||
{
|
||||
if (null === $uid) {
|
||||
$uid = self::getUID();
|
||||
}
|
||||
|
||||
return \OC::$server->get(\OCP\Files\IRootFolder::class)->getUserFolder($uid);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue