* VRFS-3519 - 2-player sessions working with custom mix

This commit is contained in:
Seth Call 2015-09-22 15:25:48 -05:00
parent 1733c8689d
commit 1dce984247
6 changed files with 310 additions and 227 deletions

View File

@ -72,7 +72,7 @@ mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged'))
monitorControls: (controls, mediaSummary) ->
if mediaSummary.mediaOpen || mediaSummary.jamTrack?
if mediaSummary.jamTrack?
if mediaSummary.jamTrackOpen?
controls.startMonitor(PLAYBACK_MONITOR_MODE.JAMTRACK)
else if mediaSummary.backingTrackOpen
controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE)

View File

@ -49,6 +49,7 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged'))
metronome: mixers.metronome
recordingName: mixers.recordingName()
jamTrackName: mixers.jamTrackName()
jamTrackMixdown: session.jamTrackMixdown()
@setState(media: state, downloadingJamTrack: session.downloadingJamTrack)
@ -76,7 +77,8 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged'))
creatingMixdown: false,
createMixdownErrors: null,
editingMixdownId: null,
downloadingJamTrack: @props.downloadingJamTrack
downloadingJamTrack: @props.downloadingJamTrack,
jamTrackMixdown: {}
}
close: () ->
@ -95,234 +97,255 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged'))
mediaName = @state.media.recordedTracks[0].recordingName
closeLinkText = 'close recording'
header = `<h3>{mediaType}: {mediaName}</h3>`
else if @state.jamTrackState.jamTrack?
jamTrack = @state.jamTrackState.jamTrack
mediaType = "JamTrack"
mediaName = jamTrack.name
closeLinkText = 'CLOSE JAMTRACK'
else if @state.media.mediaSummary.jamTrackOpen || @state.jamTrackState.jamTrack?
if @state.media.mediaSummary.isOpener || @state.jamTrackState.jamTrack?
# if you opened the JamTrack, then you get all the good info
jamTrack = @state.jamTrackState.jamTrack
mediaType = "JamTrack"
mediaName = jamTrack.name
closeLinkText = 'CLOSE JAMTRACK'
selectedMixdown = jamTrack.activeMixdown
selectedMixdown = jamTrack.activeMixdown
if selectedMixdown?
jamTrackTypeHeader = 'Custom Mix'
disabled = true
if selectedMixdown.client_state?
switch selectedMixdown.client_state
when 'cant_open'
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
when 'keying_timeout'
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
when 'download_fail'
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
when 'keying'
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif" /></h5>`
when 'downloading'
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif" /></h5>`
when 'ready'
customMixName = `<h5>{selectedMixdown.name}</h5>`
disabled = false
else
customMixName = `<h5>Creating mixdown... <img src="/assets/shared/spinner.gif" /></h5>`
if selectedMixdown?
jamTrackTypeHeader = 'Custom Mix'
disabled = true
if selectedMixdown.client_state?
switch selectedMixdown.client_state
when 'cant_open'
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
when 'keying_timeout'
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
when 'download_fail'
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
when 'keying'
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif" /></h5>`
when 'downloading'
customMixName = `<h5>Loading selected mix... <img src="/assets/shared/spinner.gif" /></h5>`
when 'ready'
customMixName = `<h5>{selectedMixdown.name}</h5>`
disabled = false
else
customMixName = `<h5>Creating mixdown... <img src="/assets/shared/spinner.gif" /></h5>`
if SessionStore.downloadingJamTrack
downloader = `<img src="/assets/shared/spinner.gif" />`
else
if SessionStore.downloadingJamTrack
downloader = `<img src="/assets/shared/spinner.gif" />`
jamTrackTypeHeader = `<span>Full JamTrack {downloader}</span>`
jamTrackTypeHeader = `<span>Full JamTrack {downloader}</span>`
header = `
<div className="header">
<h3>{mediaType}: {mediaName}</h3>
<h4>{jamTrackTypeHeader}</h4>
{customMixName}
</div>`
header = `
<div className="header">
<h3>{mediaType}: {mediaName}</h3>
<h4>{jamTrackTypeHeader}</h4>
{customMixName}
</div>`
myMixes = null
if @state.showMyMixes
myMixdowns = []
myMixes = null
if @state.showMyMixes
myMixdowns = []
boundPlayClick = this.jamTrackPlay.bind(this, jamTrack);
boundPlayClick = this.jamTrackPlay.bind(this, jamTrack);
active = jamTrack.last_mixdown_id == null
myMixdowns.push `
<div key="full-track" className={classNames({'full-track': true, 'mixdown-display': true, 'active' : active})}>
<div className="mixdown-name">
Full JamTrack
</div>
<div className="mixdown-actions">
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>
</div>
</div>`
for mixdown in jamTrack.mixdowns
boundPlayClick = this.mixdownPlay.bind(this, mixdown);
boundEditClick = this.mixdownEdit.bind(this, mixdown);
boundSaveClick = this.mixdownSave.bind(this, mixdown);
boundDeleteClick = this.mixdownDelete.bind(this, mixdown);
boundErrorClick = this.mixdownError.bind(this, mixdown);
boundEditKeydown = this.onEditKeydown.bind(this, mixdown);
mixdown_package = mixdown.myPackage
active = mixdown.id == jamTrack.last_mixdown_id
editing = mixdown.id == @state.editingMixdownId
# if there is a package, check it's state; otherwise let the user enqueue it
if mixdown_package
switch mixdown_package.signing_state
when 'QUIET_TIMEOUT'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
when 'QUIET'
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
when 'QUEUED'
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
when 'QUEUED_TIMEOUT'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
when 'SIGNING'
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
when 'SIGNING_TIMEOUT'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
when 'SIGNED'
action = `<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>`
when 'ERROR'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
else
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
if editing
mixdownName = `<input className="edit-name" type="text" defaultValue={mixdown.name} onKeyDown={boundEditKeydown} />`
editIcon = `<img src="/assets/content/icon-save@2X.png" className="mixdown-edit" onClick={boundSaveClick}/>`
else
mixdownName = mixdown.name
editIcon = `<img src="/assets/content/icon-edit@2X.png" className="mixdown-edit" onClick={boundEditClick}/>`
active = jamTrack.last_mixdown_id == null
myMixdowns.push `
<div key={mixdown.id} className={classNames({'mixdown-display': true, 'active' : active})}>
<div className="mixdown-name">
{mixdownName}
</div>
<div className="mixdown-actions">
{action}
<div key="full-track" className={classNames({'full-track': true, 'mixdown-display': true, 'active' : active})}>
<div className="mixdown-name">
Full JamTrack
</div>
<div className="mixdown-actions">
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>
</div>
</div>`
{editIcon}
for mixdown in jamTrack.mixdowns
boundPlayClick = this.mixdownPlay.bind(this, mixdown);
boundEditClick = this.mixdownEdit.bind(this, mixdown);
boundSaveClick = this.mixdownSave.bind(this, mixdown);
boundDeleteClick = this.mixdownDelete.bind(this, mixdown);
boundErrorClick = this.mixdownError.bind(this, mixdown);
boundEditKeydown = this.onEditKeydown.bind(this, mixdown);
<img src ="/assets/content/icon-delete@2X.png" className="mixdown-delete" onClick={boundDeleteClick} />
</div>
mixdown_package = mixdown.myPackage
active = mixdown.id == jamTrack.last_mixdown_id
editing = mixdown.id == @state.editingMixdownId
# if there is a package, check it's state; otherwise let the user enqueue it
if mixdown_package
switch mixdown_package.signing_state
when 'QUIET_TIMEOUT'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
when 'QUIET'
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
when 'QUEUED'
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
when 'QUEUED_TIMEOUT'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
when 'SIGNING'
action = `<img src="/assets/shared/spinner.gif" className="mixdown-play"/>`
when 'SIGNING_TIMEOUT'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
when 'SIGNED'
action = `<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundPlayClick}/>`
when 'ERROR'
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
else
action = `<img src="/assets/content/icon-mix-fail@2X.png" className="mixdown-play" onClick={boundErrorClick}/>`
if editing
mixdownName = `<input className="edit-name" type="text" defaultValue={mixdown.name} onKeyDown={boundEditKeydown} />`
editIcon = `<img src="/assets/content/icon-save@2X.png" className="mixdown-edit" onClick={boundSaveClick}/>`
else
mixdownName = mixdown.name
editIcon = `<img src="/assets/content/icon-edit@2X.png" className="mixdown-edit" onClick={boundEditClick}/>`
myMixdowns.push `
<div key={mixdown.id} className={classNames({'mixdown-display': true, 'active' : active})}>
<div className="mixdown-name">
{mixdownName}
</div>
<div className="mixdown-actions">
{action}
{editIcon}
<img src ="/assets/content/icon-delete@2X.png" className="mixdown-delete" onClick={boundDeleteClick} />
</div>
</div>`
myMixes = `<div key="my-mixes" className="my-mixes">{myMixdowns}</div>`
mixControls = null
if @state.showCustomMixes
nameClassData = {field: true}
if @state.createMixdownErrors?
errorHtml = context.JK.reactErrors(@state.createMixdownErrors, {name: 'Mix Name', settings: 'Settings', jam_track: 'JamTrack'})
createMixClasses = classNames({'button-orange' : true, 'create-mix-btn' : true, 'disabled' : @state.creatingMixdown})
mixControls = `
<div key="create-mix" className="create-mix">
<p>Use the JamTrack controls on the session screen to set levels, mute/unmute, or pan any of the parts of the JamTrack as you like. You can also use the controls below to adjust the tempo or pitch of the JamTrack. Then give your custom mix a name, and click the Create Mix button. Please note that changing the tempo or pitch of the JamTrack may take a long time, and won't be ready right away.</p>
<div className="field">
<label>Change Tempo:</label>
<select name="mix-speed">
<option value="">No change</option>
<option value="separator-1">------------</option>
<option value="-5">Slower by 5%</option>
<option value="-10">Slower by 10%</option>
<option value="-15">Slower by 15%</option>
<option value="-20">Slower by 20%</option>
<option value="-25">Slower by 25%</option>
<option value="-30">Slower by 30%</option>
<option value="-35">Slower by 35%</option>
<option value="-40">Slower by 40%</option>
<option value="-45">Slower by 45%</option>
<option value="-50">Slower by 50%</option>
<option value="-60">Slower by 60%</option>
<option value="-70">Slower by 70%</option>
<option value="-80">Slower by 80%</option>
<option value="separator-2">------------</option>
<option value="5">Faster by 5%</option>
<option value="10">Faster by 10%</option>
<option value="15">Faster by 15%</option>
<option value="20">Faster by 20%</option>
<option value="30">Faster by 30%</option>
<option value="40">Faster by 40%</option>
<option value="50">Faster by 50%</option>
</select>
</div>
<div className="field">
<label>Change Pitch:</label>
<select name="mix-pitch">
<option value="">No change</option>
<option value="separator-1">---------------</option>
<option value="-1">Down 1 Semitone</option>
<option value="-2">Down 2 Semitones</option>
<option value="-3">Down 3 Semitones</option>
<option value="-4">Down 4 Semitones</option>
<option value="-5">Down 5 Semitones</option>
<option value="-6">Down 6 Semitones</option>
<option value="-7">Down 7 Semitones</option>
<option value="-8">Down 8 Semitones</option>
<option value="-9">Down 9 Semitones</option>
<option value="-10">Down 10 Semitones</option>
<option value="-11">Down 11 Semitones</option>
<option value="-12">Down 12 Semitones</option>
<option value="separator-2">---------------</option>
<option value="1">Up 1 Semitone</option>
<option value="2">Up 2 Semitones</option>
<option value="3">Up 3 Semitones</option>
<option value="4">Up 4 Semitones</option>
<option value="5">Up 5 Semitones</option>
<option value="6">Up 6 Semitones</option>
<option value="7">Up 7 Semitones</option>
<option value="8">Up 8 Semitones</option>
<option value="9">Up 9 Semitones</option>
<option value="10">Up 10 Semitones</option>
<option value="11">Up 11 Semitones</option>
<option value="12">Up 12 Semitones</option>
</select>
</div>
<div className={classNames(nameClassData)}>
<label>Mix Name:</label>
<input type="text" name="mix-name"/>
</div>
<div className="field">
<a className={createMixClasses} onClick={this.createMix}>CREATE MIX</a>
{errorHtml}
</div>
<div className="clearall"/>
</div>`
if @state.showMyMixes
showMyMixesText = `<a onClick={this.toggleMyMixes}>hide my mixes <div className="details-arrow arrow-up" /></a>`
else
showMyMixesText = `<a onClick={this.toggleMyMixes}>show my mixes <div className="details-arrow arrow-down" /></a>`
if @state.showCustomMixes
showMixControlsText = `<a onClick={this.toggleCustomMixes}>hide mix controls <div className="details-arrow arrow-up" /></a>`
else
showMixControlsText = `<a onClick={this.toggleCustomMixes}>show mix controls <div className="details-arrow arrow-down" /></a>`
extraControls = `
<div className="extra-controls">
<h4>My Mixes {showMyMixesText}</h4>
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{myMixes}
</ReactCSSTransitionGroup>
<h4 className="custom-mix-header">Create Custom Mix {showMixControlsText}</h4>
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{mixControls}
</ReactCSSTransitionGroup>
</div>`
else
mediaType = "JamTrack"
mediaName = @state.media.jamTrackName
closeLinkText = 'CLOSE JAMTRACK'
# implies we have a mixdown
if @state.media.jamTrackMixdown.id?
jamTrackTypeHeader = 'Custom Mix'
else
jamTrackTypeHeader = 'Full JamTrack'
header = `
<div className="header">
<h3>{mediaType}: {mediaName}</h3>
<h4>{jamTrackTypeHeader}</h4>
{customMixName}
</div>`
myMixes = `<div key="my-mixes" className="my-mixes">{myMixdowns}</div>`
mixControls = null
if @state.showCustomMixes
nameClassData = {field: true}
if @state.createMixdownErrors?
errorHtml = context.JK.reactErrors(@state.createMixdownErrors, {name: 'Mix Name', settings: 'Settings', jam_track: 'JamTrack'})
createMixClasses = classNames({'button-orange' : true, 'create-mix-btn' : true, 'disabled' : @state.creatingMixdown})
mixControls = `
<div key="create-mix" className="create-mix">
<p>Use the JamTrack controls on the session screen to set levels, mute/unmute, or pan any of the parts of the JamTrack as you like. You can also use the controls below to adjust the tempo or pitch of the JamTrack. Then give your custom mix a name, and click the Create Mix button. Please note that changing the tempo or pitch of the JamTrack may take a long time, and won't be ready right away.</p>
<div className="field">
<label>Change Tempo:</label>
<select name="mix-speed">
<option value="">No change</option>
<option value="separator-1">------------</option>
<option value="-5">Slower by 5%</option>
<option value="-10">Slower by 10%</option>
<option value="-15">Slower by 15%</option>
<option value="-20">Slower by 20%</option>
<option value="-25">Slower by 25%</option>
<option value="-30">Slower by 30%</option>
<option value="-35">Slower by 35%</option>
<option value="-40">Slower by 40%</option>
<option value="-45">Slower by 45%</option>
<option value="-50">Slower by 50%</option>
<option value="-60">Slower by 60%</option>
<option value="-70">Slower by 70%</option>
<option value="-80">Slower by 80%</option>
<option value="separator-2">------------</option>
<option value="5">Faster by 5%</option>
<option value="10">Faster by 10%</option>
<option value="15">Faster by 15%</option>
<option value="20">Faster by 20%</option>
<option value="30">Faster by 30%</option>
<option value="40">Faster by 40%</option>
<option value="50">Faster by 50%</option>
</select>
</div>
<div className="field">
<label>Change Pitch:</label>
<select name="mix-pitch">
<option value="">No change</option>
<option value="separator-1">---------------</option>
<option value="-1">Down 1 Semitone</option>
<option value="-2">Down 2 Semitones</option>
<option value="-3">Down 3 Semitones</option>
<option value="-4">Down 4 Semitones</option>
<option value="-5">Down 5 Semitones</option>
<option value="-6">Down 6 Semitones</option>
<option value="-7">Down 7 Semitones</option>
<option value="-8">Down 8 Semitones</option>
<option value="-9">Down 9 Semitones</option>
<option value="-10">Down 10 Semitones</option>
<option value="-11">Down 11 Semitones</option>
<option value="-12">Down 12 Semitones</option>
<option value="separator-2">---------------</option>
<option value="1">Up 1 Semitone</option>
<option value="2">Up 2 Semitones</option>
<option value="3">Up 3 Semitones</option>
<option value="4">Up 4 Semitones</option>
<option value="5">Up 5 Semitones</option>
<option value="6">Up 6 Semitones</option>
<option value="7">Up 7 Semitones</option>
<option value="8">Up 8 Semitones</option>
<option value="9">Up 9 Semitones</option>
<option value="10">Up 10 Semitones</option>
<option value="11">Up 11 Semitones</option>
<option value="12">Up 12 Semitones</option>
</select>
</div>
<div className={classNames(nameClassData)}>
<label>Mix Name:</label>
<input type="text" name="mix-name"/>
</div>
<div className="field">
<a className={createMixClasses} onClick={this.createMix}>CREATE MIX</a>
{errorHtml}
</div>
<div className="clearall"/>
</div>`
if @state.showMyMixes
showMyMixesText = `<a onClick={this.toggleMyMixes}>hide my mixes <div className="details-arrow arrow-up" /></a>`
else
showMyMixesText = `<a onClick={this.toggleMyMixes}>show my mixes <div className="details-arrow arrow-down" /></a>`
if @state.showCustomMixes
showMixControlsText = `<a onClick={this.toggleCustomMixes}>hide mix controls <div className="details-arrow arrow-up" /></a>`
else
showMixControlsText = `<a onClick={this.toggleCustomMixes}>show mix controls <div className="details-arrow arrow-down" /></a>`
extraControls = `
<div className="extra-controls">
<h4>My Mixes {showMyMixesText}</h4>
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{myMixes}
</ReactCSSTransitionGroup>
<h4 className="custom-mix-header">Create Custom Mix {showMixControlsText}</h4>
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{mixControls}
</ReactCSSTransitionGroup>
</div>`
else if @state.media.mediaSummary.backingTrackOpen
mediaType = "Audio File"
@ -444,14 +467,14 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged'))
time = enqueued.queue_time
if time == 0
alert("It will take approximately 1 minute to create your custom mix.")
alert("Your custom mix will take about 1 minute to be created.")
else
guess = Math.ceil(time / 60.0)
if guess == 1
msg = '1 minute'
else
msg = "#{guess} minutes"
alert("Your custom mix will take approximately #{msg} to be created.")
alert("Your custom mix will take about #{msg} to be created.")
createMix: (e) ->

View File

@ -69,6 +69,7 @@ MIX_MODES = context.JK.MIX_MODES;
backingTracks = @session.backingTracks()
recordedJamTracks = @session.recordedJamTracks()
jamTracks = @session.jamTracks()
jamTrackMixdown = @session.jamTrackMixdown()
###
with mixer info, we use these to decide what kind of tracks are open in the backend
@ -92,6 +93,7 @@ MIX_MODES = context.JK.MIX_MODES;
@metronomeTrackMixers = []
@adhocTrackMixers = []
groupByType = (mixers, isLocalMixer) =>
for mixer in mixers
mediaType = mixer.media_type
@ -106,7 +108,10 @@ MIX_MODES = context.JK.MIX_MODES;
isJamTrack = false;
if jamTracks
if mixer.id == jamTrackMixdown.id
isJamTrack = true;
if !isJamTrack && jamTracks
# check if the ID matches that of an open jam track
for jamTrack in jamTracks
if mixer.id == jamTrack.id
@ -186,6 +191,8 @@ MIX_MODES = context.JK.MIX_MODES;
backingTrackOpen: @backingTracks.length > 0
metronomeOpen: @session.isMetronomeOpen()
# figure out if any media is open
mediaOpenSummary = false
for mediaType, mediaOpen of @mediaSummary

View File

@ -22,6 +22,8 @@ if jam_instance == 0
exit 1
end
#Resque.redis = "localhost:6380"
# now bring in the Jam code
require 'jam_websockets'

View File

@ -147,6 +147,7 @@ module JamWebsockets
# subscribe for any messages to users
@user_topic.subscribe(:ack => false) do |headers, msg|
time_it('user_topic') {
begin
routing_key = headers.routing_key
user_id = routing_key["user.".length..-1]
@ -175,6 +176,7 @@ module JamWebsockets
@log.error "unhandled error in messaging to client"
@log.error e
end
}
end
MQRouter.user_exchange = @users_exchange
@ -189,6 +191,7 @@ module JamWebsockets
# subscribe for any p2p messages to a client
@client_topic.subscribe(:ack => false) do |headers, msg|
time_it('p2p_topic') {
begin
routing_key = headers.routing_key
client_id = routing_key["client.".length..-1]
@ -240,6 +243,7 @@ module JamWebsockets
@log.error "unhandled error in messaging to client"
@log.error e
end
}
end
MQRouter.client_exchange = @clients_exchange
@ -252,6 +256,7 @@ module JamWebsockets
# subscribe for any p2p messages to a client
@subscription_topic.subscribe(:ack => false) do |headers, msg|
time_it('subscribe_topic') {
begin
routing_key = headers.routing_key
type_and_id = routing_key["subscription.".length..-1]
@ -276,6 +281,7 @@ module JamWebsockets
@log.error "unhandled error in messaging to client for mount"
@log.error e
end
}
end
MQRouter.subscription_exchange = @subscriptions_exchange
@ -397,8 +403,10 @@ module JamWebsockets
}
client.onclose {
@log.debug "connection closed. marking stale: #{client.context}"
cleanup_client(client)
time_it('ws_close') {
@log.debug "connection closed. marking stale: #{client.context}"
cleanup_client(client)
}
}
client.onerror { |error|
@ -478,6 +486,7 @@ module JamWebsockets
end
def route(client_msg, client)
time_it('route') {
message_type = @message_factory.get_message_type(client_msg)
if message_type.nil?
Diagnostic.unknown_message_type(client.user_id, client_msg)
@ -516,7 +525,7 @@ module JamWebsockets
else
raise SessionError, "client_msg.route_to is unknown type: #{client_msg.route_to}"
end
}
end
def handle_server_directed(client_msg, client)
@ -529,11 +538,11 @@ module JamWebsockets
elsif client_msg.type == ClientMessage::Type::HEARTBEAT
sane_logging { handle_heartbeat(client_msg.heartbeat, client_msg.message_id, client) }
elsif client_msg.type == ClientMessage::Type::SUBSCRIBE_BULK
sane_logging { handle_bulk_subscribe(client_msg.subscribe_bulk, client) }
time_it('subscribe_bulk') { sane_logging { handle_bulk_subscribe(client_msg.subscribe_bulk, client) } }
elsif client_msg.type == ClientMessage::Type::SUBSCRIBE
sane_logging { handle_subscribe(client_msg.subscribe, client) }
time_it('subscribe') { sane_logging { handle_subscribe(client_msg.subscribe, client) } }
elsif client_msg.type == ClientMessage::Type::UNSUBSCRIBE
sane_logging { handle_unsubscribe(client_msg.unsubscribe, client) }
time_it('unsubscribe') { sane_logging { handle_unsubscribe(client_msg.unsubscribe, client) } }
else
raise SessionError, "unknown message type '#{client_msg.type}' for #{client_msg.route_to}-directed message"
end
@ -1198,6 +1207,7 @@ module JamWebsockets
# removes all resources associated with a client
def cleanup_client(client)
time_it('cleanup_client') {
client.close
# unregister any subscriptions
@ -1225,6 +1235,7 @@ module JamWebsockets
end
end
end
}
end
def stats_logged_in
@ -1245,6 +1256,16 @@ module JamWebsockets
private
def time_it(cat, &blk)
start = Time.now
blk.call
time = Time.now - start
@log.warn("LONG TIME: #{cat}: #{time}") if time > 1
end
def sane_logging(&blk)
# used around repeated transactions that cause too much ActiveRecord::Base logging
begin

View File

@ -10,6 +10,8 @@ module JamWebsockets
@count=0
@router = Router.new
@ar_base_logger = ::Logging::Repository.instance[ActiveRecord::Base]
@last_conn_check = nil
end
def run(options={})
@ -26,6 +28,7 @@ module JamWebsockets
rabbitmq_port = options[:rabbitmq_port].to_i
allow_dynamic_registration = options[:allow_dynamic_registration].nil? ? true : options[:allow_dynamic_registration]
Stats::init(options)
calling_thread = options[:calling_thread]
@ -61,6 +64,18 @@ module JamWebsockets
EventMachine::stop_event_loop
end
def check_for_em_drift(timer)
# if our timer check is a full second off, say what's up
if Time.now - @last_conn_check > timer + 1
@log.error("significant drift! Should be 2 seconds. Instead was: #{Time.now - @last_conn_check}")
end
@last_conn_check = Time.now
end
def start_websocket_listener(listen_ip, port, trust_port, trust_check, emwebsocket_debug)
EventMachine::WebSocket.run(:host => listen_ip, :port => port, :debug => emwebsocket_debug) do |ws|
#@log.info "new client #{ws}"
@ -84,8 +99,11 @@ module JamWebsockets
# one cleanup on startup
@router.periodical_check_connections
EventMachine::PeriodicTimer.new(2) do
safety_net { sane_logging { @router.periodical_check_connections } }
@last_conn_check = Time.now
timer = 2
EventMachine::PeriodicTimer.new(timer) do
check_for_em_drift(timer)
time_it('conn_expire') { safety_net { sane_logging { @router.periodical_check_connections } } }
end
end
@ -94,7 +112,7 @@ module JamWebsockets
@router.periodical_check_clients
EventMachine::PeriodicTimer.new(30) do
safety_net { sane_logging { @router.periodical_check_clients } }
time_it('client_expire') { safety_net { sane_logging { @router.periodical_check_clients } } }
end
end
@ -103,13 +121,13 @@ module JamWebsockets
@router.periodical_flag_connections
EventMachine::PeriodicTimer.new(2) do
safety_net { sane_logging { @router.periodical_flag_connections } }
time_it('conn_flagger') { safety_net { sane_logging { @router.periodical_flag_connections } } }
end
end
def start_stats_dump
EventMachine::PeriodicTimer.new(60) do
safety_net { @router.periodical_stats_dump }
time_it('stats_dump') { safety_net { @router.periodical_stats_dump } }
end
end
@ -126,6 +144,18 @@ module JamWebsockets
@log.error("unhandled exception in EM Timer #{e}")
end
end
def time_it(cat, &blk)
start = Time.now
blk.call
time = Time.now - start
@log.warn("LONG TIME #{cat}: #{time}") if time > 1
end
def sane_logging(&blk)
# used around repeated transactions that cause too much ActiveRecord::Base logging
# example is handling heartbeats