2 * autodetects: new upload api or old http POST.
6 "fogg-select_file" : "Select file",
7 "fogg-select_new_file" : "Select new file",
8 "fogg-select_url" : "Select URL",
9 "fogg-save_local_file" : "Save Ogg",
10 "fogg-check_for_firefogg" : "Checking for Firefogg...",
11 "fogg-installed" : "Firefogg is installed",
12 "fogg-for_improved_uploads" : "For improved uploads:",
13 "fogg-please_install" : "<a href=\"$1\">Install Firefogg<\/a>. More <a href=\"http:\/\/commons.wikimedia.org\/wiki\/Commons:Firefogg\">about Firefogg<\/a>.",
14 "fogg-use_latest_firefox" : "Please first install <a href=\"http:\/\/www.mozilla.com\/en-US\/firefox\/upgrade.html?from=firefogg\">Firefox 3.5<\/a> (or later). <i>Then revisit this page to install the <b>Firefogg<\/b> extension.<\/i>",
15 "fogg-passthrough_mode" : "Your selected file is already Ogg or not a video file",
16 "fogg-transcoding" : "Encoding video to Ogg...",
17 "fogg-encoding-done" : "Encoding complete",
18 "fogg-badtoken" : "Token is not valid",
19 "fogg-preview" : "Preview video",
20 "fogg-hidepreview" : "Hide preview"
23 var firefogg_install_links
= {
24 'macosx': 'http://firefogg.org/macosx/Firefogg.xpi',
25 'win32': 'http://firefogg.org/win32/Firefogg.xpi',
26 'linux': 'http://firefogg.org/linux/Firefogg.xpi'
29 var default_firefogg_options
= {
30 // Callback for upload completion
31 'done_upload_cb': false,
33 // The API URL to upload to
36 // True when a file is uploaded without re-encoding
39 // True if we will be showing the encoder interface
40 'encoder_interface': false,
42 // True if we want to limit the library functionality to "only firefogg"
43 // (no upload or progress bars)
44 'only_firefogg': false,
46 // Callback which is called when the source name changes
47 'new_source_cb': false,
49 // CSS selector identifying the target control container or form (can't be left null)
52 // May be "upload" to if we are rewriting an upload form, or "local" if we are encoding a local file
55 // CSS selector for the select file button
56 'target_btn_select_file': false,
58 // CSS selector for the select new file button
59 'target_btn_select_new_file': false,
61 // CSS selector for the save local file button
62 'target_btn_save_local_file': false,
64 // CSS selector for the input file name button
65 'target_input_file_name': false,
67 // CSS selector for the "checking for firefogg..." message div
68 'target_check_for_firefogg': false,
70 // CSS selector for the "firefogg is installed" message div
71 'target_installed': false,
73 // CSS selector for the "please install firefogg" message div
74 'target_please_install': false,
76 // CSS selector for the "please use Firefox 3.5" message div
77 'target_use_latest_firefox': false,
79 // CSS selector for the message div warning that passthrough mode is enabled
80 'target_passthrough_mode': false,
82 // True if firefogg should take over the form submit action
83 'firefogg_form_action': true,
85 // True if we should show a preview of the encoding progress
88 //If we should enable chunk uploads ( mediaWiki api supports chunk uploads)
89 'enable_chunks' : false
93 var mvFirefogg = function( options
) {
94 return this.init( options
);
97 mvFirefogg
.prototype = { // extends mvBaseUploadInterface
98 min_firefogg_version
: '0.9.9.5',
99 default_encoder_settings
: { // @@todo allow the server to set these
101 'videoBitrate' : '544',
102 'audioBitrate' : '96',
105 have_firefogg
: null, // lazy initialised, use getFirefogg()
106 current_encoder_settings
: null, // lazy initialised, use getEncoderSettings()
107 sourceFileInfo
: null, // lazy initialised, use getSourceFileInfo()
108 ogg_extensions
: [ 'ogg', 'ogv', 'oga' ],
109 video_extensions
: [ 'avi', 'mov', 'mp4', 'mp2', 'mpeg', 'mpeg2', 'mpeg4', 'dv', 'wmv' ],
115 * Object initialisation
117 init: function( options
) {
121 // If we have no api_url, set upload mode to "post"
122 if ( !options
.api_url
)
123 options
.upload_mode
= 'post';
126 for ( var i
in default_firefogg_options
) {
128 this[i
] = options
[i
];
130 this[i
] = default_firefogg_options
[i
];
134 // Inherit from mvBaseUploadInterface (unless we're in only_firefogg mode)
135 if ( !this.only_firefogg
) {
136 var myBUI
= new mvBaseUploadInterface( options
);
138 // Prefix conflicting members with pe_
139 for ( var i
in myBUI
) {
141 this['pe_'+ i
] = myBUI
[i
];
148 if ( !this.selector
) {
149 js_log('firefogg: missing selector ');
154 * Rewrite the upload form, or create our own upload controls for local transcoding.
155 * Called from $j.firefogg(), in mv_embed.js.
157 doRewrite: function( callback
) {
159 js_log( 'sel len: ' + this.selector
+ '::' + $j( this.selector
).length
+
160 ' tag:' + $j( this.selector
).get( 0 ).tagName
);
161 if ( $j( this.selector
).length
>= 0 ) {
162 if ( $j( this.selector
).get( 0 ).tagName
.toLowerCase() == 'input' ) {
163 _this
.form_type
= 'upload';
166 if ( this.form_type
== 'upload' ) {
167 // Initialise existing upload form
170 // Create our own form controls
171 this.createControls();
180 * Create controls for local transcoding and add them to the page
182 createControls: function() {
185 $j
.each( default_firefogg_options
, function( target
, na
) {
186 if ( /^target/.test( target
) ) {
187 // Create the control if it doesn't already exist
188 if( _this
[target
] === false ) {
189 out
+= _this
.getControlHtml(target
) + ' ';
190 // Update the target selector
191 _this
[target
] = _this
.selector
+ ' .' + target
;
195 $j( this.selector
).append( out
).hide();
199 * Get the HTML for the control with a particular name
201 getControlHtml: function( target
) {
202 if ( /^target_btn_/.test( target
) ) {
204 var msg
= gM( target
.replace( /^target_btn_/, 'fogg-' ) );
205 return '<input style="" ' +
206 'class="' + target
+ '" ' +
208 'value="' + msg
+ '"/> ';
209 } else if ( /^target_input_/.test( target
) ) {
211 var msg
= gM( target
.replace( /^target_input_/, 'fogg-' ) );
212 return '<input style="" ' +
213 'class="' + target
+ '" ' +
215 'value="' + msg
+ '"/> ';
216 } else if ( /^target_/.test( target
) ) {
218 var msg
= gM( target
.replace( '/^target_/', 'fogg-' ) );
219 return '<div style="" class="' + target
+ '" >' + msg
+ '</div> ';
221 js_error( 'Invalid target: ' + target
);
227 * Set up events for the controls which were created with createControls()
229 bindControls: function() {
233 var hide_target_list
= '';
235 $j
.each( default_firefogg_options
, function( target
, na
) {
236 if ( /^target/.test( target
) ) {
237 hide_target_list
+= comma
+ _this
[target
];
241 $j( hide_target_list
).hide();
244 $j( _this
.selector
).show();
245 if ( _this
.getFirefogg() ) {
247 // If we're in upload mode, show the input filename
248 if ( _this
.form_type
== 'upload' )
249 $j( _this
.target_input_file_name
).show();
251 // Show the select file button
252 $j( this.target_btn_select_file
)
254 .attr( 'disabled', false )
255 .css( { 'display': 'inline' } )
257 _this
.selectSourceFile();
260 // Set up the click handler for the filename box
261 $j( this.target_input_file_name
)
263 .attr( 'readonly', 'readonly' )
265 _this
.selectSourceFile();
269 // FIXME: move this elsewhere. None of this is related to binding.
271 // Show the "use latest Firefox" message if necessary
272 if ( !( $j
.browser
.mozilla
&& $j
.browser
.version
>= '1.9.1' ) ) {
273 js_log( 'show use latest::' + _this
.target_use_latest_firefox
);
274 if ( _this
.target_use_latest_firefox
) {
275 if ( _this
.form_type
== 'upload' )
276 $j( _this
.target_use_latest_firefox
)
277 .prepend( gM( 'fogg-for_improved_uploads' ) );
279 $j( _this
.target_use_latest_firefox
).show();
284 // Otherwise show the "install Firefogg" message
285 var upMsg
= ( _this
.form_type
== 'upload' ) ? gM( 'fogg-for_improved_uploads' ) : '';
286 var firefoggUrl
= _this
.getFirefoggInstallUrl();
288 $j( _this
.target_please_install
)
289 .html( upMsg
+ gM( 'fogg-please_install', firefoggUrl
) )
290 .css( 'padding', '10px' )
295 // Set up the click handler for the "save local file" button
296 if( _this
.target_btn_save_local_file
){
297 $j( _this
.target_btn_save_local_file
)
300 _this
.doLocalEncodeAndSave();
306 * Get the URL for installing firefogg on the client OS
308 getFirefoggInstallUrl: function() {
310 if ( navigator
.oscpu
) {
311 if ( navigator
.oscpu
.search( 'Linux' ) >= 0 )
312 os_link
= firefogg_install_links
['linux'];
313 else if ( navigator
.oscpu
.search( 'Mac' ) >= 0 )
314 os_link
= firefogg_install_links
['macosx'];
315 else if (navigator
.oscpu
.search( 'Win' ) >= 0 )
316 os_link
= firefogg_install_links
['win32'];
322 * Get the Firefogg instance (or false if firefogg is unavailable)
324 getFirefogg: function() {
325 if ( this.have_firefogg
== null ) {
326 if ( typeof( Firefogg
) != 'undefined'
327 && Firefogg().version
>= this.min_firefogg_version
)
329 this.have_firefogg
= true;
330 this.fogg
= new Firefogg();
332 this.have_firefogg
= false;
340 * Set up the upload form
342 setupForm: function() {
343 js_log( 'firefogg::setupForm::' );
345 // Set up the parent if we are in upload mode
346 if ( this.form_type
== 'upload' ) {
350 // If Firefogg is not available, just show a "please install" message
351 if ( !this.getFirefogg() ) {
352 if ( !this.target_please_install
) {
353 $j( this.selector
).after( this.getControlHtml( 'target_please_install' ) );
354 this.target_please_install
= this.selector
+ ' ~ .target_please_install';
356 if ( !this.target_use_latest_firefox
) {
357 $j( this.selector
).after( this.getControlHtml( 'target_use_latest_firefox' ) );
358 this.target_use_latest_firefox
= this.selector
+ ' ~ .target_use_latest_firefox';
360 // Show download link
365 // Change the file browser to type text. We can't simply change the attribute so
366 // we have to delete and recreate.
367 var inputTag
= '<input ';
368 $j
.each( $j( this.selector
).get( 0 ).attributes
, function( i
, attr
) {
369 var val
= attr
.value
;
370 if ( attr
.name
== 'type' )
372 inputTag
+= attr
.name
+ '="' + val
+ '" ';
374 if ( !$j( this.selector
).attr( 'style' ) )
375 inputTag
+= 'style="display:inline" ';
377 var id
= $j( this.selector
).attr( 'name' ) + '_firefogg-control';
378 inputTag
+= '/><span id="' + id
+ '"></span>';
380 js_log( 'set input: ' + inputTag
);
381 $j( this.selector
).replaceWith( inputTag
);
383 this.target_input_file_name
= 'input[name=' + $j( this.selector
).attr( 'name' ) + ']';
385 // Point the selector at the span we just created
386 this.selector
= '#' + id
;
388 // Create controls for local transcoding
389 this.createControls();
394 * Display an upload progress overlay. Overrides the function in mvBaseUploadInterface.
396 displayProgressOverlay: function() {
397 this.pe_displayProgressOverlay();
398 // If we are uploading video (not in passthrough mode), show preview button
399 if( this.getFirefogg()
400 && !this.isCopyUpload()
401 && !this.getEncoderSettings()['passthrough'] )
403 this.createPreviewControls();
408 * Create controls for showing a transcode/crop/resize preview
410 createPreviewControls: function() {
413 // Set the initial button html:
415 if( _this
.show_preview
== true ){
416 buttonHtml
= $j
.btnHtml( gM( 'fogg-hidepreview' ), 'fogg_preview', 'triangle-1-s' );
418 buttonHtml
= $j
.btnHtml( gM( 'fogg-preview' ), 'fogg_preview', 'triangle-1-e' );
421 // Add the preview button and canvas
422 $j( '#upProgressDialog' ).append(
423 '<div style="clear:both;height:3em"/>' +
425 '<div style="padding:10px;">' +
426 '<canvas style="margin:auto;" id="fogg_preview_canvas" />' +
430 // Set the initial state
431 if ( _this
.show_preview
== true ) {
432 $j( '#fogg_preview_canvas' ).show();
435 // Bind the preview button
436 $j( '#upProgressDialog .fogg_preview' ).btnBind().click( function() {
437 return _this
.onPreviewClick( this );
442 * onclick handler for the hide/show preview button
444 onPreviewClick: function( sourceNode
) {
445 var button
= $j( sourceNode
);
446 var icon
= button
.children( '.ui-icon' );
447 js_log( "click .fogg_preview" + icon
.attr( 'class' ) );
449 if ( icon
.hasClass( 'ui-icon-triangle-1-e' ) ) {
451 // Toggle button class and set button text to "hide".
452 this.show_preview
= true;
453 icon
.removeClass( 'ui-icon-triangle-1-e' ).addClass( 'ui-icon-triangle-1-s' );
454 button
.children( '.btnText' ).text( gM( 'fogg-hidepreview' ) );
455 $j( '#fogg_preview_canvas' ).show( 'fast' );
458 // Toggle button class and set button text to "show".
459 this.show_preview
= false;
460 icon
.removeClass( 'ui-icon-triangle-1-s' ).addClass( 'ui-icon-triangle-1-e' );
461 button
.children( '.btnText' ).text( gM( 'fogg-preview' ) );
462 $j( '#fogg_preview_canvas' ).hide( 'slow' );
464 // Don't follow the # link
469 * Render the preview frame (asynchronously)
471 renderPreview: function() {
473 // Set up the hidden video to pull frames from
474 if( $j( '#fogg_preview_vid' ).length
== 0 )
475 $j( 'body' ).append( '<video id="fogg_preview_vid" style="display:none"></video>' );
476 var v
= $j( '#fogg_preview_vid' ).get( 0 );
478 function seekToEnd() {
479 var v
= $j( '#fogg_preview_vid' ).get( 0 );
480 // TODO: document arbitrary 0.4s constant
481 v
.currentTime
= v
.duration
- 0.4;
483 function renderFrame() {
484 var v
= $j( '#fogg_preview_vid' ).get( 0 );
485 var canvas
= $j( '#fogg_preview_canvas' ).get( 0 );
488 canvas
.height
= canvas
.width
* v
.videoHeight
/ v
.videoWidth
;
489 var ctx
= canvas
.getContext( "2d" );
490 ctx
.drawImage( v
, 0, 0, canvas
.width
, canvas
.height
);
494 // Initialize the video if it is not set up already
495 var v
= $j( '#fogg_preview_vid' ).get( 0 );
496 if ( v
.src
!= _this
.fogg
.previewUrl
) {
497 js_log( 'init preview with url:' + _this
.fogg
.previewUrl
);
498 v
.src
= _this
.fogg
.previewUrl
;
500 // Once it's loaded, seek to the end
501 v
.removeEventListener( "loadedmetadata", seekToEnd
, true );
502 v
.addEventListener( "loadedmetadata", seekToEnd
, true );
504 // When the seek is done, render a frame
505 v
.removeEventListener( "seeked", renderFrame
, true );
506 v
.addEventListener( "seeked", renderFrame
, true );
508 // Refresh the video duration and metadata
509 var previewTimer
= setInterval( function() {
510 if ( _this
.fogg
.status() != "encoding" ) {
511 clearInterval( previewTimer
);
512 _this
.show_preview
== false;
514 if ( _this
.show_preview
== true ) {
524 * Get the DOMNode of the form element we are rewriting.
525 * Returns false if it can't be found.
526 * Overrides mvBaseUploadInterface.getForm().
528 getForm: function() {
529 if ( this.form_selector
) {
530 return this.pe_getForm();
532 // No configured form selector
533 // Use the first form descendant of the current container
534 return $j( this.selector
).parents( 'form:first' ).get( 0 );
539 * Show a dialog box allowing the user to select the source file of the
540 * encode/upload operation. The filename is stored by Firefogg until the
541 * next encode/upload call.
543 * After a successful select, the UI is updated accordingly.
545 selectSourceFile: function() {
547 if( !_this
.fogg
.selectVideo() ) {
548 // User clicked "cancel"
551 _this
.clearSourceInfoCache();
552 _this
.updateSourceFileUI();
556 * Update the UI due to the source file changing
558 updateSourceFileUI: function() {
559 js_log( 'videoSelectReady' );
561 if ( !_this
.fogg
.sourceInfo
|| !_this
.fogg
.sourceFilename
) {
562 // Something wrong with the source file?
563 js_log( 'selectSourceFile: sourceInfo/sourceFilename missing' );
567 // Hide the "select file" button and show "select new"
568 $j( _this
.target_btn_select_file
).hide();
569 $j( _this
.target_btn_select_new_file
)
573 _this
.fogg
= new Firefogg();
574 _this
.selectSourceFile();
577 var settings
= this.getEncoderSettings();
579 // If we're in passthrough mode, update the interface (if not a form)
580 if ( settings
['passthrough'] == true && _this
.form_type
== 'local' ) {
581 $j( _this
.target_passthrough_mode
).show();
583 $j( _this
.target_passthrough_mode
).hide();
584 // Show the "save file" button if this is a local form
585 if ( _this
.form_type
== 'local' ) {
586 $j( _this
.target_btn_save_local_file
).show();
587 } // else the upload will be done on form submit
590 // Update the input file name box and show it
591 js_log( " should update: " + _this
.target_input_file_name
+
592 ' to: ' + _this
.fogg
.sourceFilename
);
593 $j( _this
.target_input_file_name
)
594 .val( _this
.fogg
.sourceFilename
)
598 // Notify callback new_source_cb
599 if ( _this
.new_source_cb
) {
600 if ( settings
['passthrough'] ) {
601 var fName
= _this
.fogg
.sourceFilename
;
603 var oggExt
= _this
.isSourceAudio() ? 'oga' : 'ogg';
604 oggExt
= _this
.isSourceVideo() ? 'ogv' : oggExt
;
605 oggExt
= _this
.isUnknown() ? 'ogg' : oggExt
;
606 oggName
= _this
.fogg
.sourceFilename
.substr( 0,
607 _this
.fogg
.sourceFilename
.lastIndexOf( '.' ) );
608 var fName
= oggName
+ '.' + oggExt
;
610 _this
.new_source_cb( _this
.fogg
.sourceFilename
, fName
);
615 * Get the source file info for the current file selected into this.fogg
617 getSourceFileInfo: function() {
618 if ( this.sourceFileInfo
== null ) {
619 if ( !this.fogg
.sourceInfo
) {
620 js_error( 'No firefogg source info is available' );
624 this.sourceFileInfo
= JSON
.parse( this.fogg
.sourceInfo
);
626 js_error( 'error could not parse fogg sourceInfo' );
630 return this.sourceFileInfo
;
634 * Clear the cache of the source file info, presumably due to a new file
635 * being selected into this.fogg
637 clearSourceInfoCache: function() {
638 this.sourceFileInfo
= null;
639 this.current_encoder_settings
= null;
643 * Save the result of the transcode as a local file
645 doLocalEncodeAndSave: function() {
648 js_error( 'doLocalEncodeAndSave: no Firefogg object!' );
652 // Set up the target location
653 // Firefogg shows the "save as" dialog box, and sets the path chosen as
654 // the destination for a later encode() call.
655 if ( !this.fogg
.saveVideoAs() ) {
656 // User clicked "cancel"
660 // We have a source file, now do the encode
662 function /* onProgress */ ( progress
) {
663 _this
.updateProgress( progress
);
665 function /* onDone */ () {
666 js_log( "done with encoding (no upload) " );
667 // Set status to 100% for one second
668 // FIXME: this is either a hack or a waste of time, not sure which
669 _this
.updateProgress( 1 );
670 setTimeout( function() {
671 _this
.onLocalEncodeDone();
678 * This is called when a local encode operation has completed. It updates the UI.
680 onLocalEncodeDone: function() {
682 _this
.updateProgressWin( gM( 'fogg-encoding-done' ),
683 gM( 'fogg-encoding-done' ) + '<br>' +
684 //show the video at full resolution upto 720px wide
685 '<video controls="true" style="margin:auto" id="fogg_final_vid" src="' +
686 _this
.fogg
.previewUrl
+ '"></video>'
688 //load the video and set a callback:
689 var v
= $j( '#fogg_final_vid' ).get( 0 );
690 function resizeVid() {
691 var v
= $j( '#fogg_final_vid' ).get(0);
692 if ( v
.videoWidth
> 720 ) {
694 var vH
= 720 * v
.videoHeight
/ v
.videoWidth
;
696 var vW
= v
.videoWidth
;
697 var vH
= v
.videoHeight
;
704 //if large video resize the dialog box:
706 //also resize the dialog box
707 $j( '#upProgressDialog' ).dialog( 'option', 'width', vW
+ 20 );
708 $j( '#upProgressDialog' ).dialog( 'option', 'height', vH
+ 120 );
710 //also position the dialog container
711 $j( '#upProgressDialog') .dialog( 'option', 'position', 'center' );
714 v
.removeEventListener( "loadedmetadata", resizeVid
, true );
715 v
.addEventListener( "loadedmetadata", resizeVid
, true );
720 * Get the appropriate encoder settings for the current Firefogg object,
721 * into which a video has already been selected.
723 getEncoderSettings: function() {
724 if ( this.current_encoder_settings
== null ) {
725 // Clone the default settings
726 var defaults = function () { };
727 defaults
.prototype = this.default_encoder_settings
;
728 var settings
= new defaults();
730 // Grab the extension
731 var sf
= this.fogg
.sourceFilename
;
733 js_error( 'getEncoderSettings(): No Firefogg source filename is available!' );
737 if ( sf
.lastIndexOf('.') != -1 )
738 ext
= sf
.substring( sf
.lastIndexOf( '.' ) + 1 ).toLowerCase();
740 // Determine passthrough mode
741 if ( this.isOggFormat() ) {
742 // Already Ogg, no need to encode
743 settings
['passthrough'] = true;
744 } else if ( this.isSourceAudio() || this.isSourceVideo() ) {
746 settings
['passthrough'] = false;
748 // Not audio or video, can't encode
749 settings
['passthrough'] = true;
752 js_log( 'base setupAutoEncoder::' + this.getSourceFileInfo().contentType
+
753 ' passthrough:' + settings
['passthrough'] );
754 this.current_encoder_settings
= settings
;
756 return this.current_encoder_settings
;
759 isUnknown: function() {
760 return ( this.getSourceFileInfo().contentType
.indexOf("unknown") != -1 );
763 isSourceAudio: function() {
764 return ( this.getSourceFileInfo().contentType
.indexOf("audio/") != -1 );
767 isSourceVideo: function() {
768 return ( this.getSourceFileInfo().contentType
.indexOf("video/") != -1 );
771 isOggFormat: function() {
772 var contentType
= this.getSourceFileInfo().contentType
;
773 return ( contentType
.indexOf("video/ogg") != -1
774 || contentType
.indexOf("application/ogg") != -1 );
778 * Get the default title of the progress window
780 getProgressTitle: function() {
781 js_log( "fogg:getProgressTitle f:" + ( this.getFirefogg() ? 'on' : 'off' ) +
782 ' mode:' + this.form_type
);
783 // Return the parent's title if we don't have Firefogg turned on
784 if ( !this.getFirefogg() || !this.firefogg_form_action
) {
785 return this.pe_getProgressTitle();
786 } else if ( this.form_type
== 'local' ) {
787 return gM( 'fogg-transcoding' );
789 return gM( 'mwe-upload-transcode-in-progress' );
794 * Do an upload, with the mode given by this.upload_mode
796 doUpload: function() {
798 js_log( "firefogg: doUpload:: " +
799 ( this.getFirefogg() ? 'on' : 'off' ) +
800 ' up mode:' + _this
.upload_mode
);
802 // If Firefogg is disabled or doing an copyByUrl upload, just invoke the parent method
803 if( !this.getFirefogg() || this.isCopyUpload() ) {
807 // We can do a chunk upload
808 if( _this
.upload_mode
== 'post' && _this
.enable_chunks
){
809 _this
.doChunkUpload();
810 } else if ( _this
.upload_mode
== 'post' ) {
811 // Encode and then do a post upload
813 function /* onProgress */ ( progress
) {
814 _this
.updateProgress( progress
);
816 function /* onDone */ () {
817 js_log( 'done with encoding do POST upload:' + _this
.form
.action
);
818 // ignore warnings & set source type
819 //_this.formData[ 'wpIgnoreWarning' ]='true';
820 _this
.formData
['wpSourceType'] = 'upload';
821 _this
.formData
['action'] = 'submit';
823 // wpUploadFile is set by firefogg
824 delete _this
.formData
['file'];
826 _this
.fogg
.post( _this
.editForm
.action
, 'wpUploadFile',
827 JSON
.stringify( _this
.formData
) );
828 _this
.doUploadStatus();
832 js_error( 'Error: unrecongized upload mode: ' + _this
.upload_mode
);
837 * Do both uploading and encoding at the same time. Uploads 1MB chunks as
840 doChunkUpload : function() {
841 js_log( 'firefogg::doChunkUpload' );
843 _this
.action_done
= false;
845 if ( !_this
.getEncoderSettings()['passthrough'] ) {
846 // We are transcoding to Ogg. Fix the destination extension, it
847 // must be ogg/ogv/oga.
848 var fileName
= _this
.formData
['filename'];
850 var dotPos
= fileName
.lastIndexOf( '.' );
851 if ( dotPos
!= -1 ) {
852 ext
= fileName
.substring( dotPos
).toLowerCase();
854 if ( $j
.inArray( ext
.substr( 1 ), _this
.ogg_extensions
) == -1 ) {
855 var extreg
= new RegExp( ext
+ '$', 'i' );
856 _this
.formData
['filename'] = fileName
.replace( extreg
, '.ogg' );
860 // Get the edit token from formData if it's not set already
861 if ( !_this
.editToken
&& _this
.formData
['token'] ) {
862 _this
.editToken
= _this
.formData
['token'];
865 if( _this
.editToken
) {
866 js_log( 'we already have an edit token: ' + _this
.editToken
);
867 _this
.doChunkUploadWithFormData();
871 // No edit token. Fetch it asynchronously and then do the upload.
873 'File:'+ _this
.formData
['filename'],
875 function( editToken
) {
876 if( !editToken
|| editToken
== '+\\' ) {
877 _this
.updateProgressWin( gM( 'fogg-badtoken' ), gM( 'fogg-badtoken' ) );
880 _this
.editToken
= editToken
;
881 _this
.doChunkUploadWithFormData();
887 * Internal function called from doChunkUpload() when the edit token is available
889 doChunkUploadWithFormData: function() {
891 js_log( "firefogg::doChunkUploadWithFormData" + _this
.editToken
);
896 'filename': _this
.formData
['filename'],
897 'comment': _this
.formData
['comment'],
898 'enablechunks': 'true'
901 if ( _this
.editToken
)
902 aReq
['token'] = this.editToken
;
904 if ( _this
.formData
['watch'] )
905 aReq
['watch'] = _this
.formData
['watch'];
907 if ( _this
.formData
['ignorewarnings'] )
908 aReq
['ignorewarnings'] = _this
.formData
['ignorewarnings'];
910 var encoderSettings
= this.getEncoderSettings();
911 js_log( 'do fogg upload/encode call: ' + _this
.api_url
+ ' :: ' + JSON
.stringify( aReq
) );
912 js_log( 'foggEncode: ' + JSON
.stringify( encoderSettings
) );
913 _this
.fogg
.upload( JSON
.stringify( encoderSettings
), _this
.api_url
,
914 JSON
.stringify( aReq
) );
916 // Start polling the upload status
917 _this
.doUploadStatus();
921 * Encode the video and monitor progress.
923 * Calls progressCallback() regularly with a number between 0 and 1 indicating progress.
924 * Calls doneCallback() when the encode is finished.
926 * Asynchronous, returns immediately.
928 doEncode : function( progressCallback
, doneCallback
) {
930 _this
.action_done
= false;
931 _this
.displayProgressOverlay();
932 var encoderSettings
= this.getEncoderSettings();
933 js_log( 'doEncode: with: ' + JSON
.stringify( encoderSettings
) );
934 _this
.fogg
.encode( JSON
.stringify( encoderSettings
) );
936 //show transcode status:
937 $j( '#up-status-state' ).html( gM( 'mwe-upload-transcoded-status' ) );
939 //setup a local function for timed callback:
940 var encodingStatus = function() {
941 var status
= _this
.fogg
.status();
943 if ( _this
.show_preview
== true && _this
.fogg
.state
== 'encoding' ) {
944 _this
.renderPreview();
948 progressCallback( _this
.fogg
.progress() );
950 //loop to get new status if still encoding
951 if ( _this
.fogg
.state
== 'encoding' ) {
952 setTimeout( encodingStatus
, 500 );
953 } else if ( _this
.fogg
.state
== 'encoding done' ) {
954 _this
.action_done
= true;
955 progressCallback( 1 );
957 } else if ( _this
.fogg
.state
== 'encoding fail' ) {
958 //@@todo error handling:
959 js_error( 'encoding failed' );
966 * Poll the upload progress and update the UI regularly until the upload is complete.
968 * Asynchronous, returns immediately.
970 doUploadStatus: function() {
972 $j( '#up-status-state' ).html( gM( 'mwe-uploaded-status' ) );
974 _this
.oldResponseText
= '';
976 // Create a local function for timed callback
977 var uploadStatus = function() {
978 var response_text
= _this
.fogg
.responseText
;
979 if ( !response_text
) {
981 var pstatus
= JSON
.parse( _this
.fogg
.uploadstatus() );
982 response_text
= pstatus
["responseText"];
984 js_log( "could not parse uploadstatus / could not get responseText" );
988 if ( _this
.oldResponseText
!= response_text
) {
989 js_log( 'new result text:' + response_text
+ ' state:' + _this
.fogg
.state
);
990 _this
.oldResponseText
= response_text
;
991 // Parse the response text and check for errors
993 var apiResult
= JSON
.parse( response_text
);
995 js_log( "could not parse response_text::" + response_text
+
996 ' ...for now try with eval...' );
998 var apiResult
= eval( response_text
);
1000 var apiResult
= null;
1003 if ( apiResult
&& !_this
.isApiSuccess( apiResult
) ) {
1004 // Show the error and stop the upload
1005 _this
.showApiError( apiResult
);
1006 _this
.action_done
= true;
1007 _this
.fogg
.cancel();
1011 if ( _this
.show_preview
== true ) {
1012 if ( _this
.fogg
.state
== 'encoding' ) {
1013 _this
.renderPreview();
1017 // Update the progress bar
1018 _this
.updateProgress( _this
.fogg
.progress() );
1020 // If we're still uploading or encoding, continue to poll the status
1021 if ( _this
.fogg
.state
== 'encoding' || _this
.fogg
.state
== 'uploading' ) {
1022 setTimeout( uploadStatus
, 100 );
1027 if ( -1 == $j
.inArray( _this
.fogg
.state
, [ 'upload done', 'done', 'encoding done' ] ) ) {
1028 js_log( 'Error:firefogg upload error: ' + _this
.fogg
.state
);
1031 if ( apiResult
&& apiResult
.resultUrl
) {
1033 buttons
[ gM( 'mwe-go-to-resource' ) ] = function() {
1034 window
.location
= apiResult
.resultUrl
;
1036 var go_to_url_txt
= gM( 'mwe-go-to-resource' );
1037 var showMessage
= true;
1038 if ( typeof _this
.done_upload_cb
== 'function' ) {
1039 // Call the callback
1040 // It will return false if it doesn't want us to show our own "done" message
1041 showMessage
= _this
.done_upload_cb( _this
.formData
);
1043 if ( showMessage
) {
1044 _this
.updateProgressWin( gM( 'mwe-successfulupload' ),
1045 gM( 'mwe-upload_done', apiResult
.resultUrl
), buttons
);
1047 this.action_done
= true;
1048 $j( '#upProgressDialog' ).empty().dialog( 'close' );
1051 // Done state with error? Not really possible given how firefogg works...
1052 js_log( " Upload done in chunks mode, but no resultUrl!" );
1060 * This is the cancel button handler, referenced by getCancelButton() in the parent.
1062 onCancel: function( dlElm
) {
1063 if ( !this.have_firefogg
) {
1064 return this.pe_cancel_action();
1066 js_log( 'firefogg:cancel' )
1067 if ( confirm( gM( 'mwe-cancel-confim' ) ) ) {
1068 // FIXME: sillyness ( upstream firefogg cancel fix needed )
1069 if ( navigator
.oscpu
&& navigator
.oscpu
.search( 'Win' ) >= 0 ) {
1070 alert( 'sorry we do not yet support cancel on windows' );
1072 this.action_done
= true;
1074 $j( dlElm
).empty().dialog( 'close' );
1077 // Don't follow the # link: