Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 124 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
SpecialTranscodeStatistics | |
0.00% |
0 / 124 |
|
0.00% |
0 / 7 |
462 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
renderState | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
42 | |||
getTranscodes | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
12 | |||
getTranscodesTable | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
6 | |||
getStates | |
0.00% |
0 / 47 |
|
0.00% |
0 / 1 |
42 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Special:TranscodeStatistics |
4 | * |
5 | * Show some information about unprocessed jobs |
6 | * |
7 | * @file |
8 | * @ingroup SpecialPage |
9 | */ |
10 | |
11 | namespace MediaWiki\TimedMediaHandler; |
12 | |
13 | use MediaWiki\Output\OutputPage; |
14 | use MediaWiki\SpecialPage\SpecialPage; |
15 | use MediaWiki\TimedMediaHandler\WebVideoTranscode\WebVideoTranscode; |
16 | use MediaWiki\Title\Title; |
17 | use WANObjectCache; |
18 | use Wikimedia\Rdbms\Database; |
19 | use Wikimedia\Rdbms\IConnectionProvider; |
20 | use Wikimedia\Rdbms\SelectQueryBuilder; |
21 | |
22 | class SpecialTranscodeStatistics extends SpecialPage { |
23 | /** @var string[] */ |
24 | private $transcodeStates = [ |
25 | // Note: these queries should check prefixes of the index transcode_time_inx |
26 | // phpcs:ignore Generic.Files.LineLength.TooLong |
27 | 'active' => 'transcode_time_addjob IS NOT NULL AND transcode_time_startwork IS NOT NULL AND transcode_time_success IS NULL AND transcode_time_error IS NULL', |
28 | // phpcs:ignore Generic.Files.LineLength.TooLong |
29 | 'failed' => 'transcode_time_addjob IS NOT NULL AND transcode_time_startwork IS NOT NULL AND transcode_time_success IS NULL AND transcode_time_error IS NOT NULL', |
30 | // phpcs:ignore Generic.Files.LineLength.TooLong |
31 | 'queued' => 'transcode_time_addjob IS NOT NULL AND transcode_time_startwork IS NULL AND transcode_time_success IS NULL AND transcode_time_error IS NULL', |
32 | // phpcs:ignore Generic.Files.LineLength.TooLong |
33 | 'missing' => 'transcode_time_addjob IS NULL AND transcode_time_startwork IS NULL AND transcode_time_success IS NULL AND transcode_time_error IS NULL', |
34 | ]; |
35 | // index on transcode_time_addjob,transcode_time_startwork,transcode_time_error,transcode_key,transcode_image_name |
36 | |
37 | /** @var IConnectionProvider */ |
38 | private $dbProvider; |
39 | |
40 | /** @var WANObjectCache */ |
41 | private $cache; |
42 | |
43 | /** |
44 | * @param IConnectionProvider $dbProvider |
45 | * @param WANObjectCache $cache |
46 | */ |
47 | public function __construct( |
48 | IConnectionProvider $dbProvider, |
49 | WANObjectCache $cache |
50 | ) { |
51 | parent::__construct( 'TranscodeStatistics', 'transcode-status' ); |
52 | $this->dbProvider = $dbProvider; |
53 | $this->cache = $cache; |
54 | } |
55 | |
56 | /** @inheritDoc */ |
57 | public function execute( $par ) { |
58 | $this->setHeaders(); |
59 | $this->checkPermissions(); |
60 | $out = $this->getOutput(); |
61 | |
62 | $out->addModuleStyles( 'mediawiki.special' ); |
63 | |
64 | $states = $this->getStates(); |
65 | $this->renderState( $out, 'transcodes', $states, false ); |
66 | foreach ( $this->transcodeStates as $state => $condition ) { |
67 | $this->renderState( $out, $state, $states, $state !== 'missing' ); |
68 | } |
69 | } |
70 | |
71 | /** |
72 | * @param OutputPage $out |
73 | * @param string $state |
74 | * @param array $states |
75 | * @param bool $showTable |
76 | */ |
77 | private function renderState( $out, $state, $states, $showTable = true ) { |
78 | $allTranscodes = WebVideoTranscode::enabledTranscodes(); |
79 | if ( $states[ $state ][ 'total' ] ) { |
80 | // Give grep a chance to find the usages: |
81 | // timedmedia-derivative-state-transcodes, timedmedia-derivative-state-active, |
82 | // timedmedia-derivative-state-queued, timedmedia-derivative-state-failed, |
83 | // timedmedia-derivative-state-missing |
84 | $out->addHTML( |
85 | "<h2>" |
86 | . $this->msg( |
87 | 'timedmedia-derivative-state-' . $state |
88 | )->numParams( $states[ $state ]['total'] )->escaped() |
89 | . "</h2>" |
90 | ); |
91 | foreach ( $allTranscodes as $key ) { |
92 | if ( isset( $states[$state][$key] ) && $states[$state][$key] ) { |
93 | $out->addHTML( |
94 | htmlspecialchars( $this->getLanguage()->formatNum( $states[ $state ][ $key ] ) ) |
95 | . ' ' |
96 | . $this->msg( 'timedmedia-derivative-desc-' . $key )->escaped() |
97 | . "<br>" ); |
98 | } |
99 | } |
100 | if ( $showTable ) { |
101 | $out->addHTML( $this->getTranscodesTable( $state ) ); |
102 | } |
103 | } |
104 | } |
105 | |
106 | /** |
107 | * @param string $state |
108 | * @param int $limit |
109 | * |
110 | * @return false|array |
111 | */ |
112 | private function getTranscodes( $state, $limit = 50 ) { |
113 | $fname = __METHOD__; |
114 | |
115 | return $this->cache->getWithSetCallback( |
116 | $this->cache->makeKey( 'TimedMediaHandler-files', $state ), |
117 | $this->cache::TTL_MINUTE, |
118 | function ( $oldValue, &$ttl, array &$setOpts ) use ( $state, $limit, $fname ) { |
119 | $dbr = $this->dbProvider->getReplicaDatabase(); |
120 | $setOpts += Database::getCacheSetOptions( $dbr ); |
121 | |
122 | $files = []; |
123 | $res = $dbr->newSelectQueryBuilder() |
124 | ->select( [ 'transcode_image_name', 'transcode_key' ] ) |
125 | ->from( 'transcode' ) |
126 | ->where( $this->transcodeStates[ $state ] ) |
127 | ->limit( $limit ) |
128 | ->orderBy( [ |
129 | 'transcode_time_addjob', |
130 | 'transcode_time_startwork', |
131 | 'transcode_time_success', |
132 | 'transcode_time_error', |
133 | ], SelectQueryBuilder::SORT_DESC ) |
134 | ->caller( $fname ) |
135 | ->fetchResultSet(); |
136 | |
137 | foreach ( $res as $row ) { |
138 | $transcode = []; |
139 | foreach ( $row as $k => $v ) { |
140 | $transcode[ str_replace( 'transcode_', '', $k ) ] = $v; |
141 | } |
142 | $files[] = $transcode; |
143 | } |
144 | |
145 | return $files; |
146 | } |
147 | ); |
148 | } |
149 | |
150 | /** |
151 | * @param string $state |
152 | * @param int $limit |
153 | * |
154 | * @return string |
155 | */ |
156 | private function getTranscodesTable( $state, $limit = 50 ) { |
157 | $linkRenderer = $this->getLinkRenderer(); |
158 | $table = '<table class="wikitable">' . "\n" |
159 | . '<tr>' |
160 | . '<th>' . $this->msg( 'timedmedia-transcodeinfo' )->escaped() . '</th>' |
161 | . '<th>' . $this->msg( 'timedmedia-file' )->escaped() . '</th>' |
162 | . '</tr>' |
163 | . "\n"; |
164 | |
165 | foreach ( $this->getTranscodes( $state, $limit ) as $transcode ) { |
166 | $title = Title::newFromText( $transcode[ 'image_name' ], NS_FILE ); |
167 | $table .= '<tr>' |
168 | . '<td>' . $this->msg( |
169 | 'timedmedia-derivative-desc-' . $transcode[ 'key' ] |
170 | )->escaped() . '</td>' |
171 | . '<td>' . $linkRenderer->makeLink( $title, $transcode[ 'image_name' ] ) . '</td>' |
172 | . '</tr>' |
173 | . "\n"; |
174 | } |
175 | $table .= '</table>'; |
176 | return $table; |
177 | } |
178 | |
179 | /** |
180 | * @return array |
181 | */ |
182 | private function getStates() { |
183 | $fname = __METHOD__; |
184 | |
185 | return $this->cache->getWithSetCallback( |
186 | $this->cache->makeKey( 'TimedMediaHandler-states' ), |
187 | $this->cache::TTL_MINUTE, |
188 | function ( $oldValue, &$ttl, array &$setOpts ) use ( $fname ) { |
189 | $dbr = $this->dbProvider->getReplicaDatabase(); |
190 | $setOpts += Database::getCacheSetOptions( $dbr ); |
191 | |
192 | $allTranscodes = WebVideoTranscode::enabledTranscodes(); |
193 | |
194 | $states = []; |
195 | $states[ 'transcodes' ] = [ 'total' => 0 ]; |
196 | foreach ( $this->transcodeStates as $state => $condition ) { |
197 | $states[ $state ] = [ 'total' => 0 ]; |
198 | foreach ( $allTranscodes as $type ) { |
199 | // Important to pre-initialize, as can give |
200 | // warnings if you don't have a lot of things in transcode table. |
201 | $states[ $state ][ $type ] = 0; |
202 | } |
203 | } |
204 | foreach ( $this->transcodeStates as $state => $condition ) { |
205 | $cond = [ 'transcode_key' => $allTranscodes ]; |
206 | $cond[] = $condition; |
207 | $res = $dbr->newSelectQueryBuilder() |
208 | ->select( [ 'COUNT(*) as count', 'transcode_key' ] ) |
209 | ->from( 'transcode' ) |
210 | ->where( $cond ) |
211 | ->groupBy( 'transcode_key' ) |
212 | ->caller( $fname ) |
213 | ->fetchResultSet(); |
214 | foreach ( $res as $row ) { |
215 | $key = $row->transcode_key; |
216 | $states[ $state ][ $key ] = $row->count; |
217 | $states[ $state ][ 'total' ] += $states[ $state ][ $key ]; |
218 | } |
219 | } |
220 | $res = $dbr->newSelectQueryBuilder() |
221 | ->select( [ 'COUNT(*) as count', 'transcode_key' ] ) |
222 | ->from( 'transcode' ) |
223 | ->where( [ 'transcode_key' => $allTranscodes ] ) |
224 | ->groupBy( 'transcode_key' ) |
225 | ->caller( $fname ) |
226 | ->fetchResultSet(); |
227 | foreach ( $res as $row ) { |
228 | $key = $row->transcode_key; |
229 | $states[ 'transcodes' ][ $key ] = $row->count; |
230 | $states[ 'transcodes' ][ $key ] -= $states[ 'queued' ][ $key ]; |
231 | $states[ 'transcodes' ][ $key ] -= $states[ 'missing' ][ $key ]; |
232 | $states[ 'transcodes' ][ $key ] -= $states[ 'active' ][ $key ]; |
233 | $states[ 'transcodes' ][ $key ] -= $states[ 'failed' ][ $key ]; |
234 | $states[ 'transcodes' ][ 'total' ] += $states[ 'transcodes' ][ $key ]; |
235 | } |
236 | |
237 | return $states; |
238 | }, |
239 | [ 'lockTSE' => 30 ] |
240 | ); |
241 | } |
242 | |
243 | /** @inheritDoc */ |
244 | protected function getGroupName() { |
245 | return 'media'; |
246 | } |
247 | } |