Lot of de-duplication

pull/221/head
Varun Patil 2022-11-09 22:19:44 -08:00
parent 67e5c835ce
commit 11afad852b
7 changed files with 86 additions and 165 deletions

View File

@ -53,8 +53,7 @@ return [
['name' => 'Faces#preview', 'url' => '/api/faces/preview/{id}', 'verb' => 'GET'], ['name' => 'Faces#preview', 'url' => '/api/faces/preview/{id}', 'verb' => 'GET'],
['name' => 'Image#info', 'url' => '/api/image/info/{id}', 'verb' => 'GET'], ['name' => 'Image#info', 'url' => '/api/image/info/{id}', 'verb' => 'GET'],
['name' => 'Image#edit', 'url' => '/api/image/edit/{id}', 'verb' => 'PATCH'], ['name' => 'Image#setExif', 'url' => '/api/image/set-exif/{id}', 'verb' => 'PATCH'],
['name' => 'Image#setExif', 'url' => '/api/image/set-exif/{id}', 'verb' => 'PUT'],
['name' => 'Archive#archive', 'url' => '/api/archive/{id}', 'verb' => 'PATCH'], ['name' => 'Archive#archive', 'url' => '/api/archive/{id}', 'verb' => 'PATCH'],

View File

@ -31,6 +31,7 @@ use OCP\App\IAppManager;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http; use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\Files\File;
use OCP\Files\Folder; use OCP\Files\Folder;
use OCP\Files\IRootFolder; use OCP\Files\IRootFolder;
use OCP\IConfig; use OCP\IConfig;
@ -133,6 +134,23 @@ class ApiBase extends Controller
return $folder; return $folder;
} }
protected function getUserFile(int $id): File
{
$user = $this->userSession->getUser();
if (null === $user) {
return null;
}
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
// Check for permissions and get numeric Id
$file = $userFolder->getById($id);
if (0 === \count($file)) {
return null;
}
return $file[0];
}
protected function isRecursive() protected function isRecursive()
{ {
return null === $this->request->getParam('folder'); return null === $this->request->getParam('folder');

View File

@ -38,18 +38,10 @@ class ImageController extends ApiBase
*/ */
public function info(string $id): JSONResponse public function info(string $id): JSONResponse
{ {
$user = $this->userSession->getUser(); $file = $this->getUserFile((int) $id);
if (null === $user) { if (!$file) {
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
}
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
// Check for permissions and get numeric Id
$file = $userFolder->getById((int) $id);
if (0 === \count($file)) {
return new JSONResponse([], Http::STATUS_NOT_FOUND); return new JSONResponse([], Http::STATUS_NOT_FOUND);
} }
$file = $file[0];
// Get the image info // Get the image info
$basic = false !== $this->request->getParam('basic', false); $basic = false !== $this->request->getParam('basic', false);
@ -57,85 +49,21 @@ class ImageController extends ApiBase
// Get latest exif data if requested // Get latest exif data if requested
if ($this->request->getParam('current', false)) { if ($this->request->getParam('current', false)) {
$info["current"] = Exif::getExifFromFile($file); $info['current'] = Exif::getExifFromFile($file);
} }
return new JSONResponse($info, Http::STATUS_OK); return new JSONResponse($info, Http::STATUS_OK);
} }
/** /**
* @NoAdminRequired * Set the exif data for a file.
*
* Change exif data for one file
*
* @param string fileid
*/
public function edit(string $id): JSONResponse
{
$user = $this->userSession->getUser();
if (null === $user) {
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
}
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
// Check for permissions and get numeric Id
$file = $userFolder->getById((int) $id);
if (0 === \count($file)) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
$file = $file[0];
// Check if user has permissions
if (!$file->isUpdateable()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
// Get new date from body
$body = $this->request->getParams();
if (!isset($body['date'])) {
return new JSONResponse(['message' => 'Missing date'], Http::STATUS_BAD_REQUEST);
}
// Make sure the date is valid
try {
Exif::parseExifDate($body['date']);
} catch (\Exception $e) {
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
}
// Update date
try {
$res = Exif::updateExifDate($file, $body['date']);
if (false === $res) {
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}
} catch (\Exception $e) {
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
}
// Reprocess the file
$this->timelineWrite->processFile($file, true);
return $this->info($id);
}
/**
* Set the exif data for a file
*/ */
public function setExif(string $id): JSONResponse public function setExif(string $id): JSONResponse
{ {
$user = $this->userSession->getUser(); $file = $this->getUserFile((int) $id);
if (null === $user) { if (!$file) {
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
}
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
// Check for permissions and get numeric Id
$file = $userFolder->getById((int) $id);
if (0 === \count($file)) {
return new JSONResponse([], Http::STATUS_NOT_FOUND); return new JSONResponse([], Http::STATUS_NOT_FOUND);
} }
$file = $file[0];
// Check if user has permissions // Check if user has permissions
if (!$file->isUpdateable()) { if (!$file->isUpdateable()) {
@ -145,15 +73,45 @@ class ImageController extends ApiBase
// Get original file from body // Get original file from body
$exif = $this->request->getParam('raw'); $exif = $this->request->getParam('raw');
$path = $file->getStorage()->getLocalFile($file->getInternalPath()); $path = $file->getStorage()->getLocalFile($file->getInternalPath());
try { try {
Exif::setExif($path, $exif); Exif::setExif($path, $exif);
} catch (\Exception $e) { } catch (\Exception $e) {
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR); return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
} }
// Update remote file if not local
if (!$file->getStorage()->isLocal()) {
$file->putContent(fopen($path, 'r')); // closes the handler
}
// Reprocess the file // Reprocess the file
$this->timelineWrite->processFile($file, true); $this->timelineWrite->processFile($file, true);
return new JSONResponse([], Http::STATUS_OK); return new JSONResponse([], Http::STATUS_OK);
} }
/**
* Get a full resolution PNG for editing from a file.
*/
public function getPNG(string $id)
{
$file = $this->getUserFile((int) $id);
if (!$file) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
// Get the image info
$info = $this->timelineQuery->getInfoById($file->getId(), true);
// Get the image
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
$image = Exif::getPNG($path, $info['exif']);
// Return the image
$response = new Http\DataDisplayResponse($image, Http::STATUS_OK, ['Content-Type' => 'image/png']);
$response->cacheFor(0);
return $response;
}
} }

View File

@ -203,8 +203,7 @@ trait TimelineQueryDays
$row['w'] = (int) $row['w']; $row['w'] = (int) $row['w'];
$row['h'] = (int) $row['h']; $row['h'] = (int) $row['h'];
if (!$row['isvideo']) { if (!$row['isvideo']) {
unset($row['isvideo']); unset($row['isvideo'], $row['video_duration']);
unset($row['video_duration']);
} }
if ($row['categoryid']) { if ($row['categoryid']) {
$row['isfavorite'] = 1; $row['isfavorite'] = 1;

View File

@ -230,29 +230,35 @@ class Exif
} }
/** /**
* Update exif date using exiftool. * Set exif data using raw json.
* *
* @param string $newDate formatted in standard Exif format (YYYY:MM:DD HH:MM:SS) * @param string $path to local file
* @param array $data exif data
*
* @throws \Exception on failure
*/ */
public static function updateExifDate(File &$file, string $newDate) public static function setExif(string &$path, array &$data)
{ {
// Don't want to mess these up, definitely $data['SourceFile'] = $path;
if ($file->isEncrypted()) { $raw = json_encode([$data]);
throw new \Exception('Cannot update exif date on encrypted files'); $cmd = array_merge(self::getExiftool(), ['-json=-', $path]);
} $proc = proc_open($cmd, [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
], $pipes);
// Get path to local (copy) of the file fwrite($pipes[0], $raw);
$path = $file->getStorage()->getLocalFile($file->getInternalPath()); fclose($pipes[0]);
if (!\is_string($path)) {
throw new \Exception('Failed to get local file path');
}
// Update exif data $stdout = self::readOrTimeout($pipes[1], 30000);
self::updateExifDateForLocalFile($path, $newDate); fclose($pipes[1]);
fclose($pipes[2]);
proc_terminate($proc);
if (false !== strpos($stdout, 'error')) {
error_log("Exiftool error: {$stdout}");
// Update remote file if not local throw new \Exception('Could not set exif data: '.$stdout);
if (!$file->getStorage()->isLocal()) {
$file->putContent(fopen($path, 'r')); // closes the handler
} }
} }
@ -415,62 +421,4 @@ class Exif
return $json[0]; return $json[0];
} }
/**
* Update exif date using exiftool for a local file.
*
* @param string $newDate formatted in standard Exif format (YYYY:MM:DD HH:MM:SS)
*
* @throws \Exception on failure
*/
private static function updateExifDateForLocalFile(string $path, string $newDate)
{
$cmd = array_merge(self::getExiftool(), ['-api', 'QuickTimeUTC=1', '-overwrite_original', '-DateTimeOriginal='.$newDate, $path]);
$proc = proc_open($cmd, [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
], $pipes);
$stdout = self::readOrTimeout($pipes[1], 300000);
fclose($pipes[1]);
fclose($pipes[2]);
proc_terminate($proc);
if (false !== strpos($stdout, 'error')) {
error_log("Exiftool error: {$stdout}");
throw new \Exception('Could not update exif date: '.$stdout);
}
}
/**
* Set exif data using raw json.
*
* @param string $path to local file
* @param array $data exif data
*
* @throws \Exception on failure
*/
public static function setExif(string &$path, array &$data)
{
$data['SourceFile'] = $path;
$raw = json_encode([$data]);
$cmd = array_merge(self::getExiftool(), ['-json=-', $path]);
$proc = proc_open($cmd, [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
], $pipes);
fwrite($pipes[0], $raw);
fclose($pipes[0]);
$stdout = self::readOrTimeout($pipes[1], 30000);
fclose($pipes[1]);
fclose($pipes[2]);
proc_terminate($proc);
if (false !== strpos($stdout, 'error')) {
error_log("Exiftool error: {$stdout}");
throw new \Exception('Could not set exif data: '.$stdout);
}
}
} }

View File

@ -215,7 +215,7 @@ export default class ImageEditor extends Mixins(GlobalMixin) {
delete exif.ExifImageSize; delete exif.ExifImageSize;
// Update exif data // Update exif data
await axios.put( await axios.patch(
generateUrl("/apps/memories/api/image/set-exif/{id}", { generateUrl("/apps/memories/api/image/set-exif/{id}", {
id: fileid, id: fileid,
}), }),

View File

@ -144,7 +144,7 @@ import * as utils from "../../services/Utils";
import * as dav from "../../services/DavRequests"; import * as dav from "../../services/DavRequests";
const INFO_API_URL = "/apps/memories/api/image/info/{id}"; const INFO_API_URL = "/apps/memories/api/image/info/{id}";
const EDIT_API_URL = "/apps/memories/api/image/edit/{id}"; const EDIT_API_URL = "/apps/memories/api/image/set-exif/{id}";
@Component({ @Component({
components: { components: {
@ -269,12 +269,11 @@ export default class EditDate extends Mixins(GlobalMixin) {
try { try {
this.processing = true; this.processing = true;
const fileid = this.photos[0].fileid; const fileid = this.photos[0].fileid;
const res = await axios.patch<any>( await axios.patch<any>(generateUrl(EDIT_API_URL, { id: fileid }), {
generateUrl(EDIT_API_URL, { id: fileid }), raw: {
{ DateTimeOriginal: this.getExifFormat(this.getDate()),
date: this.getExifFormat(this.getDate()), },
} });
);
emit("files:file:updated", { fileid }); emit("files:file:updated", { fileid });
this.emitRefresh(true); this.emitRefresh(true);
this.close(); this.close();