Completed
Push — master ( 4411cb...e1c860 )
by Stephan
15:24
created

MediaPlayer::findFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
namespace SRF;
4
5
use File;
6
use FormatJson;
7
use Html;
8
use MediaWiki\MediaWikiServices;
9
use Skin;
10
use SMW\ResultPrinter;
11
use SMWDataItem;
12
use SMWDataValue;
13
use SMWOutputs;
14
use SMWQueryResult;
15
use SMWResultArray;
16
use SRFUtils;
17
use Title;
18
19
/**
20
 * HTML5 Audio / Video media query printer
21
 *
22
 * This printer integrates jPlayer which is a HTML5 Audio / Video
23
 * Javascript library under GPL/MIT license.
24
 *
25
 * @see http://www.semantic-mediawiki.org/wiki/Help:Media_format
26
 *
27
 * @since 1.9
28
 *
29
 * @file
30
 * @ingroup SRF
31
 * @ingroup QueryPrinter
32
 *
33
 * @licence GNU GPL v2 or later
34
 * @author mwjames
35
 */
36
37
/**
38
 * This printer integrates jPlayer which is a HTML5 Audio / Video
39
 * Javascript libray under GPL/MIT license.
40
 *
41
 * @ingroup SRF
42
 * @ingroup QueryPrinter
43
 */
44
class MediaPlayer extends ResultPrinter {
45
46
	/**
47
	 * Specifies valid mime types supported by jPlayer
48
	 *
49
	 * @var array
50
	 */
51
	protected $validMimeTypes = [ 'mp3', 'mp4', 'webm', 'webma', 'webmv', 'ogg', 'oga', 'ogv', 'm4v', 'm4a' ];
52
53
	/**
54
	 * @see SMWResultPrinter::getName
55
	 * @return string
56
	 */
57
	public function getName() {
58
		return $this->msg( 'srf-printername-media' )->text();
59
	}
60
61
	/**
62
	 * @see SMWResultPrinter::getResultText
63
	 *
64
	 * @param SMWQueryResult $result
65
	 * @param $outputMode
66
	 *
67
	 * @return string
68
	 */
69
	protected function getResultText( SMWQueryResult $result, $outputMode ) {
70
71
		// Data processing
72
		$data = $this->getResultData( $result, $outputMode );
73
74
		// Check if the data processing returned any results otherwise just bailout
75
		if ( $data !== [] ) {
76
			// Return formatted results
77
			return $this->getFormatOutput( $data );
78
		}
79
80
		if ( $this->params[ 'default' ] !== '' ) {
81
			return $this->params[ 'default' ];
82
		}
83
84
		$result->addErrors( [ $this->msg( 'srf-no-results' )->inContentLanguage()->text() ] );
85
		return '';
86
	}
87
88
	/**
89
	 * Returns an array with data
90
	 *
91
	 * @since 1.9
92
	 *
93
	 * @param SMWQueryResult $result
94
	 * @param $outputMode
95
	 *
96
	 * @return array
97
	 */
98
	protected function getResultData( SMWQueryResult $result, $outputMode ) {
99
100
		$data = [];
101
102
		/**
103
		 * Get all values for all rows that belong to the result set
104
		 *
105
		 * @var SMWResultArray $rows
106
		 */
107
		while ( $rows = $result->getNext() ) {
108
			$rowData = [];
109
			$mediaType = null;
110
			$mimeType = null;
111
112
			/**
113
			 * @var SMWResultArray $field
114
			 * @var SMWDataValue $dataValue
115
			 */
116
			foreach ( $rows as $field ) {
117
118
				// Label for the current property
119
				$propertyLabel = $field->getPrintRequest()->getLabel();
120
121
				// Label for the current subject
122
				$subjectLabel = $field->getResultSubject()->getTitle()->getFullText();
123
124
				if ( $propertyLabel === '' || $propertyLabel === '-' ) {
125
					$propertyLabel = 'subject';
126
				} elseif ( $propertyLabel === 'poster' ) {
127
					// Label "poster" is a special case where we set the media type to video in order
128
					// to use the same resources that can display video and cover art
129
					// $data['mediaTypes'][] = 'video';
130
				}
131
132
				// Check if the subject itself is a media source
133
				if ( $field->getResultSubject()->getTitle()->getNamespace() === NS_FILE && $mimeType === null ) {
134
					list( $mediaType, $mimeType, $source ) = $this->getMediaSource(
135
						$field->getResultSubject()->getTitle()
136
					);
137
					$rowData[$mimeType] = $source;
138
				}
139
140
				while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
141
					// Get other data value item details
142
					$value = $this->getDataValueItem(
143
						$propertyLabel,
144
						$dataValue,
145
						$mediaType,
146
						$mimeType,
147
						$rowData
148
					);
149
					$rowData[$propertyLabel] = $value;
150
				}
151
			}
152
153
			// Only select relevant source data that match the validMimeTypes
154
			if ( $mimeType !== '' && in_array( $mimeType, $this->validMimeTypes ) ) {
155
				$data['mimeTypes'][] = $mimeType;
156
				$data['mediaTypes'][] = $mediaType;
157
				$data[$subjectLabel] = $rowData;
0 ignored issues
show
Bug introduced by
The variable $subjectLabel does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
158
			}
159
		}
160
161
		return $data;
162
	}
163
164
	/**
165
	 * Returns media source information
166
	 *
167
	 * @since 1.9
168
	 *
169
	 * @param Title $title
170
	 *
171
	 * @return string[]
172
	 *
173
	 */
174
	private function getMediaSource( Title $title ) {
175
176
		// Find the file source
177
		$source = $this->findFile( $title );
178
179
		if ( $source ) {
180
			// $source->getExtension() returns ogg even though it is a ogv/oga (same goes for m4p) file
181
			// this doesn't help much therefore we do it ourselves
182
			$extension = $source->getExtension();
183
184
			if ( in_array( $extension, [ 'ogg', 'oga', 'ogv' ] ) ) {
185
				$extension = strtolower( substr( $source->getName(), strrpos( $source->getName(), '.' ) + 1 ) );
186
187
				// Xiph.Org recommends that .ogg only be used for Ogg Vorbis audio files
188
				$extension = $extension === 'ogg' ? 'oga' : $extension;
189
190
				$params = [ $extension === 'ogv' ? 'video' : 'audio', $extension, $source->getUrl() ];
191
			} elseif ( in_array( $extension, [ 'm4v', 'm4a', 'm4p' ] ) ) {
192
				$params = [ $extension === 'm4v' ? 'video' : 'audio', $extension, $source->getUrl() ];
193
			} else {
194
				list( $major, $minor ) = File::splitMime( $source->getMimeType() );
0 ignored issues
show
Unused Code introduced by
The assignment to $minor is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
195
				$params = [ $major, $extension, $source->getUrl() ];
196
			}
197
		} else {
198
			$params = [];
199
		}
200
		return $params;
201
	}
202
203
	/**
204
	 * Returns single data value item
205
	 *
206
	 * @param string $label
207
	 * @param SMWDataValue $dataValue
208
	 * @param string $mediaType
209
	 * @param string $mimeType
210
	 *
211
	 * @param $rowData
212
	 *
213
	 * @return mixed
214
	 * @since 1.9
215
	 *
216
	 */
217
	private function getDataValueItem( &$label, SMWDataValue $dataValue, &$mediaType, &$mimeType, &$rowData ) {
218
219
		$dataItem = $dataValue->getDataItem();
220
		$type = $dataItem->getDIType();
221
222
		if ( $type === SMWDataItem::TYPE_WIKIPAGE ) {
223
224
			$title = $dataItem->getTitle();
225
226
			if ( $title instanceof Title && $title->getNamespace() === NS_FILE ) {
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
227
228
				if ( $label === 'source' && $mimeType === null ) {
229
230
					// Identify the media source
231
					// and get media information
232
					list( $mediaType, $mimeType, $source ) = $this->getMediaSource( $title );
233
					$label = $mimeType;
234
					return $source;
235
				} elseif ( $label === 'poster' ) {
236
					$mediaType = 'video';
237
238
					// Get the cover art image url
239
					$source = $this->findFile( $title );
240
241
					return $source->getUrl();
242
				}
243
			}
244
		}
245
246
		if ( $type === SMWDataItem::TYPE_URI ) {
247
248
			$source = $dataItem->getURI();
249
			$mimeType = '';
250
251
			// Get file extension from the URI
252
			$extension = strtolower( substr( $source, strrpos( $source, '.' ) + 1 ) );
253
254
			// Xiph.Org recommends that .ogg only be used for Ogg Vorbis audio files
255
			if ( in_array( $extension, [ 'ogg', 'oga', 'ogv' ] ) ) {
256
				$mimeType = $extension === 'ogg' ? 'oga' : $extension;
257
				$mediaType = $extension === 'ogv' ? 'video' : 'audio';
258
			} elseif ( in_array( $extension, [ 'm4v', 'm4a', 'm4p' ] ) ) {
259
				$mimeType = $extension;
260
				$mediaType = $extension === 'm4v' ? 'video' : 'audio';
261
			} else {
262
				$mimeType = $extension;
263
				$mediaType = strpos( $extension, 'v' ) !== false ? 'video' : 'audio';
264
			}
265
266
			if ( $mimeType !== '' ) {
267
				$rowData[$mimeType] = $source;
268
			}
269
270
			return $source;
271
		}
272
273
		return $dataValue->getWikiValue();
274
	}
275
276
	/**
277
	 * Prepare data for the output
278
	 *
279
	 * @since 1.9
280
	 *
281
	 * @param array $data
282
	 *
283
	 * @return string
284
	 */
285
	protected function getFormatOutput( $data ) {
286
287
		$ID = 'srf-' . uniqid();
288
		$this->isHTML = true;
289
290
		// Get the media/mime types
291
		if ( in_array( 'video', $data['mediaTypes'] ) ) {
292
			$mediaType = 'video';
293
		} else {
294
			$mediaType = 'audio';
295
		}
296
		unset( $data['mediaTypes'] );
297
298
		$mimeTypes = array_unique( $data['mimeTypes'] );
299
		unset( $data['mimeTypes'] );
300
301
		// Reassign output array
302
		$output = [
303
			'data' => $data,
304
			'count' => count( $data ),
305
			'mediaType' => $mediaType,
306
			'mimeTypes' => implode( ',', $mimeTypes ),
307
			'inspector' => $this->params['inspector']
308
		];
309
310
		$requireHeadItem = [ $ID => FormatJson::encode( $output ) ];
311
		SMWOutputs::requireHeadItem( $ID, Skin::makeVariablesScript( $requireHeadItem, false ) );
312
313
		SMWOutputs::requireResource( 'ext.jquery.jplayer.skin.' . $this->params['theme'] );
314
		SMWOutputs::requireResource( 'ext.srf.formats.media' );
315
316
		$processing = SRFUtils::htmlProcessingElement();
317
318
		return Html::rawElement(
319
			'div',
320
			[
321
				'class' => $this->params['class'] !== '' ? 'srf-media ' . $this->params['class'] : 'srf-media'
322
			],
323
			$processing . Html::element(
324
				'div',
325
				[
326
					'id' => $ID,
327
					'class' => 'media-container',
328
					'style' => 'display:none;'
329
				],
330
				null
331
			)
332
		);
333
	}
334
335
	/**
336
	 * @see SMWResultPrinter::getParamDefinitions
337
	 *
338
	 * @since 1.9
339
	 *
340
	 * @param $definitions array of IParamDefinition
341
	 *
342
	 * @return array of IParamDefinition|array
343
	 */
344
	public function getParamDefinitions( array $definitions ) {
345
		$params = parent::getParamDefinitions( $definitions );
346
347
		$params['class'] = [
348
			'message' => 'srf-paramdesc-class',
349
			'default' => '',
350
		];
351
352
		$params['theme'] = [
353
			'message' => 'srf-paramdesc-theme',
354
			'default' => 'blue.monday',
355
			'values' => [ 'blue.monday', 'morning.light' ],
356
		];
357
358
		$params['inspector'] = [
359
			'type' => 'boolean',
360
			'message' => 'srf-paramdesc-mediainspector',
361
			'default' => false,
362
		];
363
364
		return $params;
365
	}
366
367
	/**
368
	 * @param Title $title
369
	 *
370
	 * @return bool|File
371
	 */
372
	private function findFile( Title $title ) {
373
374
		if ( method_exists( MediaWikiServices::class, 'getRepoGroup' ) ) {
375
			return MediaWikiServices::getInstance()->getRepoGroup()->findFile( $title );
376
		}
377
378
		return wfFindFile( $title ); // TODO: Remove when min MW version is 1.34
379
	}
380
}
381