$ = 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) } )