Lot of de-duplication
parent
67e5c835ce
commit
11afad852b
|
@ -53,8 +53,7 @@ return [
|
|||
['name' => 'Faces#preview', 'url' => '/api/faces/preview/{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' => 'PUT'],
|
||||
['name' => 'Image#setExif', 'url' => '/api/image/set-exif/{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\Http;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IConfig;
|
||||
|
@ -133,6 +134,23 @@ class ApiBase extends Controller
|
|||
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()
|
||||
{
|
||||
return null === $this->request->getParam('folder');
|
||||
|
|
|
@ -38,18 +38,10 @@ class ImageController extends ApiBase
|
|||
*/
|
||||
public function info(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)) {
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
$file = $file[0];
|
||||
|
||||
// Get the image info
|
||||
$basic = false !== $this->request->getParam('basic', false);
|
||||
|
@ -57,85 +49,21 @@ class ImageController extends ApiBase
|
|||
|
||||
// Get latest exif data if requested
|
||||
if ($this->request->getParam('current', false)) {
|
||||
$info["current"] = Exif::getExifFromFile($file);
|
||||
$info['current'] = Exif::getExifFromFile($file);
|
||||
}
|
||||
|
||||
return new JSONResponse($info, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
*
|
||||
* 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
|
||||
* Set the exif data for a file.
|
||||
*/
|
||||
public function setExif(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)) {
|
||||
$file = $this->getUserFile((int) $id);
|
||||
if (!$file) {
|
||||
return new JSONResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
$file = $file[0];
|
||||
|
||||
// Check if user has permissions
|
||||
if (!$file->isUpdateable()) {
|
||||
|
@ -145,15 +73,45 @@ class ImageController extends ApiBase
|
|||
// Get original file from body
|
||||
$exif = $this->request->getParam('raw');
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
|
||||
try {
|
||||
Exif::setExif($path, $exif);
|
||||
} catch (\Exception $e) {
|
||||
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
|
||||
$this->timelineWrite->processFile($file, true);
|
||||
|
||||
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['h'] = (int) $row['h'];
|
||||
if (!$row['isvideo']) {
|
||||
unset($row['isvideo']);
|
||||
unset($row['video_duration']);
|
||||
unset($row['isvideo'], $row['video_duration']);
|
||||
}
|
||||
if ($row['categoryid']) {
|
||||
$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
|
||||
if ($file->isEncrypted()) {
|
||||
throw new \Exception('Cannot update exif date on encrypted files');
|
||||
}
|
||||
$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);
|
||||
|
||||
// Get path to local (copy) of the file
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
if (!\is_string($path)) {
|
||||
throw new \Exception('Failed to get local file path');
|
||||
}
|
||||
fwrite($pipes[0], $raw);
|
||||
fclose($pipes[0]);
|
||||
|
||||
// Update exif data
|
||||
self::updateExifDateForLocalFile($path, $newDate);
|
||||
$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}");
|
||||
|
||||
// Update remote file if not local
|
||||
if (!$file->getStorage()->isLocal()) {
|
||||
$file->putContent(fopen($path, 'r')); // closes the handler
|
||||
throw new \Exception('Could not set exif data: '.$stdout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,62 +421,4 @@ class Exif
|
|||
|
||||
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;
|
||||
|
||||
// Update exif data
|
||||
await axios.put(
|
||||
await axios.patch(
|
||||
generateUrl("/apps/memories/api/image/set-exif/{id}", {
|
||||
id: fileid,
|
||||
}),
|
||||
|
|
|
@ -144,7 +144,7 @@ import * as utils from "../../services/Utils";
|
|||
import * as dav from "../../services/DavRequests";
|
||||
|
||||
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({
|
||||
components: {
|
||||
|
@ -269,12 +269,11 @@ export default class EditDate extends Mixins(GlobalMixin) {
|
|||
try {
|
||||
this.processing = true;
|
||||
const fileid = this.photos[0].fileid;
|
||||
const res = await axios.patch<any>(
|
||||
generateUrl(EDIT_API_URL, { id: fileid }),
|
||||
{
|
||||
date: this.getExifFormat(this.getDate()),
|
||||
}
|
||||
);
|
||||
await axios.patch<any>(generateUrl(EDIT_API_URL, { id: fileid }), {
|
||||
raw: {
|
||||
DateTimeOriginal: this.getExifFormat(this.getDate()),
|
||||
},
|
||||
});
|
||||
emit("files:file:updated", { fileid });
|
||||
this.emitRefresh(true);
|
||||
this.close();
|
||||
|
|
Loading…
Reference in New Issue