diff --git a/db/up/sms_index_optimize.sql b/db/up/sms_index_optimize.sql index 9f013d902..d5120f165 100644 --- a/db/up/sms_index_optimize.sql +++ b/db/up/sms_index_optimize.sql @@ -9,7 +9,7 @@ CREATE OR REPLACE FUNCTION sms_index (my_user_id VARCHAR, my_locidispid BIGINT, -- XXX: we should pass in enough info to match pagination/query to reduce the impact of this step INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency FROM music_sessions - WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) + WHERE old = FALSE AND (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE AND description != 'Jam Track Session' AND id NOT IN (SELECT id FROM active_music_sessions); @@ -204,6 +204,24 @@ CREATE INDEX index_music_sessions_on_canceled ON music_sessions USING btree(canc CREATE INDEX index_music_sessions_on_session_removed_at ON music_sessions USING btree(session_removed_at); CREATE INDEX index_music_sessions_on_started_at ON music_sessions USING btree(started_at); +CREATE INDEX index_recordings_on_first_quick_mix_id ON recordings USING btree(first_quick_mix_id); +CREATE INDEX index_recordings_on_has_final_mix ON recordings USING btree(has_final_mix); +CREATE INDEX index_quick_mixes_on_recording_id ON quick_mixes USING btree(recording_id); +CREATE INDEX index_quick_mixes_on_cleaned ON quick_mixes USING btree(cleaned); +CREATE INDEX index_quick_mixes_on_completed ON quick_mixes USING btree(completed); +CREATE INDEX index_recordings_on_deleted ON recordings USING btree(deleted); +CREATE INDEX index_recordings_on_all_discarded ON recordings USING btree(all_discarded); +CREATE INDEX index_claimed_recordings_on_is_public ON claimed_recordings USING btree(is_public); +CREATE INDEX index_claimed_recordings_on_recording_id ON claimed_recordings USING btree(recording_id); + + +CREATE INDEX index_charges_on_type ON charges USING btree(type); +CREATE INDEX index_charges_on_billing_should_retry ON charges USING btree(billing_should_retry); +CREATE INDEX index_lesson_sessions_on_charge_id ON lesson_sessions USING btree(charge_id); +CREATE INDEX index_music_sessions_on_lesson_session_id ON music_sessions USING btree(lesson_session_id); +CREATE INDEX index_lesson_sessions_on_status ON lesson_sessions USING btree(status); +CREATE INDEX index_lesson_sessions_on_sent_counter_reminder ON lesson_sessions USING btree(sent_counter_reminder); + -- update music_sessions set canceled = true WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE AND description != 'Jam Track Session' AND id NOT IN (SELECT id FROM active_music_sessions) AND id NOT IN (select distinct on(name, user_id) id FROM music_sessions WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE AND description != 'Jam Track Session' AND id NOT IN (SELECT id FROM active_music_sessions) order by name, user_id); diff --git a/ruby/lib/jam_ruby/models/active_music_session.rb b/ruby/lib/jam_ruby/models/active_music_session.rb index 41a8ebfa5..a24595552 100644 --- a/ruby/lib/jam_ruby/models/active_music_session.rb +++ b/ruby/lib/jam_ruby/models/active_music_session.rb @@ -552,6 +552,10 @@ module JamRuby icecast_server = IcecastServer.find_best_server_for_user(user) if music_session.fan_access icecast_server.lock! if icecast_server + music_session.running_recordings.each do |recording| + recording.stop + end + # check if we are connected to rabbitmq active_music_session = ActiveMusicSession.new active_music_session.id = music_session.id # copy the .id from music_session to active_music_session diff --git a/ruby/lib/jam_ruby/models/chat_message.rb b/ruby/lib/jam_ruby/models/chat_message.rb index bd38c1c1f..b67a619ab 100644 --- a/ruby/lib/jam_ruby/models/chat_message.rb +++ b/ruby/lib/jam_ruby/models/chat_message.rb @@ -114,12 +114,19 @@ module JamRuby attachment_type = music_notation.attachment_type attachment_name = music_notation.file_name elsif claimed_recording - attachment_id = claimed_recording.id - attachment_type = 'recording' - attachment_name = claimed_recording.name + if purpose == 'Video Uploaded' + attachment_id = claimed_recording.video_id + attachment_type = 'video' + else + attachment_id = claimed_recording.id + attachment_type = 'recording' + attachment_name = claimed_recording.name + + end end + msg = @@message_factory.chat_message( music_session_id, user.name, diff --git a/ruby/lib/jam_ruby/models/claimed_recording.rb b/ruby/lib/jam_ruby/models/claimed_recording.rb index 7d2727138..15d91fae6 100644 --- a/ruby/lib/jam_ruby/models/claimed_recording.rb +++ b/ruby/lib/jam_ruby/models/claimed_recording.rb @@ -31,6 +31,9 @@ module JamRuby SHARE_TOKEN_LENGTH = 8 FIXNUM_MAX = (2**(0.size * 8 -2) -1) + def video_id + recording.external_video_id + end def user_belongs_to_recording if user && recording && !recording.users.exists?(user.id) diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb index 3d2eb41b6..2280ad086 100644 --- a/ruby/lib/jam_ruby/models/lesson_booking.rb +++ b/ruby/lib/jam_ruby/models/lesson_booking.rb @@ -470,6 +470,10 @@ module JamRuby active end + def has_access?(user) + user.id == student.id || user.id == teacher.id || (self.school.nil? ? false : self.school.user.id == user.id) + end + def validate_accepted # accept is multipe purpose; either accept the initial request, or a counter slot if self.status_was != STATUS_REQUESTED && self.status_was != STATUS_COUNTERED diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb index 66760d083..bb97f9d2c 100644 --- a/ruby/lib/jam_ruby/models/lesson_session.rb +++ b/ruby/lib/jam_ruby/models/lesson_session.rb @@ -11,7 +11,7 @@ module JamRuby @@log = Logging.logger[LessonSession] delegate :sent_billing_notices, :last_billing_attempt_at, :billing_attempts, :billing_should_retry, :billed_at, :billing_error_detail, :billing_error_reason, :is_card_declined?, :is_card_expired?, :last_billed_at_date, :sent_billing_notices, to: :lesson_payment_charge, allow_nil: true - delegate :is_test_drive?, :is_single_free?, :is_normal?, :approved_before?, :is_active?, :recurring, :is_monthly_payment?, :school_on_school?, :school_on_school_payment?, :no_school_on_school_payment?, :payment_if_school_on_school?, :scheduling_email, :teacher_school_emails, :school_and_teacher, :school_over_teacher, :school_and_teacher_ids, :school_over_teacher_ids, :posa_card, :remaining_roll_forward_amount_in_cents, :has_recurring_counter?, :ever_accepted?, to: :lesson_booking + delegate :is_test_drive?, :is_single_free?, :is_normal?, :approved_before?, :is_active?, :recurring, :is_monthly_payment?, :school_on_school?, :school_on_school_payment?, :no_school_on_school_payment?, :payment_if_school_on_school?, :scheduling_email, :teacher_school_emails, :school_and_teacher, :school_over_teacher, :school_and_teacher_ids, :school_over_teacher_ids, :posa_card, :remaining_roll_forward_amount_in_cents, :has_recurring_counter?, :ever_accepted?, :has_access?, to: :lesson_booking delegate :pretty_scheduled_start, to: :music_session @@ -338,6 +338,21 @@ module JamRuby self.save(validate: false) end + def video_uploaded(user, recording, data) + + claim = recording.claim_for_user(user) + msg = ChatMessage.create(user, nil, "Video Recording '#{claim.name}'", ChatMessage::CHANNEL_LESSON, nil, other_user(user), self, "Video Uploaded", nil, claim) + Notification.send_lesson_message('video uploaded', self, false) + Notification.send_lesson_message('video uploaded', self, true) + end + + def other_user(user) + if user.id == student.id + teacher + else + student + end + end def session_completed LessonSession.transaction do self.lock! diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 290cf416c..544b61a5b 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -482,10 +482,22 @@ module JamRuby if self.musician_access || self.fan_access true else - self.creator == user || self.invited_musicians.exists?(user.id) + self.creator == user || self.invited_musicians.exists?(user.id) || self.approved_rsvps.include?(user) || self.has_lesson_access?(user) end end + def has_lesson_access?(user) + if is_lesson? + puts "HAS LESSON ACCESS" + result = lesson_session.has_access?(user) + puts "HAS LESSON ACCESS #{ result}" + result + else + false + end + + end + def set_session_controller(current_user, user) # only allow update of session controller by the creator or the currently marked user @@ -896,6 +908,9 @@ module JamRuby RsvpRequest.index(self, nil, {status: 'pending'}) end + def running_recordings + recordings.where(duration: nil) + end def recordings Recording.where(music_session_id: self.id) end diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 348ab6de5..46fac5e82 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -755,8 +755,12 @@ module JamRuby self.save(:validate => false) end - def add_video_data(data) + def add_video_data(user, data) Recording.where(id: self.id).update_all(external_video_id: data[:video_id]) + + if music_session.music_session.is_lesson? + music_session.lesson_session.video_uploaded(user, self, data) + end end def add_timeline(timeline) diff --git a/ruby/lib/jam_ruby/models/user_authorization.rb b/ruby/lib/jam_ruby/models/user_authorization.rb index a78bad549..28853954b 100644 --- a/ruby/lib/jam_ruby/models/user_authorization.rb +++ b/ruby/lib/jam_ruby/models/user_authorization.rb @@ -38,6 +38,10 @@ module JamRuby auth.save return auth rescue Exception => e + puts "could not refresh; #{e}" + if auth + auth.destroy + end # couldn't refresh; probably the user has revoked the app's rights return nil end diff --git a/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee b/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee index 3b012781b..27b77eec6 100644 --- a/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/ChatDialog.js.jsx.coffee @@ -2,7 +2,7 @@ context = window @ChatDialog = React.createClass({ - mixins: [@PostProcessorMixin, Reflux.listenTo(@AppStore, "onAppInit")] + mixins: [@PostProcessorMixin, Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@SessionStore, 'onSessionChanged')] teacher: false beforeShow: (args) -> @@ -27,7 +27,8 @@ context = window @app.ajaxError(jqXHR) afterHide: () -> - window.ChatActions.activateChannel('global') + if !@session?.isLesson + window.ChatActions.activateChannel('global') parseId:(id) -> if !id? @@ -47,6 +48,9 @@ context = window @app.bindDialog('chat-dialog', dialogBindings); + onSessionChanged: (session) -> + @session = session + componentDidMount: () -> @root = $(@getDOMNode()) diff --git a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee index beb1fc840..901be0624 100644 --- a/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/ChatWindow.js.jsx.coffee @@ -45,6 +45,7 @@ ChatActions = @ChatActions when 'Audio File' then 'attached an audio file' when 'JamKazam Recording' then 'attached a recording' when 'Lesson Timeout' then 'canceled by system' + when 'Video Uploaded' then 'uploaded video' else purpose notationClicked: (music_notation, e) -> @@ -61,6 +62,10 @@ ChatActions = @ChatActions e.preventDefault() context.JK.popExternalLink("/recordings/#{recording.id}") + videoClicked: (recording, e) -> + e.preventDefault() + context.JK.popExternalLink("https://www.youtube.com/watch?v=#{recording.video_id}") + openMenu: (lesson, e) -> $this = $(e.target) if !$this.is('.lesson-session-actions-btn') @@ -132,6 +137,8 @@ ChatActions = @ChatActions additional = `{msg.music_notation.file_name}` else if msg.purpose == 'JamKazam Recording' additional = `{msg.claimed_recording.name}` + else if msg.purpose == 'Video Uploaded' + additional = `Watch on YouTube` msgs.push(`
{sender}{purpose} @@ -209,6 +216,7 @@ ChatActions = @ChatActions e.preventDefault() @sendMessage() + handleCloseMessage: (e) -> e.preventDefault() this.props.onCloseClicked() diff --git a/web/app/assets/javascripts/react-components/SessionFilesBtn.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionFilesBtn.js.jsx.coffee new file mode 100644 index 000000000..bc824b703 --- /dev/null +++ b/web/app/assets/javascripts/react-components/SessionFilesBtn.js.jsx.coffee @@ -0,0 +1,18 @@ +context = window +RecordingActions = @RecordingActions + +@SessionFilesBtn = React.createClass({ + + + getInitialState: () -> + {} + + openLessonChat: () -> + this.props.app.layout.showDialog('chat-dialog', {d1: 'lesson_' + this.props.lessonId}) + + render: () -> + ` + + FILES + ` +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee index c3e0e0381..c029f7428 100644 --- a/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionScreen.js.jsx.coffee @@ -3,17 +3,25 @@ MIX_MODES = context.JK.MIX_MODES SessionActions = @SessionActions + @SessionScreen = React.createClass({ - mixins: [Reflux.listenTo(@AppStore,"onAppInit"), Reflux.listenTo(@SessionActions.allowLeaveSession, "onAllowLeaveSession")] + mixins: [Reflux.listenTo(@AppStore,"onAppInit"), Reflux.listenTo(@SessionActions.allowLeaveSession, "onAllowLeaveSession"), Reflux.listenTo(@SessionStore,"onSessionStoreChanged")] + getInitialState: () -> + {isLesson: false, lessonId: null} + render: () -> videoBtn = null + filesBtn = null + if gon.global.video_available != 'none' videoBtn = `` + if this.state.isLesson + filesBtn = `` `
@@ -22,6 +30,7 @@ SessionActions = @SessionActions {videoBtn} + {filesBtn} @@ -70,6 +79,8 @@ SessionActions = @SessionActions @logger.debug("session beforeDisconnect") return { freezeInteraction: true }; + onSessionStoreChanged: (session) -> + this.setState({isLesson: session.isLesson, lessonId: session.lessonId}) onAllowLeaveSession: () -> @allowLeave = true diff --git a/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee b/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee index 51f385a46..8db01b34d 100644 --- a/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee +++ b/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee @@ -9,10 +9,15 @@ context = window @isRecording = isRecording @downloadingJamTrack = downloadingJamTrack @preppingVstEnable = preppingVstEnable + @isLesson = @session?.lesson_session? + if @isLesson + @lessonId = @session.lesson_session.id inSession: () -> @session? + + participants: () -> if @session return @session.participants diff --git a/web/app/assets/stylesheets/client/react-components/ChatWindow.scss b/web/app/assets/stylesheets/client/react-components/ChatWindow.scss index c574014f1..4a7707d91 100644 --- a/web/app/assets/stylesheets/client/react-components/ChatWindow.scss +++ b/web/app/assets/stylesheets/client/react-components/ChatWindow.scss @@ -23,7 +23,7 @@ div[data-react-class="ChatWindow"] { } .additional { - margin-left:4px; + margin-left:0px; display: block; margin-top:4px; } @@ -131,7 +131,7 @@ div[data-react-class="ChatWindow"] { overflow: auto; margin: 0px 15px; /*height: 210px;*/ - height: 100%; + height: 98%; } .chart-text-section { diff --git a/web/app/assets/stylesheets/dialogs/chatDialog.scss b/web/app/assets/stylesheets/dialogs/chatDialog.scss index 43378f972..fc196e13c 100644 --- a/web/app/assets/stylesheets/dialogs/chatDialog.scss +++ b/web/app/assets/stylesheets/dialogs/chatDialog.scss @@ -10,7 +10,8 @@ } .dialog-inner { width: auto; - height:calc(100% - 29px) + height:calc(100% - 29px); + padding-top:3px; } div[data-react-class="ChatDialog"] { diff --git a/web/app/controllers/api_recordings_controller.rb b/web/app/controllers/api_recordings_controller.rb index 38447f664..be3b07b26 100644 --- a/web/app/controllers/api_recordings_controller.rb +++ b/web/app/controllers/api_recordings_controller.rb @@ -477,7 +477,7 @@ class ApiRecordingsController < ApiController end def add_video_data - @recording.add_video_data(params) + @recording.add_video_data(current_user, params) video_id = params[:video_id] @@ -488,7 +488,7 @@ class ApiRecordingsController < ApiController body << "User: " + current_user.admin_url + "\n\n" body << "Recording Landing: #{recording_detail_url(@recording.id)}\n" - private_public = @recording.is_public ? 'Public' : 'Private' + private_public = @recording.is_public? ? 'Public' : 'Private' AdminMailer.social({ subject:"#{private_public } Video Uploaded by #{current_user.name}", body:body diff --git a/web/app/views/api_chats/show.rabl b/web/app/views/api_chats/show.rabl index ec147a8dc..bc3a24d44 100644 --- a/web/app/views/api_chats/show.rabl +++ b/web/app/views/api_chats/show.rabl @@ -15,5 +15,5 @@ child :music_notation do end child :claimed_recording do - attributes :id, :name + attributes :id, :name, :video_id end