diff --git a/admin/config/application.rb b/admin/config/application.rb index 573e946f0..08b11d58c 100644 --- a/admin/config/application.rb +++ b/admin/config/application.rb @@ -126,6 +126,8 @@ module JamAdmin config.twitter_app_secret = ENV['TWITTER_APP_SECRET'] || 'Azcy3QqfzYzn2fsojFPYXcn72yfwa0vG6wWDrZ3KT8' config.ffmpeg_path = ENV['FFMPEG_PATH'] || (File.exist?('/usr/local/bin/ffmpeg') ? '/usr/local/bin/ffmpeg' : '/usr/bin/ffmpeg') + config.normalize_ogg_path = ENV['NORMALIZE_OGG_PATH'] || (File.exist?('/usr/local/bin/normalize-ogg') ? '/usr/local/bin/normalize-ogg' : '/usr/bin/normalize-ogg') + config.normalize_mp3_path = ENV['NORMALIZE_MP3_PATH'] || (File.exist?('/usr/local/bin/normalize-mp3') ? '/usr/local/bin/normalize-mp3' : '/usr/bin/normalize-mp3') config.max_audio_downloads = 100 diff --git a/pb/src/client_container.proto b/pb/src/client_container.proto index b9a7aeba0..5515757d9 100644 --- a/pb/src/client_container.proto +++ b/pb/src/client_container.proto @@ -481,6 +481,7 @@ message RecordingMasterMixComplete { optional string msg = 3; optional string notification_id = 4; optional string created_at = 5; + optional string claimed_recording_id = 6; } message DownloadAvailable { diff --git a/ruby/lib/jam_ruby/lib/nav.rb b/ruby/lib/jam_ruby/lib/nav.rb index 7f58dad61..bfc788471 100644 --- a/ruby/lib/jam_ruby/lib/nav.rb +++ b/ruby/lib/jam_ruby/lib/nav.rb @@ -27,6 +27,10 @@ module JamRuby "#{base_url}/findSession" end + def self.session(session) + "#{base_url}/session/#{session.id}" + end + private def self.base_url diff --git a/ruby/lib/jam_ruby/message_factory.rb b/ruby/lib/jam_ruby/message_factory.rb index 7ece76e28..f5666089a 100644 --- a/ruby/lib/jam_ruby/message_factory.rb +++ b/ruby/lib/jam_ruby/message_factory.rb @@ -712,9 +712,10 @@ module JamRuby ) end - def recording_master_mix_complete(receiver_id, recording_id, band_id, msg, notification_id, created_at) + def recording_master_mix_complete(receiver_id, recording_id, claimed_recording_id, band_id, msg, notification_id, created_at) recording_master_mix_complete = Jampb::RecordingMasterMixComplete.new( :recording_id => recording_id, + :claimed_recording_id => claimed_recording_id, :band_id => band_id, :msg => msg, :notification_id => notification_id, diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index 77b2c2394..adb481c81 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -1147,6 +1147,7 @@ module JamRuby msg = @@message_factory.recording_master_mix_complete( claimed_recording.user_id, recording.id, + claimed_recording.id, notification.band_id, notification_msg, notification.id, diff --git a/ruby/lib/jam_ruby/resque/audiomixer.rb b/ruby/lib/jam_ruby/resque/audiomixer.rb index 66973b12a..da3038622 100644 --- a/ruby/lib/jam_ruby/resque/audiomixer.rb +++ b/ruby/lib/jam_ruby/resque/audiomixer.rb @@ -322,7 +322,7 @@ module JamRuby raise "no output ogg file after mix" unless File.exist? @output_ogg_filename - ffmpeg_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{@output_ogg_filename}\" -ab 128k -metadata JamRecordingId=#{@manifest[:recording_id]} -metadata JamMixId=#{@mix_id} -metadata JamType=Mix \"#{@output_mp3_filename}\"" + ffmpeg_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{@output_ogg_filename}\" -ab 192k -metadata JamRecordingId=#{@manifest[:recording_id]} -metadata JamMixId=#{@mix_id} -metadata JamType=Mix \"#{@output_mp3_filename}\"" system(ffmpeg_cmd) @@ -335,6 +335,30 @@ module JamRuby end raise "no output mp3 file after conversion" unless File.exist? @output_mp3_filename + + # time to normalize both mp3 and ogg files + + normalize_ogg_cmd = "#{APP_CONFIG.normalize_ogg_path} --bitrate 128 -i \"#{@output_ogg_filename}\"" + system(normalize_ogg_cmd) + unless $? == 0 + @error_reason = 'normalize-ogg-failed' + @error_detail = $?.to_s + error_msg = "normalize-ogg failed status=#{$?} error_reason=#{@error_reason} error_detail=#{@error_detail}" + @@log.info(error_msg) + raise error_msg + end + raise "no output ogg file after normalization" unless File.exist? @output_ogg_filename + + normalize_mp3_cmd = "#{APP_CONFIG.normalize_mp3_path} --bitrate 128 -i \"#{@output_mp3_filename}\"" + system(normalize_mp3_cmd) + unless $? == 0 + @error_reason = 'normalize-mp3-failed' + @error_detail = $?.to_s + error_msg = "normalize-mp3 failed status=#{$?} error_reason=#{@error_reason} error_detail=#{@error_detail}" + @@log.info(error_msg) + raise error_msg + end + raise "no output mp3 file after conversion" unless File.exist? @output_mp3_filename end def symbolize_keys(obj) diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb index 7c625ed78..0ed42f4d9 100644 --- a/ruby/spec/support/utilities.rb +++ b/ruby/spec/support/utilities.rb @@ -38,6 +38,14 @@ def app_config ENV['FFMPEG_PATH'] || '/usr/local/bin/ffmpeg' end + def normalize_ogg_path + ENV['NORMALIZE_OGG_PATH'] || '/usr/local/bin/normalize-ogg' + end + + def normalize_mp3_path + ENV['NORMALIZE_MP3_PATH'] || '/usr/local/bin/normalize-mp3' + end + def icecast_reload_cmd 'true' # as in, /bin/true end diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index 1e56483f3..e91e049b8 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -25,6 +25,7 @@ var notificationBatchSize = 20; var currentNotificationPage = 0; var didLoadAllNotifications = false, isLoading = false; + var ui = new context.JK.UIHelper(JK.app); function isNotificationsPanelVisible() { return $contents.is(':visible') @@ -1101,7 +1102,7 @@ "class": "button-orange", callback: shareRecording, callback_args: { - "recording_id": payload.recording_id + "claimed_recording_id": payload.claimed_recording_id } }] ); @@ -1109,7 +1110,9 @@ } function shareRecording(args) { - var recordingId = args.recording_id; + var claimedRecordingId = args.claimed_recording_id; + + ui.launchShareDialog(claimedRecordingId, 'recording'); } function registerBandInvitation() { diff --git a/web/app/assets/javascripts/recording_utils.js.coffee b/web/app/assets/javascripts/recording_utils.js.coffee index 3a6dbd002..925029c7d 100644 --- a/web/app/assets/javascripts/recording_utils.js.coffee +++ b/web/app/assets/javascripts/recording_utils.js.coffee @@ -84,17 +84,25 @@ class RecordingUtils mixStateClass = 'discarded' mixState = 'discarded' else - mixStateMsg = 'STILL UPLOADING' - mixStateClass = 'still-uploading' - mixState = 'still-uploading' - return { + if mix.fake and mix.discarded + mixStateMsg = 'DISCARDED' + mixStateClass = 'discarded' + mixState = 'discarded' + else + mixStateMsg = 'STILL UPLOADING' + mixStateClass = 'still-uploading' + mixState = 'still-uploading' + + result = { mixStateMsg: mixStateMsg, mixStateClass: mixStateClass, mixState: mixState, isError: mixState == 'error' } + result + onMixHover: () -> $mix = $(this).closest('.mix') mixStateInfo = $mix.data('mix-state') @@ -114,7 +122,10 @@ class RecordingUtils serverInfo = $mix.data('server-info') # lie if this is a virtualized mix (i.e., mix is created after recording is made) - mixState = 'still-uploading' if !serverInfo? or serverInfo.fake + if !serverInfo? + mixState = 'still-uploading' + else if serverInfo.fake + mixState = if serverInfo.discarded then 'discarded' else 'still-uploading' summary = '' if mixState == 'still-uploading' diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index a49148209..c3dbad3b9 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -371,6 +371,14 @@ if(response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track") { app.notifyAlert("No Inputs Configured", $('You will need to reconfigure your audio device.')); } + else if(response["errors"] && response["errors"]["music_session"] && response["errors"]["music_session"][0] == ["is currently recording"]) { + promptLeave = false; + context.window.location = "/client#/findSession"; + app.notify( { title: "Unable to Join Session", text: "The session is currently recording." }, null, true); + } + else { + app.notifyServerError(xhr, 'Unable to Join Session'); + } } else { app.notifyServerError(xhr, 'Unable to Join Session'); @@ -396,7 +404,12 @@ if(screenActive) { // this path is possible if FTUE is invoked on session page, and they cancel sessionModel.leaveCurrentSession() - .fail(app.ajaxError); + .fail(function(jqXHR) { + if(jqXHR.status != 404) { + logger.debug("leave session failed"); + app.ajaxError(arguments) + } + }); } screenActive = false; diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index 9bfb3409d..60d8862b8 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -184,10 +184,10 @@ leaveSessionRest(currentSessionId) .done(function() { sessionChanged(); - deferred.resolve(arguments); + deferred.resolve(arguments[0], arguments[1], arguments[2]); }) .fail(function() { - deferred.reject(arguments); + deferred.reject(arguments[0], arguments[1], arguments[2]); }); // 'unregister' for callbacks diff --git a/web/app/assets/javascripts/session_utils.js b/web/app/assets/javascripts/session_utils.js index 482aaeab1..0c22b0108 100644 --- a/web/app/assets/javascripts/session_utils.js +++ b/web/app/assets/javascripts/session_utils.js @@ -133,6 +133,11 @@ rest.getSession(sessionId) .done(function(response) { session = response; + if(session && session.recording) { + context.JK.app.notify( { title: "Unable to Join Session", text: "The session is currently recording." }, null, true); + return; + } + if ("invitations" in session) { var invitation; // user has invitations for this session diff --git a/web/app/assets/javascripts/sync_viewer.js.coffee b/web/app/assets/javascripts/sync_viewer.js.coffee index 51dd2637f..4be93ba13 100644 --- a/web/app/assets/javascripts/sync_viewer.js.coffee +++ b/web/app/assets/javascripts/sync_viewer.js.coffee @@ -220,6 +220,7 @@ context.JK.SyncViewer = class SyncViewer updateTrackState: ($track) => clientInfo = $track.data('client-info') serverInfo = $track.data('server-info') + myTrack = serverInfo.user.id == context.JK.currentUserId # determine client state clientStateMsg = 'UNKNOWN' @@ -261,7 +262,7 @@ context.JK.SyncViewer = class SyncViewer uploadStateClass = 'error' uploadState = @uploadStates.too_many_upload_failures else - if serverInfo.user.id == context.JK.currentUserId + if myTrack if clientInfo? if clientInfo.local_state == 'HQ' uploadStateMsg = 'PENDING UPLOAD' @@ -282,7 +283,7 @@ context.JK.SyncViewer = class SyncViewer else uploadStateMsg = 'UPLOADED' uploadStateClass = 'uploaded' - if serverInfo.user.id == context.JK.currentUserId + if myTrack uploadState = @uploadStates.me_uploaded else uploadState = @uploadStates.them_uploaded @@ -301,6 +302,30 @@ context.JK.SyncViewer = class SyncViewer $uploadStateMsg.text(uploadStateMsg) $uploadStateProgress.css('width', '0') + # this allows us to make styling decisions based on the combination of both client and upload state. + $track.addClass("clientState-#{clientStateClass}").addClass("uploadState-#{uploadStateClass}") + + $clientRetry = $clientState.find('.retry') + $uploadRetry = $uploadState.find('.retry') + + if gon.isNativeClient + # handle client state + + # only show RETRY button if you have a SQ or if it's missing, and it's been uploaded already + if (clientState == @clientStates.sq or clientState == @clientStates.missing) and (uploadState == @uploadStates.me_uploaded or uploadState == @uploadStates.them_uploaded) + $clientRetry.show() + else + $clientRetry.hide() + + # only show RETRY button if you have the HQ track, it's your track, and the server doesn't yet have it + if myTrack and @clientStates.hq and (uploadState == @uploadStates.error or uploadState == @uploadStates.me_upload_soon) + $uploadRetry.show() + else + $uploadRetry.hide() + else + $clientRetry.hide() + $uploadRetry.hide() + associateClientInfo: (recording) => for clientInfo in recording.local_tracks $track = @list.find(".recorded-track[data-recording-id='#{recording.recording_id}'][data-client-track-id='#{clientInfo.client_track_id}']") @@ -485,7 +510,14 @@ context.JK.SyncViewer = class SyncViewer recordingInfo = null if userSync == 'fake' recordingInfo = arguments[1] - userSync = { recording_id: recordingInfo.id, duration: recordingInfo.duration, fake:true } + # sift through the recorded_tracks in here; if they are marked discarded, then we can also mark this one discarded too + discarded = true + for claim in recordingInfo.claimed_recordings + if claim.user_id == context.JK.currentUserId + discarded = false + break + + userSync = { recording_id: recordingInfo.id, duration: recordingInfo.duration, fake:true, discarded: discarded } $mix = $(context._.template(@templateMix.html(), userSync, {variable: 'data'})) else $mix = $(context._.template(@templateMix.html(), userSync, {variable: 'data'})) @@ -504,14 +536,16 @@ context.JK.SyncViewer = class SyncViewer $track.data('sync-viewer', this) $clientState = $track.find('.client-state') $uploadState = $track.find('.upload-state') - $clientState.find('.retry').click(this.retryDownloadRecordedTrack) - $uploadState.find('.retry').click(this.retryUploadRecordedTrack) + $clientStateRetry = $clientState.find('.retry') + $clientStateRetry.click(this.retryDownloadRecordedTrack) + $uploadStateRetry = $uploadState.find('.retry') + $uploadStateRetry.click(this.retryUploadRecordedTrack) context.JK.bindHoverEvents($track) context.JK.bindInstrumentHover($track, {positions:['top'], shrinkToFit: true}); context.JK.hoverBubble($clientState, this.onHoverOfStateIndicator, {width:'450px', closeWhenOthersOpen: true, positions:['left']}) context.JK.hoverBubble($uploadState, this.onHoverOfStateIndicator, {width:'450px', closeWhenOthersOpen: true, positions:['right']}) - $clientState.addClass('is-native-client') if context.jamClient.IsNativeClient() - $uploadState.addClass('is-native-client') if context.jamClient.IsNativeClient() + $clientState.addClass('is-native-client') if gon.isNativeClient + $uploadState.addClass('is-native-client') if gon.isNativeClient $track createStreamMix: (userSync) => @@ -523,8 +557,8 @@ context.JK.SyncViewer = class SyncViewer $uploadState.find('.retry').click(this.retryUploadRecordedTrack) context.JK.hoverBubble($clientState, this.onStreamMixHover, {width:'450px', closeWhenOthersOpen: true, positions:['left']}) context.JK.hoverBubble($uploadState, this.onStreamMixHover, {width:'450px', closeWhenOthersOpen: true, positions:['right']}) - $clientState.addClass('is-native-client') if context.jamClient.IsNativeClient() - $uploadState.addClass('is-native-client') if context.jamClient.IsNativeClient() + $clientState.addClass('is-native-client') if gon.isNativeClient + $uploadState.addClass('is-native-client') if gon.isNativeClient $track exportRecording: (e) => diff --git a/web/app/assets/stylesheets/client/common.css.scss b/web/app/assets/stylesheets/client/common.css.scss index 856ad5f21..e2f174fe0 100644 --- a/web/app/assets/stylesheets/client/common.css.scss +++ b/web/app/assets/stylesheets/client/common.css.scss @@ -180,6 +180,7 @@ $fair: #cc9900; @mixin client-state-box { + .client-state { position:relative; text-align:center; @@ -193,7 +194,6 @@ $fair: #cc9900; &.error { background-color: $error; - &.is-native-client { .retry { display:inline-block; } } } &.hq { @@ -202,12 +202,10 @@ $fair: #cc9900; &.sq { background-color: $good; - &.is-native-client { .retry { display:inline-block; } } } &.missing { background-color: $error; - &.is-native-client { .retry { display:inline-block; } } } &.discarded { @@ -240,7 +238,6 @@ $fair: #cc9900; &.error { background-color: $error; - &.is-native-client { .retry { display:inline-block; } } } &.missing { @@ -249,7 +246,6 @@ $fair: #cc9900; &.upload-soon { background-color: $fair; - &.is-native-client { .retry { display:inline-block; } } } &.uploaded { @@ -278,7 +274,7 @@ $fair: #cc9900; color:white; &.still-uploading { background-color: $fair; } - &.discard {background-color: $unknown; } + &.discarded {background-color: $unknown; } &.unknown { background-color: $unknown; } &.error { background-color: $error; } &.mixed { background-color: $good; } diff --git a/web/app/controllers/api_recordings_controller.rb b/web/app/controllers/api_recordings_controller.rb index abd4dc5c3..1fcd99b1b 100644 --- a/web/app/controllers/api_recordings_controller.rb +++ b/web/app/controllers/api_recordings_controller.rb @@ -1,6 +1,7 @@ class ApiRecordingsController < ApiController before_filter :api_signed_in_user, :except => [ :add_like ] + before_filter :lookup_recording, :only => [ :show, :stop, :claim, :discard, :keep ] before_filter :lookup_recorded_track, :only => [ :download, :upload_next_part, :upload_sign, :upload_part_complete, :upload_complete ] before_filter :lookup_recorded_video, :only => [ :video_upload_sign, :video_upload_start, :video_upload_complete ] @@ -239,6 +240,61 @@ class ApiRecordingsController < ApiController end + def upload_next_part_stream_mix + length = params[:length] + md5 = params[:md5] + + @quick_mix.upload_next_part(length, md5) + + if @quick_mix.errors.any? + + response.status = :unprocessable_entity + # this is not typical, but please don't change this line unless you are sure it won't break anything + # this is needed because after_rollback in the RecordedTrackObserver touches the model and something about it's + # state doesn't cause errors to shoot out like normal. + render :json => { :errors => @quick_mix.errors }, :status => 422 + else + result = { + :part => @quick_mix.next_part_to_upload, + :offset => @quick_mix.file_offset.to_s + } + + render :json => result, :status => 200 + end + + end + + def upload_sign_stream_mix + render :json => @quick_mix.upload_sign(params[:md5]), :status => 200 + end + + def upload_part_complete_stream_mix + part = params[:part] + offset = params[:offset] + + @quick_mix.upload_part_complete(part, offset) + + if @quick_mix.errors.any? + response.status = :unprocessable_entity + respond_with @quick_mix + else + render :json => {}, :status => 200 + end + end + + def upload_complete_stream_mix + @quick_mix.upload_complete + + if @quick_mix.errors.any? + response.status = :unprocessable_entity + respond_with @quick_mix + return + else + render :json => {}, :status => 200 + end + end + + def upload_next_part_stream_mix length = params[:length] md5 = params[:md5] @@ -305,14 +361,14 @@ class ApiRecordingsController < ApiController raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @recorded_track.recording.has_access?(current_user) end - def lookup_recorded_video - @recorded_video = RecordedVideo.find_by_recording_id_and_client_video_source_id!(params[:id], params[:video_id]) - raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @recorded_video.recording.has_access?(current_user) - end - def lookup_stream_mix @quick_mix = QuickMix.find_by_recording_id_and_user_id!(params[:id], current_user.id) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @quick_mix.recording.has_access?(current_user) end + def lookup_recorded_video + @recorded_video = RecordedVideo.find_by_recording_id_and_client_video_source_id!(params[:id], params[:video_id]) + raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @recorded_video.recording.has_access?(current_user) + end + end # class diff --git a/web/app/views/recordings/show.html.erb b/web/app/views/recordings/show.html.erb index 747f6c383..b8e2bf74a 100644 --- a/web/app/views/recordings/show.html.erb +++ b/web/app/views/recordings/show.html.erb @@ -18,7 +18,7 @@ <% end %>
-<% if @claimed_recording.is_public %> +<% if @claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user) %>
<% unless @claimed_recording.recording.band.blank? %>
@@ -97,7 +97,7 @@ <% end %>
-<% if @claimed_recording.is_public %> +<% if @claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user) %> <% if signed_in? %> <% unless @claimed_recording.recording.band.nil? %> <%= render :partial => "shared/landing_sidebar", :locals => {:user => @claimed_recording.recording.band, :recent_history => @claimed_recording.recording.band.recent_history} %> diff --git a/web/config/application.rb b/web/config/application.rb index 407ce9cab..7ef9314e1 100644 --- a/web/config/application.rb +++ b/web/config/application.rb @@ -179,6 +179,8 @@ if defined?(Bundler) config.audiomixer_path = "/var/lib/audiomixer/audiomixer/audiomixerapp" config.ffmpeg_path = ENV['FFMPEG_PATH'] || (File.exist?('/usr/local/bin/ffmpeg') ? '/usr/local/bin/ffmpeg' : '/usr/bin/ffmpeg') + config.normalize_ogg_path = ENV['NORMALIZE_OGG_PATH'] || (File.exist?('/usr/local/bin/normalize-ogg') ? '/usr/local/bin/normalize-ogg' : '/usr/bin/normalize-ogg') + config.normalize_mp3_path = ENV['NORMALIZE_MP3_PATH'] || (File.exist?('/usr/local/bin/normalize-mp3') ? '/usr/local/bin/normalize-mp3' : '/usr/bin/normalize-mp3') # if it looks like linux, use init.d script; otherwise use kill config.icecast_reload_cmd = ENV['ICECAST_RELOAD_CMD'] || (File.exist?('/usr/local/bin/icecast2') ? "bash -l -c #{Shellwords.escape("sudo /etc/init.d/icecast2 reload")}" : "bash -l -c #{Shellwords.escape("kill -1 `ps -f | grep /usr/local/bin/icecast | grep -v grep | awk \'{print $2}\'`")}") diff --git a/web/config/routes.rb b/web/config/routes.rb index 0ec493e04..6d0228f24 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -401,8 +401,8 @@ SampleApp::Application.routes.draw do match '/recordings/:id/comments' => 'api_recordings#add_comment', :via => :post, :as => 'api_recordings_add_comment' match '/recordings/:id/likes' => 'api_recordings#add_like', :via => :post, :as => 'api_recordings_add_like' match '/recordings/:id/discard' => 'api_recordings#discard', :via => :post, :as => 'api_recordings_discard' - # Recordings - recorded_tracks + match '/recordings/:id/tracks/:track_id' => 'api_recordings#show_recorded_track', :via => :get, :as => 'api_recordings_show_recorded_track' match '/recordings/:id/tracks/:track_id/download' => 'api_recordings#download', :via => :get, :as => 'api_recordings_download' match '/recordings/:id/tracks/:track_id/upload_next_part' => 'api_recordings#upload_next_part', :via => :get diff --git a/web/spec/features/music_sessions_spec.rb b/web/spec/features/music_sessions_spec.rb index ee22f4158..4f7fba61e 100644 --- a/web/spec/features/music_sessions_spec.rb +++ b/web/spec/features/music_sessions_spec.rb @@ -11,6 +11,26 @@ describe "Music Session", :js => true, :type => :feature, :capybara_feature => t subject { page } + describe "recorded session" do + + before(:each) do + ActiveMusicSession.delete_all + MusicSession.delete_all + end + + let(:searcher) { FactoryGirl.create(:user) } + let(:creator) { FactoryGirl.create(:user) } + let(:conn) { FactoryGirl.create(:connection, :user => creator) } + let(:description) {'hot recordings in here'} + let(:session) {FactoryGirl.create(:active_music_session, creator:creator, description: description)} + let(:recording) {FactoryGirl.create(:recording, music_session: session, owner: creator)} + + it "won't let user join" do + recording.touch + join_session(searcher, description:'hot recordings in here', no_verify:true) + find('#notification p').text('The session is currently recording.') + end + end context "last person" do before(:each) do diff --git a/web/spec/features/recording_landing_spec.rb b/web/spec/features/recording_landing_spec.rb index 45875b53b..6bf9f7beb 100644 --- a/web/spec/features/recording_landing_spec.rb +++ b/web/spec/features/recording_landing_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Landing", :js => true, :type => :feature, :capybara_feature => true do +describe "Landing" do let (:user) { FactoryGirl.create(:user) } @@ -10,41 +10,70 @@ describe "Landing", :js => true, :type => :feature, :capybara_feature => true do Recording.delete_all end - before(:each) do - MusicSession.delete_all - sign_in_poltergeist(user) - end let (:claimed_recording) { FactoryGirl.create(:claimed_recording) } + let (:recording) { claimed_recording.recording } - it "should render comments" do + describe "no js required" do - recording = ClaimedRecording.first - comment = "test comment" - timestamp = "less than a minute ago" - url = "/recordings/#{claimed_recording.id}" - visit url + it "shows private recording to someone who was in the session" do + # make the session hidden + claimed_recording.is_public = false + claimed_recording.save! - fill_in "txtRecordingComment", with: comment - find('#btnPostComment').trigger(:click) + visit "/recordings/#{claimed_recording.id}" - # (1) Test a user creating a comment and ensure it displays. + # and verify that after we visit the page, we can see the name of it + find('strong', text: 'RECORDING NOT FOUND') - # comment body - find('div.comment-text', text: comment) + # log in the user who was a part of the session + sign_in(claimed_recording.user) - # timestamp - find('div.comment-timestamp', text: timestamp) + visit "/recordings/#{claimed_recording.id}" - # (2) Test a user visiting a landing page with an existing comment. + # and verify that after we visit the page, we can see the name of it + find('h2', text: claimed_recording.name) + end + end - # re-visit page to reload from database - visit url - # comment body - find('div.comment-text', text: comment) + describe "js required", :js => true, :type => :feature, :capybara_feature => true do - # timestamp - find('div.comment-timestamp', text: timestamp) + before(:each) do + MusicSession.delete_all + sign_in_poltergeist(user) + end + + + it "should render comments" do + + recording = ClaimedRecording.first + comment = "test comment" + timestamp = "less than a minute ago" + url = "/recordings/#{claimed_recording.id}" + visit url + + fill_in "txtRecordingComment", with: comment + find('#btnPostComment').trigger(:click) + + # (1) Test a user creating a comment and ensure it displays. + + # comment body + find('div.comment-text', text: comment) + + # timestamp + find('div.comment-timestamp', text: timestamp) + + # (2) Test a user visiting a landing page with an existing comment. + + # re-visit page to reload from database + visit url + + # comment body + find('div.comment-text', text: comment) + + # timestamp + find('div.comment-timestamp', text: timestamp) + end end end \ No newline at end of file diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index cb1268ba5..647b8f1f7 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -457,9 +457,11 @@ def join_session(joiner, options) # verify the session description is seen by second client expect(page).to have_text(description) find('.join-link').trigger(:click) - find('#btn-accept-terms').trigger(:click) - expect(page).to have_selector('h2', text: 'my tracks') - find('#session-screen .session-mytracks .session-track') + unless options[:no_verify] + find('#btn-accept-terms').trigger(:click) + expect(page).to have_selector('h2', text: 'my tracks') + find('#session-screen .session-mytracks .session-track') + end end end diff --git a/websocket-gateway/lib/jam_websockets/server.rb b/websocket-gateway/lib/jam_websockets/server.rb index f03ecf11e..9d27ea040 100644 --- a/websocket-gateway/lib/jam_websockets/server.rb +++ b/websocket-gateway/lib/jam_websockets/server.rb @@ -80,7 +80,7 @@ module JamWebsockets @router.periodical_check_connections EventMachine::PeriodicTimer.new(2) do - sane_logging { @router.periodical_check_connections } + safety_net { sane_logging { @router.periodical_check_connections } } end end @@ -89,7 +89,7 @@ module JamWebsockets @router.periodical_check_clients EventMachine::PeriodicTimer.new(30) do - sane_logging { @router.periodical_check_clients } + safety_net { sane_logging { @router.periodical_check_clients } } end end @@ -98,16 +98,29 @@ module JamWebsockets @router.periodical_flag_connections EventMachine::PeriodicTimer.new(2) do - sane_logging { @router.periodical_flag_connections } + safety_net { sane_logging { @router.periodical_flag_connections } } end end def start_stats_dump EventMachine::PeriodicTimer.new(60) do - @router.periodical_stats_dump + safety_net { @router.periodical_stats_dump } end end + # this was added for this reason: https://jamkazam.atlassian.net/browse/VRFS-2425 + # if an unhandled exception occurs in PeriodicTimer, it just kills all future timers; doesn't kill the app. + # not really what you want. + + # so, we signal to Bugsnag, so we know really bad stuff is happening, but we also move + def safety_net(&blk) + begin + blk.call + rescue => e + Bugsnag.notify(e) + @log.error("unhandled exception in EM Timer #{e}") + end + end def sane_logging(&blk) # used around repeated transactions that cause too much ActiveRecord::Base logging # example is handling heartbeats