Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 104
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
TranscodeStatusTable
0.00% covered (danger)
0.00%
0 / 104
0.00% covered (danger)
0.00%
0 / 9
702
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getHTML
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 codecFromTranscodeKey
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 getTranscodesTable
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
56
 transcodeRowsToTemplateParams
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
 getSourceUrl
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTranscodeDuration
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getTranscodeBitrate
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getStatusMsg
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3namespace MediaWiki\TimedMediaHandler;
4
5use File;
6use IContextSource;
7use MediaWiki\Html\Html;
8use MediaWiki\Html\TemplateParser;
9use MediaWiki\Linker\LinkRenderer;
10use MediaWiki\TimedMediaHandler\WebVideoTranscode\WebVideoTranscode;
11use MediaWiki\Utils\MWTimestamp;
12
13/**
14 * TranscodeStatusTable outputs a "transcode" status table to the ImagePage
15 *
16 * If logged in as autoconfirmed users can reset transcode states
17 * via the transcode api entry point
18 *
19 */
20class TranscodeStatusTable {
21    /** @var IContextSource */
22    private $context;
23
24    /** @var LinkRenderer */
25    private $linkRenderer;
26
27    /** @var TemplateParser */
28    private $templateParser;
29
30    /**
31     * @param IContextSource $context
32     * @param LinkRenderer $linkRenderer
33     */
34    public function __construct(
35        IContextSource $context,
36        LinkRenderer $linkRenderer
37    ) {
38        $this->context = $context;
39        $this->linkRenderer = $linkRenderer;
40        $this->templateParser = new TemplateParser( __DIR__ . '/../templates' );
41    }
42
43    /**
44     * @param File $file
45     * @return string
46     */
47    public function getHTML( $file ) {
48        // Add transcode table css and javascript:
49        $this->context->getOutput()->addModules( [ 'ext.tmh.transcodetable' ] );
50
51        $o = '<h2 id="transcodestatus">' . wfMessage( 'timedmedia-status-header' )->escaped() . '</h2>';
52        // Give the user a purge page link
53        $o .= $this->linkRenderer->makeLink(
54            $file->getTitle(),
55            $this->context->msg( 'timedmedia-update-status' )->text(),
56            [],
57            [ 'action' => 'purge' ]
58        );
59
60        $o .= $this->getTranscodesTable( $file );
61
62        return $o;
63    }
64
65    /**
66     * Get the video or audio codec for the defined transcode,
67     * for grouping/sorting purposes.
68     * @param string $key
69     * @return string
70     */
71    public static function codecFromTranscodeKey( $key ) {
72        if ( isset( WebVideoTranscode::$derivativeSettings[$key] ) ) {
73            $settings = WebVideoTranscode::$derivativeSettings[$key];
74            if ( isset( $settings['videoCodec'] ) ) {
75                return $settings['videoCodec'];
76            }
77
78            if ( isset( $settings['audioCodec'] ) ) {
79                return $settings['audioCodec'];
80            }
81            // else
82            // this this shouldn't happen...
83            // fall through
84        }
85        // else
86        // derivative type no longer defined or invalid def?
87        // fall through
88        return $key;
89    }
90
91    /**
92     * @param File $file
93     * @return string
94     */
95    public function getTranscodesTable( $file ) {
96        $transcodeRows = WebVideoTranscode::getTranscodeState( $file );
97
98        if ( !$transcodeRows ) {
99            return '<p>' . wfMessage( 'timedmedia-no-derivatives' )->escaped() . '</p>';
100        }
101
102        uksort( $transcodeRows, static function ( $a, $b ) {
103            $formatOrder = [ 'vp9', 'vp8', 'h264', 'theora', 'mpeg4', 'mjpeg', 'opus', 'mp3', 'vorbis', 'aac' ];
104
105            $aFormat = self::codecFromTranscodeKey( $a );
106            $bFormat = self::codecFromTranscodeKey( $b );
107            $aIndex = array_search( $aFormat, $formatOrder );
108            $bIndex = array_search( $bFormat, $formatOrder );
109
110            if ( $aIndex === false && $bIndex === false ) {
111                return -strnatcmp( $a, $b );
112            }
113            if ( $aIndex === false ) {
114                return 1;
115            }
116            if ( $bIndex === false ) {
117                return -1;
118            }
119            if ( $aIndex === $bIndex ) {
120                return -strnatcmp( $a, $b );
121            }
122            return ( $aIndex - $bIndex );
123        } );
124
125        return $this->templateParser->processTemplate(
126            'TranscodeStatusTable',
127            $this->transcodeRowsToTemplateParams( $transcodeRows, $file )
128        );
129    }
130
131    /**
132     * @param array $transcodeRows
133     * @param File $file
134     * @return array
135     */
136    private function transcodeRowsToTemplateParams( $transcodeRows, $file ) {
137        $transcodeRowsForTemplate = [];
138        foreach ( $transcodeRows as $transcodeKey => $state ) {
139            $transcodeRowsForTemplate[] = [
140                'transcodeKey' => $transcodeKey,
141                'msg-derivative-key' => wfMessage( 'timedmedia-derivative-' . $transcodeKey ),
142                'bitrate' => $this->getTranscodeBitrate( $file, $state ),
143                'transcode-success' => $state['time_success'] !== null,
144                'msg-timedmedia-download' => wfMessage( 'timedmedia-download' ),
145                // Download file
146                //
147                // Note the <a download> attribute only is applied on same-origin URLs.
148                // The "?download" query string append will work on servers configured
149                // the way the Wikimedia production servers are, but other sites that
150                // store files offsite may not have the same setup.
151                //
152                // On failure, these should devolve to either downloading or loading a
153                // media file inline, depending on the format and browser and server
154                // config.
155                'downloadUrl' => wfAppendQuery( self::getSourceUrl( $file, $transcodeKey ), 'download' ),
156                'msg-timedmedia-reset' => wfMessage( 'timedmedia-reset' ),
157                'html-transcode-status' => self::getStatusMsg( $file, $state ),
158                'transcode-duration' => $this->getTranscodeDuration( $file, $state ),
159            ];
160        }
161
162        $templateParams = [
163            'msg-timedmedia-transcodeinfo' => wfMessage( 'timedmedia-transcodeinfo' ),
164            'msg-timedmedia-transcodebitrate' => wfMessage( 'timedmedia-transcodebitrate' ),
165            'msg-timedmedia-not-ready' => wfMessage( 'timedmedia-not-ready' ),
166            'msg-timedmedia-direct-link' => wfMessage( 'timedmedia-direct-link' ),
167            'msg-timedmedia-actions' => wfMessage( 'timedmedia-actions' ),
168            'msg-timedmedia-status' => wfMessage( 'timedmedia-status' ),
169            'msg-timedmedia-transcodeduration' => wfMessage( 'timedmedia-transcodeduration' ),
170            'has-reset' => $this->context->getUser()->isAllowed( 'transcode-reset' ),
171            'transcodeRows' => $transcodeRowsForTemplate,
172        ];
173        return $templateParams;
174    }
175
176    /**
177     * @param File $file
178     * @param string $transcodeKey
179     * @return string
180     */
181    public static function getSourceUrl( $file, $transcodeKey ) {
182        return WebVideoTranscode::getTranscodedUrlForFile( $file, $transcodeKey );
183    }
184
185    /**
186     * @param File $file
187     * @param array $state
188     * @return string
189     */
190    public function getTranscodeDuration( File $file, array $state ) {
191        if ( $state['time_success'] !== null ) {
192            $startTime = (int)wfTimestamp( TS_UNIX, $state['time_startwork'] );
193            $endTime = (int)wfTimestamp( TS_UNIX, $state['time_success'] );
194            $delta = $endTime - $startTime;
195            return $this->context->getLanguage()->formatTimePeriod( $delta );
196        }
197        return '';
198    }
199
200    /**
201     * @param File $file
202     * @param array $state
203     * @return string
204     */
205    public function getTranscodeBitrate( File $file, array $state ) {
206        if ( $state['time_success'] !== null ) {
207            return $this->context->getLanguage()->formatBitrate( $state['final_bitrate'] );
208        }
209        return '';
210    }
211
212    /**
213     * @param File $file
214     * @param array $state
215     * @return string
216     */
217    public static function getStatusMsg( $file, $state ) {
218        // Check for success:
219        if ( $state['time_success'] !== null ) {
220            return wfMessage( 'timedmedia-completed-on' )
221                ->dateTimeParams( $state[ 'time_success' ] )->escaped();
222        }
223        // Check for error:
224        if ( $state['time_error'] !== null ) {
225            $attribs = [];
226            if ( $state['error'] !== null ) {
227                $attribs = [
228                    'class' => 'mw-tmh-pseudo-error-link',
229                    'data-error' => $state['error'],
230                ];
231            }
232
233            return Html::rawElement( 'span', $attribs,
234                wfMessage( 'timedmedia-error-on' )
235                    ->dateTimeParams( $state['time_error'] )->escaped()
236            );
237        }
238
239        // Check for started encoding
240        if ( $state['time_startwork'] !== null ) {
241            // Get the rough estimate of time done: ( this is not very costly considering everything else
242            // that happens in an action=purge video page request )
243            /*$filePath = WebVideoTranscode::getTargetEncodePath( $file, $state['key'] );
244            if ( is_file( $filePath ) ) {
245                $targetSize = WebVideoTranscode::getProjectedFileSize( $file, $state['key'] );
246                if ( $targetSize === false ) {
247                    $doneMsg = wfMessage( 'timedmedia-unknown-target-size',
248                        $wgLang->formatSize( filesize( $filePath ) ) )->escaped();
249                } else {
250                    $doneMsg = wfMessage('timedmedia-percent-done',
251                        round( filesize( $filePath ) / $targetSize, 2 ) )->escaped();
252                }
253            }    */
254            // Predicting percent done is not working well right now ( disabled for now )
255            $doneMsg = '';
256            return wfMessage(
257                'timedmedia-started-transcode',
258                ( new MWTimestamp( $state['time_startwork'] ) )->getRelativeTimestamp(), $doneMsg
259            )->escaped();
260        }
261        // Check for job added ( but not started encoding )
262        if ( $state['time_addjob'] !== null ) {
263            return wfMessage(
264                'timedmedia-in-job-queue',
265                ( new MWTimestamp( $state['time_addjob'] ) )->getRelativeTimestamp()
266            )->escaped();
267        }
268        // Return unknown status error:
269        return wfMessage( 'timedmedia-status-unknown' )->escaped();
270    }
271}