@@ -42,6 +42,23 @@ public function isAvailable(FileInfo $file): bool {
4242 return is_string ($ this ->binary );
4343 }
4444
45+ private function connectDirect (File $ file ): string |false {
46+ if ($ file ->isEncrypted ()) {
47+ return false ;
48+ }
49+
50+ // Checks for availability to access the video file directly via HTTP/HTTPS.
51+ // Returns a string containing URL if available. Only implemented and tested
52+ // with Amazon S3 currently. In all other cases, return false. ffmpeg
53+ // supports other protocols so this function may expand in the future.
54+ $ gddValues = $ file ->getStorage ()->getDirectDownloadById ((string )$ file ->getId ());
55+
56+ if (is_array ($ gddValues ) && array_key_exists ('url ' , $ gddValues )) {
57+ return str_starts_with ($ gddValues ['url ' ], 'http ' ) ? $ gddValues ['url ' ] : false ;
58+ }
59+ return false ;
60+ }
61+
4562 /**
4663 * {@inheritDoc}
4764 */
@@ -54,74 +71,87 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
5471
5572 $ result = null ;
5673
74+ $ connectDirect = $ this ->connectDirect ($ file );
75+
5776 // Timestamps to make attempts to generate a still
5877 $ timeAttempts = [5 , 1 , 0 ];
5978
60- // By default, download $sizeAttempts from the file along with
61- // the 'moov' atom.
62- // Example bitrates in the higher range:
63- // 4K HDR H265 60 FPS = 75 Mbps = 9 MB per second needed for a still
64- // 1080p H265 30 FPS = 10 Mbps = 1.25 MB per second needed for a still
65- // 1080p H264 30 FPS = 16 Mbps = 2 MB per second needed for a still
66- $ sizeAttempts = [1024 * 1024 * 10 ];
67-
68- if ($ this ->useTempFile ($ file )) {
69- if ($ file ->getStorage ()->isLocal ()) {
70- // Temp file required but file is local, so retrieve $sizeAttempt bytes first,
71- // and if it doesn't work, retrieve the entire file.
72- $ sizeAttempts [] = null ;
79+ // If HTTP/HTTPS direct connect is not available or if the file is encrypted,
80+ // process normally
81+ if ($ connectDirect === false ) {
82+ // By default, download $sizeAttempts from the file along with
83+ // the 'moov' atom.
84+ // Example bitrates in the higher range:
85+ // 4K HDR H265 60 FPS = 75 Mbps = 9 MB per second needed for a still
86+ // 1080p H265 30 FPS = 10 Mbps = 1.25 MB per second needed for a still
87+ // 1080p H264 30 FPS = 16 Mbps = 2 MB per second needed for a still
88+ $ sizeAttempts = [1024 * 1024 * 10 ];
89+
90+ if ($ this ->useTempFile ($ file )) {
91+ if ($ file ->getStorage ()->isLocal ()) {
92+ // Temp file required but file is local, so retrieve $sizeAttempt bytes first,
93+ // and if it doesn't work, retrieve the entire file.
94+ $ sizeAttempts [] = null ;
95+ }
96+ } else {
97+ // Temp file is not required and file is local so retrieve entire file.
98+ $ sizeAttempts = [null ];
7399 }
74- } else {
75- // Temp file is not required and file is local so retrieve entire file.
76- $ sizeAttempts = [null ];
77- }
78100
79- foreach ($ sizeAttempts as $ size ) {
80- $ absPath = false ;
81- // File is remote, generate a sparse file
82- if (!$ file ->getStorage ()->isLocal ()) {
83- $ absPath = $ this ->getSparseFile ($ file , $ size );
84- }
85- // Defaults to existing routine if generating sparse file fails
86- if ($ absPath === false ) {
87- $ absPath = $ this ->getLocalFile ($ file , $ size );
88- }
89- if ($ absPath === false ) {
90- Server::get (LoggerInterface::class)->error (
91- 'Failed to get local file to generate thumbnail for: ' . $ file ->getPath (),
92- ['app ' => 'core ' ]
93- );
94- return null ;
95- }
101+ foreach ($ sizeAttempts as $ size ) {
102+ $ absPath = false ;
103+ // File is remote, generate a sparse file
104+ if (!$ file ->getStorage ()->isLocal ()) {
105+ $ absPath = $ this ->getSparseFile ($ file , $ size );
106+ }
107+ // Defaults to existing routine if generating sparse file fails
108+ if ($ absPath === false ) {
109+ $ absPath = $ this ->getLocalFile ($ file , $ size );
110+ }
111+ if ($ absPath === false ) {
112+ Server::get (LoggerInterface::class)->error (
113+ 'Failed to get local file to generate thumbnail for: ' . $ file ->getPath (),
114+ ['app ' => 'core ' ]
115+ );
116+ return null ;
117+ }
118+
119+ // Attempt still image grabs from selected timestamps
120+ foreach ($ timeAttempts as $ timeStamp ) {
121+ $ result = $ this ->generateThumbNail ($ maxX , $ maxY , $ absPath , $ timeStamp );
122+ if ($ result !== null ) {
123+ break ;
124+ }
125+ Server::get (LoggerInterface::class)->debug (
126+ 'Movie preview generation attempt failed '
127+ . ', file= ' . $ file ->getPath ()
128+ . ', time= ' . $ timeStamp
129+ . ', size= ' . ($ size ?? 'entire file ' ),
130+ ['app ' => 'core ' ]
131+ );
132+ }
133+
134+ $ this ->cleanTmpFiles ();
96135
97- // Attempt still image grabs from selected timestamps
98- foreach ($ timeAttempts as $ timeStamp ) {
99- $ result = $ this ->generateThumbNail ($ maxX , $ maxY , $ absPath , $ timeStamp );
100136 if ($ result !== null ) {
137+ Server::get (LoggerInterface::class)->debug (
138+ 'Movie preview generation attempt success '
139+ . ', file= ' . $ file ->getPath ()
140+ . ', time= ' . $ timeStamp
141+ . ', size= ' . ($ size ?? 'entire file ' ),
142+ ['app ' => 'core ' ]
143+ );
101144 break ;
102145 }
103- Server::get (LoggerInterface::class)->debug (
104- 'Movie preview generation attempt failed '
105- . ', file= ' . $ file ->getPath ()
106- . ', time= ' . $ timeStamp
107- . ', size= ' . ($ size ?? 'entire file ' ),
108- ['app ' => 'core ' ]
109- );
110146 }
111-
112- $ this ->cleanTmpFiles ();
113-
114- if ($ result !== null ) {
115- Server::get (LoggerInterface::class)->debug (
116- 'Movie preview generation attempt success '
117- . ', file= ' . $ file ->getPath ()
118- . ', time= ' . $ timeStamp
119- . ', size= ' . ($ size ?? 'entire file ' ),
120- ['app ' => 'core ' ]
121- );
122- break ;
147+ } else {
148+ // HTTP/HTTPS direct connect is available so pass the URL directly to ffmpeg
149+ foreach ($ timeAttempts as $ timeStamp ) {
150+ $ result = $ this ->generateThumbNail ($ maxX , $ maxY , $ connectDirect , $ timeStamp );
151+ if ($ result !== null ) {
152+ break ;
153+ }
123154 }
124-
125155 }
126156 if ($ result === null ) {
127157 Server::get (LoggerInterface::class)->error (
@@ -330,7 +360,6 @@ private function generateThumbNail(int $maxX, int $maxY, string $absPath, int $s
330360 }
331361 }
332362
333-
334363 unlink ($ tmpPath );
335364 return null ;
336365 }
0 commit comments