livephoto: multiple trailers in Google (fix #373)
parent
7cedcf70a8
commit
457ac16db1
|
@ -139,6 +139,15 @@ class VideoController extends ApiBase
|
|||
} catch (\Exception $e) {
|
||||
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 {
|
||||
// Get stored video file (Apple MOV)
|
||||
$lp = $this->timelineQuery->getLivePhoto($fileid);
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\Db;
|
||||
|
||||
use OCA\Memories\Exif;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\File;
|
||||
use OCP\IDBConnection;
|
||||
|
@ -26,7 +27,7 @@ class LivePhoto
|
|||
}
|
||||
|
||||
/** Get liveid from photo part */
|
||||
public function getLivePhotoId(array &$exif)
|
||||
public function getLivePhotoId(File &$file, array &$exif)
|
||||
{
|
||||
// Apple JPEG (MOV has ContentIdentifier)
|
||||
if (\array_key_exists('MediaGroupUUID', $exif)) {
|
||||
|
@ -41,7 +42,36 @@ class LivePhoto
|
|||
// Google JPEG and Samsung HEIC (Apple?)
|
||||
if (\array_key_exists('MotionPhoto', $exif)) {
|
||||
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';
|
||||
}
|
||||
if ('image/heic' === $exif['MIMEType']) {
|
||||
|
|
|
@ -282,6 +282,11 @@ class Exif
|
|||
}
|
||||
}
|
||||
|
||||
public static function getExifWithDuplicates(string $path)
|
||||
{
|
||||
return self::getExifFromLocalPathWithSeparateProc($path, ['-G4']);
|
||||
}
|
||||
|
||||
/** Get path to exiftool binary */
|
||||
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 = [];
|
||||
$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'],
|
||||
2 => ['pipe', 'w'],
|
||||
], $pipes);
|
||||
|
|
Loading…
Reference in New Issue