diff --git a/ruby/lib/jam_ruby/lib/subscription_message.rb b/ruby/lib/jam_ruby/lib/subscription_message.rb index 62409b6aa..8f4320249 100644 --- a/ruby/lib/jam_ruby/lib/subscription_message.rb +++ b/ruby/lib/jam_ruby/lib/subscription_message.rb @@ -14,11 +14,11 @@ module JamRuby end def self.mount_source_up_requested(mount) - Notification.send_subscription_message('mount', mount.id, {type: 'source_up_requested'}.to_json ) + Notification.send_subscription_message('mount', mount.id, {change_type: IcecastSourceChange::CHANGE_TYPE_MOUNT_UP_REQUEST}.to_json ) end def self.mount_source_down_requested(mount) - Notification.send_subscription_message('mount', mount.id, {type: 'source_down_requested'}.to_json ) + Notification.send_subscription_message('mount', mount.id, {change_type: IcecastSourceChange::CHANGE_TYPE_MOUNT_DOWN_REQUEST}.to_json ) end end end diff --git a/ruby/lib/jam_ruby/models/icecast_mount.rb b/ruby/lib/jam_ruby/models/icecast_mount.rb index 29f3a6888..be1cfb382 100644 --- a/ruby/lib/jam_ruby/models/icecast_mount.rb +++ b/ruby/lib/jam_ruby/models/icecast_mount.rb @@ -69,7 +69,7 @@ module JamRuby #IcecastSourceChange.delete_all(["icecast_mount_id = ?", self.id]) if source_direction # and tell anyone listening that the direction has changed - SubscriptionMessage.mount_source_direction(self) + # SubscriptionMessage.mount_source_direction(self) end # Note: @@ -139,7 +139,7 @@ module JamRuby first = source_changes.first # don't check source_changes if actual source state mirrors desired source state... just say we are good, and pass down relevant sourcing user ID if present - result = success_state('source_' + (source_direction ? 'up' : 'down'), first.nil? ? nil : first.user_id) + result = success_state('source_' + (sourced ? 'up' : 'down'), first.nil? ? nil : first.user_id) elsif source_changes.count > 0 # if the desired source direction is up, but we haven't sourced yet... let's try and find out why @@ -200,7 +200,7 @@ module JamRuby def source_up with_lock do self.sourced = true - self.sourced_needs_changing_at = nil + self.sourced_needs_changing_at = Time.now self.no_config_changed = true save(validate: false) end @@ -209,7 +209,7 @@ module JamRuby def source_down with_lock do self.sourced = false - self.sourced_needs_changing_at = nil + self.sourced_needs_changing_at = Time.now self.no_config_changed = true save(validate: false) end diff --git a/ruby/spec/jam_ruby/models/icecast_mount_spec.rb b/ruby/spec/jam_ruby/models/icecast_mount_spec.rb index 847e9697c..553b5a34d 100644 --- a/ruby/spec/jam_ruby/models/icecast_mount_spec.rb +++ b/ruby/spec/jam_ruby/models/icecast_mount_spec.rb @@ -296,7 +296,7 @@ describe IcecastMount do let(:mount_needing_source) {FactoryGirl.create(:iceast_mount_with_music_session, sourced: false, sourced_needs_changing_at: Time.now, listeners: 1)} it "happy sourced mount" do - sourced_mount.state.should == success_state('source_down') + sourced_mount.state.should == success_state('source_up') end it "just transitioned" do @@ -317,7 +317,7 @@ describe IcecastMount do it "happy sourced mount" do mount_needing_source.sourced = true mount_needing_source.save! - mount_needing_source.state.should == success_state('source_down', change1.user.id) + mount_needing_source.state.should == success_state('source_up', change1.user.id) end it "succeeded recently in correct transition" do diff --git a/web/app/assets/images/content/join-icon.jpg b/web/app/assets/images/content/join-icon.jpg new file mode 100644 index 000000000..19342ce3e Binary files /dev/null and b/web/app/assets/images/content/join-icon.jpg differ diff --git a/web/app/assets/images/content/listen-icon.jpg b/web/app/assets/images/content/listen-icon.jpg new file mode 100644 index 000000000..1f7416525 Binary files /dev/null and b/web/app/assets/images/content/listen-icon.jpg differ diff --git a/web/app/assets/images/content/pause-icon.jpg b/web/app/assets/images/content/pause-icon.jpg new file mode 100644 index 000000000..085139efc Binary files /dev/null and b/web/app/assets/images/content/pause-icon.jpg differ diff --git a/web/app/assets/images/content/rsvp-icon.jpg b/web/app/assets/images/content/rsvp-icon.jpg new file mode 100644 index 000000000..b9f0797c8 Binary files /dev/null and b/web/app/assets/images/content/rsvp-icon.jpg differ diff --git a/web/app/assets/javascripts/globals.js b/web/app/assets/javascripts/globals.js index 4f68a3125..584eba17b 100644 --- a/web/app/assets/javascripts/globals.js +++ b/web/app/assets/javascripts/globals.js @@ -44,7 +44,8 @@ MUTE_SELECTED: 'mute_selected', SUBSCRIBE_NOTIFICATION: 'subscribe_notification', CONNECTION_UP: 'connection_up', - CONNECTION_DOWN: 'connection_down' + CONNECTION_DOWN: 'connection_down', + SCREEN_CHANGED: 'screen_changed' }; context.JK.ALERT_NAMES = { diff --git a/web/app/assets/javascripts/jquery.listenbroadcast.js b/web/app/assets/javascripts/jquery.listenbroadcast.js index d8dba3e4a..349305b57 100644 --- a/web/app/assets/javascripts/jquery.listenbroadcast.js +++ b/web/app/assets/javascripts/jquery.listenbroadcast.js @@ -35,7 +35,6 @@ var WAIT_FOR_BUFFER_TIMEOUT = 5000; var RETRY_ATTEMPTS = 5; // we try 4 times, so the user will wait up until RETRY_ATTEMPTS * WAIT_FOR_BUFFER_TIMEOUTS - var logger = context.JK.logger; var rest = context.JK.Rest(); var $parent = $parentElement; @@ -44,10 +43,15 @@ var musicSessionId = null; var waitForBufferingTimeout = null; var fanAccess = null; + var audioSrc = null; + var audioType = null; var retryAttempts = 0; var self = this; var mountInfo = null; var $mountState = null; + var lazyAudioInit = options && options.lazyAudioInit; + var hoverOptions = (options && options.hoverOptions) ? options.hoverOptions : {} + var $detailHelper = options && options.detailHelper; var destroyed = false; @@ -73,6 +77,18 @@ e.stopPropagation(); } + if(lazyAudioInit) { + if($audio.length == 0) { + $audio = + $('') + $parent.append($audio) + audioDomElement = $audio.get(0); + audioBind(); + } + } + if(destroyed) return; if(!audioDomElement) throw "no audio element supplied; the user should not be able to attempt a play" @@ -117,7 +133,7 @@ if(destroyed) return; - if(!audioDomElement) throw "no audio element supplied; the user should not be able to attempt a pause" + if(!lazyAudioInit && !audioDomElement) throw "no audio element supplied; the user should not be able to attempt a pause" transition(PlayStateNone); @@ -133,6 +149,12 @@ } } + function onScreenChanged(e, data) { + if(context.JK.ListenBroadcastCurrentlyPlaying) { + context.JK.ListenBroadcastCurrentlyPlaying.pause(); + } + } + // this is the only way to make audio stop buffering after the user hits pause function recreateAudioElement() { // jeez: http://stackoverflow.com/questions/4071872/html5-video-force-abort-of-buffering/13302599#13302599 @@ -408,6 +430,8 @@ logger.error("unknown state: " + playState) } + $parent.data('listenbroadcast-playstate', playState); + $parent.triggerHandler('statechange.listenBroadcast', { element: $parent, @@ -509,7 +533,9 @@ logger.debug("subscription notification received: type:" + data.type) - updateMountInfo(data.body.mount); + if(data.body.mount) { + updateMountInfo(data.body.mount); + } var id = data.id var type = data.type @@ -519,7 +545,7 @@ var $detailHolder = $mountState.find('.detail-items') var msgType = data.body.type; - var sourceChange = data.body.source_change; + var sourceChange = data.body; if(sourceChange) { var $detail = addSourceChange($detailHolder, sourceChange) if ($detail) { @@ -549,8 +575,8 @@ $detailText.text('Start Broadcast Requested') } else if(sourceChange.change_type == 'source_down_request') { - $who.text('Server') - $detailText.text('Start Broadcast Requested') + $who.html('Server') + $detailText.text('Stop Broadcast Requested') } else { @@ -597,43 +623,61 @@ return $detail; } + function cleanupDetails() { + var mountId = $parent.data('mount-id') + if(mountId) { + context.JK.SubscriptionUtils.unsubscribe('mount', mountId) + } + $mountState = null; + mountInfo = null; + } + + function openBubble() { + checkServer().done(function(response) { + var mountId = response.mount ? response.mount.id : null + + if(mountId) { + rest.getMount({id: mountId}) + .done(function (mount) { + mountInfo = mount + $parent.data('mount-id', mountId) + context.JK.SubscriptionUtils.subscribe('mount', mountId).on(context.JK.EVENTS.SUBSCRIBE_NOTIFICATION, onDetailEvent) + $parent.btOn() + }) + .fail(context.JK.app.ajaxError) + } + else { + mountInfo = null; + context.JK.app.layout.notify('This session can not currently broadcast') + } + }) + .fail(function() { + logger.debug("session is over") + }) + } + function bindHoverDetail() { var stateHolderHtml = context._.template($('#template-listen-broadcast-state').html(), {}, {variable: 'data'}); - context.JK.hoverBubble($parent, stateHolderHtml, {trigger: 'none', preShow: broadcastDetailPreShow, positions:['bottom'], width:'300px', height:'250px'}) + var btOptions = $.extend({}, {trigger: 'none', preShow: broadcastDetailPreShow, postHide: cleanupDetails, closeWhenOthersOpen: true, positions:['bottom', 'right', 'top', 'left'], width:'300px', height:'250px'}, hoverOptions) + context.JK.hoverBubble($parent, stateHolderHtml, btOptions) - $parent.hoverIntent({ - over: function() { - checkServer().done(function(response) { - var mountId = response.mount ? response.mount.id : null + // in case the bt is destroyed + $parent.on('remove', cleanupDetails); - if(mountId) { - rest.getMount({id: mountId}) - .done(function (mount) { - mountInfo = mount - $parent.data('mount-id', mountId) - context.JK.SubscriptionUtils.subscribe('mount', mountId).on(context.JK.EVENTS.SUBSCRIBE_NOTIFICATION, onDetailEvent) - $parent.btOn() - }) - .fail(context.JK.app.ajaxError) - } - else { - mountInfo = null; - context.JK.app.layout.notify('This session can not currently broadcast') - } - }) - .fail(function() { - logger.debug("session is over") - }) - }, - out: function() { - var mountId = $parent.data('mount-id') - context.JK.SubscriptionUtils.unsubscribe('mount', mountId) - $parent.btOff(); - $mountState = null; - mountInfo = null; - } - }) + if($detailHelper) { + $detailHelper.click(function() { openBubble(); return false; }) + } + else { + $parent.hoverIntent({ + over: function() { + openBubble(); + }, + out: function() { + + } + }) + } } function initialize() { @@ -645,23 +689,35 @@ if(fanAccess === null) throw 'fan-access must be specified in $parentElement'; fanAccess = $parent.attr('fan-access') === 'true' // coerce to boolean + if(lazyAudioInit) { + audioSrc = $parent.attr('data-audio-src'); + if(audioSrc === null) throw 'data-audio-src must be specified in $parentElement'; + audioType = $parent.attr('data-audio-type'); + if(audioType === null) throw 'data-audio-type must be specified in $parentElement'; + } + bindHoverDetail(); $audio = $('audio', $parent); - if($audio.length == 0) { + if($audio.length == 0 && !lazyAudioInit) { return; } + if($audio.length > 1) { throw "more than one