@@ -54,22 +54,30 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
5454
5555		$ resultnull ;
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  ($ filegetStorage ()->isLocal ()) {
62- 				$ sizeAttempts5242880 , null ];
61+ 				// File is local, make two attempts: 10 MB, then the entire file 
62+ 				$ sizeAttempts10485760 , null ];
6363			} else  {
64- 				$ sizeAttempts5242880 ];
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+ 				$ sizeAttempts10485760 ];
6567			}
6668		} else  {
6769			// size is irrelevant, only attempt once 
6870			$ sizeAttemptsnull ];
6971		}
7072
7173		foreach  ($ sizeAttemptsas  $ size
72- 			$ absPath$ this getLocalFile ($ file$ size
74+ 			if  ($ filegetStorage ()->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  ($ absPathfalse ) {
7482				Server::get (LoggerInterface::class)->error (
7583					'Failed to get local file to generate thumbnail for:  '  . $ filegetPath (),
@@ -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$ absPath5 );
82- 			if  ($ resultnull ) {
83- 				$ result$ this generateThumbNail ($ maxX$ maxY$ absPath1 );
84- 				if  ($ resultnull ) {
85- 					$ result$ this generateThumbNail ($ maxX$ maxY$ absPath0 );
86- 				}
87- 			}
89+ 			// Attempt still image grab from 1 second and 0 second timestamp 
90+ 			$ result$ this generateThumbNail ($ maxX$ maxY$ absPath1 ) ?? $ this generateThumbNail ($ maxX$ maxY$ absPath0 );
8891
8992			$ this cleanTmpFiles ();
9093
@@ -95,6 +98,38 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
9598
9699		return  $ result
97100	}
101+ 	
102+ 	private  function  getSparseFile (File $ fileint  $ sizestring |false  {
103+ 
104+ 		$ absPathget (ITempManager::class)->getTemporaryFile ();
105+ 		if  ($ absPathfalse ) {
106+ 			Server::get (LoggerInterface::class)->error (
107+ 				'Failed to get sparse file to generate thumbnail for:  '  . $ filegetPath (),
108+ 				['app '  => 'core ' ]
109+ 			);
110+ 			return  false ;
111+ 		}
112+ 		$ sparseFilefopen ($ absPath'w ' );
113+ 		$ content$ filefopen ('r ' );
114+ 
115+ 		// If filesize is small (i.e. <= $size + 5 MB) then just download entire file 
116+ 		if  (($ size5242880 ) >= $ filegetSize ()) {
117+ 			stream_copy_to_stream ($ content$ sparseFile
118+ 		} else  {
119+ 			// Create a sparse file of equal size to original video 
120+ 			ftruncate ($ sparseFile$ filegetSize ());
121+ 			// Copy $size bytes to front end 
122+ 			fseek ($ sparseFile0 );
123+ 			stream_copy_to_stream ($ content$ sparseFile$ size0 );
124+ 			// Copy 5 MB to tail end of file 
125+ 			fseek ($ sparseFile$ filegetSize () - 5242880 ));
126+ 			stream_copy_to_stream ($ content$ sparseFile5242880 , ($ filegetSize () - 5242880 ));
127+ 		}
128+ 
129+ 		fclose ($ sparseFile
130+ 		fclose ($ content
131+ 		return  $ absPath
132+ 	}
98133
99134	private  function  useHdr (string  $ absPathbool  {
100135		// load ffprobe path from configuration, otherwise generate binary path using ffmpeg binary path 
0 commit comments