livephoto: multiple trailers in Google (fix #373)

pull/387/head
Varun Patil 2023-01-26 10:50:41 -08:00
parent 7cedcf70a8
commit 457ac16db1
3 changed files with 48 additions and 4 deletions

View File

@ -139,6 +139,15 @@ class VideoController extends ApiBase
} catch (\Exception $e) { } catch (\Exception $e) {
return new JSONResponse(['message' => 'Embedded video not found'], Http::STATUS_NOT_FOUND); return new JSONResponse(['message' => 'Embedded video not found'], Http::STATUS_NOT_FOUND);
} }
} 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);
}
// Read file from offset to end
$blob = file_get_contents($path, false, null, $offset);
} else { } else {
// Get stored video file (Apple MOV) // Get stored video file (Apple MOV)
$lp = $this->timelineQuery->getLivePhoto($fileid); $lp = $this->timelineQuery->getLivePhoto($fileid);

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace OCA\Memories\Db; namespace OCA\Memories\Db;
use OCA\Memories\Exif;
use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\File; use OCP\Files\File;
use OCP\IDBConnection; use OCP\IDBConnection;
@ -26,7 +27,7 @@ class LivePhoto
} }
/** Get liveid from photo part */ /** Get liveid from photo part */
public function getLivePhotoId(array &$exif) public function getLivePhotoId(File &$file, array &$exif)
{ {
// Apple JPEG (MOV has ContentIdentifier) // Apple JPEG (MOV has ContentIdentifier)
if (\array_key_exists('MediaGroupUUID', $exif)) { if (\array_key_exists('MediaGroupUUID', $exif)) {
@ -41,7 +42,36 @@ class LivePhoto
// Google JPEG and Samsung HEIC (Apple?) // Google JPEG and Samsung HEIC (Apple?)
if (\array_key_exists('MotionPhoto', $exif)) { if (\array_key_exists('MotionPhoto', $exif)) {
if ('image/jpeg' === $exif['MIMEType']) { if ('image/jpeg' === $exif['MIMEType']) {
// Google JPEG -- image should hopefully be in trailer // Google Motion Photo JPEG
// We need to read the DirectoryItemLength key to get the length of the video
// These keys are duplicate, one for the image and one for the video
// With exiftool -G4, we get the following:
//
// "Unknown:DirectoryItemSemantic": "Primary"
// "Unknown:DirectoryItemLength": 0
// "Copy1:DirectoryItemSemantic": "MotionPhoto"
// "Copy1:DirectoryItemLength": 3011435 // <-- this is the length of the video
//
// The video is then located at the end of the file, so we can get the offset.
// Match each DirectoryItemSemantic to find MotionPhoto, then get the length.
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
$extExif = Exif::getExifWithDuplicates($path);
foreach ($extExif as $key => $value) {
if (str_ends_with($key, ':DirectoryItemSemantic')) {
if ('MotionPhoto' === $value) {
$videoLength = $extExif[str_replace('Semantic', 'Length', $key)];
if (\is_int($videoLength) && $videoLength > 0) {
$videoOffset = $file->getSize() - $videoLength;
return 'self__traileroffset='.((string) $videoOffset);
}
}
}
}
// Fallback: video should hopefully be in trailer
return 'self__trailer'; return 'self__trailer';
} }
if ('image/heic' === $exif['MIMEType']) { if ('image/heic' === $exif['MIMEType']) {

View File

@ -282,6 +282,11 @@ class Exif
} }
} }
public static function getExifWithDuplicates(string $path)
{
return self::getExifFromLocalPathWithSeparateProc($path, ['-G4']);
}
/** Get path to exiftool binary */ /** Get path to exiftool binary */
private static function getExiftool() private static function getExiftool()
{ {
@ -407,10 +412,10 @@ class Exif
} }
} }
private static function getExifFromLocalPathWithSeparateProc(string &$path) private static function getExifFromLocalPathWithSeparateProc(string &$path, array $extraArgs = [])
{ {
$pipes = []; $pipes = [];
$proc = proc_open(array_merge(self::getExiftool(), ['-api', 'QuickTimeUTC=1', '-n', '-U', '-json', '--b', $path]), [ $proc = proc_open(array_merge(self::getExiftool(), ['-api', 'QuickTimeUTC=1', '-n', '-U', '-json', '--b'], $extraArgs, [$path]), [
1 => ['pipe', 'w'], 1 => ['pipe', 'w'],
2 => ['pipe', 'w'], 2 => ['pipe', 'w'],
], $pipes); ], $pipes);