diff --git a/public/css/media-editor.less b/public/css/media-editor.less index 7a8155b0d..069f0f959 100644 --- a/public/css/media-editor.less +++ b/public/css/media-editor.less @@ -5,11 +5,13 @@ .media-editor { @titleBarHeight: 60px; - @addClipHeight: 165px; + @addClipHeight: 130px; @durationInputHeight: 48px; + @ytUploaderHeight: 385px; .butter-editor-body { top: @titleBarHeight; + bottom: 0; } .fl-right { @@ -50,6 +52,28 @@ } } + .record-webcam { + &.hidden, .hidden { + display: none; + } + #youtube-upload { + margin-top: 20px; + } + a.close-btn { + position: absolute; + top: -2px; + right: -8px; + + &:hover { + cursor: pointer; + } + + &.hidden { + display: none; + } + } + } + &.add-media-collapsed { .add-media-panel { margin: 0; @@ -59,22 +83,37 @@ .media-gallery { top: 0; } + iframe { + &.hidden { + display: none; + } + } } .add-media-panel { - height: @addClipHeight; .transition( all 0.2s ease ); overflow: hidden; opacity: 1; margin: 0 20px; + + &.hidden { + display: none; + } + } + + .yt-upload-panel { + position: relative; + top: 0; } .media-gallery { - position: absolute; - bottom: @durationInputHeight; - top: @addClipHeight + 15px; width: 100%; margin-top: -1px; + left: 0; + right: 0; + position: absolute; + top: @titleBarHeight + @addClipHeight; + bottom: @durationInputHeight; .media-gallery-heading { background: #FFF; @@ -85,11 +124,13 @@ font-size: 11px; font-weight: 700; text-transform: uppercase; - position: absolute; - top: 0; left: 0; right: 0; } + + &.shrink-gallery { + top: @ytUploaderHeight; + } } .media-gallery-base-media.butter-form-inline.form-single { @@ -119,11 +160,12 @@ .media-gallery-scroll-container { position: absolute; - top: 46px; + top: 48px; left: 0; right: 0; bottom: 0; } + .media-gallery-list { list-style: none; padding: 0; diff --git a/public/src/editor/media-gallery-editor.js b/public/src/editor/media-gallery-editor.js index 9d7eb1dfc..03aa29383 100644 --- a/public/src/editor/media-gallery-editor.js +++ b/public/src/editor/media-gallery-editor.js @@ -1,15 +1,21 @@ /* This Source Code Form is subject to the terms of the MIT license * If a copy of the MIT license was not distributed with this file, you can * obtain one at https://raw.github.com/mozilla/butter/master/LICENSE */ +/*global YT */ define( [ "util/lang", "util/uri", "util/keys", "util/mediatypes", "editor/editor", - "util/time", "util/dragndrop", "text!layouts/media-editor.html" ], - function( LangUtils, URI, KeysUtils, MediaUtils, Editor, Time, DragNDrop, EDITOR_LAYOUT ) { + "util/time", "util/dragndrop", "ui/widget/tooltip", "text!layouts/media-editor.html" ], + function( LangUtils, URI, KeysUtils, MediaUtils, Editor, Time, DragNDrop, ToolTip, EDITOR_LAYOUT ) { - var _parentElement = LangUtils.domFragment( EDITOR_LAYOUT,".media-editor" ), + var _parentElement = LangUtils.domFragment( EDITOR_LAYOUT, ".media-editor" ), _addMediaTitle = _parentElement.querySelector( ".add-new-media" ), _addMediaPanel = _parentElement.querySelector( ".add-media-panel" ), + _recordWebcam = _parentElement.querySelector( ".record-webcam a" ), + _progress = _parentElement.querySelector( ".record-webcam p.youtube-upload-progress" ), + _ytUploadError = _parentElement.querySelector( ".record-webcam p.youtube-upload-error" ), + _closeBtn = _parentElement.querySelector( ".record-webcam a.close-btn" ), + _urlInput = _addMediaPanel.querySelector( ".add-media-input" ), _addBtn = _addMediaPanel.querySelector( ".add-media-btn" ), _errorMessage = _parentElement.querySelector( ".media-error-message" ), @@ -39,13 +45,14 @@ define( [ "util/lang", "util/uri", "util/keys", "util/mediatypes", "editor/edito _urlInput.value = ""; clearTimeout( _mediaLoadTimeout ); - clearTimeout( _cancelSpinner ); _urlInput.classList.remove( "error" ); _addMediaPanel.classList.remove( "invalid-field" ); _errorMessage.classList.add( "hidden" ); _loadingSpinner.classList.add( "hidden" ); _addBtn.classList.add( "hidden" ); + + _recordWebcam.classList.remove( "hidden" ); } function setBaseDuration( duration ) { @@ -193,7 +200,10 @@ define( [ "util/lang", "util/uri", "util/keys", "util/mediatypes", "editor/edito var el = _GALLERYITEM.cloneNode( true ), source = data.source; + if ( !_media.clipData[ source ] ) { + clearTimeout( _cancelSpinner ); + _recordWebcam.classList.remove( "hidden" ); _media.clipData[ source ] = source; _butter.dispatch( "mediaclipadded" ); @@ -234,8 +244,10 @@ define( [ "util/lang", "util/uri", "util/keys", "util/mediatypes", "editor/edito function onInput() { if ( _urlInput.value ) { _addBtn.classList.remove( "hidden" ); + _recordWebcam.classList.add( "hidden" ); } else { _addBtn.classList.add( "hidden" ); + _recordWebcam.classList.remove( "hidden" ); } clearTimeout( _cancelSpinner ); clearTimeout( _mediaLoadTimeout ); @@ -285,6 +297,108 @@ define( [ "util/lang", "util/uri", "util/keys", "util/mediatypes", "editor/edito } } + var loadYouTubeRecorder = function() { + + var widget, + pending = false; + + _ytUploadError.classList.add( "hidden" ); + _closeBtn.classList.remove( "hidden" ); + + function closeWidget() { + pending = false; + widget.destroy(); + widget = null; + _galleryPanel.classList.remove( "shrink-gallery" ); + _addMediaPanel.classList.remove( "hidden" ); + _closeBtn.classList.add( "hidden" ); + _closeBtn.removeEventListener( "click", clickCloseBtn ); + } + + function clickCloseBtn() { + closeWidget(); + _recordWebcam.classList.remove( "hidden" ); + _progress.classList.add( "hidden" ); + } + + _closeBtn.addEventListener( "click", clickCloseBtn ); + + widget = new YT.UploadWidget( "youtube-upload", { + width: 295, + height: 295, + events: { + "onApiReady": function() { + widget.setVideoKeywords([ "webcam", "video", "popcorn" ]); + widget.setVideoPrivacy( "unlisted" ); + }, + "onUploadSuccess": function() { + _progress.classList.remove( "hidden" ); + }, + "onProcessingComplete": function( event ) { + closeWidget(); + _progress.classList.add( "hidden" ); + // transitionend event is not reliable and not cross browser supported. + _cancelSpinner = setTimeout( function() { + _loadingSpinner.classList.remove( "hidden" ); + }, 300 ); + addMediaToGallery( "http://www.youtube.com/watch?v=" + event.data.videoId, onDenied ); + }, + "onStateChange": function( event ) { + var state = event.data.state; + + if ( state === YT.UploadWidgetState.ERROR ) { + closeWidget(); + _progress.classList.add( "hidden" ); + _recordWebcam.classList.remove( "hidden" ); + _ytUploadError.classList.remove( "hidden" ); + } else if ( state === YT.UploadWidgetState.PENDING ) { + pending = true; + } else if ( state === YT.UploadWidgetState.STOPPED ) { + if ( !pending ) { + closeWidget(); + _recordWebcam.classList.remove( "hidden" ); + } + } + } + } + }); + }; + + _recordWebcam.addEventListener( "click", function() { + + var tag, + protocol, + youTubeReadyFn = window.onYouTubeIframeAPIReady; + + _recordWebcam.classList.add( "hidden" ); + _galleryPanel.classList.add( "shrink-gallery" ); + _addMediaPanel.classList.add( "hidden" ); + + if ( window.YT ) { + loadYouTubeRecorder(); + } else { + tag = document.createElement( "script" ); + protocol = window.location.protocol === "file:" ? "http:" : ""; + + tag.src = protocol + "//www.youtube.com/iframe_api"; + document.head.appendChild( tag ); + + window.onYouTubeIframeAPIReady = function() { + if ( youTubeReadyFn ) { + youTubeReadyFn(); + } + loadYouTubeRecorder(); + }; + } + }, false ); + + ToolTip.create({ + name: "tooltip-record-media", + element: _recordWebcam, + top: "35px", + message: "Record a video with your webcam and upload it to YouTube" + }); + Editor.register( "media-editor", null, function( rootElement, butter ) { rootElement = _parentElement; _this = this; diff --git a/public/src/layouts/media-editor.html b/public/src/layouts/media-editor.html index 16f69f07d..ec315ca5e 100644 --- a/public/src/layouts/media-editor.html +++ b/public/src/layouts/media-editor.html @@ -21,8 +21,23 @@

Create new media clip

+
+
+ + + Record From Webcam + + +
+ + +
+
+