Index: trunk/extensions/TimedMediaHandler/TranscodeStatusTable.php |
— | — | @@ -24,7 +24,7 @@ |
25 | 25 | . '<th>' . wfMsgHtml( 'timedmedia-transcodeinfo' ) . '</th>' |
26 | 26 | . '<th>'.wfMsgHtml( 'timedmedia-direct-link' ) .'</th>'; |
27 | 27 | |
28 | | - if( array_search( 'resetTranscode', $wgUser->getRights() )!== false ){ |
| 28 | + if( $wgUser->isAllowed( 'transcode-reset' ) ){ |
29 | 29 | $o.= '<th>' . wfMsgHtml( 'timedmedia-actions' ) . '</th>'; |
30 | 30 | } |
31 | 31 | |
— | — | @@ -57,9 +57,9 @@ |
58 | 58 | $o.='</td>'; |
59 | 59 | |
60 | 60 | // Check if we should include actions: |
61 | | - if( array_search( 'resetTranscode', $wgUser->getRights() )!== false ){ |
| 61 | + if( $wgUser->isAllowed( 'transcode-reset' ) ){ |
62 | 62 | // include reset transcode action buttons |
63 | | - |
| 63 | + $o.='<td class="transcodereset"><a href="#" data-transcodekey="' . htmlspecialchars( $transcodeKey ). '">' . wfMsg('timedmedia-reset') . '</a></td>'; |
64 | 64 | } |
65 | 65 | $o.='</tr>'; |
66 | 66 | } |
— | — | @@ -90,13 +90,14 @@ |
91 | 91 | } |
92 | 92 | return wfMsgHtml('timedmedia-error-on', $state['time_error'] ) . $showErrorLink; |
93 | 93 | } |
| 94 | + $db = wfGetDB( DB_SLAVE ); |
94 | 95 | // Check for started encoding |
95 | 96 | if( !is_null( $state['time_startwork'] ) ){ |
96 | | - $timePassed = wfTimestamp() - wfTimestamp( TS_UNIX, strtotime( $state['time_startwork'] ) ); |
| 97 | + $timePassed = wfTimestampNow() - $db->timestamp( $state['time_startwork'] ); |
97 | 98 | |
98 | 99 | // Get the rough estimate of time done: ( this is not very costly considering everything else |
99 | 100 | // that happens in an action=purge video page request ) |
100 | | - $filePath = WebVideoTranscode::getTargetEncodePath( $file, $state['key'] ); |
| 101 | + /*$filePath = WebVideoTranscode::getTargetEncodePath( $file, $state['key'] ); |
101 | 102 | if( is_file( $filePath ) ){ |
102 | 103 | $targetSize = WebVideoTranscode::getProjectedFileSize( $file, $state['key'] ); |
103 | 104 | if( $targetSize === false ){ |
— | — | @@ -104,12 +105,14 @@ |
105 | 106 | } else { |
106 | 107 | $doneMsg = wfMsgHtml('timedmedia-percent-done', round( filesize( $filePath ) / $targetSize, 2 ) ); |
107 | 108 | } |
108 | | - } |
| 109 | + } */ |
| 110 | + // predicting percent done is not working well right now ( disabled for now ) |
| 111 | + $doneMsg = ''; |
109 | 112 | return wfMsgHtml('timedmedia-started-transcode', self::getTimePassedMsg( $timePassed ), $doneMsg ); |
110 | 113 | } |
111 | 114 | // Check for job added ( but not started encoding ) |
112 | 115 | if( !is_null( $state['time_addjob'] ) ){ |
113 | | - $timePassed = wfTimestamp() - wfTimestamp( TS_UNIX, strtotime( $state['time_addjob'] ) ); |
| 116 | + $timePassed = wfTimestampNow() - $db->timestamp( $state['time_addjob'] ) ; |
114 | 117 | return wfMsgHtml('timedmedia-in-job-queue', self::getTimePassedMsg( $timePassed ) ); |
115 | 118 | } |
116 | 119 | // Return unknown status error: |
— | — | @@ -120,21 +123,18 @@ |
121 | 124 | $t['hours'] = floor($timePassed/60/60)%24; |
122 | 125 | $t['minutes'] = floor($timePassed/60)%60; |
123 | 126 | $t['seconds'] = $timePassed%60; |
| 127 | + |
124 | 128 | foreach( $t as $k => $v ){ |
125 | 129 | if($v == 0 ){ |
126 | | - $t[$k] = ''; |
| 130 | + unset( $t[$k] ); |
127 | 131 | }else{ |
128 | 132 | $t[$k] = wfMsg( 'timedmedia-' . $k, $v); |
129 | 133 | } |
130 | 134 | } |
131 | | - // Add the tailing and $1 text: |
132 | | - if( $t['seconds'] !== '' ){ |
133 | | - $t['seconds'] = wfMsg('timedmedia-and', $t['seconds'] ); |
134 | | - } else if( $t['minutes'] !== '' ){ |
135 | | - $t['minutes'] = wfMsg('timedmedia-and', $t['minutes'] ); |
136 | | - } else if( $t['hours'] !== '' ){ |
137 | | - $t['hours'] = wfMsg('timedmedia-and', $t['hours'] ); |
| 135 | + if( count( $t ) == 0 ){ |
| 136 | + $t = array( wfMsg( 'timedmedia-seconds', 0) ) ; |
138 | 137 | } |
139 | | - return wfMsgHtml('timedmedia-days-hours-min-sec-time', $t['days'], $t['hours'], $t['minutes'], $t['seconds'] ); |
| 138 | + // Call to the correct set of significant measurements: |
| 139 | + return wfMsgHtml( 'timedmedia-time-' . count($t) . '-measurements', $t); |
140 | 140 | } |
141 | 141 | } |
\ No newline at end of file |
Index: trunk/extensions/TimedMediaHandler/TimedMediaHandler.php |
— | — | @@ -29,10 +29,10 @@ |
30 | 30 | /*** end MwEmbed module configuration: ******************************/ |
31 | 31 | |
32 | 32 | // Add the rest transcode right: |
33 | | -$wgAvailableRights[] = 'resetTranscode'; |
| 33 | +$wgAvailableRights[] = 'transcode-reset'; |
34 | 34 | |
35 | 35 | // Which users can restart failed or expired transcode jobs |
36 | | -$wgGroupPermissions['sysop']['resetTranscode'] = true; |
| 36 | +$wgGroupPermissions['sysop']['transcode-reset'] = true; |
37 | 37 | |
38 | 38 | // The minimum size for an embed video player: |
39 | 39 | $wgMinimumVideoPlayerSize = 200; |
— | — | @@ -48,7 +48,7 @@ |
49 | 49 | $wgEnableIframeEmbed = true; |
50 | 50 | |
51 | 51 | // If transcoding is enabled for this wiki ( if disabled, no transcode jobs are added and no |
52 | | -// transcode status is displayed. Note if remote embeding an asset we will still check if |
| 52 | +// transcode status is displayed. Note if remote embedding an asset we will still check if |
53 | 53 | // the remote repo has transcoding enabled and associated flavors for that media embed. |
54 | 54 | $wgEnableTranscode = true; |
55 | 55 | |
— | — | @@ -82,9 +82,6 @@ |
83 | 83 | // A high end web streamable ogg video |
84 | 84 | WebVideoTranscode::ENC_OGV_9MBS, |
85 | 85 | |
86 | | - // High quality 720P ogg video: |
87 | | - WebVideoTranscode::ENC_OGV_HQ_VBR, |
88 | | - |
89 | 86 | // A web streamable WebM video |
90 | 87 | WebVideoTranscode::ENC_WEBM_9MBS, |
91 | 88 | |
— | — | @@ -130,8 +127,18 @@ |
131 | 128 | |
132 | 129 | // Transcode support |
133 | 130 | $wgAutoloadClasses['WebVideoTranscodeJob'] = "$timedMediaDir/WebVideoTranscode/WebVideoTranscodeJob.php"; |
| 131 | + |
| 132 | +// Api modules: |
134 | 133 | $wgAutoloadClasses['ApiQueryVideoInfo'] = "$timedMediaDir/ApiQueryVideoInfo.php"; |
| 134 | +$wgAPIPropModules ['videoinfo'] = 'ApiQueryVideoInfo'; |
135 | 135 | |
| 136 | +$wgAutoloadClasses['ApiTranscodeStatus'] = "$timedMediaDir/ApiTranscodeStatus.php"; |
| 137 | +$wgAPIPropModules ['transcodestatus'] = 'ApiTranscodeStatus'; |
| 138 | + |
| 139 | +$wgAutoloadClasses['ApiTranscodeReset'] = "$timedMediaDir/ApiTranscodeReset.php"; |
| 140 | +$wgAPIModules['transcodereset'] = 'ApiTranscodeReset'; |
| 141 | + |
| 142 | + |
136 | 143 | // Localization |
137 | 144 | $wgExtensionMessagesFiles['TimedMediaHandler'] = "$timedMediaDir/TimedMediaHandler.i18n.php"; |
138 | 145 | $wgExtensionMessagesFiles['TimedMediaHandlerMagic'] = "$timedMediaDir/TimedMediaHandler.i18n.magic.php"; |
— | — | @@ -145,7 +152,7 @@ |
146 | 153 | $wgExtensionCredits['media'][] = array( |
147 | 154 | 'path' => __FILE__, |
148 | 155 | 'name' => 'TimedMediaHandler', |
149 | | - 'author' => array( 'Michael Dale', 'Tim Starling' ), |
| 156 | + 'author' => array( 'Michael Dale', 'Tim Starling', 'James Heinrich' ), |
150 | 157 | 'url' => 'http://www.mediawiki.org/wiki/Extension:TimedMediaHandler', |
151 | 158 | 'descriptionmsg' => 'timedmedia-desc', |
152 | 159 | 'version' => '0.2', |
Index: trunk/extensions/TimedMediaHandler/ApiQueryVideoInfo.php |
— | — | @@ -6,6 +6,12 @@ |
7 | 7 | * see: http://www.mediawiki.org/wiki/User:Catrope/Extension_review/TimedMediaHandler#ApiQueryVideoInfo.php |
8 | 8 | * |
9 | 9 | */ |
| 10 | + |
| 11 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 12 | + // Eclipse helper - will be ignored in production |
| 13 | + require_once( "ApiBase.php" ); |
| 14 | +} |
| 15 | + |
10 | 16 | class ApiQueryVideoInfo extends ApiQueryImageInfo { |
11 | 17 | |
12 | 18 | public function __construct( $query, $moduleName, $prefix = 'vi' ) { |
Index: trunk/extensions/TimedMediaHandler/TimedMediaHandler.i18n.php |
— | — | @@ -37,10 +37,10 @@ |
38 | 38 | |
39 | 39 | 'timedmedia-status-header' => 'Transcode status', |
40 | 40 | 'timedmedia-update-status' => 'Update transcode status', |
41 | | - 'timedmedia-status' => 'status', |
42 | | - 'timedmedia-status-unknown' => 'unknown status', |
| 41 | + 'timedmedia-status' => 'Status', |
| 42 | + 'timedmedia-status-unknown' => 'Unknown status', |
43 | 43 | 'timedmedia-transcodeinfo' => 'Transcode derivative description', |
44 | | - 'timedmedia-actions' => 'actions', |
| 44 | + 'timedmedia-actions' => 'Actions', |
45 | 45 | 'timedmedia-direct-link' => 'Download derivative', |
46 | 46 | 'timedmedia-not-ready' => 'Not ready', |
47 | 47 | 'timedmedia-completed-on' => 'Completed transcode $1', |
— | — | @@ -53,9 +53,15 @@ |
54 | 54 | 'timedmedia-hours' => '{{PLURAL:$1|hour|$1 hours}}', |
55 | 55 | 'timedmedia-minutes' => '{{PLURAL:$1|minute|$1 minutes}}', |
56 | 56 | 'timedmedia-seconds' => '{{PLURAL:$1|second|$1 seconds}}', |
57 | | - 'timedmedia-and' => 'and $1', |
58 | | - 'timedmedia-days-hours-min-sec-time' => '$1, $2, $3 $4', |
| 57 | + 'timedmedia-time-4-measurements' => '$1, $2, $3 and $4', |
| 58 | + 'timedmedia-time-3-measurements' => '$1, $2 and $3', |
| 59 | + 'timedmedia-time-2-measurements' => '$1 and $2', |
| 60 | + 'timedmedia-time-1-measurements' => '$1', |
59 | 61 | 'timedmedia-show-error' => 'Show error', |
| 62 | + 'timedmedia-reset' => 'Reset transcode', |
| 63 | + 'timedmedia-reset-confirm' => 'Resetting this transcode, will remove any existing file ( if present ), and it will re-add the transcode to the job queue. It will take some time to re-transcode. <br /><br />' . |
| 64 | +'Are you sure you want to proceed?', |
| 65 | + 'timedmedia-reset-error' => 'Error in reseting transcode job', |
60 | 66 | |
61 | 67 | // Original uploaded asset |
62 | 68 | 'timedmedia-ogg' => 'Ogg', |
Index: trunk/extensions/TimedMediaHandler/ApiTranscodeReset.php |
— | — | @@ -0,0 +1,104 @@ |
| 2 | +<?php |
| 3 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 4 | + // Eclipse helper - will be ignored in production |
| 5 | + require_once( 'ApiBase.php' ); |
| 6 | +} |
| 7 | + |
| 8 | +/** |
| 9 | + * Allows users with the 'transcode-reset' right to reset / re-run a transcode job. |
| 10 | + * |
| 11 | + * You can specify must specify a media asset title. You optionally can specify |
| 12 | + * a transcode key, to only reset a single transcode job for a particular media asset. |
| 13 | + * @ingroup API |
| 14 | + */ |
| 15 | +class ApiTranscodeReset extends ApiBase { |
| 16 | + |
| 17 | + public function execute() { |
| 18 | + global $wgUser, $wgEnabledTranscodeSet, $wgEnableTranscode; |
| 19 | + // Check if transcoding is enabled on this wiki at all: |
| 20 | + if( !$wgEnableTranscode ){ |
| 21 | + $this->dieUsage( 'Transcode is disabled on this wiki', 'disabledtranscode' ); |
| 22 | + } |
| 23 | + |
| 24 | + // Confirm the user has the transcode-reset right |
| 25 | + if( !$wgUser->isAllowed( 'transcode-reset' ) ){ |
| 26 | + $this->dieUsage( 'You don\'t have permission to reset transcodes', 'missingpermission' ); |
| 27 | + } |
| 28 | + $params = $this->extractRequestParams(); |
| 29 | + |
| 30 | + // Make sure we have a valid Title |
| 31 | + $titleObj = Title::newFromText( $params['title'] ); |
| 32 | + if ( !$titleObj || $titleObj->isExternal() ) { |
| 33 | + $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); |
| 34 | + } |
| 35 | + // Make sure the title can be transcoded |
| 36 | + if( !TimedMediaHandlerHooks::isTranscodableTitle( $titleObj ) ){ |
| 37 | + $this->dieUsage( array( 'invalidtranscodetitle', $params['title'] ) ); |
| 38 | + } |
| 39 | + $file = wfFindFile( $titleObj ); |
| 40 | + WebVideoTranscode::removeTranscodeJobs( $file, isset( $params['transcodekey'] )? $params['transcodekey']: false ); |
| 41 | + |
| 42 | + $this->getResult()->addValue(null, 'success', 'removed transcode'); |
| 43 | + } |
| 44 | + |
| 45 | + public function mustBePosted() { |
| 46 | + return true; |
| 47 | + } |
| 48 | + |
| 49 | + public function isWriteMode() { |
| 50 | + return true; |
| 51 | + } |
| 52 | + |
| 53 | + protected function getDescription() { |
| 54 | + return 'Users with the \'transcode-reset\' right can reset and re-run a transcode job'; |
| 55 | + } |
| 56 | + |
| 57 | + public function getPossibleErrors() { |
| 58 | + return array_merge( parent::getPossibleErrors(), array( |
| 59 | + array( 'code' => 'missingpermission', 'info' => 'You don\'t have permission to reset transcodes'), |
| 60 | + array( 'code' => 'disabledtranscode', 'info' => 'Transcode is disabled on this wiki' ), |
| 61 | + array( 'invalidtitle', 'title' ), |
| 62 | + ) ); |
| 63 | + } |
| 64 | + |
| 65 | + protected function getAllowedParams() { |
| 66 | + return array( |
| 67 | + 'title' => array( |
| 68 | + ApiBase::PARAM_TYPE => 'string', |
| 69 | + ApiBase::PARAM_REQUIRED => true |
| 70 | + ), |
| 71 | + 'transcodekey' => null, |
| 72 | + 'token' => null, |
| 73 | + ); |
| 74 | + } |
| 75 | + |
| 76 | + protected function getParamDescription() { |
| 77 | + return array( |
| 78 | + 'title' => 'The media file title', |
| 79 | + 'transcodekey' => 'The transcode key you wish to reset', |
| 80 | + 'token' => 'You can get one of these through prop=info', |
| 81 | + ); |
| 82 | + } |
| 83 | + |
| 84 | + public function needsToken() { |
| 85 | + return true; |
| 86 | + } |
| 87 | + |
| 88 | + public function getTokenSalt() { |
| 89 | + return ''; |
| 90 | + } |
| 91 | + |
| 92 | + protected function getExamples() { |
| 93 | + return array( |
| 94 | + 'Reset all transcodes for Clip.webm :', |
| 95 | + ' api.php?action=transcodereset&title=File:Clip.webm&token=%2B\\', |
| 96 | + 'Reset the \'360_560kbs.webm\' transcode key for clip.webm. Get a list of transcode keys via a \'transcodestatus\' query', |
| 97 | + ' api.php?action=transcodereset&title=File:Clip.webm&transcodekey=360_560kbs.webm&token=%2B\\', |
| 98 | + ); |
| 99 | + } |
| 100 | + |
| 101 | + public function getVersion() { |
| 102 | + return __CLASS__ . ': $Id: ApiTranscodeReset.php 89751 $'; |
| 103 | + } |
| 104 | + |
| 105 | +} |
\ No newline at end of file |
Index: trunk/extensions/TimedMediaHandler/ApiTranscodeStatus.php |
— | — | @@ -0,0 +1,62 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Allows for api queries to get detailed information about the transcode state of a particular |
| 5 | + * media asset. ( basically directly returns the transcode status table ) |
| 6 | + * |
| 7 | + * This information can be used to generate status tables similar to the one seen |
| 8 | + * on the image page. |
| 9 | + */ |
| 10 | + |
| 11 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 12 | + // Eclipse helper - will be ignored in production |
| 13 | + require_once( "ApiBase.php" ); |
| 14 | +} |
| 15 | + |
| 16 | +class ApiTranscodeStatus extends ApiQueryBase { |
| 17 | + public function execute() { |
| 18 | + $pageIds = $this->getPageSet()->getAllTitlesByNamespace(); |
| 19 | + // Make sure we have files in the title set: |
| 20 | + if ( !empty( $pageIds[NS_FILE] ) ) { |
| 21 | + $titles = array_keys( $pageIds[NS_FILE] ); |
| 22 | + asort( $titles ); // Ensure the order is always the same |
| 23 | + |
| 24 | + $result = $this->getResult(); |
| 25 | + $images = RepoGroup::singleton()->findFiles( $titles ); |
| 26 | + foreach ( $images as $img ) { |
| 27 | + // if its a "transcode" add the transcode status table output |
| 28 | + if( TimedMediaHandlerHooks::isTranscodableTitle( $img->getTitle() ) ){ |
| 29 | + $transcodeStatus = WebVideoTranscode::getTranscodeState( $img->getTitle()->getDBKey() ); |
| 30 | + // remove useless properties |
| 31 | + foreach($transcodeStatus as $key=>&$val ){ |
| 32 | + unset( $val['id'] ); |
| 33 | + unset( $val['image_name']); |
| 34 | + unset( $val['key'] ); |
| 35 | + } |
| 36 | + $result->addValue( array( 'query', 'pages', $img->getTitle()->getArticleID() ), 'transcodestatus', $transcodeStatus ); |
| 37 | + } |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | + public function getCacheMode( $params ) { |
| 42 | + return 'public'; |
| 43 | + } |
| 44 | + public function getAllowedParams() { |
| 45 | + return array(); |
| 46 | + } |
| 47 | + |
| 48 | + public function getDescription() { |
| 49 | + return array( |
| 50 | + 'Get transcode status for a given file page' |
| 51 | + ); |
| 52 | + } |
| 53 | + |
| 54 | + protected function getExamples() { |
| 55 | + return array ( |
| 56 | + 'api.php?action=query&prop=transcodestatus&titles=File:Clip.webm', |
| 57 | + ); |
| 58 | + } |
| 59 | + |
| 60 | + public function getVersion() { |
| 61 | + return __CLASS__ . ': $Id: ApiQueryFlagged.php 85713 2011-04-09 04:30:07Z aaron $'; |
| 62 | + } |
| 63 | +} |
\ No newline at end of file |
Index: trunk/extensions/TimedMediaHandler/WebVideoTranscode/WebVideoTranscodeJob.php |
— | — | @@ -40,21 +40,34 @@ |
41 | 41 | |
42 | 42 | // Build the destination target |
43 | 43 | $destinationFile = WebVideoTranscode::getTargetEncodePath( $file, $transcodeKey ); |
44 | | - |
45 | 44 | if( ! isset( WebVideoTranscode::$derivativeSettings[ $transcodeKey ] )){ |
46 | | - $this->output("Transcode key $transcodeKey not found, skipping"); |
| 45 | + $this->output( "Transcode key $transcodeKey not found, skipping" ); |
47 | 46 | return false; |
48 | 47 | } |
49 | 48 | $options = WebVideoTranscode::$derivativeSettings[ $transcodeKey ]; |
50 | 49 | |
51 | 50 | $this->output( "Encoding to codec: " . $options['videoCodec'] ); |
52 | 51 | |
53 | | - $db =wfGetDB( DB_MASTER ); |
| 52 | + $dbw = wfGetDB( DB_MASTER ); |
| 53 | + $db = wfGetDB( DB_SLAVE ); |
54 | 54 | |
55 | | - // Update the transcode table letting it know we have "started work": |
56 | | - $db->update( |
| 55 | + // Check if we have "already started" the transcode ( possible error ) |
| 56 | + $dbStartTime = $db->selectField( 'transcode', 'transcode_time_startwork', |
| 57 | + array( 'transcode_image_name = ' . $db->addQuotes( $this->title->getDBKey() ), |
| 58 | + 'transcode_key =' . $db->addQuotes( $transcodeKey ) ) |
| 59 | + ); |
| 60 | + if( ! is_null( $dbStartTime ) ){ |
| 61 | + $this->output( 'Error, running transcode job, for job that has already started' ); |
| 62 | + // back out of this job. ( if there was a transcode error it should be restarted with api transcode-reset ) |
| 63 | + // not some strange out-of-order error. |
| 64 | + return false; |
| 65 | + } |
| 66 | + |
| 67 | + // Update the transcode table letting it know we have "started work": |
| 68 | + $jobStartTimeCache = $db->timestamp(); |
| 69 | + $dbw->update( |
57 | 70 | 'transcode', |
58 | | - array( 'transcode_time_startwork' => $db->timestamp() ), |
| 71 | + array( 'transcode_time_startwork' => $jobStartTimeCache ), |
59 | 72 | array( |
60 | 73 | 'transcode_image_name' => $this->title->getDBkey(), |
61 | 74 | 'transcode_key' => $transcodeKey |
— | — | @@ -62,8 +75,8 @@ |
63 | 76 | __METHOD__, |
64 | 77 | array( 'LIMIT' => 1 ) |
65 | 78 | ); |
66 | | - |
67 | 79 | |
| 80 | + |
68 | 81 | // Check the codec see which encode method to call; |
69 | 82 | if( $options['videoCodec'] == 'theora' ){ |
70 | 83 | $status = $this->ffmpeg2TheoraEncode( $file, $destinationFile, $options ); |
— | — | @@ -92,6 +105,25 @@ |
93 | 106 | $status = 'Error unknown target encode codec:' . $options['codec']; |
94 | 107 | } |
95 | 108 | |
| 109 | + // Do a quick check to confirm the job was not restarted or removed while we were transcoding |
| 110 | + // Confirm the in memory $jobStartTimeCache matches db start time |
| 111 | + $dbStartTime = $db->selectField( 'transcode', 'transcode_time_startwork', |
| 112 | + array( 'transcode_image_name = ' . $db->addQuotes( $this->title->getDBKey() ), |
| 113 | + 'transcode_key =' . $db->addQuotes( $transcodeKey ) |
| 114 | + ) |
| 115 | + ); |
| 116 | + |
| 117 | + // Check for ( hopefully rare ) issue of or job restarted while transcode in progress |
| 118 | + if( $jobStartTimeCache != $dbStartTime ){ |
| 119 | + wfDebug('Possible Error, transcode task restarted, removed, or completed while transcode was in progress'); |
| 120 | + // if an error; just error out, we can't remove temp files or update states, because the new job may be doing stuff. |
| 121 | + if( $status !== true ){ |
| 122 | + return false; |
| 123 | + } |
| 124 | + // else just continue with db updates, and when the new job comes around it won't start because it will see |
| 125 | + // that the job has already been started. |
| 126 | + } |
| 127 | + |
96 | 128 | // If status is oky move the file to its final destination. ( timedMediaHandler will look for it there ) |
97 | 129 | if( $status === true ){ |
98 | 130 | $finalDerivativeFilePath = WebVideoTranscode::getDerivativeFilePath( $file, $transcodeKey); |
— | — | @@ -100,7 +132,7 @@ |
101 | 133 | wfRestoreWarnings(); |
102 | 134 | $bitrate = round( intval( filesize( $finalDerivativeFilePath ) / $file->getLength() ) * 8 ); |
103 | 135 | // Update the transcode table with success time: |
104 | | - $db->update( |
| 136 | + $dbw->update( |
105 | 137 | 'transcode', |
106 | 138 | array( |
107 | 139 | 'transcode_time_success' => $db->timestamp(), |
— | — | @@ -113,10 +145,10 @@ |
114 | 146 | __METHOD__, |
115 | 147 | array( 'LIMIT' => 1 ) |
116 | 148 | ); |
117 | | - $this->invalidateCache(); |
| 149 | + WebVideoTranscode::invalidatePagesWithAsset( $this->title ); |
118 | 150 | } else { |
119 | 151 | // Update the transcode table with failure time and error |
120 | | - $db->update( |
| 152 | + $dbw->update( |
121 | 153 | 'transcode', |
122 | 154 | array( |
123 | 155 | 'transcode_time_error' => $db->timestamp(), |
— | — | @@ -128,36 +160,15 @@ |
129 | 161 | ), |
130 | 162 | __METHOD__, |
131 | 163 | array( 'LIMIT' => 1 ) |
132 | | - ); |
| 164 | + ); |
| 165 | + // no need to invalidate cache. Because all pages remain valid ( no $transcodeKey derivative ) |
133 | 166 | } |
| 167 | + // Clear the webVideoTranscode cache ( so we don't keep out dated table cache around ) |
| 168 | + webVideoTranscode::clearTranscodeCache( $this->title->getDBkey() ); |
| 169 | + |
134 | 170 | // pass along result status: |
135 | 171 | return $status; |
136 | 172 | } |
137 | | - /** |
138 | | - * Invalidate the cache of all pages where the video is displayed |
139 | | - */ |
140 | | - function invalidateCache(){ |
141 | | - // The static cache of webVideoTranscode should be cleared |
142 | | - webVideoTranscode::clearTranscodeCache(); |
143 | | - |
144 | | - // The file page cache should be invalidated: |
145 | | - $this->title->invalidateCache(); |
146 | | - |
147 | | - // TODO if the video is used in over 500 pages add to 'job queue' |
148 | | - $limit = 500; |
149 | | - $dbr = wfGetDB( DB_SLAVE ); |
150 | | - $res = $dbr->select( |
151 | | - array( 'imagelinks', 'page' ), |
152 | | - array( 'page_namespace', 'page_title' ), |
153 | | - array( 'il_to' => $this->title->getDBkey(), 'il_from = page_id' ), |
154 | | - __METHOD__, |
155 | | - array( 'LIMIT' => $limit + 1 ) |
156 | | - ); |
157 | | - foreach ( $res as $page ) { |
158 | | - $title = Title::makeTitle( $page->page_namespace, $page->page_title ); |
159 | | - $title->invalidateCache(); |
160 | | - } |
161 | | - } |
162 | 173 | function removeFffmpgeLogFiles( $dir ){ |
163 | 174 | if (is_dir($dir)) { |
164 | 175 | if ($dh = opendir($dir)) { |
Index: trunk/extensions/TimedMediaHandler/WebVideoTranscode/WebVideoTranscode.php |
— | — | @@ -144,8 +144,8 @@ |
145 | 145 | /** |
146 | 146 | * Get the target encode path for a video encode |
147 | 147 | * |
148 | | - * @param File $file |
149 | | - * @param String $transcodeKey |
| 148 | + * @param $file File |
| 149 | + * @param $transcodeKey String |
150 | 150 | * |
151 | 151 | * @returns the local target encode path |
152 | 152 | */ |
— | — | @@ -154,7 +154,7 @@ |
155 | 155 | // in-progress encodes, its nice having it publicly accessible for debugging though |
156 | 156 | $filePath = self::getDerivativeFilePath( $file, $transcodeKey ); |
157 | 157 | $ext = strtolower( pathinfo( "$filePath", PATHINFO_EXTENSION ) ); |
158 | | - return "{$filePath}.queue.{$ext}"; |
| 158 | + return "{$filePath}.tmp.{$ext}"; |
159 | 159 | } |
160 | 160 | |
161 | 161 | /** |
— | — | @@ -201,7 +201,7 @@ |
202 | 202 | /** |
203 | 203 | * Grabs sources from the remote repo via ApiQueryVideoInfo.php entry point. |
204 | 204 | * |
205 | | - * Because this works on both TimedMediaHandler commons and no TimedMediaHandler commons |
| 205 | + * Because this works with commons regardless of whether TimedMediaHandler is installed or not |
206 | 206 | */ |
207 | 207 | static public function getRemoteSources(&$file , $options = array() ){ |
208 | 208 | global $wgMemc; |
— | — | @@ -398,28 +398,75 @@ |
399 | 399 | * Remove any transcode jobs associated with a given $fileName |
400 | 400 | * |
401 | 401 | * also remove the transcode files: |
| 402 | + * @param $file File Object |
| 403 | + * @param $transcodeKey String Optional transcode key to remove only this key |
402 | 404 | */ |
403 | | - public static function removeTranscodeJobs( &$file ){ |
404 | | - $fileName = $file->getTitle()->getDbKey(); |
| 405 | + public static function removeTranscodeJobs( &$file, $transcodeKey = false ){ |
| 406 | + $titleObj = $file->getTitle(); |
| 407 | + print " \n\n " . $titleObj->getDBKey() . "\n\n"; |
| 408 | + // if transcode key is non-false, non-null: |
| 409 | + if( $transcodeKey ){ |
| 410 | + // only remove the requested $transcodeKey |
| 411 | + $removeKeys = array( $transcodeKey ); |
| 412 | + } else { |
| 413 | + // Remove any existing files ( regardless of their state ) |
| 414 | + $res = wfGetDB( DB_SLAVE )->select( 'transcode', |
| 415 | + array( 'transcode_key' ), |
| 416 | + array( 'transcode_image_name' => $titleObj->getDBKey() ) |
| 417 | + ); |
| 418 | + $removeKeys = array(); |
| 419 | + foreach( $res as $transcodeRow ){ |
| 420 | + $removeKeys[] = $transcodeRow->transcode_key; |
| 421 | + } |
| 422 | + } |
405 | 423 | |
406 | | - $res = wfGetDB( DB_SLAVE )->select( 'transcode', |
407 | | - array( 'transcode_key' ), |
408 | | - array( 'transcode_image_name' => $fileName ) |
409 | | - ); |
410 | | - // remove the file |
411 | | - foreach( $res as $transcodeRow ){ |
412 | | - $filePath = self::getDerivativeFilePath($file, $transcodeRow->transcode_key ); |
413 | | - if( ! @unlink( $filePath ) ){ |
414 | | - wfDebug( "Could not delete file $filePath\n" ); |
| 424 | + // Remove files by key: |
| 425 | + foreach( $removeKeys as $tKey){ |
| 426 | + $filePath = self::getDerivativeFilePath($file, $tKey); |
| 427 | + if( is_file( $filePath ) ){ |
| 428 | + if( ! @unlink( $filePath ) ){ |
| 429 | + wfDebug( "Could not delete file $filePath\n" ); |
| 430 | + } |
415 | 431 | } |
416 | | - } |
| 432 | + } |
| 433 | + |
| 434 | + // Build the sql query: |
| 435 | + $dbw = wfGetDB( DB_MASTER ); |
| 436 | + $deleteWhere = array( 'transcode_image_name ='. $dbw->addQuotes( $titleObj->getDBkey() ) ); |
| 437 | + // Check if we are removing a specific transcode key |
| 438 | + if( $transcodeKey !== false ){ |
| 439 | + $deleteWhere[] = 'transcode_key =' . $dbw->addQuotes( $transcodeKey ); |
| 440 | + } |
417 | 441 | // Remove the db entries |
418 | | - wfGetDB( DB_MASTER )->delete( 'transcode', |
419 | | - array( 'transcode_image_name' => $fileName ), |
420 | | - __METHOD__ |
| 442 | + $dbw->delete( 'transcode', $deleteWhere, __METHOD__ ); |
| 443 | + |
| 444 | + // Purge the cache for pages that include this video: |
| 445 | + self::invalidatePagesWithAsset( $titleObj ); |
| 446 | + |
| 447 | + // Remove from local WebVideoTranscode cache: |
| 448 | + self::clearTranscodeCache( $titleObj->getDBKey() ); |
| 449 | + } |
| 450 | + |
| 451 | + public static function invalidatePagesWithAsset( &$titleObj ){ |
| 452 | + wfDebug("WebVideoTranscode:: Invalidate pages that include: " . $titleObj->getDBKey() ); |
| 453 | + // Purge the main image page: |
| 454 | + $titleObj->invalidateCache(); |
| 455 | + |
| 456 | + // TODO if the video is used in over 500 pages add to 'job queue' |
| 457 | + // TODO interwiki invalidation ? |
| 458 | + $limit = 500; |
| 459 | + $dbr = wfGetDB( DB_SLAVE ); |
| 460 | + $res = $dbr->select( |
| 461 | + array( 'imagelinks', 'page' ), |
| 462 | + array( 'page_namespace', 'page_title' ), |
| 463 | + array( 'il_to' => $titleObj->getDBkey(), 'il_from = page_id' ), |
| 464 | + __METHOD__, |
| 465 | + array( 'LIMIT' => $limit + 1 ) |
421 | 466 | ); |
422 | | - // Remove from local cache: |
423 | | - self::clearTranscodeCache( $fileName ); |
| 467 | + foreach ( $res as $page ) { |
| 468 | + $title = Title::makeTitle( $page->page_namespace, $page->page_title ); |
| 469 | + $title->invalidateCache(); |
| 470 | + } |
424 | 471 | } |
425 | 472 | |
426 | 473 | /** |
— | — | @@ -429,7 +476,7 @@ |
430 | 477 | public static function addSourceIfReady( &$file, &$sources, $transcodeKey, $dataPrefix = '' ){ |
431 | 478 | global $wgLang; |
432 | 479 | $fileName = $file->getTitle()->getDbKey(); |
433 | | - // Check if the transcode is ready: |
| 480 | + // Check if the transcode is ready: |
434 | 481 | if( self::isTranscodeReady( $fileName, $transcodeKey ) ){ |
435 | 482 | $sources[] = self::getDerivativeSourceAttributes( $file, $transcodeKey, $dataPrefix ); |
436 | 483 | } else { |
— | — | @@ -510,8 +557,8 @@ |
511 | 558 | } |
512 | 559 | /** |
513 | 560 | * Update the job queue if the file is not already in the job queue: |
514 | | - * @param object File object |
515 | | - * @param |
| 561 | + * @param $file File object |
| 562 | + * @param $transcodeKey String transcode key |
516 | 563 | */ |
517 | 564 | public static function updateJobQueue( &$file, $transcodeKey ){ |
518 | 565 | wfProfileIn( __METHOD__ ); |
— | — | @@ -520,9 +567,8 @@ |
521 | 568 | |
522 | 569 | // Check if we need to update the transcode state: |
523 | 570 | $transcodeState = self::getTranscodeState( $fileName ); |
524 | | - |
525 | 571 | // Check if the job has been added: |
526 | | - if( isset( $transcodeState[ $transcodeKey ] ) && is_null( $transcodeState[ $transcodeKey ]['time_addjob'] ) ) { |
| 572 | + if( !isset( $transcodeState[ $transcodeKey ] ) || is_null( $transcodeState[ $transcodeKey ]['time_addjob'] ) ) { |
527 | 573 | // Add to job queue and update the db |
528 | 574 | $job = new WebVideoTranscodeJob( $file->getTitle(), array( |
529 | 575 | 'transcodeMode' => 'derivative', |
Index: trunk/extensions/TimedMediaHandler/TimedMediaHandler.hooks.php |
— | — | @@ -22,12 +22,6 @@ |
23 | 23 | $wgMediaHandlers['application/ogg'] = 'OggHandler'; |
24 | 24 | $wgMediaHandlers['video/webm'] = 'WebMHandler'; |
25 | 25 | |
26 | | - // Setup a hook for iframe embed handling: |
27 | | - $wgHooks['ArticleFromTitle'][] = 'TimedMediaIframeOutput::iframeHook'; |
28 | | - |
29 | | - // Add parser hook |
30 | | - $wgParserOutputHooks['TimedMediaHandler'] = array( 'TimedMediaHandler', 'outputHook' ); |
31 | | - |
32 | 26 | // Add transcode job class: |
33 | 27 | $wgJobClasses+= array( |
34 | 28 | 'webVideoTranscode' => 'WebVideoTranscodeJob' |
— | — | @@ -53,10 +47,25 @@ |
54 | 48 | ) ), |
55 | 49 | 'ext.tmh.transcodetable' => array_merge($baseExtensionResource, array( |
56 | 50 | 'scripts' => 'resources/ext.tmh.transcodetable.js', |
57 | | - 'styles' => 'resources/transcodeTable.css' |
| 51 | + 'styles' => 'resources/transcodeTable.css', |
| 52 | + 'messages'=> array( |
| 53 | + 'mwe-ok', |
| 54 | + 'mwe-cancel', |
| 55 | + 'timedmedia-reset-error', |
| 56 | + 'timedmedia-reset', |
| 57 | + 'timedmedia-reset-confirm' |
| 58 | + ) |
58 | 59 | ) ) |
59 | 60 | ); |
| 61 | + // Setup a hook for iframe embed handling: |
| 62 | + $wgHooks['ArticleFromTitle'][] = 'TimedMediaIframeOutput::iframeHook'; |
| 63 | + |
| 64 | + // When an upload completes ( check clear any existing transcodes ) |
| 65 | + $wgHooks['UploadComplete'][] = 'TimedMediaHandlerHooks::checkUploadComplete'; |
60 | 66 | |
| 67 | + // Add parser hook |
| 68 | + $wgParserOutputHooks['TimedMediaHandler'] = array( 'TimedMediaHandler', 'outputHook' ); |
| 69 | + |
61 | 70 | // We should probably move this script output to a parser function but not working correctly in |
62 | 71 | // dynamic contexts ( for example in special upload, when there is an "existing file" warning. ) |
63 | 72 | $wgHooks['BeforePageDisplay'][] = 'TimedMediaHandlerHooks::pageOutputHook'; |
— | — | @@ -71,18 +80,12 @@ |
72 | 81 | // Also add the .log file ( used in two pass encoding ) |
73 | 82 | // ( probably should move in-progress encodes out of web accessible directory ) |
74 | 83 | $wgExcludeFromThumbnailPurge[] = 'log'; |
75 | | - |
76 | | - // Api hooks for derivatives and query video derivatives |
77 | | - $wgAPIPropModules += array( |
78 | | - 'videoinfo' => 'ApiQueryVideoInfo' |
79 | | - ); |
80 | | - |
| 84 | + |
81 | 85 | $wgHooks['LoadExtensionSchemaUpdates'][] = 'TimedMediaHandlerHooks::loadExtensionSchemaUpdates'; |
82 | 86 | |
83 | 87 | // Add unit tests |
84 | 88 | $wgHooks['UnitTestsList'][] = 'TimedMediaHandlerHooks::registerUnitTests'; |
85 | 89 | |
86 | | - |
87 | 90 | /** |
88 | 91 | * Add support for the "TimedText" NameSpace |
89 | 92 | */ |
— | — | @@ -107,43 +110,67 @@ |
108 | 111 | } |
109 | 112 | return true; |
110 | 113 | } |
111 | | - public static function checkForTranscodeStatus( $article, &$html ){ |
| 114 | + |
| 115 | + /** |
| 116 | + * Wraps the isTranscodableFile function |
| 117 | + * @param $title Title |
| 118 | + */ |
| 119 | + public static function isTranscodableTitle( $title ){ |
| 120 | + if( $title->getNamespace() != NS_FILE ){ |
| 121 | + return false; |
| 122 | + } |
| 123 | + $file = wfFindFile( $title ); |
| 124 | + return self::isTranscodableFile( $file ); |
| 125 | + } |
| 126 | + |
| 127 | + /** |
| 128 | + * Utility function to check if a given file can be "transcoded" |
| 129 | + * @param $file File object |
| 130 | + */ |
| 131 | + public static function isTranscodableFile( & $file ){ |
112 | 132 | global $wgEnableTranscode; |
| 133 | + |
113 | 134 | // don't show the transcode table if transcode is disabled |
114 | 135 | if( $wgEnableTranscode === false ){ |
115 | | - return true; |
| 136 | + return false; |
116 | 137 | } |
117 | | - // load the file: |
118 | | - $file = wfFindFile( $article->getTitle() ); |
119 | | - // cant find file |
| 138 | + // Can't find file |
120 | 139 | if( !$file ){ |
121 | | - return true; |
| 140 | + return false; |
122 | 141 | } |
123 | | - // We don't show transcode status for remote files: |
| 142 | + // We can only transcode local files |
124 | 143 | if( !$file->isLocal() ){ |
125 | | - return true; |
| 144 | + return false; |
126 | 145 | } |
127 | | - // get mediaType |
128 | 146 | $mediaType = $file->getHandler()->getMetadataType( $image = '' ); |
129 | | - // if ogg or webm format and not audio show transcode page: |
| 147 | + // If ogg or webm format and not audio we can "transcode" this file |
130 | 148 | if( ( $mediaType == 'webm' || $mediaType == 'ogg' ) && ! $file->getHandler()->isAudio( $file ) ){ |
| 149 | + return true; |
| 150 | + } |
| 151 | + return false; |
| 152 | + } |
| 153 | + |
| 154 | + public static function checkForTranscodeStatus( $article, &$html ){ |
| 155 | + // load the file: |
| 156 | + $file = wfFindFile( $article->getTitle() ); |
| 157 | + if( self::isTranscodableFile( $file ) ){ |
131 | 158 | $html = TranscodeStatusTable::getHTML( $file ); |
132 | 159 | } |
133 | 160 | return true; |
134 | 161 | } |
| 162 | + public static function checkUploadComplete( &$image ){ |
| 163 | + if( self::isTranscodableTitle( $image->getTitle() ) ){ |
| 164 | + // clear transcode data: |
| 165 | + } |
| 166 | + return true; |
| 167 | + } |
135 | 168 | public static function checkArticleDeleteComplete( &$article, &$user, $reason, $id ){ |
136 | 169 | // Check if the article is a file and remove transcode jobs: |
137 | 170 | if( $article->getTitle()->getNamespace() == NS_FILE ) { |
138 | | - |
139 | 171 | $file = wfFindFile( $article->getTitle() ); |
140 | | - if ( $file ) { |
141 | | - $mediaType = $file->getHandler()->getMetadataType( $image = '' ); |
142 | | - |
143 | | - if( $mediaType == 'webm' || $mediaType == 'ogg' ){ |
144 | | - WebVideoTranscode::removeTranscodeJobs( $file ); |
145 | | - } |
| 172 | + if( self::isTranscodableFile( $file ) ){ |
| 173 | + WebVideoTranscode::removeTranscodeJobs( $file ); |
146 | 174 | } |
147 | | - |
148 | 175 | } |
149 | 176 | return true; |
150 | 177 | } |
— | — | @@ -161,7 +188,7 @@ |
162 | 189 | |
163 | 190 | /** |
164 | 191 | * Hook to add list of PHPUnit test cases. |
165 | | - * @param array $files |
| 192 | + * @param $files Array of files |
166 | 193 | */ |
167 | 194 | public static function registerUnitTests( array &$files ) { |
168 | 195 | $testDir = dirname( __FILE__ ) . '/tests/phpunit/'; |
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayer.js |
— | — | @@ -202,7 +202,6 @@ |
203 | 203 | |
204 | 204 | var playerInterface = new mw.EmbedPlayer( playerElement ); |
205 | 205 | var swapPlayer = swapEmbedPlayerElement( playerElement, playerInterface ); |
206 | | - |
207 | 206 | // Trigger the EmbedPlayerNewPlayer for embedPlayer interface |
208 | 207 | mw.log("EmbedPlayer::EmbedPlayerNewPlayer:trigger " + playerInterface.id ); |
209 | 208 | $( mw ).trigger ( 'EmbedPlayerNewPlayer', $( '#' + playerInterface.id ).get(0) ); |
Index: trunk/extensions/TimedMediaHandler/resources/ext.tmh.transcodetable.js |
— | — | @@ -19,4 +19,49 @@ |
20 | 20 | .css('overflow', 'hidden'); |
21 | 21 | return false; |
22 | 22 | }) |
| 23 | + // Reset transcode action: |
| 24 | + $j('.transcodereset a').click( function(){ |
| 25 | + var tKey = $(this).attr('data-transcodekey'); |
| 26 | + var buttons = {}; |
| 27 | + buttons[ gM('mwe-ok') ] = function(){ |
| 28 | + var _thisDialog = this; |
| 29 | + $(this).loadingSpinner(); |
| 30 | + var apiUrl = mw.config.get('wgServer') + mw.config.get( 'wgScriptPath' ) + '/api.php'; |
| 31 | + // Do an api post action: |
| 32 | + $.post( apiUrl, { |
| 33 | + 'action' : 'transcodereset', |
| 34 | + 'transcodekey' : tKey, |
| 35 | + 'title' : mw.config.get('wgPageName'), |
| 36 | + 'token' : mw.user.tokens.get('editToken'), |
| 37 | + 'format' : 'json' |
| 38 | + }, function( data ){ |
| 39 | + if( data && data['success'] ){ |
| 40 | + // refresh the page |
| 41 | + window.location.reload(); |
| 42 | + } else { |
| 43 | + if( data.error && data.error.info ){ |
| 44 | + $( _thisDialog ).text( data.error.info ); |
| 45 | + } else { |
| 46 | + $( _thisDialog ).text( gM( 'timedmedia-reset-error' ) ); |
| 47 | + } |
| 48 | + var okBtn = {}; |
| 49 | + okBtn[ gM('mwe-ok') ] = function() { $(this).dialog("close"); } |
| 50 | + $( _thisDialog ).dialog( "option", "buttons", okBtn ); |
| 51 | + } |
| 52 | + }) |
| 53 | + }; |
| 54 | + buttons[ gM('mwe-cancel') ] =function(){ |
| 55 | + $(this).dialog('close'); |
| 56 | + } |
| 57 | + // pop up dialog |
| 58 | + mw.addDialog({ |
| 59 | + 'width' : '400', |
| 60 | + 'height' : '200', |
| 61 | + 'title' : gM('timedmedia-reset'), |
| 62 | + 'content' : gM('timedmedia-reset-confirm'), |
| 63 | + 'buttons': buttons |
| 64 | + }) |
| 65 | + .css('overflow', 'hidden'); |
| 66 | + return false; |
| 67 | + }) |
23 | 68 | }) |