Skip to content

Commit 2595107

Browse files
feat(previews): previews for large remote files without full file download
Co-authored-by: Kate <26026535+provokateurin@users.noreply.github.com> Signed-off-by: invario <67800603+invario@users.noreply.github.com>
1 parent 8210e12 commit 2595107

File tree

1 file changed

+44
-14
lines changed

1 file changed

+44
-14
lines changed

lib/private/Preview/Movie.php

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,30 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
5454

5555
$result = null;
5656
if ($this->useTempFile($file)) {
57-
// Try downloading 5 MB first, as it's likely that the first frames are present there.
58-
// In some cases this doesn't work, for example when the moov atom is at the
59-
// end of the file, so if it fails we fall back to getting the full file.
60-
// Unless the file is not local (e.g. S3) as we do not want to download the whole (e.g. 37Gb) file
57+
// Try downloading 10 MB first, as it's likely that the first needed frames are present
58+
// there along with the 'moov atom" (used in MP4/MOV files). In some cases this doesn't
59+
// work, (e.g. the 'moov atom' is at the end, or the videos is high bitrate)
6160
if ($file->getStorage()->isLocal()) {
62-
$sizeAttempts = [5242880, null];
61+
// File is local, make two attempts: 10 MB, then the entire file
62+
$sizeAttempts = [10485760, null];
6363
} else {
64-
$sizeAttempts = [5242880];
64+
// File is remote, make one attempt: 10 MB will be downloaded from the file along with
65+
// 5 MB from the end with filler (null zeroes) in the middle.
66+
$sizeAttempts = [10485760];
6567
}
6668
} else {
6769
// size is irrelevant, only attempt once
6870
$sizeAttempts = [null];
6971
}
7072

7173
foreach ($sizeAttempts as $size) {
72-
$absPath = $this->getLocalFile($file, $size);
74+
if ($file->getStorage()->isLocal()) {
75+
// File is local
76+
$absPath = $this->getLocalFile($file, $size);
77+
} else {
78+
// File is remote, generate a sparse file
79+
$absPath = $this->getSparseFile($file, $size);
80+
}
7381
if ($absPath === false) {
7482
Server::get(LoggerInterface::class)->error(
7583
'Failed to get local file to generate thumbnail for: ' . $file->getPath(),
@@ -78,13 +86,8 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
7886
return null;
7987
}
8088

81-
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 5);
82-
if ($result === null) {
83-
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 1);
84-
if ($result === null) {
85-
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 0);
86-
}
87-
}
89+
// Attempt still image grab from 1 second and 0 second timestamp
90+
$result = $this->generateThumbNail($maxX, $maxY, $absPath, 1) ?? $this->generateThumbNail($maxX, $maxY, $absPath, 0);
8891

8992
$this->cleanTmpFiles();
9093

@@ -95,6 +98,33 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
9598

9699
return $result;
97100
}
101+
102+
private function getSparseFile(File $file, int $size): string|false {
103+
$absPath = Server::get(ITempManager::class)->getTemporaryFile();
104+
if ($absPath === false) {
105+
Server::get(LoggerInterface::class)->error(
106+
'Failed to get sparse file to generate thumbnail for: ' . $file->getPath(),
107+
['app' => 'core']
108+
);
109+
return false;
110+
}
111+
$sparseFile = fopen($absPath,'w');
112+
$content = $file->fopen('r');
113+
$zeroes = fopen('/dev/zero','r');
114+
115+
// If filesize is small (i.e. <= $size + 5 MB) then just download entire file
116+
if (($size+5242880) >= $file->getSize()) {
117+
stream_copy_to_stream($content, $sparseFile);
118+
} else {
119+
stream_copy_to_stream($content, $sparseFile, $size);
120+
stream_copy_to_stream($zeroes, $sparseFile, ($file->getSize()-($size+5242880)));
121+
stream_copy_to_stream($content, $sparseFile, 5242880, ($file->getSize()-5242880));
122+
}
123+
fclose($zeroes);
124+
fclose($sparseFile);
125+
fclose($content);
126+
return $absPath;
127+
}
98128

99129
private function useHdr(string $absPath): bool {
100130
// load ffprobe path from configuration, otherwise generate binary path using ffmpeg binary path

0 commit comments

Comments
 (0)