From 276442a9b48c7b59553fad4398da8ded02dce9cf Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 7 Oct 2015 21:12:26 -0500 Subject: [PATCH 1/9] * VRFS-3512 - upload videos to youtube --- admin/config/application.rb | 1 + admin/config/environments/development.rb | 1 + db/up/video_recording.sql | 4 +- ruby/lib/jam_ruby/app/mailers/admin_mailer.rb | 10 +- ruby/lib/jam_ruby/models/recording.rb | 4 + .../lib/jam_ruby/models/user_authorization.rb | 27 ++- ruby/spec/support/utilities.rb | 4 + web/Gemfile | 10 +- .../dialog/deleteVideoConfirmDialog.js | 69 ++++++ .../javascripts/dialog/editRecordingDialog.js | 2 +- .../dialog/recordingFinishedDialog.js | 33 ++- web/app/assets/javascripts/feedHelper.js | 40 ++++ .../assets/javascripts/feed_item_recording.js | 1 + web/app/assets/javascripts/jam_rest.js | 46 ++++ .../assets/javascripts/react-components.js | 1 + .../PopupConfigureVideoGear.js.jsx.coffee | 2 +- .../PopupVideoUploader.js.jsx.coffee | 209 +++++++++++++++++ .../actions/VideoUploaderActions.js.coffee | 12 + .../stores/RecordingStore.js.jsx.coffee | 9 + .../stores/VideoUploaderStore.js.coffee | 211 ++++++++++++++++++ .../assets/javascripts/sync_viewer.js.coffee | 12 + .../dialogs/deleteVideoConfirmDialog.css.scss | 13 ++ .../minimal/video_uploader.css.scss | 58 +++++ .../stylesheets/users/syncViewer.css.scss | 2 +- .../stylesheets/web/recordings.css.scss | 40 ++++ .../controllers/api_recordings_controller.rb | 28 ++- web/app/controllers/api_users_controller.rb | 15 +- web/app/controllers/popups_controller.rb | 6 + web/app/controllers/sessions_controller.rb | 21 +- .../views/api_claimed_recordings/show.rabl | 2 +- web/app/views/api_feeds/show.rabl | 2 +- web/app/views/api_recordings/show.rabl | 4 +- web/app/views/api_users/authorizations.rabl | 6 + web/app/views/api_users/google_auth.rabl | 16 ++ .../clients/_sync_viewer_templates.html.slim | 2 + web/app/views/clients/index.html.erb | 3 + .../_deleteVideoConfirmDialog.html.slim | 15 ++ web/app/views/dialogs/_dialogs.html.haml | 1 + .../_recordingFinishedDialog.html.haml | 6 +- web/app/views/popups/video_upload.html.slim | 3 + web/app/views/sessions/oauth_complete.erb | 2 + .../users/_feed_recording_ajax.html.haml | 3 + web/config/application.rb | 1 + web/config/environments/development.rb | 1 + web/config/initializers/omniauth.rb | 4 +- web/config/routes.rb | 7 +- .../lib/jam_websockets/router.rb | 4 +- 47 files changed, 938 insertions(+), 35 deletions(-) create mode 100644 web/app/assets/javascripts/dialog/deleteVideoConfirmDialog.js create mode 100644 web/app/assets/javascripts/react-components/PopupVideoUploader.js.jsx.coffee create mode 100644 web/app/assets/javascripts/react-components/actions/VideoUploaderActions.js.coffee create mode 100644 web/app/assets/javascripts/react-components/stores/VideoUploaderStore.js.coffee create mode 100644 web/app/assets/stylesheets/dialogs/deleteVideoConfirmDialog.css.scss create mode 100644 web/app/assets/stylesheets/minimal/video_uploader.css.scss create mode 100644 web/app/views/api_users/authorizations.rabl create mode 100644 web/app/views/api_users/google_auth.rabl create mode 100644 web/app/views/dialogs/_deleteVideoConfirmDialog.html.slim create mode 100644 web/app/views/popups/video_upload.html.slim diff --git a/admin/config/application.rb b/admin/config/application.rb index 0e13be779..eb5c1c612 100644 --- a/admin/config/application.rb +++ b/admin/config/application.rb @@ -110,6 +110,7 @@ module JamAdmin config.redis_host = "localhost:6379" + config.email_social_alias = 'social@jamkazam.com' config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails config.email_generic_from = 'nobody@jamkazam.com' config.email_smtp_address = 'smtp.sendgrid.net' diff --git a/admin/config/environments/development.rb b/admin/config/environments/development.rb index b449898e8..8ae1ae4ed 100644 --- a/admin/config/environments/development.rb +++ b/admin/config/environments/development.rb @@ -46,4 +46,5 @@ JamAdmin::Application.configure do config.email_generic_from = 'nobody-dev@jamkazam.com' config.email_alerts_alias = 'alerts-dev@jamkazam.com' + config.email_social_alias = 'social-dev@jamkazam.com' end diff --git a/db/up/video_recording.sql b/db/up/video_recording.sql index 44a976d30..46502b104 100644 --- a/db/up/video_recording.sql +++ b/db/up/video_recording.sql @@ -1 +1,3 @@ -ALTER TABLE recordings ADD video BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file +ALTER TABLE recordings ADD video BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE user_authorizations ADD refresh_token VARCHAR; +ALTER TABLE recordings ADD external_video_id VARCHAR; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb b/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb index f14d8e424..abe1b9564 100644 --- a/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/admin_mailer.rb @@ -1,6 +1,6 @@ module JamRuby # sends out a boring ale - class AdminMailer < ActionMailer::Base + class AdminMailer < ActionMailer::Base include SendGrid @@ -20,6 +20,14 @@ module JamRuby subject: options[:subject]) end + def social(options) + mail(to: APP_CONFIG.email_social_alias, + from: APP_CONFIG.email_generic_from, + body: options[:body], + content_type: "text/plain", + subject: options[:subject]) + end + def recurly_alerts(user, options) body = options[:body] diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 1ec43b9aa..0fbf186e8 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -701,6 +701,10 @@ module JamRuby self.save(:validate => false) end + def add_video_data(data) + Recording.where(id: self.id).update_all(external_video_id: data[:video_id]) + end + def add_timeline(timeline) global = timeline["global"] raise JamArgumentError, "global must be specified" unless global diff --git a/ruby/lib/jam_ruby/models/user_authorization.rb b/ruby/lib/jam_ruby/models/user_authorization.rb index e83bb8db4..7dfa2168c 100644 --- a/ruby/lib/jam_ruby/models/user_authorization.rb +++ b/ruby/lib/jam_ruby/models/user_authorization.rb @@ -1,7 +1,7 @@ module JamRuby class UserAuthorization < ActiveRecord::Base - attr_accessible :provider, :uid, :token, :token_expiration, :secret, :user + attr_accessible :provider, :uid, :token, :token_expiration, :secret, :user, :refresh_token self.table_name = "user_authorizations" @@ -12,6 +12,31 @@ module JamRuby validates_uniqueness_of :uid, scope: :provider # token, secret, token_expiration can be missing + def self.refreshing_google_auth(user) + auth = self.where(:user_id => user.id) + .where(:provider => 'google_login') + .limit(1).first + + # if we have an auth that will expire in less than 10 minutes + if auth && auth.refresh_token && auth.token_expiration < Time.now - 60 * 10 + + oauth_client = OAuth2::Client.new( + Rails.application.config.google_client_id, Rails.application.config.google_secret, + :site => "https://accounts.google.com", + :token_url => "/o/oauth2/token", + :authorize_url => "/o/oauth2/auth") + access_token = OAuth2::AccessToken.from_hash(oauth_client, {:refresh_token => auth.refresh_token}) + access_token = access_token.refresh! + auth.token = access_token.token + puts "access_token #{Time.now + access_token.expires_in} #{access_token.expires_in}" + auth.token_expiration = Time.now + access_token.expires_in + auth.save + auth + else + auth + end + end + def self.google_auth(user) self .where(:user_id => user.id) diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb index d909b57d2..e33df4093 100644 --- a/ruby/spec/support/utilities.rb +++ b/ruby/spec/support/utilities.rb @@ -7,6 +7,10 @@ def app_config 'http://localhost:3333' end + def email_social_alias + 'social@jamkazam.com' + end + def email_alerts_alias 'alerts@jamkazam.com' end diff --git a/web/Gemfile b/web/Gemfile index 49217de8a..dbf0f8454 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -41,12 +41,12 @@ gem 'eventmachine', '1.0.4' gem 'faraday', '~>0.9.0' gem 'amqp', '0.9.8' gem 'logging-rails', :require => 'logging/rails' -gem 'omniauth', '1.1.1' -gem 'omniauth-facebook', '1.4.1' +gem 'omniauth' +gem 'omniauth-facebook' gem 'omniauth-twitter' -gem 'omniauth-google-oauth2', '0.2.1' -gem 'google-api-client', '0.7.1' -gem 'google-api-omniauth', '0.1.1' +gem 'omniauth-google-oauth2' +gem 'google-api-client' #, '0.7.1' +#gem 'google-api-omniauth' #, '0.1.1' gem 'signet', '0.5.0' gem 'twitter' gem 'fb_graph', '2.5.9' diff --git a/web/app/assets/javascripts/dialog/deleteVideoConfirmDialog.js b/web/app/assets/javascripts/dialog/deleteVideoConfirmDialog.js new file mode 100644 index 000000000..8b6c06811 --- /dev/null +++ b/web/app/assets/javascripts/dialog/deleteVideoConfirmDialog.js @@ -0,0 +1,69 @@ +(function (context, $) { + + "use strict"; + context.JK = context.JK || {}; + context.JK.DeleteVideoConfirmDialog = function (app) { + var logger = context.JK.logger; + var rest = context.JK.Rest(); + var recordingId = null; + var $dialog = null; + var $deleteFromDiskChkBox = null; + var $deleteBtn = null; + var deleting = false; + function resetForm() { + + } + + function beforeShow(args) { + + recordingId = args.d1; + + if(!recordingId) throw "recordingId must be specified"; + + $dialog.data('result', null); + deleting = false; + + } + + function afterHide() { + + } + + function attemptDelete() { + if(deleting) return; + + deleting = true; + + rest.deleteRecordingVideoData(recordingId) + .done(function(){ + $dialog.data('result', true); + app.layout.closeDialog('delete-video-confirm-dialog'); + }) + .fail(app.ajaxError) + } + + function events() { + $deleteBtn.click(attemptDelete); + } + + + function initialize() { + var dialogBindings = { + 'beforeShow': beforeShow, + 'afterHide': afterHide + }; + + app.bindDialog('delete-video-confirm-dialog', dialogBindings); + + $dialog = $('#deleteVideoConfirmDialog'); + $deleteFromDiskChkBox = $dialog.find('.delete-from-disk'); + $deleteBtn = $dialog.find('.delete-btn'); + + events(); + + context.JK.checkbox($deleteFromDiskChkBox); + }; + + this.initialize = initialize; + } +})(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/dialog/editRecordingDialog.js b/web/app/assets/javascripts/dialog/editRecordingDialog.js index 9aac9d33a..409318c0a 100644 --- a/web/app/assets/javascripts/dialog/editRecordingDialog.js +++ b/web/app/assets/javascripts/dialog/editRecordingDialog.js @@ -14,7 +14,7 @@ var $isPublic = null; var $saveBtn = null; var $deleteBtn = null; - + var $videoField = null; var updating = false; var deleting = false; diff --git a/web/app/assets/javascripts/dialog/recordingFinishedDialog.js b/web/app/assets/javascripts/dialog/recordingFinishedDialog.js index c9440dd80..9cd5c2ab8 100644 --- a/web/app/assets/javascripts/dialog/recordingFinishedDialog.js +++ b/web/app/assets/javascripts/dialog/recordingFinishedDialog.js @@ -8,6 +8,8 @@ var playbackControls = null; var recording = null; // deferred object var $dialog = null; + var $saveVideoCheckbox = null + var $uploadToYoutube = null function resetForm() { // remove all display errors @@ -156,6 +158,7 @@ window._oauth_callback = function() { window._oauth_win.close() + logger.debug("closing window") setGoogleAuthState() } return false; @@ -167,6 +170,8 @@ var upload_to_youtube = $('#recording-finished-dialog form input[name=upload_to_youtube]').is(':checked') + upload_to_youtube = false // don't prevent user from getting through dialog because popup now handles auth + if (upload_to_youtube) { $.ajax({ type: "GET", @@ -197,6 +202,7 @@ var save_video = $('#recording-finished-dialog form input[name=save_video]').is(':checked') var upload_to_youtube = $('#recording-finished-dialog form input[name=upload_to_youtube]').is(':checked') + var recording_id = recording.id rest.claimRecording({ id: recording.id, name: name, @@ -210,6 +216,11 @@ $dialog.data('result', {keep:true}); app.layout.closeDialog('recordingFinished'); context.JK.GA.trackMakeRecording(); + if(save_video && upload_to_youtube) { + // you have to have elected to save video to have upload to youtube have any effect + context.VideoUploaderActions.showUploader(recording_id); + } + }) .fail(function (jqXHR) { if (jqXHR.status == 422) { @@ -296,9 +307,9 @@ // Check for google authorization using AJAX and show/hide the // google login button / "signed in" label as appropriate: - $(window).on('focus', function() { + /**$(window).on('focus', function() { setGoogleAuthState(); - }); + }); */ } function setGoogleAuthState() { @@ -333,10 +344,24 @@ } function initializeButtons() { + $saveVideoCheckbox = $('#recording-finished-dialog input[name="save_video"]') + $uploadToYoutube = $('#recording-finished-dialog input[name="upload_to_youtube"]') var isPublic = $('#recording-finished-dialog input[name="is_public"]'); context.JK.checkbox(isPublic); - context.JK.checkbox($('#recording-finished-dialog input[name="save_video"]')); - context.JK.checkbox($('#recording-finished-dialog input[name="upload_to_youtube"]')); + context.JK.checkbox($saveVideoCheckbox); + context.JK.checkbox($uploadToYoutube); + + $saveVideoCheckbox.on('ifChanged', function() { + var saveVideoToDisk = $saveVideoCheckbox.is(':checked') + + if(saveVideoToDisk) { + $uploadToYoutube.iCheck('enable') + } + else { + $uploadToYoutube.iCheck('disable') + } + + }) } function initialize() { diff --git a/web/app/assets/javascripts/feedHelper.js b/web/app/assets/javascripts/feedHelper.js index cdf32c7f9..d7ce6765f 100644 --- a/web/app/assets/javascripts/feedHelper.js +++ b/web/app/assets/javascripts/feedHelper.js @@ -471,6 +471,43 @@ $controls.data('server-info', feed.mix) // for recordingUtils helper methods $controls.data('view-context', 'feed') + // tack on video if available + if(feed.external_video_id) { + + var $videoWrapper = $feedItem.find('.video-wrapper') + var $videoContainer = $feedItem.find('.video-container') + if(gon.isNativeClient) { + var $embed = $('' + + '' + + '') + $videoContainer.append($embed).addClass('no-embed') + $videoWrapper.removeClass('hidden') + $embed.click(function() { + context.JK.popExternalLink($(this).attr('href')) + return false; + }) + } + else { + var $embed = $('