Completed
Push — master ( 81153d...2a6722 )
by Thomas
52s
created

Movie::getThumbnail()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 32
Code Lines 18

Duplication

Lines 13
Ratio 40.63 %

Importance

Changes 0
Metric Value
cc 6
eloc 18
nc 24
nop 4
dl 13
loc 32
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Georg Ehrke <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Morris Jobke <[email protected]>
6
 * @author Olivier Paroz <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 * @author Lorenzo Perone <[email protected]>
9
 *
10
 * @copyright Copyright (c) 2018, ownCloud GmbH
11
 * @license AGPL-3.0
12
 *
13
 * This code is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License, version 3,
15
 * as published by the Free Software Foundation.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License, version 3,
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
24
 *
25
 */
26
namespace OC\Preview;
27
28
use OCP\Files\File;
29
use OCP\Preview\IProvider2;
30
31
class Movie implements IProvider2 {
32
	public static $avconvBinary;
33
	public static $ffmpegBinary;
34
	public static $atomicParsleyBinary;
35
36
	/**
37
	 * Keep track of movies without artwork to avoid retries in same request
38
	 * @var array
39
	 */
40
	private $noArtworkIndex = array();
41
42
	/**
43
	 * {@inheritDoc}
44
	 */
45
	public function getMimeType() {
46
		return '/video\/.*/';
47
	}
48
49
	/**
50
	 * {@inheritDoc}
51
	 */
52
	public function getThumbnail(File $file, $maxX, $maxY, $scalingUp) {
53
		// TODO: use proc_open() and stream the source file ?
54
55
		$useFileDirectly = (!$file->isEncrypted() && !$file->isMounted());
56 View Code Duplication
		if ($useFileDirectly) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
57
			$absPath = $file->getStorage()->getLocalFile($file->getInternalPath());
58
		} else {
59
			$absPath = \OC::$server->getTempManager()->getTemporaryFile();
60
61
			$handle = $file->fopen('rb');
62
63
			// we better use 5MB (1024 * 1024 * 5 = 5242880) instead of 1MB.
64
			// in some cases 1MB was no enough to generate thumbnail
65
			$firstmb = stream_get_contents($handle, 5242880);
66
			file_put_contents($absPath, $firstmb);
67
			fclose($handle);
68
		}
69
70
		$result = $this->generateThumbNail($maxX, $maxY, $absPath, 5);
0 ignored issues
show
Security Bug introduced by
It seems like $absPath defined by $file->getStorage()->get...ile->getInternalPath()) on line 57 can also be of type false; however, OC\Preview\Movie::generateThumbNail() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
71
		if ($result === false) {
72
			$result = $this->generateThumbNail($maxX, $maxY, $absPath, 1);
0 ignored issues
show
Security Bug introduced by
It seems like $absPath defined by $file->getStorage()->get...ile->getInternalPath()) on line 57 can also be of type false; however, OC\Preview\Movie::generateThumbNail() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
73
			if ($result === false) {
74
				$result = $this->generateThumbNail($maxX, $maxY, $absPath, 0);
0 ignored issues
show
Security Bug introduced by
It seems like $absPath defined by $file->getStorage()->get...ile->getInternalPath()) on line 57 can also be of type false; however, OC\Preview\Movie::generateThumbNail() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
75
			}
76
		}
77
78
		if (!$useFileDirectly) {
79
			unlink($absPath);
80
		}
81
82
		return $result;
83
	}
84
85
	/**
86
	 * @param $absPath
87
	 * @return bool|string
88
	 */
89
	private function extractMp4CoverArtwork($absPath) {
90
		if (isset($this->noArtworkIndex[$absPath])) {
91
			return false;
92
		}
93
94
		if (self::$atomicParsleyBinary) {
95
			$suffix = substr($absPath, -4);
96
			if ('.mp4' === strtolower($suffix)) {
97
				$tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder();
98
				$tmpBase = $tmpFolder.'/Cover';
99
				$cmd = self::$atomicParsleyBinary . ' ' .
100
					escapeshellarg($absPath).
101
					' --extractPixToPath ' . escapeshellarg($tmpBase) .
102
					' > /dev/null 2>&1';
103
104
				exec($cmd, $output, $returnCode);
105
106
				if ($returnCode === 0) {
107
					$endings = array('.jpg', '.png');
108
					foreach ($endings as $ending) {
109
						$extractedFile = $tmpBase.'_artwork_1'.$ending;
110
						if (is_file($extractedFile) &&
111
							filesize($extractedFile) > 0) {
112
							return $extractedFile;
113
						}
114
					}
115
				}
116
			}
117
		}
118
		$this->noArtworkIndex[$absPath] = true;
119
		return false;
120
	}
121
122
	/**
123
	 * @param $absPath
124
	 * @param $second
125
	 * @return bool|string
126
	 */
127
	private function generateFromMovie($absPath, $second) {
128
		$tmpPath = \OC::$server->getTempManager()->getTemporaryFile();
129
130
		if (self::$avconvBinary) {
131
			$cmd = self::$avconvBinary . ' -y -ss ' . escapeshellarg($second) .
132
				' -i ' . escapeshellarg($absPath) .
133
				' -an -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) .
134
				' > /dev/null 2>&1';
135
		} else {
136
			$cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) .
137
				' -i ' . escapeshellarg($absPath) .
138
				' -f mjpeg -vframes 1' .
139
				' ' . escapeshellarg($tmpPath) .
140
				' > /dev/null 2>&1';
141
		}
142
143
		exec($cmd, $output, $returnCode);
144
145
		if ($returnCode === 0) {
146
			return $tmpPath;
147
		}
148
149
		return false;
150
	}
151
152
	/**
153
	 * @param int $maxX
154
	 * @param int $maxY
155
	 * @param string $absPath
156
	 * @param int $second
157
	 * @return bool|\OCP\IImage
158
	 */
159
	private function generateThumbNail($maxX, $maxY, $absPath, $second) {
160
161
		$extractedCover = $this->extractMp4CoverArtwork($absPath);
162
		if (false !== $extractedCover) {
163
			$tmpPath = $extractedCover;
164
		} else {
165
			$tmpPath = $this->generateFromMovie($absPath, $second);
166
		}
167
168
		if (is_string($tmpPath) && is_file($tmpPath)) {
169
			$image = new \OC_Image();
170
			$image->loadFromFile($tmpPath);
171
			unlink($tmpPath);
172
			if ($image->valid()) {
173
				$image->scaleDownToFit($maxX, $maxY);
174
				return $image;
175
			}
176
		}
177
		return false;
178
	}
179
180
	/**
181
	 * Check if a preview can be generated for $path
182
	 *
183
	 * @param File $file
184
	 * @return bool
185
	 * @since 10.1.0
186
	 */
187
	public function isAvailable(File $file) {
188
		return true;
189
	}
190
}
191