Completed
Push — master ( ea0e5d...10ff2a )
by mw
02:37
created

MediaPlayer::getFormatOutput()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 49
Code Lines 32

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 49
rs 9.2258
cc 3
eloc 32
nc 2
nop 1
1
<?php
2
3
namespace SRF;
4
5
use SMW\ResultPrinter;
6
use SMWQueryResult;
7
use SMWDataItem;
8
use SMWDataValue;
9
use SMWOutputs;
10
use SRFUtils;
11
12
use FormatJson;
13
use Skin;
14
use Html;
15
use Title;
16
use File;
17
18
/**
19
 * HTML5 Audio / Video media query printer
20
 *
21
 * This printer integrates jPlayer which is a HTML5 Audio / Video
22
 * Javascript libray under GPL/MIT license.
23
 *
24
 * @see http://www.semantic-mediawiki.org/wiki/Help:Media_format
25
 *
26
 * @since 1.9
27
 *
28
 * @file
29
 * @ingroup SRF
30
 * @ingroup QueryPrinter
31
 *
32
 * @licence GNU GPL v2 or later
33
 * @author mwjames
34
 */
35
36
/**
37
 * This printer integrates jPlayer which is a HTML5 Audio / Video
38
 * Javascript libray under GPL/MIT license.
39
 *
40
 * @ingroup SRF
41
 * @ingroup QueryPrinter
42
 */
43
class MediaPlayer extends ResultPrinter {
44
45
	/**
46
	 * Specifies valid mime types supported by jPlayer
47
	 * @var array
48
	 */
49
	protected $validMimeTypes = array( 'mp3', 'mp4', 'webm', 'webma', 'webmv', 'ogg', 'oga', 'ogv', 'm4v', 'm4a' );
50
51
	/**
52
	 * @see SMWResultPrinter::getName
53
	 * @return string
54
	 */
55
	public function getName() {
56
		return $this->msg( 'srf-printername-media' )->text();
57
	}
58
59
	/**
60
	 * @see SMWResultPrinter::getResultText
61
	 *
62
	 * @param SMWQueryResult $result
63
	 * @param $outputMode
64
	 *
65
	 * @return string
66
	 */
67
	protected function getResultText( SMWQueryResult $result, $outputMode ) {
68
69
		// Data processing
70
		$data = $this->getResultData( $result, $outputMode );
71
72
		// Check if the data processing returned any results otherwise just bailout
73
		if ( $data === array() ) {
74
			if ( $this->params['default'] !== '' ) {
75
				return $this->params['default'];
76
			} else{
77
				$result->addErrors( array( $this->msg( 'srf-no-results' )->inContentLanguage()->text() ) );
78
				return '';
79
			}
80
		} else {
81
			// Return formatted results
82
			return $this->getFormatOutput( $data );
83
		}
84
	}
85
86
	/**
87
	 * Returns an array with data
88
	 *
89
	 * @since 1.9
90
	 *
91
	 * @param SMWQueryResult $result
92
	 * @param $outputMode
93
	 *
94
	 * @return array
95
	 */
96
	protected function getResultData( SMWQueryResult $result, $outputMode ) {
97
98
		$data = array();
99
100
		/**
101
		 * Get all values for all rows that belong to the result set
102
		 * @var SMWResultArray $rows
103
		 */
104
		while ( $rows = $result->getNext() ) {
105
			$rowData = array();
106
			$mediaType = null;
107
			$mimeType = null;
108
109
			/**
110
			 * @var SMWResultArray $field
111
			 * @var SMWDataValue $dataValue
112
			 */
113
			foreach ( $rows as $field ) {
114
115
				// Label for the current property
116
				$propertyLabel = $field->getPrintRequest()->getLabel();
117
118
				// Label for the current subject
119
				$subjectLabel = $field->getResultSubject()->getTitle()->getFullText();
120
121
				if ( $propertyLabel === '' || $propertyLabel === '-' ){
122
					$propertyLabel = 'subject';
123
				} elseif ( $propertyLabel === 'poster' ){
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
124
					// Label "poster" is a special case where we set the media type to video in order
125
					// to use the same resources that can display video and cover art
126
					// $data['mediaTypes'][] = 'video';
127
				}
128
129
				// Check if the subject itself is a media source
130
				if ( $field->getResultSubject()->getTitle()->getNamespace() === NS_FILE && $mimeType === null ){
131
					list( $mediaType, $mimeType, $source ) = $this->getMediaSource( $field->getResultSubject()->getTitle() );
132
					$rowData[$mimeType] = $source;
133
				}
134
135
				while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
136
					// Get other data value item details
137
					$value = $this->getDataValueItem( $propertyLabel, $dataValue->getDataItem()->getDIType(), $dataValue, $mediaType, $mimeType, $rowData );
138
					$rowData[$propertyLabel] = $value;
139
				}
140
			}
141
142
			// Only select relevant source data that match the validMimeTypes
143
			if ( $mimeType !== '' && in_array( $mimeType, $this->validMimeTypes ) ){
144
				$data['mimeTypes'][]  = $mimeType;
145
				$data['mediaTypes'][] = $mediaType;
146
				$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...
147
			}
148
		}
149
150
		return $data;
151
	}
152
153
	/**
154
	 * Returns media source information
155
	 *
156
	 * @since 1.9
157
	 *
158
	 * @param Title $title
159
	 */
160
	private function getMediaSource( Title $title ){
161
162
		// Find the file source
163
		$source = wfFindFile ( $title );
164
		if ( $source ){
165
			// $source->getExtension() returns ogg even though it is a ogv/oga (same goes for m4p) file
166
			// this doesn't help much therefore we do it ourselves
167
			$extension = $source->getExtension();
168
169
			if ( in_array( $extension, array( 'ogg', 'oga', 'ogv' ) ) ) {
170
				$extension = strtolower( substr( $source->getName(), strrpos( $source->getName(), '.' ) + 1 ) );
171
172
				// Xiph.Org recommends that .ogg only be used for Ogg Vorbis audio files
173
				$extension = $extension === 'ogg' ? 'oga' : $extension;
174
175
				$params = array( $extension === 'ogv' ? 'video' : 'audio', $extension, $source->getUrl() );
176
			} elseif ( in_array( $extension, array( 'm4v', 'm4a', 'm4p' ) ) ) {
177
				$params = array( $extension === 'm4v' ? 'video' : 'audio', $extension, $source->getUrl() );
178
			} else {
179
				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...
180
				$params = array( $major, $extension, $source->getUrl() );
181
			}
182
		} else {
183
			$params = array();
184
		}
185
		return $params;
186
	}
187
188
	/**
189
	 * Returns single data value item
190
	 *
191
	 * @since 1.9
192
	 *
193
	 * @param string $label
194
	 * @param integer $type
195
	 * @param SMWDataValue $dataValue
196
	 * @param string $mediaType
197
	 * @param string $mimeType
198
	 *
199
	 * @return mixed
200
	 */
201
	private function getDataValueItem( &$label, $type, SMWDataValue $dataValue, &$mediaType, &$mimeType, &$rowData ) {
202
203
		if ( $type == SMWDataItem::TYPE_WIKIPAGE && $dataValue->getTitle()->getNamespace() === NS_FILE ) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getTitle() does only exist in the following sub-classes of SMWDataValue: SMWWikiPageValue. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
204
205
			if ( $label === 'source' && $mimeType === null ) {
206
207
				// Identify the media source
208
				// and get media information
209
				list( $mediaType, $mimeType, $source ) = $this->getMediaSource( $dataValue->getTitle() );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getTitle() does only exist in the following sub-classes of SMWDataValue: SMWWikiPageValue. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
210
				$label = $mimeType;
211
				return $source;
212
			} elseif ( $label === 'poster' ) {
213
				$mediaType = 'video';
214
215
				// Get the cover art image url
216
				$source = wfFindFile ( $dataValue->getTitle() );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMWDataValue as the method getTitle() does only exist in the following sub-classes of SMWDataValue: SMWWikiPageValue. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
217
				return $source->getUrl();
218
			}
219
		}
220
221
		if ( $type == SMWDataItem::TYPE_URI ) {
222
223
			$source = $dataValue->getDataItem()->getURI();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class SMWDataItem as the method getURI() does only exist in the following sub-classes of SMWDataItem: SMWDIUri. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
224
			$mimeType = '';
225
226
			// Get file extension from the URI
227
			$extension = strtolower( substr( $source, strrpos( $source, '.' ) + 1 ) );
228
229
			// Xiph.Org recommends that .ogg only be used for Ogg Vorbis audio files
230
			if ( in_array( $extension, array( 'ogg', 'oga', 'ogv' ) ) ) {
231
				$mimeType = $extension === 'ogg' ? 'oga' : $extension;
232
				$mediaType = $extension === 'ogv' ? 'video' : 'audio';
233
			} elseif ( in_array( $extension, array( 'm4v', 'm4a', 'm4p' ) ) ) {
234
				$mimeType = $extension;
235
				$mediaType = $extension === 'm4v' ? 'video' : 'audio';
236
			} else {
237
				$mimeType = $extension;
238
				$mediaType = strpos( $extension, 'v' ) !== false ? 'video' : 'audio';
239
			}
240
241
			if ( $mimeType !== '' ) {
242
				$rowData[$mimeType] = $source;
243
			}
244
245
			return $source;
246
		}
247
248
		return $dataValue->getWikiValue();
249
	}
250
251
	/**
252
	 * Prepare data for the output
253
	 *
254
	 * @since 1.9
255
	 *
256
	 * @param array $data
257
	 *
258
	 * @return string
259
	 */
260
	protected function getFormatOutput( $data ) {
261
262
		$ID = 'srf-' . uniqid();
263
		$this->isHTML = true;
264
265
		// Get the media/mime types
266
		if ( in_array( 'video', $data['mediaTypes'] ) ){
267
			$mediaType = 'video';
268
		} else {
269
			$mediaType = 'audio';
270
		}
271
		unset( $data['mediaTypes'] );
272
273
		$mimeTypes = array_unique( $data['mimeTypes'] );
274
		unset( $data['mimeTypes'] );
275
276
		// Reassign output array
277
		$output = array(
278
			'data'  => $data,
279
			'count' => count( $data ),
280
			'mediaType' => $mediaType,
281
			'mimeTypes' => implode( ',', $mimeTypes ),
282
			'inspector' => $this->params['inspector']
283
		);
284
285
		$requireHeadItem = array ( $ID => FormatJson::encode( $output ) );
286
		SMWOutputs::requireHeadItem( $ID, Skin::makeVariablesScript( $requireHeadItem ) );
287
288
		SMWOutputs::requireResource( 'ext.jquery.jplayer.skin.' . $this->params['theme'] );
289
		SMWOutputs::requireResource( 'ext.srf.formats.media' );
290
291
		$processing = SRFUtils::htmlProcessingElement();
292
293
		return Html::rawElement(
294
			'div',
295
			array(
296
				'class' => $this->params['class'] !== '' ? 'srf-media ' . $this->params['class'] : 'srf-media'
297
			),
298
			$processing . Html::element(
299
				'div',
300
				array(
301
					'id' => $ID,
302
					'class' => 'media-container',
303
					'style' => 'display:none;'
304
				),
305
				null
306
			)
307
		);
308
	}
309
310
	/**
311
	 * @see SMWResultPrinter::getParamDefinitions
312
	 *
313
	 * @since 1.9
314
	 *
315
	 * @param $definitions array of IParamDefinition
316
	 *
317
	 * @return array of IParamDefinition|array
318
	 */
319
	public function getParamDefinitions( array $definitions ) {
320
		$params = parent::getParamDefinitions( $definitions );
321
322
		$params['class'] = array(
323
			'message' => 'srf-paramdesc-class',
324
			'default' => '',
325
		);
326
327
		$params['theme'] = array(
328
			'message' => 'srf-paramdesc-theme',
329
			'default' => 'blue.monday',
330
			'values' => array( 'blue.monday', 'morning.light' ),
331
		);
332
333
		$params['inspector'] = array(
334
			'type' => 'boolean',
335
			'message' => 'srf-paramdesc-mediainspector',
336
			'default' => false,
337
		);
338
339
		return $params;
340
	}
341
}
342