jam-cloud/web/app/assets/javascripts/react-components/stores/JamTrackStore.js.coffee

553 lines
19 KiB
CoffeeScript

$ = jQuery
context = window
logger = context.JK.logger
rest = context.JK.Rest()
EVENTS = context.JK.EVENTS
JamTrackActions = @JamTrackActions
@JamTrackStore = Reflux.createStore(
{
listenables: [JamTrackActions, JamTrackMixdownActions]
jamTrack: null
previous: null
requestedSearch: null
requestedFilter: null
subscriptions: {}
enqueuedMixdowns: {}
init: ->
# Register with the app store to get @app
this.listenTo(context.AppStore, this.onAppInit)
onAppInit: (app) ->
@app = app
getState: () ->
@state
pickMyPackage: () ->
return unless @jamTrack?
for mixdown in @jamTrack.mixdowns
myPackage = null
for mixdown_package in mixdown.packages
if mixdown_package.file_type == 'ogg' && mixdown_package.encrypt_type == 'jkz' && mixdown_package.sample_rate == @sampleRate
myPackage = mixdown_package
break
mixdown.myPackage = myPackage
subscriptionKey: (mixdown_package) ->
"mixdown-#{mixdown_package.id}"
subscribe: (mixdown_package) ->
key = @subscriptionKey(mixdown_package)
if !@watchedMixdowns[key]?
# we need to register
context.JK.SubscriptionUtils.subscribe('mixdown', mixdown_package.id).on(context.JK.EVENTS.SUBSCRIBE_NOTIFICATION, this.onMixdownSubscriptionEvent)
@watchedMixdowns[key] = {type:'mixdown', id: mixdown_package.id}
unsubscribe: (mixdown_package) ->
key = @subscriptionKey(mixdown_package)
if @watchedMixdowns[key]?
context.JK.SubscriptionUtils.unsubscribe('mixdown', mixdown_package.id)
delete @watchedMixdowns[key]
manageWatchedMixdowns: () ->
if @jamTrack?
for mixdown in @jamTrack.mixdowns
if mixdown.myPackage
if mixdown.myPackage.signing_state == 'SIGNED'
@unsubscribe(mixdown.myPackage)
else
@subscribe(mixdown.myPackage)
else
for key, subscription of @watchedMixdowns
logger.debug("unsubscribing bulk", key, subscription)
context.JK.SubscriptionUtils.unsubscribe(subscription.type, subscription.id)
# we cleared them all out; clear out storage
@watchedMixdowns = {}
onMixdownSubscriptionEvent: (e, data) ->
logger.debug("JamTrackStore: subscription notification received: type:" + data.type, data)
return unless @jamTrack?
mixdown_package_id = data.id
for mixdown in @jamTrack.mixdowns
for mixdown_package in mixdown.packages
if mixdown_package.id == mixdown_package_id
mixdown_package.signing_state = data.body.signing_state
mixdown_package.packaging_steps = data.body.packaging_steps
mixdown_package.current_packaging_step = data.body.current_packaging_step
logger.debug("updated package with subscription notification event")
if mixdown_package.signing_state == 'SIGNING_TIMEOUT' || mixdown_package.signing_state == 'QUEUED_TIMEOUT' || mixdown_package.signing_state == 'QUIET_TIMEOUT' || mixdown_package.signing_state == 'ERROR'
@reportError(mixdown)
@changed()
break
# this drives the state engine required to get a Mixdown from 'available on the server' to
manageMixdownSynchronization: () ->
@jamTrack.activeMixdown = null if @jamTrack
# let's see if we have a mixdown active?
if !@jamTrack?.last_mixdown_id?
logger.debug("JamTrackStore: no mixdown active")
@clearMixdownTimers()
return
for mixdown in @jamTrack.mixdowns
if mixdown.id == @jamTrack.last_mixdown_id
@jamTrack.activeMixdown = mixdown
logger.debug("JamTrackStore: mixdown active:", mixdown)
break
if @jamTrack.activeMixdown?
# if we don't have this on the server yet, don't engage the rest of this logic...
return if @jamTrack.activeMixdown?.myPackage?.signing_state != 'SIGNED'
fqId = "#{@jamTrack.id}_#{@jamTrack.activeMixdown.id}-#{@sampleRate}"
@trackDetail = context.jamClient.JamTrackGetTrackDetail (fqId)
logger.debug("JamTrackStore: JamTrackGetTrackDetail(#{fqId}).key_state: " + @trackDetail.key_state, @trackDetail)
# first check if the version is not the same; if so, invalidate.
if @trackDetail.version? && @jamTrack.activeMixdown.myPackage?
if @jamTrack.activeMixdown.myPackage.version != @trackDetail.version
logger.info("JamTrackStore: JamTrack Mixdown on disk is different version (stored: #{@trackDetail.version}, server: #{@jamTrack.activeMixdown.myPackage.version}. Invalidating")
context.jamClient.InvalidateJamTrack(fqId)
@trackDetail = context.jamClient.JamTrackGetTrackDetail (fqId)
if @trackDetail.version?
logger.error("after invalidating package, the version is still wrong!")
throw "after invalidating package, the version is still wrong!"
if @jamTrack.activeMixdown.client_state == 'cant_open'
logger.debug(" skipping state check because of earlier 'cant_open'. user should hit retry. ")
return
if @jamTrack.activeMixdown.client_state == 'download_fail'
logger.debug("skipping state check because of earlier 'download_fail'. user should hit retry. ")
return
if @jamTrack.activeMixdown.client_state == 'downloading'
logger.debug("skipping state check because we are downloading")
switch @trackDetail.key_state
when 'pending'
@attemptKeying()
when 'not authorized'
# TODO: if not authorized, do we need to re-initiate a keying attempt?
@attemptKeying()
when 'ready'
if @jamTrack.activeMixdown.client_state != 'ready'
@clearMixdownTimers()
@jamTrack.activeMixdown.client_state = 'ready'
# now load it:
# JamTrackPlay means 'load'
logger.debug("JamTrackStore: loading mixdown")
context.jamClient.JamTrackStopPlay();
result = context.jamClient.JamTrackPlay(fqId);
if !result
@jamTrack.activeMixdown.client_state = 'cant_open'
@reportError(@jamTrack.activeMixdown)
@app.notify(
{
title: "Mixdown Can Not Open",
text: "Unable to open your JamTrack Mixdown. Please contact support@jamkazam.com"
}
, null, true)
when 'unknown'
# we need to check if @keyCheckTimeout exists; because if it does, we don't want to download while keying.
# 'unknown' is tricky here because the file probably is actually on disk, but the bridge API can say unknown until you've tried to key at least once
if @jamTrack.activeMixdown.client_state != 'downloading' && !@keyCheckTimeout?
@jamTrack.activeMixdown.client_state = 'downloading'
logger.debug("JamTrackStore: initiating download of mixdown")
context.jamClient.JamTrackDownload(@jamTrack.id, @jamTrack.activeMixdown.id, context.JK.currentUserId,
this.makeDownloadProgressCallback(),
this.makeDownloadSuccessCallback(),
this.makeDownloadFailureCallback())
else
logger.debug("JamTrackStore: already downloading")
attemptKeying: () ->
if @keyCheckTimeout?
logger.debug("JamTrackStore: attemptKeying: skipping because already keying")
return
else if @jamTrack.activeMixdown.client_state == 'keying_timeout'
# if we have timed out keying, we shouldn't automatically retry
logger.debug("JamTrackStore: attempKeying: skipping because we have timed out before and user hasn't requested RETRY")
return
else
@keyCheckTimeout = setTimeout(@onKeyCheckTimeout, 10000)
@keyCheckoutInterval = setInterval(@checkOnKeying, 1000)
@jamTrack.activeMixdown.client_state = 'keying'
logger.debug("JamTrackStore: initiating keying requested")
context.jamClient.JamTrackKeysRequest()
onKeyCheckTimeout: () ->
@keyCheckTimeout = null
clearInterval(@keyCheckoutInterval)
@keyCheckoutInterval = null
if @jamTrack?.activeMixdown?
@jamTrack.activeMixdown.client_state = 'keying_timeout'
@reportError(@jamTrack.activeMixdown)
@changed()
checkOnKeying: () ->
@manageMixdownSynchronization()
# if we exit keying state, we can clear our timers and poke state
if @jamTrack.activeMixdown.client_state != 'keying'
@clearMixdownTimers()
@changed()
# clear out any timer/watcher stuff
clearMixdownTimers: () ->
logger.debug("JamTrackStore: clearing mixdown timers", @keyCheckTimeout, @keyCheckoutInterval)
clearTimeout(@keyCheckTimeout) if @keyCheckTimeout?
clearInterval(@keyCheckoutInterval) if @keyCheckoutInterval?
@keyCheckTimeout = null
@keyCheckoutInterval = null
changed: () ->
@pickMyPackage()
@manageWatchedMixdowns()
@manageMixdownSynchronization()
@state = {
jamTrack: @jamTrack,
opened: @previous == null && @jamTrack != null,
closed: @previous != null && @jamTrack == null,
fullTrackActivated: @previousMixdown != null && @jamTrack?.activeMixdown == null}
@previous = @jamTrack
@previousMixdown = @jamTrack?.activeMixdown
this.trigger(@state)
onOpen: (jamTrack) ->
if @jamTrack?
@app.notify({text: 'Unable to open JamTrack because another one is already open.'})
return
@enqueuedMixdowns = {}
@jamTrack = jamTrack
# we can cache this because you can't switch gear while in a session (and possible change sample rate!)
sampleRate = context.jamClient.GetSampleRate()
@sampleRate = if sampleRate == 48 then 48 else 44
@changed()
onClose: () ->
@jamTrack = null
@changed()
onRequestSearch:(searchType, searchData) ->
@requestedSearch = {searchType: searchType, searchData: searchData}
window.location.href = '/client#/jamtrack/search'
# needed by the JamTrackSearchScreen
checkRequestedSearch:() ->
requested = @requestedSearch
@requestedSearch = null
requested
onRequestFilter:(genre, instrument) ->
@requestedFilter = {genre: genre, instrument:instrument}
window.location.href = '/client#/jamtrack/filter'
# needed by the JamTrackSearchScreen
checkRequestedFilter:() ->
requested = @requestedFilter
@requestedFilter = null
requested
onCreateMixdown: (mixdown, done, fail) ->
volumeSettings = context.jamClient.GetJamTrackSettings();
track_settings = []
for track in volumeSettings.tracks
track_settings.push({id: track.id, pan: track.pan, vol: track.vol_l, mute: track.mute})
mixdown.settings.tracks = track_settings
logger.debug("creating mixdown", mixdown)
rest.createMixdown(mixdown)
.done((created) =>
@addMixdown(created)
logger.debug("created mixdown", created)
@onEnqueueMixdown({id: created.id}, done, fail)
)
.fail((jqxhr) =>
fail(jqxhr)
)
onEditMixdown: (mixdown) ->
logger.debug("editing mixdown", mixdown)
rest.editMixdown(mixdown)
.done((updatedMixdown) =>
logger.debug("edited mixdown")
@updateMixdown(updatedMixdown)
).fail((jqxhr) =>
@app.layout.notify({title:'Unable to Edit Custom Mix', text: 'The server was unable to edit this mix.'})
)
onDeleteMixdown: (mixdown) ->
logger.debug("deleting mixdown", mixdown)
rest.deleteMixdown(mixdown)
.done(() =>
logger.debug("deleted mixdown")
@deleteMixdown(mixdown)
)
.fail((jqxhr) =>
@app.layout.notify({title:'Unable to Deleted Custom Mix', text: 'The server was unable to delete this mix.'})
)
onOpenMixdown: (mixdown) ->
logger.debug("opening mixdown", mixdown)
# check if it's already available in the backend or not
rest.markMixdownActive({id: @jamTrack.id, mixdown_id: mixdown.id})
.done((edited) =>
logger.debug("marked mixdown as active")
@jamTrack = edited
# unload any currently loaded JamTrack
context.jamClient.JamTrackStopPlay();
@changed()
SessionActions.mixdownActive(mixdown)
)
.fail((jqxhr) =>
@app.layout.notify({title:'Unable to Edit Mixdown', text: 'Unable to mark this mixdown as active.'})
)
onActivateNoMixdown: (jamTrack) ->
logger.debug("activating no mixdown")
rest.markMixdownActive({id: @jamTrack.id, mixdown_id: null})
.done((edited) =>
logger.debug("marked JamTrack as active")
@jamTrack = edited
@changed()
SessionActions.mixdownActive({id:null})
)
.fail((jqxhr) =>
@app.layout.notify({title:'Unable to Edit Mixdown', text: 'Unable to mark this mixdown as active.'})
)
onCloseMixdown: (mixdown) ->
logger.debug("closing mixdown", mixdown)
onEnqueueMixdown: (mixdown, done, fail) ->
logger.debug("enqueuing mixdown", mixdown)
package_settings = {file_type: 'ogg', encrypt_type: 'jkz', sample_rate: @sampleRate}
package_settings.id = mixdown.id
rest.enqueueMixdown(package_settings)
.done((enqueued) =>
@enqueuedMixdowns[mixdown.id] = {}
logger.debug("enqueued mixdown package", package_settings)
@addOrUpdatePackage(enqueued)
done(enqueued) if done
)
.fail((jqxhr) =>
@app.layout.notify({title:'Unable to Create Custom Mix', text: 'Click the error icon to retry.'})
fail(jqxhr) if fail?
)
onDownloadMixdown: (mixdown) ->
logger.debug("download mixdown", mixdown)
onOpenDownloader: (mixdown) ->
logger.debug("open mixdown dowloader", mixdown)
window.open("/popups/jamtrack/download/#{mixdown.jam_track_id}/mixdowns/#{mixdown.id}", 'Mixdown Downloader', 'scrollbars=yes,toolbar=no,status=no,height=171,width=340')
onRefreshMixdown: (mixdown) ->
logger.debug("refresh mixdown", mixdown)
addMixdown: (mixdown) ->
if @jamTrack?
logger.debug("adding mixdown to JamTrackStore", mixdown)
@jamTrack.mixdowns.splice(0, 0, mixdown)
@changed()
else
logger.warn("no jamtrack to add mixdown to in JamTrackStore", mixdown)
deleteMixdown: (mixdown) ->
if @jamTrack?
logger.debug("deleting mixdown from JamTrackStore", mixdown)
index = null
for matchMixdown, i in @jamTrack.mixdowns
if mixdown.id == matchMixdown.id
index = i
if index?
@jamTrack.mixdowns.splice(index, 1)
if @jamTrack.activeMixdown?.id == mixdown.id
@onActivateNoMixdown(@jamTrack)
@changed()
else
logger.warn("unable to find mixdown to delete in JamTrackStore", mixdown)
else
logger.warn("no jamtrack to delete mixdown for in JamTrackStore", mixdown)
updateMixdown: (mixdown) ->
if @jamTrack?
logger.debug("editing mixdown from JamTrackStore", mixdown)
index = null
for matchMixdown, i in @jamTrack.mixdowns
if mixdown.id == matchMixdown.id
index = i
if index?
@jamTrack.mixdowns[index] = mixdown
@changed()
else
logger.warn("unable to find mixdown to edit in JamTrackStore", mixdown)
else
logger.warn("no jamtrack to edit mixdown for in JamTrackStore", mixdown)
addOrUpdatePackage: (mixdown_package) ->
if @jamTrack?
added = false
index = null
for mixdown in @jamTrack.mixdowns
existing = false
if mixdown_package.jam_track_mixdown_id == mixdown.id
for possiblePackage, i in mixdown.packages
if possiblePackage.id == mixdown_package.id
existing = true
index = i
break
if existing
mixdown.packages[index] = mixdown_package
logger.debug("replacing mixdown package in JamTrackStore", mixdown_package)
else
mixdown.packages.splice(0, 0, mixdown_package)
logger.debug("adding mixdown package in JamTrackStore")
added = true
@changed()
break
if !added
logger.debug("couldn't find the mixdown associated with package in JamTrackStore", mixdown_package)
else
logger.warn("no mixdown to add package to in JamTrackStore", mixdown_package)
updateDownloadProgress: () ->
if @bytesReceived? and @bytesTotal?
progress = "#{Math.round(@bytesReceived/@bytesTotal * 100)}%"
else
progress = '0%'
#@root.find('.state-downloading .progress').text(progress)
downloadProgressCallback: (bytesReceived, bytesTotal) ->
logger.debug("download #{bytesReceived}/#{bytesTotal}")
@bytesReceived = Number(bytesReceived)
@bytesTotal = Number(bytesTotal)
# the reason this timeout is set is because, without it,
# we observe that the client will hang. So, if you remove this timeout, make sure to test with real client
setTimeout(this.updateDownloadProgress, 100)
downloadSuccessCallback: (updateLocation) ->
# is the package loadable yet?
logger.debug("JamTrackStore: download complete - on to keying")
@attemptKeying()
@changed()
downloadFailureCallback: (errorMsg) ->
logger.debug("mixdown download failed", errorMsg);
if @jamTrack?.activeMixdown?
@jamTrack.activeMixdown.client_state = 'download_fail'
@reportError(@jamTrack.activeMixdown)
@changed()
# makes a function name for the backend
makeDownloadProgressCallback: () ->
"JamTrackStore.downloadProgressCallback"
# makes a function name for the backend
makeDownloadSuccessCallback: () ->
"JamTrackStore.downloadSuccessCallback"
# makes a function name for the backend
makeDownloadFailureCallback: () ->
"JamTrackStore.downloadFailureCallback"
reportError: (mixdown) ->
enqueued = @enqueuedMixdowns[mixdown?.id]
# don't double-report
if !enqueued? || enqueued.marked
return
enqueued.marked = true
data = {
value: 1,
user_id: context.JK.currentUserId,
user_name: context.JK.currentUserName,
result: "signing state: #{mixdown.myPackage?.signing_state}, client state: #{mixdown.client_state}",
mixdown: mixdown.id,
package: mixdown.myPackage?.id
detail: mixdown.myPackage?.error_reason
}
rest.createAlert("Mixdown Sync failed for #{context.JK.currentUserName}", data)
context.stats.write('web.mixdown.error', data)
}
)