Lot of de-duplication
parent
67e5c835ce
commit
11afad852b
|
@ -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'],
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
98
lib/Exif.php
98
lib/Exif.php
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue