diff --git a/db/manifest b/db/manifest
index 60ea9c45d..53fd01705 100755
--- a/db/manifest
+++ b/db/manifest
@@ -358,4 +358,5 @@ audio_in_music_notations.sql
lesson_time_tracking.sql
packaged_test_drive.sql
packaged_test_drive2.sql
-jamclass_report.sql
\ No newline at end of file
+jamclass_report.sql
+jamblasters_network.sql
\ No newline at end of file
diff --git a/db/up/jamblasters_network.sql b/db/up/jamblasters_network.sql
new file mode 100644
index 000000000..e986360cd
--- /dev/null
+++ b/db/up/jamblasters_network.sql
@@ -0,0 +1,3 @@
+ALTER TABLE jamblasters ADD COLUMN ipv6_link_local VARCHAR;
+ALTER TABLE jamblasters ADD COLUMN ipv4_link_local VARCHAR;
+ALTER TABLE jamblasters ADD COLUMN display_name VARCHAR;
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/jam_track_importer.rb b/ruby/lib/jam_ruby/jam_track_importer.rb
index ce764a953..31e049d96 100644
--- a/ruby/lib/jam_ruby/jam_track_importer.rb
+++ b/ruby/lib/jam_ruby/jam_track_importer.rb
@@ -505,6 +505,11 @@ module JamRuby
@storage_format == 'Tency'
end
+ def is_helbing_storage?
+ assert_storage_set
+ @storage_format == 'Helbing'
+ end
+
def is_paris_storage?
assert_storage_set
@storage_format == 'Paris'
@@ -550,7 +555,7 @@ module JamRuby
song = metalocation[(first_dash+3)..-1].strip
bits << song
- elsif is_tency_storage? || is_tim_tracks_storage?
+ elsif is_tency_storage? || is_tim_tracks_storage? || is_helbing_storage?
suffix = '/meta.yml'
@@ -582,6 +587,9 @@ module JamRuby
if is_tim_tracks_storage?
song = metalocation[(first_dash+3)..-1].strip
bits << song
+ elsif is_helbing_storage?
+ song = metalocation[(first_dash+3)..-1].strip
+ bits << song
elsif is_tency_storage?
last_dash = metalocation.rindex('-')
if last_dash
@@ -881,6 +889,9 @@ module JamRuby
elsif is_tim_tracks_storage?
jam_track.vendor_id = metadata[:id]
jam_track.licensor = JamTrackLicensor.find_by_name!('Tim Waurick')
+ elsif is_helbing_storage?
+ jam_track.vendor_id = metadata[:id]
+ jam_track.licensor = JamTrackLicensor.find_by_name!('Stockton Helbing')
elsif is_drumma_storage?
jam_track.vendor_id = metadata[:id]
jam_track.licensor = JamTrackLicensor.find_by_name!('Drumma Boy')
@@ -2231,6 +2242,8 @@ module JamRuby
tim_tracks_s3_manager
elsif is_drumma_storage?
drumma_s3_manager
+ elsif is_helbing_storage?
+ helbing_s3_manager
else
s3_manager
end
@@ -2256,6 +2269,10 @@ module JamRuby
@tim_tracks_s3_manager ||= S3Manager.new('jamkazam-timtracks', APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
+ def helbing_s3_manager
+ @tim_tracks_s3_manager ||= S3Manager.new('jamkazam-helbing', APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
+ end
+
def s3_manager
@s3_manager ||= S3Manager.new(APP_CONFIG.aws_bucket_jamtracks, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
@@ -2328,6 +2345,12 @@ module JamRuby
@storage_format == 'TimTracks'
end
+ def is_helbing_storage?
+ assert_storage_set
+ @storage_format == 'Helbing'
+ end
+
+
def assert_storage_set
raise "no storage_format set" if @storage_format.nil?
end
@@ -2418,6 +2441,22 @@ module JamRuby
end
end
+ def iterate_helbing_song_storage(&blk)
+ count = 0
+ song_storage_manager.list_directories('mapped').each do |song|
+ @@log.debug("searching through song directory '#{song}'")
+
+ metalocation = "#{song}meta.yml"
+
+ metadata = load_metalocation(metalocation)
+
+ blk.call(metadata, metalocation)
+
+ count += 1
+ #break if count > 100
+ end
+ end
+
def iterate_song_storage(&blk)
if is_tency_storage?
iterate_tency_song_storage do |metadata, metalocation|
@@ -2435,6 +2474,10 @@ module JamRuby
iterate_drumma_song_storage do |metadata, metalocation|
blk.call(metadata, metalocation)
end
+ elsif is_helbing_storage?
+ iterate_helbing_song_storage do |metadata, metalocation|
+ blk.call(metadata, metalocation)
+ end
else
iterate_default_song_storage do |metadata, metalocation|
blk.call(metadata, metalocation)
@@ -3387,7 +3430,10 @@ module JamRuby
if is_tim_tracks_storage?
meta[:genres] = ['acapella']
+ elsif is_helbing_storage?
+ meta[:genres] = ['jazz']
end
+
meta
rescue AWS::S3::Errors::NoSuchKey
return nil
diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb
index 5b1444fc7..862a7c0d2 100644
--- a/ruby/lib/jam_ruby/models/connection.rb
+++ b/ruby/lib/jam_ruby/models/connection.rb
@@ -20,6 +20,7 @@ module JamRuby
has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all
has_many :backing_tracks, :class_name => "JamRuby::BackingTrack", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all
has_many :video_sources, :class_name => "JamRuby::VideoSource", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all
+ has_one :jamblaster, class_name: "JamRuby::Jamblaster", foreign_key: "client_id"
validates :metronome_open, :inclusion => {:in => [true, false]}
validates :as_musician, :inclusion => {:in => [true, false, nil]}
@@ -87,6 +88,12 @@ module JamRuby
joining_session
end
+ def same_network_jamblasters
+ # return all jamblasters that are currently connected with the same public IP address (don't include this one though)
+ Jamblaster.joins(:connection).where("connections.ip_address = ?", ip_address).where("connections.id != ?", id).limit(100)
+ end
+
+
def can_join_music_session
# puts "can_join_music_session: #{music_session_id} was #{music_session_id_was}" if music_session_id_changed?
diff --git a/ruby/lib/jam_ruby/models/jamblaster.rb b/ruby/lib/jam_ruby/models/jamblaster.rb
index 7b6d051fc..b1e19bfc1 100644
--- a/ruby/lib/jam_ruby/models/jamblaster.rb
+++ b/ruby/lib/jam_ruby/models/jamblaster.rb
@@ -8,7 +8,7 @@ module JamRuby
has_many :jamblasters_users, class_name: "JamRuby::JamblasterUser"
has_many :users, class_name: 'JamRuby::User', through: :jamblasters_users
has_many :jamblaster_pairing_requests, class_name: "JamRuby::JamblasterPairingRequest", foreign_key: :jamblaster_id
-
+ belongs_to :connection, class_name: "JamRuby::Connection", foreign_key: "client_id"
validates :user, presence: true
validates :serial_no, uniqueness: true
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index 04fd37f4c..4723e5588 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -42,7 +42,6 @@ module JamRuby
# updating_password corresponds to a lost_password
attr_accessor :test_drive_packaging, :validate_instruments, :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json, :expecting_gift_card
-
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
has_many :controlled_sessions, :class_name => "JamRuby::MusicSession", inverse_of: :session_controller, foreign_key: :session_controller_id
diff --git a/web/Gemfile b/web/Gemfile
index df30e75e5..79e366455 100644
--- a/web/Gemfile
+++ b/web/Gemfile
@@ -102,6 +102,13 @@ gem 'zip-codes'
gem 'email_validator'
#gem "browserify-rails", "~> 0.7"
+
+if ENV['FASTER_PATH'] == '1'
+ # https://github.com/danielpclark/faster_path
+ # supposed to dramatically speed up page load time. Gotta install rust. go to github if interested
+ gem 'faster_path', '~> 0.1.0', :group => :development
+end
+
source 'https://rails-assets.org' do
gem 'rails-assets-reflux', '0.3.0'
gem 'rails-assets-classnames'
diff --git a/web/app/assets/javascripts/globals.js b/web/app/assets/javascripts/globals.js
index 3954949c6..cfe9d7a3c 100644
--- a/web/app/assets/javascripts/globals.js
+++ b/web/app/assets/javascripts/globals.js
@@ -63,7 +63,8 @@
PREVIEW_PLAYED: 'preview_played',
VST_OPERATION_SELECTED: 'vst_operation_selected',
VST_EFFECT_SELECTED: 'vst_effect_selected',
- LESSON_SESSION_ACTION: 'lesson_session_action'
+ LESSON_SESSION_ACTION: 'lesson_session_action',
+ JAMBLASTER_ACTION: 'jamblaster_action'
};
context.JK.PLAYBACK_MONITOR_MODE = {
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index bd8dd4f7a..ea15e3e00 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -595,6 +595,19 @@
return detail;
}
+ function getUserJamBlasters(options) {
+ if(!options) {
+ options = {}
+ }
+ var id = getId(options);
+ return $.ajax({
+ type: "GET",
+ dataType: "json",
+ url: "/api/users/" + id + '/jamblasters?' + $.param(options),
+ processData: false
+ });
+ }
+
function getUserProfile(options) {
if (!options) {
options = {}
@@ -2550,6 +2563,7 @@
this.cancelSession = cancelSession;
this.updateScheduledSession = updateScheduledSession;
this.getUserDetail = getUserDetail;
+ this.getUserJamBlasters = getUserJamBlasters;
this.getUserAuthorizations = getUserAuthorizations;
this.getGoogleAuth = getGoogleAuth;
this.getUserProfile = getUserProfile;
diff --git a/web/app/assets/javascripts/jquery.jamblasterOptions.js b/web/app/assets/javascripts/jquery.jamblasterOptions.js
new file mode 100644
index 000000000..9f947359e
--- /dev/null
+++ b/web/app/assets/javascripts/jquery.jamblasterOptions.js
@@ -0,0 +1,79 @@
+(function(context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+
+
+ // creates an iconic/graphical instrument selector. useful when there is minimal real-estate
+
+ $.fn.jamblasterOptions = function(options) {
+
+ return this.each(function(index) {
+
+ function close() {
+ $parent.btOff();
+ $parent.focus();
+ }
+
+ var $parent = $(this);
+ if($parent.data('jamblasterOptions')) {
+ //return;
+ }
+
+ $parent.data('jamblasterOptions', options)
+ function onJamBlasterOptionSelected() {
+ var $li = $(this);
+ var option = $li.attr('data-jamblaster-option');
+
+ close();
+ $parent.triggerHandler(context.JK.EVENTS.JAMBLASTER_ACTION, {option: option, options: $parent.data('jamblasterOptions')});
+ return false;
+ };
+
+ // if the user goes into the bubble, remove
+ function waitForBubbleHover($bubble) {
+ $bubble.hoverIntent({
+ over: function() {
+ if(timeout) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ },
+ out: function() {
+ $parent.btOff();
+ }});
+ }
+
+ var timeout = null;
+
+ var html = context._.template($('#template-jamblaster-options').html(), options, { variable: 'data' })
+
+ context.JK.hoverBubble($parent, html, {
+ trigger:'none',
+ cssClass: 'jamblaster-options-popup',
+ spikeGirth:0,
+ spikeLength:0,
+ overlap: -10,
+ width:120,
+ closeWhenOthersOpen: true,
+ offsetParent: $parent.closest('.screen'),
+ positions:['bottom'],
+ preShow: function() {
+
+ },
+ postShow:function(container) {
+ $(container).find('li').click(onJamBlasterOptionSelected)
+ if(timeout) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ waitForBubbleHover($(container))
+ timeout = setTimeout(function() {$parent.btOff()}, 6000)
+ }
+ });
+ });
+ }
+
+
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/jquery.lessonSessionActions.js b/web/app/assets/javascripts/jquery.lessonSessionActions.js
index 85db4c285..86859618e 100644
--- a/web/app/assets/javascripts/jquery.lessonSessionActions.js
+++ b/web/app/assets/javascripts/jquery.lessonSessionActions.js
@@ -12,7 +12,7 @@
return this.each(function(index) {
function close() {
- //$parent.btOff();
+ $parent.btOff();
$parent.focus();
}
@@ -41,7 +41,7 @@
}
},
out: function() {
- //$parent.btOff();
+ $parent.btOff();
}});
}
@@ -103,7 +103,7 @@
timeout = null;
}
waitForBubbleHover($(container))
- //timeout = setTimeout(function() {$parent.btOff()}, 6000)
+ timeout = setTimeout(function() {$parent.btOff()}, 6000)
}
});
});
diff --git a/web/app/assets/javascripts/landing/landing.js b/web/app/assets/javascripts/landing/landing.js
index 3cc86db70..b979e6786 100644
--- a/web/app/assets/javascripts/landing/landing.js
+++ b/web/app/assets/javascripts/landing/landing.js
@@ -22,6 +22,7 @@
//= require jquery.exists
//= require jquery.manageVsts
//= require jquery.lessonSessionActions
+//= require jquery.jamblasterOptions
//= require ResizeSensor
//= require AAA_Log
//= require AAC_underscore
diff --git a/web/app/assets/javascripts/react-components/CheckBoxList.js.jsx.coffee b/web/app/assets/javascripts/react-components/CheckBoxList.js.jsx.coffee
index 2364c52c5..3f9e62b31 100644
--- a/web/app/assets/javascripts/react-components/CheckBoxList.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/CheckBoxList.js.jsx.coffee
@@ -44,7 +44,7 @@ logger = context.JK.logger
for object in this.props.sourceObjects
nm = "check_#{object.id}"
checked = @isChecked(object.id)
- object_options.push `
{object.description}
`
+ object_options.push `{object.description}
`
`
diff --git a/web/app/assets/javascripts/react-components/ConfigureTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/ConfigureTracks.js.jsx.coffee
index dac708c9b..f23f1fb57 100644
--- a/web/app/assets/javascripts/react-components/ConfigureTracks.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/ConfigureTracks.js.jsx.coffee
@@ -41,11 +41,11 @@ MIDI_TRACK = context.JK.MIDI_TRACK
for midiDevice in @state.configureTracks.attachedMidiDevices.midiDevices
if midiDevice.deviceIndex == inputsForTrack.vst?.midiDeviceIndex
midiDeviceName = midiDevice.deviceName
- inputs.push(`
{midiDeviceName}
`)
+ inputs.push(`
{midiDeviceName}
`)
else
trackTypeLabel = 'AUDIO'
for input in inputsForTrack
- inputs.push(`
{input.name}
`)
+ inputs.push(`
{input.name}
`)
if !inputsForTrack.instrument_id?
instrument = `
? `
diff --git a/web/app/assets/javascripts/react-components/JamBlasterNameDialog.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamBlasterNameDialog.js.jsx.coffee
new file mode 100644
index 000000000..33d68f43e
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/JamBlasterNameDialog.js.jsx.coffee
@@ -0,0 +1,92 @@
+context = window
+@JamBlasterNameDialog = React.createClass({
+
+ mixins: [Reflux.listenTo(@AppStore, "onAppInit")]
+ teacher: null
+
+ beforeShow: (args) ->
+ logger.debug("JamBlasterNameDialog.beforeShow", args.d1)
+ @setState({name: args.d1})
+
+
+ afterHide: () ->
+
+ onAppInit: (@app) ->
+ dialogBindings = {
+ 'beforeShow': @beforeShow,
+ 'afterHide': @afterHide
+ };
+
+ @app.bindDialog('jamblaster-name-dialog', dialogBindings);
+
+ getInitialState: () ->
+ {
+ name: ''
+ }
+
+
+ componentDidMount: () ->
+ @root = $(@getDOMNode())
+ @dialog = @root.closest('.dialog')
+
+ doCancel: (e) ->
+ e.preventDefault()
+ @app.layout.closeDialog('jamblaster-name-dialog', true);
+
+ onNameChange: (e) ->
+
+ @setState({name: $(e.target).val()})
+
+ updateName: (e) ->
+ e.preventDefault()
+
+ # validate
+
+ name = @root.find('.name').val()
+
+ characterMatch = /^[a-z0-9,' -]+$/i
+
+ if name.length == 0 || name == ''
+ context.JK.Banner.showAlert('invalid name', 'Please specify a name.')
+ return
+ else if name.length < 2
+ context.JK.Banner.showAlert('invalid name', 'Please specify a name at least 3 characters long.')
+ return
+ else if name.length > 63
+ context.JK.Banner.showAlert('invalid name', 'The name must be less than 64 characters long.')
+ return
+ else if characterMatch.test(name)
+ context.JK.Banner.showAlert('invalid name',
+ 'The can only contain A-Z, 0-9, commas, apostrophes, spaces, or hyphens.')
+ return
+
+ result = context.jamClient.setJBName(name.trim())
+
+ if !result
+ context.JK.Banner.showAlert('unable to set the name',
+ 'Please email support@jamkazam.com with the name you are trying to set, or refresh the page and try again.')
+ else
+ @app.layout.closeDialog('jamblaster-name-dialog')
+ render: () ->
+ `
+
+
+
+
update name of JamBlaster
+
+
+
+
You can change the display name for this JamBlaster. The name can only contain A-Z, 0-9, commas, apostrophes,
+ spaces, or hyphens. A valid example: "John Doe's JamBlaster"
+
+
JamBlaster Name:
+
+
+
+
+
`
+
+})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/JamBlasterPairingDialog.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamBlasterPairingDialog.js.jsx.coffee
new file mode 100644
index 000000000..70b14ec44
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/JamBlasterPairingDialog.js.jsx.coffee
@@ -0,0 +1,162 @@
+context = window
+@JamBlasterPairingDialog = React.createClass({
+
+ mixins: [@BonjourMixin, Reflux.listenTo(@AppStore, "onAppInit")]
+ teacher: null
+
+ beforeShow: (args) ->
+ logger.debug("JamBlasterPairingDialog.beforeShow", args.d1)
+ @setState({timer: null, pairing: false, bonjourClientId: args.d1, pairingTimeout: false, paired: false})
+ @resyncBonjour()
+ @setTimer(false)
+
+ afterHide: () ->
+ @clearTimer()
+
+ onAppInit: (@app) ->
+ dialogBindings = {
+ 'beforeShow': @beforeShow,
+ 'afterHide': @afterHide
+ };
+
+ @app.bindDialog('jamblaster-pairing-dialog', dialogBindings);
+
+ getInitialState: () ->
+ {
+ timer: null
+ pairing: false,
+ pairStart: null,
+ clients: [],
+ pairingTimeout: false
+ paired: false,
+ userJamBlasters: [],
+ localClients: []
+ }
+
+ clearTimer: () ->
+ if @interval?
+ clearInterval(@interval)
+ @interval = null
+
+ clearPairingTimer: () ->
+ if @pairingInterval?
+ clearInterval(@pairingInterval)
+ @pairingInterval = null
+
+ setTimer: (connecting) ->
+ @clearTimer()
+
+ if connecting
+ time = 5000 # every 5 seconds
+ else
+ time = 60000 # every minute
+
+ @interval = setInterval((() => @resyncBonjour()), time)
+
+ pairingTimer: () ->
+ @clearPairingTimer()
+
+ @pairingInterval = setInterval((() => @updatePairingTimer()), 800)
+
+ updatePairingTimer: () ->
+
+ now = new Date().getTime()
+
+ delta = (now - @state.pairStart) / 1000
+
+ if delta > 60
+ @clearPairingTimer()
+ @setTimer(false)
+ @setState({pairing: false, pairingTimeout: true, timer: null})
+ else
+ client = @findJamBlaster(@state.bonjourClientId)
+ if client.isPaired
+ @clearPairingTimer()
+ @setTimer(false)
+ @setState({pairing: false, pairingTimeout: false, timer: null, paired: true})
+ else
+ @setState({timer: 60 - delta})
+
+ componentDidMount: () ->
+ @root = $(@getDOMNode())
+ @dialog = @root.closest('.dialog')
+
+ componentDidUpdate: () ->
+
+ doCancel: (e) ->
+ e.preventDefault()
+
+ if @state.pairing
+ return
+
+ @app.layout.closeDialog('jamblaster-pairing-dialog', true);
+
+ close: (e) ->
+ e.preventDefault()
+ @app.layout.closeDialog('jamblaster-pairing-dialog')
+
+ pair: (e) ->
+ e.preventDefault()
+
+ if @state.pairing
+ return
+
+ @setState({pairing: true, pairStart: new Date().getTime(), timer: 60})
+ @setTimer(true)
+
+ client = @findJamBlaster(this.state.bonjourClientId)
+
+ if client.isPaired
+ context.JK.Banner.showNotice("JamBlaster already paired", "This JamBlaster is already paired.")
+ @app.layout.closeDialog("jamblaster-pairing-dialog", true)
+ else if client?
+ if client.connect_url?
+ context.jamClient.startPairing(client.connect_url)
+ else
+ context.JK.Banner.showAlert("JamBlaster offline", "JamBlaster appears to be offline. Please reboot it")
+ else
+ context.JK.Banner.showAlert("JamBlaster offline", "JamBlaster appears to be offline. Please reboot it")
+
+ render: () ->
+
+ if @state.pairing
+ countdown = @state.timer
+ else
+ countdown = null
+
+ actions = `
`
+
+
+ if @state.paired
+ message = `
You have successfully connected to this JamBlaster!
`
+ actions = `
`
+ else if @state.pairingTimeout
+ message = `
No connection established. You may click the CONNECT button to try again. If you cannot connect, please contact us at support@jamkazam.com.
`
+ else
+
+ cancelClasses = {"button-grey": true, disabled: @state.pairing}
+ connectClasses = {"button-orange": true, disabled: @state.pairing}
+
+ `
+
+
+
+
connect to JamBlaster
+
+
+
+
To connect this application/device with the selected JamBlaster, please click the Connect button below, and then push the small black plastic button located on the back of the JamBlaster between the USB and power ports to confirm this pairing within 60 seconds of clicking the Connect button below.
+
+ {message}
+
+ {actions}
+
+
`
+
+})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/JamBlasterScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamBlasterScreen.js.jsx.coffee
new file mode 100644
index 000000000..45c8e1ba2
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/JamBlasterScreen.js.jsx.coffee
@@ -0,0 +1,184 @@
+context = window
+rest = context.JK.Rest()
+logger = context.JK.logger
+
+@JamBlasterScreen = React.createClass({
+
+ mixins: [
+ @ICheckMixin,
+ @PostProcessorMixin,
+ @BonjourMixin,
+ Reflux.listenTo(AppStore, "onAppInit"),
+ Reflux.listenTo(UserStore, "onUserChanged")
+ ]
+
+ TILE_AUDIO: 'audio'
+ TILE_INTERNET: 'internet'
+ TILE_MANAGEMENT: 'management'
+ TILE_USB: 'usb'
+
+ TILES: ['audio', 'internet', 'management', 'usb']
+
+ onAppInit: (@app) ->
+ @app.bindScreen('jamblaster',
+ {beforeShow: @beforeShow, afterShow: @afterShow, beforeHide: @beforeHide})
+
+ onUserChanged: (userState) ->
+ @setState({user: userState?.user})
+
+ componentDidMount: () ->
+ @root = $(@getDOMNode())
+
+ componentWillUpdate: (nextProps, nextState) ->
+
+ componentDidUpdate: () ->
+ items = @root.find('.jamtable .optionsColumn .jamblaster-options-btn')
+
+ $.each(items, (i, node) => (
+ $node = $(node)
+
+ jamblaster = @findJamBlaster($node.attr('data-jamblaster-id'))
+
+ $node.jamblasterOptions(jamblaster).off(context.JK.EVENTS.JAMBLASTER_ACTION).on(context.JK.EVENTS.JAMBLASTER_ACTION,
+ @jamblasterOptionSelected)
+ ))
+#context.JK.popExternalLinks(@root)
+
+ jamblasterOptionSelected: (e, data) ->
+ jamblasterId = data.options.id
+ jamblaster = @findJamBlaster(jamblasterId)
+
+ if data.option == 'auto-connect'
+ context.JK.Banner.showNotice('Auto-Connect',
+ 'Auto-Connect is always on by default. It can not currently configurable.')
+ else if data.option == 'restart'
+ context.JK.Banner.showNotice('Restart',
+ 'To restart the JamBlaster, you must manually cycle power (unplug, then plug).')
+ else if data.option == 'name'
+ context.layout.showDialog('jamblaster-name-dialog').one(context.JK.EVENTS.DIALOG_CLOSED, (e, data) =>
+ @resyncBonjour()
+ )
+
+ else if data.option == 'check-for-updates'
+ context.JK.Banner.showNotice('Check for Update',
+ 'The JamBlaster only checks for updates on start up. Please reboot the JamBlaster')
+ else if data.option == 'set-static-ports'
+ context.layout.showDialog('jamblaster-port-dialog')
+ else if data.option == 'factory-reset'
+ context.JK.Banner.showNotice('Factory Reset',
+ 'The JamBlaster only checks for updates when it boots up, and if there is an update available, it will automatically begin updating.
Please reboot the JamBlaster to initiate an update check.')
+ else
+ logger.debug("unknown action")
+ getInitialState: () ->
+ {
+ selected: 'management',
+ user: null,
+ userJamBlasters: [],
+ localClients: [],
+ clients: []
+ }
+
+ beforeHide: (e) ->
+
+
+ beforeShow: (e) ->
+
+ afterShow: (e) ->
+ @resyncBonjour()
+
+ openMenu: (client, e) ->
+ logger.debug("open jamblaster options menu")
+ $this = $(e.target)
+ if !$this.is('.jamblaster-options-btn')
+ $this = $this.closest('.jamblaster-options-btn')
+ $this.btOn()
+
+ connect: (client, e) ->
+ logger.debug("beginning pairing to #{client.connect_url}")
+
+ context.jamClient.startPairing(client.connect_url)
+
+ disconnect: (client, e) ->
+ logger.debug("disconnecting from currently paired client #{client.connect_url}")
+ context.jamClient.endPairing()
+ mergeClients: () ->
+
+ clientsJsx = []
+ for client in @state.clients
+ if client.display_name?
+ displayName = client.display_name
+ else
+ displayName = client.name
+
+ if client.serial_no? && displayName? && displayName.indexOf(client.serial_no) == -1
+ displayName = "#{displayName} (#{client.serial_no})"
+ name = `
{displayName} `
+
+ if client.isPaired
+ connect = `
disconnect `
+ else if client.has_local
+ connect = `
connect `
+ else
+ connect = `
offline `
+
+ options = `
more options
+
+ `
+
+ clientsJsx.push(`
+ {name}{options}{connect}
+ `)
+
+ clientsJsx
+
+ mainContent: () ->
+ if @state.selected == @TILE_AUDIO
+ @audio()
+ else if @state.selected == @TILE_INTERNET
+ @internet()
+ else if @state.selected == @TILE_MANAGEMENT
+ @management()
+ else if @state.selected == @TILE_USB
+ @usb()
+
+ management: () ->
+ clients = @mergeClients()
+ `
+
+
+
+ JAMBLASTERS ON YOUR NETWORK
+
+
+
+ {clients}
+
+
+
If you don't see your JamBlaster listed above, please check to make sure you have power connected to your JamBlaster,
+ and make sure your JamBlaster is connected via an Ethernet cable to the same router/network as the device on which you are viewing this application.
+
+
`
+
+ render: () ->
+ disabled = @state.updating
+
+ if !@state.user?.id || !@state.userJamBlasters? || !@state.localClients?
+ return `
Loading
`
+
+
+ `
+
+
jamblaster settings
+
+
+
+
+
+ {this.mainContent()}
+
+
+
+
`
+})
\ No newline at end of file
diff --git a/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee
index a56e05525..b63955402 100644
--- a/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/LessonBookingDecision.js.jsx.coffee
@@ -204,8 +204,8 @@
Time:
- {this.hours} : {this.minutes}
- {this.am_pm}
+ {this.hours} : {this.minutes}
+ {this.am_pm}
* Time will be local to {window.JK.currentTimezone()}
{errorText}
@@ -227,8 +227,8 @@
Time:
- {this.hours} : {this.minutes}
- {this.am_pm}
+ {this.hours} : {this.minutes}
+ {this.am_pm}
*Time will be local to {window.JK.currentTimezone()}
{errorText}
diff --git a/web/app/assets/javascripts/react-components/mixins/BonjourMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/BonjourMixin.js.coffee
new file mode 100644
index 000000000..504600c92
--- /dev/null
+++ b/web/app/assets/javascripts/react-components/mixins/BonjourMixin.js.coffee
@@ -0,0 +1,97 @@
+context = window
+teacherActions = window.JK.Actions.Teacher
+
+@BonjourMixin = {
+
+ resyncBonjour: () ->
+ rest.getUserJamBlasters({client_id: @app.clientId}).done((response) => @getUserJamBlastersDone(response)).fail((response) => @getUserJamBlastersFail(response))
+
+ getUserJamBlastersDone: (response) ->
+ @setState({userJamBlasters: response})
+
+ @getLocalClients(response)
+
+
+ findJamBlaster: (id) ->
+ found = null
+ if @clients?
+ for client in @clients
+ if client.id == id
+ found = client
+ break
+ if client.ipv6_addr == id
+ found = client
+ break
+
+ found
+
+ getUserJamBlastersFail: (jqXHR) ->
+ @app.layout.ajaxError(jqXHR)
+
+ mergeBonjourClients: (localClients, userJamBlasters) ->
+ console.log("@state.localClients", localClients)
+ console.log("@state.userJamBlasters", userJamBlasters)
+
+ # for localClient in @state.localClients
+
+ for localClient in localClients
+ if localClient.connect_url.indexOf(':30330') && localClient.is_jb
+ client = {}
+ client.ipv6_addr = localClient.ipv6_addr
+ client.isPaired = localClient.isPaired
+ client.name = localClient.name
+ client.has_local = true
+ client.has_server = false
+ client.id = client.ipv6_addr
+ client.connect_url = localClient.connect_url
+
+
+ for serverClient in userJamBlasters
+ # see if we can join on ipv6
+ if ipv6_addr == serverClient.ipv6_link_local
+ # ok, matched! augment with server data
+ client.serial_no = serverClient.serial_no
+ client.user_id = serverClient.user_id
+ client.id = serverClient.id
+ client.client_id = serverClient.client_id
+ client.ipv4_link_local = serverClient.ipv4_link_local
+ client.display_name = serverClient.display_name
+ client.has_server = true
+ break
+ clients.push(client)
+
+ for serverClient in userJamBlasters
+
+ foundLocal = false
+ for localClient in localClients
+ if ipv6_addr == serverClient.ipv6_link_local
+ foundLocal = true
+ break
+ if !foundLocal
+ # this server version of the client has not been spoken for in the earlier loop above
+ # so we need to add it in to the client list
+
+ client = {}
+ client.serial_no = serverClient.serial_no
+ client.user_id = serverClient.user_id
+ client.id = serverClient.id
+ client.client_id = serverClient.client_id
+ client.ipv4_link_local = serverClient.ipv4_link_local
+ client.display_name = serverClient.display_name
+ client.has_local = false
+ client.has_server = true
+ clients.push(client)
+
+ console.log("all client", clients)
+
+ @clients = clients
+ @setState({clients: clients})
+
+ getLocalClients: (userJamBlasters) ->
+ localClients = context.jamClient.getLocalClients()
+
+ @mergeBonjourClients(localClients, userJamBlasters)
+
+ @setState({localClients: localClients})
+
+}
\ No newline at end of file
diff --git a/web/app/assets/javascripts/web/web.js b/web/app/assets/javascripts/web/web.js
index 6bad6ced2..4e5e4cb9a 100644
--- a/web/app/assets/javascripts/web/web.js
+++ b/web/app/assets/javascripts/web/web.js
@@ -23,6 +23,7 @@
//= require jquery.exists
//= require jquery.visible
//= require jquery.lessonSessionActions
+//= require jquery.jamblasterOptions
//= require jquery.manageVsts
//= require jquery.scrollTo
//= require jquery.pulse
diff --git a/web/app/assets/stylesheets/client/client.css b/web/app/assets/stylesheets/client/client.css
index 380fd67dd..7dee21c07 100644
--- a/web/app/assets/stylesheets/client/client.css
+++ b/web/app/assets/stylesheets/client/client.css
@@ -56,6 +56,7 @@
*= require ./muteSelect
*= require ./manageVsts
*= require ./lessonSessionActions
+ *= require ./jamblasterOptions
*= require ./vstEffects
*= require ./metronomePlaybackModeSelect
*= require ./terms
diff --git a/web/app/assets/stylesheets/client/jamblasterOptions.css.scss b/web/app/assets/stylesheets/client/jamblasterOptions.css.scss
new file mode 100644
index 000000000..187dbb767
--- /dev/null
+++ b/web/app/assets/stylesheets/client/jamblasterOptions.css.scss
@@ -0,0 +1,33 @@
+@import "client/common";
+
+.jamblaster-options-popup {
+
+ width:120px;
+ height:100px;
+ a{
+ color:#fc0 !important;
+ }
+ li {
+ border-bottom:0 !important;
+ }
+
+ .bt-content {
+ height:80px;
+ width:100px;
+ background-color:#333;
+ overflow:auto;
+ border:1px solid #ED3618;
+ text-align:left;
+ font-family: 'Raleway', Arial, Helvetica, sans-serif;
+ ul {
+ @include vertical-align-column;
+ height:100%;
+ margin-left: 0 !important;
+ }
+ li {
+ font-size:12px;
+ margin-left:0 !important;
+ list-style-type: none;
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/react-components/JamBlasterScreen.css.scss b/web/app/assets/stylesheets/client/react-components/JamBlasterScreen.css.scss
new file mode 100644
index 000000000..23d93c0dc
--- /dev/null
+++ b/web/app/assets/stylesheets/client/react-components/JamBlasterScreen.css.scss
@@ -0,0 +1,181 @@
+@import "client/common";
+
+#jamblaster-screen {
+
+ div[data-react-class="JamBlasterScreen"] {
+ height:100%;
+ }
+ .content-body-scroller {
+ height:100%;
+ padding:30px;
+ @include border_box_sizing;
+ }
+
+ ol {
+ li {
+ margin-left:15px;
+ margin-bottom:2px;
+ list-style:decimal;
+ color:$ColorTextTypical;
+ }
+ }
+
+ .student-right-content {
+ p {
+ margin-bottom:10px;
+ }
+ ul {
+ margin-bottom:25px;
+ li {
+ margin-left:-10px;
+ margin-bottom:2px;
+ list-style:disc;
+ color:$ColorTextTypical;
+ }
+ }
+ }
+ h2 {
+ font-size: 20px;
+ font-weight:700;
+ margin-bottom: 20px !important;
+ display:inline-block;
+ }
+ .column {
+ @include border_box_sizing;
+ width:50%;
+ }
+ .column-left {
+ float:left;
+ width:70%;
+ }
+ .column-right {
+ float:right;
+ width:30%;
+ padding-left:20px;
+
+ .jamclass-section {
+ padding-top:36px;
+ }
+ }
+ span.price {
+ color:white;
+ }
+ .test-drive-main {
+ }
+ p {
+ line-height:125% !important;
+ font-size:14px !important;
+ margin:0 0 20px 0 !important;
+ color: $ColorTextTypical;
+ }
+ .avatar {
+ display:inline-block;
+ padding:1px;
+ width:36px;
+ height:36px;
+ background-color:#ed4818;
+ margin:0 10px 0 0;
+ -webkit-border-radius:18px;
+ -moz-border-radius:18px;
+ border-radius:18px;
+ float:none;
+ }
+ .avatar img {
+ width: 36px;
+ height: 36px;
+ -webkit-border-radius:18px;
+ -moz-border-radius:18px;
+ border-radius:18px;
+ }
+
+ .calender-integration-notice {
+ display:block;
+ text-align:center;
+ }
+ .actions {
+ display:block;
+ text-align:center;
+ }
+ .jamclass-section {
+ margin-bottom:40px;
+ &.my-lessons {
+ max-height:300px;
+ overflow:auto;
+ }
+ }
+ .management-content {
+ min-width:400px;
+ max-width:700px;
+ }
+ .jamtable {
+
+ height:100%;
+ width:100%;
+ table-layout:fixed;
+ margin-bottom:20px;
+ a {
+ text-decoration: none !important;
+ color:#fc0 !important;
+ }
+ th {
+ font-size:14px;
+ padding:3px 10px;
+ @include border_box_sizing;
+ &.role {
+ text-transform: uppercase;
+ }
+ }
+ td {
+ background-color: #2b2b2b;
+ padding:4px 15px;
+ font-size:14px;
+ vertical-align:middle;
+ color: $ColorTextTypical;
+ @include border_box_sizing;
+ }
+ tbody {
+
+ }
+
+ .displayNameColumn {
+ float:left;
+ }
+
+ .connectColumn {
+ float:right;
+ margin-right:20px;
+ }
+
+ .optionsColumn{
+ float:right;
+ text-align:center;
+ }
+ .jamblaster-options-btn {
+ white-space: nowrap;
+ cursor: pointer;
+ vertical-align: bottom;
+ color:#fc0;
+
+ .arrow-down {
+ float:none;
+ margin-left:5px;
+ margin-top:0;
+ margin-right:0;
+ border-top: 4px solid #fc0;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ display:inline-block;
+ }
+ .arrow-up {
+ float:none;
+ margin-right:0;
+ margin-left:5px;
+ margin-bottom:2px;
+ border-bottom: 4px solid #fc0;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ display:inline-block;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/dialogs/jamblasterNameDialog.css.scss b/web/app/assets/stylesheets/dialogs/jamblasterNameDialog.css.scss
new file mode 100644
index 000000000..eaf1ea52c
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/jamblasterNameDialog.css.scss
@@ -0,0 +1,40 @@
+@import "client/common";
+
+#jamblaster-name-dialog {
+ width: 600px;
+ max-height:600px;
+
+ h2 {
+ color:white;
+ margin-bottom:10px;
+ font-size:16px;
+ }
+ .dialog-inner {
+ width: auto;
+ height:calc(100% - 29px)
+ }
+
+ .field {
+ margin-bottom:10px;
+ }
+
+ input {
+ display:inline-block;
+ }
+ label {
+ display:inline-block;
+ }
+ .iradio_minimal {
+ display:inline-block;
+ margin-right: 5px;
+ top: 4px;
+ }
+
+ div[data-react-class="JamBlasterNameDialog"] {
+
+ }
+ .actions {
+ float:right;
+ margin:0 -13px 30px 0;
+ }
+}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/dialogs/jamblasterPairingDialog.css.scss b/web/app/assets/stylesheets/dialogs/jamblasterPairingDialog.css.scss
new file mode 100644
index 000000000..86c0b6bdb
--- /dev/null
+++ b/web/app/assets/stylesheets/dialogs/jamblasterPairingDialog.css.scss
@@ -0,0 +1,48 @@
+@import "client/common";
+
+#jamblaster-pairing-dialog {
+ width: 600px;
+ max-height:600px;
+
+ h2 {
+ color:white;
+ margin-bottom:10px;
+ font-size:16px;
+ }
+ .dialog-inner {
+ width: auto;
+ height:calc(100% - 29px)
+ }
+
+ .field {
+ margin-bottom:10px;
+ }
+
+ input {
+ display:inline-block;
+ }
+ label {
+ display:inline-block;
+ }
+ .iradio_minimal {
+ display:inline-block;
+ margin-right: 5px;
+ top: 4px;
+ }
+
+ div[data-react-class="JamBlasterNameDialog"] {
+
+ }
+ .actions {
+ float:right;
+ margin:0 -13px 30px 0;
+ }
+ .countdown {
+ color:white;
+ font-size:18px;
+ font-weight:bold;
+ padding:0 10px;
+ width:30px;
+ display:inline-block;
+ }
+}
\ No newline at end of file
diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb
index 846796c33..6687f758a 100644
--- a/web/app/controllers/api_users_controller.rb
+++ b/web/app/controllers/api_users_controller.rb
@@ -46,6 +46,13 @@ class ApiUsersController < ApiController
respond_with @user, responder: ApiResponder, :status => 200
end
+ def jamblasters
+ @user = current_user
+ @connection = Connection.find_by_client_id(params[:client_id])
+
+ respond_with @user, responder: ApiResponder, :status => 200
+ end
+
def google_auth
@user = current_user
respond_with @user, responder: ApiResponder, :status => 200
diff --git a/web/app/views/api_users/jamblasters.rabl b/web/app/views/api_users/jamblasters.rabl
new file mode 100644
index 000000000..8e6185a82
--- /dev/null
+++ b/web/app/views/api_users/jamblasters.rabl
@@ -0,0 +1,18 @@
+object @user
+
+child :jamblasters do
+ attributes :id, :user_id, :serial_no, :client_id, :ipv6_link_local, :ipv4_link_local, :display_name
+end
+
+node :same_network_jamblasters do
+ if @connection
+ jamblasters = @connection.same_network_jamblasters
+ result = []
+ jamblasters.each do |jamblaster|
+ result.push ({id: jamblaster.id, user_id: jamblaster.user_id, serial_no: jamblaster.serial_no, client_id: jamblaster.client_id, ipv4_link_local: jamblaster.ipv4_link_local, ipv6_link_local: jamblaster.ipv6_link_local, display_name: jamblaster.display_name})
+ end
+ result
+ else
+ []
+ end
+end
diff --git a/web/app/views/clients/_jamblasterOptions.html.slim b/web/app/views/clients/_jamblasterOptions.html.slim
new file mode 100644
index 000000000..a9f22181a
--- /dev/null
+++ b/web/app/views/clients/_jamblasterOptions.html.slim
@@ -0,0 +1,20 @@
+script type='text/template' id='template-jamblaster-options'
+
+ ul
+ li data-jamblaster-option="auto-connect"
+ a href='#' Auto Connect
+
+ li data-jamblaster-option="restart"
+ a href='#' Restart
+
+ li data-jamblaster-option="name"
+ a href='#' Name
+
+ li data-jamblaster-option="check-for-updates"
+ a href='#' Check for Updates
+
+ li data-jamblaster-option="set-static-ports"
+ a href='#' Set Static Ports
+
+ li data-jamblaster-option="factory-reset"
+ a href='#' Factory Reset
diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb
index 724449d3c..4a4574a14 100644
--- a/web/app/views/clients/index.html.erb
+++ b/web/app/views/clients/index.html.erb
@@ -21,6 +21,7 @@
<%= render "muteSelect" %>
<%= render "manageVsts" %>
<%= render "lessonSessionActions" %>
+<%= render "jamblasterOptions" %>
<%= render "vstEffects" %>
<%= render "metronome_playback_mode" %>
<%= render "clients/wizard/buttons" %>
@@ -38,6 +39,7 @@
<%= render "bandProfile" %>
<%= render "band_setup" %>
<%= render "band_setup_photo" %>
+<%= render "clients/jamclass/jamblaster" %>
<%= render "clients/teachers/setup/introduction" %>
<%= render "clients/teachers/setup/basics" %>
<%= render "clients/teachers/setup/experience" %>
diff --git a/web/app/views/clients/jamclass/_jamblaster.html.slim b/web/app/views/clients/jamclass/_jamblaster.html.slim
new file mode 100644
index 000000000..8ab2b6372
--- /dev/null
+++ b/web/app/views/clients/jamclass/_jamblaster.html.slim
@@ -0,0 +1,10 @@
+#jamblaster-screen.screen.secondary layout="screen" layout-id="jamblaster"
+ .content-head
+ .content-icon
+ = image_tag "content/icon_jamtracks.png", :size => "24x24"
+ h1
+ | jamblaster
+ = render "screen_navigation"
+ .content-body
+ = react_component 'JamBlasterScreen', {}
+
diff --git a/web/app/views/dialogs/_dialogs.html.haml b/web/app/views/dialogs/_dialogs.html.haml
index 5cc2b9531..9e2b83c75 100644
--- a/web/app/views/dialogs/_dialogs.html.haml
+++ b/web/app/views/dialogs/_dialogs.html.haml
@@ -55,4 +55,6 @@
= render 'dialogs/rescheduleLessonDialog'
= render 'dialogs/rateUserDialog'
= render 'dialogs/musicNotationUploadDialog'
-= render 'dialogs/testDrivePackageDialog'
\ No newline at end of file
+= render 'dialogs/testDrivePackageDialog'
+= render 'dialogs/jamblasterNameDialog'
+= render 'dialogs/jamblasterPairingDialog'
\ No newline at end of file
diff --git a/web/app/views/dialogs/_jamblasterNameDialog.html.slim b/web/app/views/dialogs/_jamblasterNameDialog.html.slim
new file mode 100644
index 000000000..ac1645832
--- /dev/null
+++ b/web/app/views/dialogs/_jamblasterNameDialog.html.slim
@@ -0,0 +1,2 @@
+.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='jamblaster-name-dialog' id='jamblaster-name-dialog'
+ = react_component 'JamBlasterNameDialog', {}
diff --git a/web/app/views/dialogs/_jamblasterPairingDialog.html.slim b/web/app/views/dialogs/_jamblasterPairingDialog.html.slim
new file mode 100644
index 000000000..b4e20e8ab
--- /dev/null
+++ b/web/app/views/dialogs/_jamblasterPairingDialog.html.slim
@@ -0,0 +1,2 @@
+.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='jamblaster-pairing-dialog' id='jamblaster-pairing-dialog'
+ = react_component 'JamBlasterPairingDialog', {}
diff --git a/web/app/views/layouts/landing.html.erb b/web/app/views/layouts/landing.html.erb
index 34eb82f5a..fb3f0d8e8 100644
--- a/web/app/views/layouts/landing.html.erb
+++ b/web/app/views/layouts/landing.html.erb
@@ -42,6 +42,7 @@
<%= render "clients/lessonSessionActions" %>
+ <%= render "clients/jamblasterOptions" %>
<%= render "clients/manageVsts" %>
<%= render 'dialogs/dialogs' %>
diff --git a/web/app/views/layouts/web.html.erb b/web/app/views/layouts/web.html.erb
index 7774d8a2c..b3c836504 100644
--- a/web/app/views/layouts/web.html.erb
+++ b/web/app/views/layouts/web.html.erb
@@ -103,6 +103,7 @@
<%= render "clients/jam_track_preview" %>
<%= render "clients/help" %>
<%= render "clients/lessonSessionActions" %>
+ <%= render "clients/jamblasterOptions" %>
<%= render "clients/manageVsts" %>
<%= render 'dialogs/dialogs' %>
diff --git a/web/config/application.rb b/web/config/application.rb
index d732ae4ab..0cbecfa5a 100644
--- a/web/config/application.rb
+++ b/web/config/application.rb
@@ -119,10 +119,6 @@ if defined?(Bundler)
config.websocket_gateway_port = 6767 + ENV['JAM_INSTANCE'].to_i
config.websocket_gateway_port_ssl = 6443 + ENV['JAM_INSTANCE'].to_i
# Runs the websocket gateway within the web app
- config.websocket_gateway_uri = "ws://localhost:#{config.websocket_gateway_port}/websocket"
- config.websocket_gateway_trusted_uri = "ws://localhost:#{config.websocket_gateway_port + 1}/websocket"
- config.websocket_gateway_uri_ssl = "wss://localhost:#{config.websocket_gateway_port_ssl}/websocket"
- config.websocket_gateway_trusted_uri_ssl = "wss://localhost:#{config.websocket_gateway_port_ssl + 1}/websocket"
config.force_ssl = ENV['FORCE_SSL'].nil? ? false : ENV['FORCE_SSL'] == 'true'
config.websocket_gateway_max_connections_per_user = 20
config.lock_connections = false
@@ -137,6 +133,12 @@ if defined?(Bundler)
# set this to false if you want to disable signups (lock down public user creation)
config.signup_enabled = true
+ config.websocket_gateway_uri = "ws://#{config.external_hostname}:#{config.websocket_gateway_port}/websocket"
+ config.websocket_gateway_trusted_uri = "ws://#{config.external_hostname}:#{config.websocket_gateway_port + 1}/websocket"
+ config.websocket_gateway_uri_ssl = "wss://#{config.external_hostname}:#{config.websocket_gateway_port_ssl}/websocket"
+ config.websocket_gateway_trusted_uri_ssl = "wss://#{config.external_hostname}:#{config.websocket_gateway_port_ssl + 1}/websocket"
+
+
config.storage_type = :fog
# config.storage_type = :file # or :fog, if using AWS
diff --git a/web/config/environment.rb b/web/config/environment.rb
index 3463d46ae..47ba4f7b2 100644
--- a/web/config/environment.rb
+++ b/web/config/environment.rb
@@ -1,3 +1,10 @@
+if defined? FasterPath
+ puts "OMG"
+ require "faster_path/optional/monkeypatches"
+ FasterPath.sledgehammer_everything!
+end
+
+
# Load the rails application
require File.expand_path('../application', __FILE__)
diff --git a/web/config/routes.rb b/web/config/routes.rb
index ed3634dba..159f8caa8 100644
--- a/web/config/routes.rb
+++ b/web/config/routes.rb
@@ -325,6 +325,7 @@ SampleApp::Application.routes.draw do
match '/users' => 'api_users#create', :via => :post
match '/users/:id' => 'api_users#show', :via => :get, :as => 'api_user_detail'
match '/users/:id/authorizations' => 'api_users#authorizations', :via => :get
+ match '/users/:id/jamblasters' => 'api_users#jamblasters', :via => :get
#match '/users' => 'api_users#create', :via => :post
match '/users/:id' => 'api_users#update', :via => :post
match '/users/:id' => 'api_users#delete', :via => :delete
diff --git a/web/lib/tasks/jam_tracks.rake b/web/lib/tasks/jam_tracks.rake
index 2098bcf06..dc8a4e9d5 100644
--- a/web/lib/tasks/jam_tracks.rake
+++ b/web/lib/tasks/jam_tracks.rake
@@ -18,6 +18,18 @@ namespace :jam_tracks do
end
task dry_run: :environment do |task, args|
+ if ENV['STORAGE']
+ JamTrackImporter.storage_format = ENV['STORAGE']
+ end
+ JamTrackImporter.dry_run
+ end
+
+ # valid values for STORAGE
+ # Helbing
+ task dry_run: :environment do |task, args|
+ if ENV['STORAGE']
+ JamTrackImporter.storage_format = ENV['STORAGE']
+ end
JamTrackImporter.dry_run
end
@@ -192,6 +204,11 @@ namespace :jam_tracks do
JamTrackImporter.synchronize_all(skip_audio_upload: false)
end
+ task sync_helbing: :environment do |task, args|
+ JamTrackImporter.storage_format = 'Helbing'
+ JamTrackImporter.synchronize_all(skip_audio_upload: false)
+ end
+
task tency_dups: :environment do |task, args|
end
diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb
index c08488e64..8c7fd9893 100644
--- a/websocket-gateway/lib/jam_websockets/router.rb
+++ b/websocket-gateway/lib/jam_websockets/router.rb
@@ -737,6 +737,8 @@ module JamWebsockets
os = options["os"]
udp_reachable = options["udp_reachable"].nil? ? true : options["udp_reachable"] == 'true'
jamblaster_serial_no = options["jamblaster_serial_no"]
+ ipv4_link_local = options["ipv4_link_local"]
+ ipv6_link_local = options["ipv6_link_local"]
# TESTING
#if jamblaster_serial_no.nil?
@@ -745,8 +747,8 @@ module JamWebsockets
client.subscriptions = Set.new # list of subscriptions that this client is watching in real-time
- @log.info("handle_login: client_type=#{client_type} token=#{token} client_id=#{client_id} channel_id=#{client.channel_id} udp_reachable=#{udp_reachable}")
-
+ serial_no_debug = jamblaster_serial_no ? "serial_no=#{jamblaster_serial_no}" : ''
+ @log.info("handle_login: type=#{client_type} username=#{username} password=#{password ? '*' : 'null' } token=#{token} client_id=#{client_id} channel_id=#{client.channel_id} udp_reachable=#{udp_reachable} #{serial_no_debug}")
if client_type == Connection::TYPE_LATENCY_TESTER
handle_latency_tester_login(client_id, client_type, client, override_ip)
return
@@ -775,6 +777,8 @@ module JamWebsockets
end
if jamblaster && connecting
jamblaster.client_id = client_id
+ jamblaster.ipv4_link_local = ipv4_link_local
+ jamblaster.ipv6_link_local = ipv6_link_local
jamblaster.save
end
end