Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 88
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiTranscodeReset
0.00% covered (danger)
0.00%
0 / 88
0.00% covered (danger)
0.00%
0 / 9
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
72
 checkTimeSinceLastReset
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
30
 getStateResetTime
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 mustBePosted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isWriteMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 needsToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\TimedMediaHandler;
4
5use ApiBase;
6use ApiMain;
7use File;
8use ManualLogEntry;
9use MediaWiki\TimedMediaHandler\WebVideoTranscode\WebVideoTranscode;
10use MediaWiki\Title\Title;
11use RepoGroup;
12use Wikimedia\ParamValidator\ParamValidator;
13use Wikimedia\Rdbms\IConnectionProvider;
14
15/**
16 * Allows users with the 'transcode-reset' right to reset / re-run a transcode job.
17 *
18 * You can specify must specify a media asset title. You optionally can specify
19 * a transcode key, to only reset a single transcode job for a particular media asset.
20 * @ingroup API
21 */
22class ApiTranscodeReset extends ApiBase {
23    /** @var IConnectionProvider */
24    private $dbProvider;
25
26    /** @var RepoGroup */
27    private $repoGroup;
28
29    /** @var TranscodableChecker */
30    private $transcodableChecker;
31
32    /**
33     * @param ApiMain $main
34     * @param string $action
35     * @param IConnectionProvider $dbProvider
36     * @param RepoGroup $repoGroup
37     */
38    public function __construct(
39        ApiMain $main,
40        $action,
41        IConnectionProvider $dbProvider,
42        RepoGroup $repoGroup
43    ) {
44        parent::__construct( $main, $action );
45        $this->dbProvider = $dbProvider;
46        $this->repoGroup = $repoGroup;
47        $this->transcodableChecker = new TranscodableChecker(
48            $this->getConfig(),
49            $repoGroup
50        );
51    }
52
53    public function execute() {
54        // Check if transcoding is enabled on this wiki at all:
55        if ( !$this->getConfig()->get( 'EnableTranscode' ) ) {
56            $this->dieWithError( 'apierror-timedmedia-disabledtranscode', 'disabledtranscode' );
57        }
58
59        $params = $this->extractRequestParams();
60        $titleObj = Title::newFromText( $params['title'] );
61
62        // Make sure we have a valid Title
63        if ( !$titleObj || $titleObj->isExternal() ) {
64            $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
65        }
66
67        // Check that the user has permmission to reset transcodes on the file
68        $this->checkTitleUserPermissions( $titleObj, 'transcode-reset' );
69
70        // Make sure the title can be transcoded
71        if ( !$this->transcodableChecker->isTranscodableTitle( $titleObj ) ) {
72            $this->dieWithError(
73                [
74                    'apierror-timedmedia-invalidtranscodetitle',
75                    wfEscapeWikiText( $titleObj->getPrefixedText() )
76                ],
77                'invalidtranscodetitle'
78            );
79        }
80        $transcodeKey = false;
81        // Make sure it's an enabled transcode key we are trying to remove:
82        // ( if you update your transcode keys the api is not how you purge the database of expired keys )
83        if ( isset( $params['transcodekey'] ) ) {
84            $transcodeSet = WebVideoTranscode::enabledTranscodes();
85            if ( !in_array( $params['transcodekey'], $transcodeSet, true ) ) {
86                $this->dieWithError(
87                    [ 'apierror-timedmedia-badtranscodekey', wfEscapeWikiText( $params['transcodekey'] ) ],
88                    'badtranscodekey'
89                );
90            } else {
91                $transcodeKey = $params['transcodekey'];
92            }
93        }
94
95        // Don't reset if less than 1 hour has passed and we have no error )
96        $file = $this->repoGroup->findFile( $titleObj );
97        $timeSinceLastReset = $this->checkTimeSinceLastReset( $file, $transcodeKey );
98        $waitTimeForTranscodeReset = $this->getConfig()->get( 'WaitTimeForTranscodeReset' );
99        if ( $timeSinceLastReset < $waitTimeForTranscodeReset ) {
100            $msg = $this->msg(
101                'apierror-timedmedia-notenoughtimereset',
102            )->durationParams( $waitTimeForTranscodeReset - $timeSinceLastReset );
103            $this->dieWithError( $msg, 'notenoughtimereset' );
104        }
105
106        // All good do the transcode removal:
107        WebVideoTranscode::removeTranscodes( $file, $transcodeKey );
108
109        // Oh and we wanted to reset it, right? Trigger again.
110        $options = [
111            'manualOverride' => true,
112        ];
113        WebVideoTranscode::updateJobQueue( $file, $transcodeKey, $options );
114
115        $logEntry = new ManualLogEntry( 'timedmediahandler', 'resettranscode' );
116        $logEntry->setPerformer( $this->getUser() );
117        $logEntry->setTarget( $titleObj );
118        $logEntry->setParameters( [
119            '4::transcodekey' => $transcodeKey,
120        ] );
121        $logEntry->insert();
122
123        $this->getResult()->addValue( null, 'success', 'removed transcode' );
124    }
125
126    /**
127     * @param File $file
128     * @param string|false $transcodeKey
129     * @return int|string
130     */
131    public function checkTimeSinceLastReset( $file, $transcodeKey ) {
132        $dbw = $file->repo->getPrimaryDB();
133        $transcodeStates = WebVideoTranscode::getTranscodeState( $file, $dbw );
134        if ( $transcodeKey ) {
135            if ( !$transcodeStates[$transcodeKey] ) {
136                // transcode key not found
137                return $this->getConfig()->get( 'WaitTimeForTranscodeReset' ) + 1;
138            }
139            return $this->getStateResetTime( $transcodeStates[$transcodeKey] );
140        }
141        // least wait is set to reset time:
142        $leastWait = $this->getConfig()->get( 'WaitTimeForTranscodeReset' ) + 1;
143        // else check for lowest reset time
144        foreach ( $transcodeStates as $state ) {
145            $ctime = $this->getStateResetTime( $state );
146            if ( $ctime < $leastWait ) {
147                $leastWait = $ctime;
148            }
149        }
150        return $leastWait;
151    }
152
153    /**
154     * @param array $state
155     * @return int|string
156     */
157    public function getStateResetTime( $state ) {
158        $db = $this->dbProvider->getReplicaDatabase();
159        // if an error return waitTime +1
160        if ( $state['time_error'] !== null ) {
161            return $this->getConfig()->get( 'WaitTimeForTranscodeReset' ) + 1;
162        }
163        // return wait time from most recent event
164        foreach ( [ 'time_success', 'time_startwork', 'time_addjob' ] as $timeField ) {
165            if ( ( $state[ $timeField ] ) !== null ) {
166                return (int)$db->timestamp() - (int)$db->timestamp( $state[ $timeField ] );
167            }
168        }
169        // No time info, return resetWaitTime
170        return $this->getConfig()->get( 'WaitTimeForTranscodeReset' ) + 1;
171    }
172
173    /** @inheritDoc */
174    public function mustBePosted() {
175        return true;
176    }
177
178    /** @inheritDoc */
179    public function isWriteMode() {
180        return true;
181    }
182
183    /** @inheritDoc */
184    protected function getAllowedParams() {
185        return [
186            'title' => [
187                ParamValidator::PARAM_TYPE => 'string',
188                ParamValidator::PARAM_REQUIRED => true
189            ],
190            'transcodekey' => null,
191            'token' => null,
192        ];
193    }
194
195    /** @inheritDoc */
196    public function needsToken() {
197        return 'csrf';
198    }
199
200    /**
201     * @see ApiBase::getExamplesMessages()
202     * @return array
203     */
204    protected function getExamplesMessages() {
205        return [
206            'action=transcodereset&title=File:Clip.webm&token=123ABC'
207                => 'apihelp-transcodereset-example-1',
208            'action=transcodereset&title=File:Clip.webm&transcodekey=360_560kbs.webm&token=123ABC'
209                => 'apihelp-transcodereset-example-2',
210        ];
211    }
212}