From b107df247853fffff5f2b3b058509f479a439d6a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 17 Sep 2015 17:31:00 -0500 Subject: [PATCH] * fix bugs --- ruby/lib/jam_ruby/models/jam_track_mixdown.rb | 22 +- .../resque/jam_track_mixdown_packager.rb | 74 +++-- .../javascripts/download_jamtrack.js.coffee | 2 +- web/app/assets/javascripts/fakeJamClient.js | 4 + .../assets/javascripts/react-components.js | 2 +- .../MediaControls.js.jsx.coffee | 12 +- .../PopupMediaControls.js.jsx.coffee | 160 ++++++++-- .../SessionMediaTracks.js.jsx.coffee | 17 +- .../SessionTrackVolumeHover.js.jsx.coffee | 5 +- .../actions/JamTrackActions.js.coffee | 1 + .../actions/SessionActions.js.coffee | 1 + .../helpers/MixerHelper.js.coffee | 117 +++++--- .../helpers/SessionHelper.js.coffee | 3 + .../mixins/SessionMediaTracksMixin.js.coffee | 1 + .../stores/JamTrackStore.js.coffee | 284 +++++++++++++++++- .../stores/SessionStore.js.coffee | 6 + .../minimal/media_controls.css.scss | 19 ++ .../api_jam_track_mixdowns_controller.rb | 21 +- .../controllers/api_jam_tracks_controller.rb | 34 ++- .../api_jam_track_mixdowns/show_package.rabl | 2 +- web/app/views/api_jam_tracks/keys.rabl | 28 ++ web/app/views/api_music_sessions/show.rabl | 6 + web/app/views/clients/_help.html.slim | 3 + web/config/routes.rb | 1 + 24 files changed, 712 insertions(+), 113 deletions(-) diff --git a/ruby/lib/jam_ruby/models/jam_track_mixdown.rb b/ruby/lib/jam_ruby/models/jam_track_mixdown.rb index f1841e365..3c6a30bc8 100644 --- a/ruby/lib/jam_ruby/models/jam_track_mixdown.rb +++ b/ruby/lib/jam_ruby/models/jam_track_mixdown.rb @@ -7,7 +7,7 @@ module JamRuby belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track belongs_to :jam_track, class_name: "JamRuby::JamTrack" - has_many :jam_track_mixdown_packages, class_name: "JamRuby::JamTrackMixdownPackage" + has_many :jam_track_mixdown_packages, class_name: "JamRuby::JamTrackMixdownPackage", order: 'created_at DESC' has_one :jam_track_right, class_name: 'JamRuby::JamTrackRight', foreign_key: 'last_mixdown_id', inverse_of: :last_mixdown validates :name, presence: true, length: {maximum: 100} @@ -19,6 +19,7 @@ module JamRuby validates_uniqueness_of :name, scope: :user_id validate :verify_settings + validate :verify_max_mixdowns def self.index(params, user) jam_track_id = params[:id] @@ -38,11 +39,19 @@ module JamRuby end end + def verify_max_mixdowns + if self.jam_track && self.user && self.jam_track.mixdowns_for_user(self.user).length >= 5 + errors.add(:jam_track, 'allowed 5 mixes') + end + end + def verify_settings # the user has to specify at least at least one tweak to volume, speed, pitch, pan. otherwise there is nothing to do tweaked = false + all_quiet = true + parsed = JSON.parse(self.settings) if parsed["speed"] tweaked = true @@ -50,6 +59,8 @@ module JamRuby if parsed["pitch"] tweaked = true end + + if parsed["tracks"] parsed["tracks"].each do |track| if track["mute"] @@ -61,8 +72,17 @@ module JamRuby if track["pan"] && track["pan"] != 0 tweaked = true end + + # there is at least one track with volume specified. + if !track["mute"] && track["vol"] != 0 + all_quiet = false + end end end + + if all_quiet + errors.add(:settings, 'are all muted') + end if !tweaked errors.add(:settings, 'have nothing specified') end diff --git a/ruby/lib/jam_ruby/resque/jam_track_mixdown_packager.rb b/ruby/lib/jam_ruby/resque/jam_track_mixdown_packager.rb index 7cb790360..c52db8f6c 100644 --- a/ruby/lib/jam_ruby/resque/jam_track_mixdown_packager.rb +++ b/ruby/lib/jam_ruby/resque/jam_track_mixdown_packager.rb @@ -114,12 +114,14 @@ module JamRuby vol = 1.0 pan = 0 match = false + skipped = false # is this stem in the altered_tracks list? altered_tracks.each do |alteration| if alteration["id"] == stem.id if alteration["mute"] || alteration["vol"] == 0 - log.debug("leaving out track because muted or 0 volume") + log.debug("leaving out track because muted or 0 volume #{alteration.inspect}") + skipped = true next else vol = alteration["vol"] || vol @@ -131,7 +133,8 @@ module JamRuby end end - unless match + # if we didn't deliberately skip this one, and if there was no 'match' (meaning user did not specify), then we leave this in unchanged + if !skipped && !match @track_settings << {stem:stem, vol:vol, pan:pan} end end @@ -146,7 +149,7 @@ module JamRuby # k = f(i) = (i)/(2*MAX_PAN) + 0.5 # so f(MIN_PAN) = -0.5 + 0.5 = 0 - k = ((i * (1.0))/ (2.0 * MAX_PAN )) + 0.5 + k = ((pan * (1.0))/ (2.0 * MAX_PAN )) + 0.5 l, r = 0 if k == 0 @@ -165,6 +168,7 @@ module JamRuby puts @settings.inspect puts @track_count puts @track_settings + puts @track_settings.count Dir.mktmpdir do |tmp_dir| @@ -217,6 +221,8 @@ module JamRuby else pan_l, pan_r = slider_to_pan(track[:pan]) + vol = track[:vol] + # short channel_l = pan_l * vol channel_r = pan_r * vol @@ -227,7 +233,7 @@ module JamRuby volumed_file = File.join(tmp_dir, jam_track_track.id + '-volumed.ogg') - cmd("sox \"#{file}\" \"#{volumed_file}\" remix 1v#{channel_l} 2v#{channel_r}") + cmd("sox \"#{file}\" \"#{volumed_file}\" remix 1v#{channel_r} 2v#{channel_l}", 'vol_pan') track[:volumed_file] = volumed_file end @@ -239,21 +245,30 @@ module JamRuby bump_step(@mixdown_package) - # sox -m will divide by number of inputs by default. But we purposefully leave out tracks that are mute/no volume (to save downloading/processing time in this job) - # so we need to tell sox to divide by how many tracks there are as a constant, because this is how the client works today - #sox -m -v 1/n file1 -v 1/n file2 out - cmd = "sox -m" - mix_divide = 1.0/@track_count - @track_settings.each do |track| - volumed_file = track[:volumed_file] - cmd << " -v #{mix_divide} \"#{volumed_file}\"" + @mix_file = File.join(tmp_dir, "mix.ogg") + + # if there is only one track to mix, we need to skip mixing (sox will barf if you try to mix one file), but still divide by number of tracks + if @track_settings.count == 1 + mix_divide = 1.0/@track_count + cmd = "sox -v #{mix_divide} \"#{@track_settings[0][:volumed_file]}\" \"#{@mix_file}\"" + cmd(cmd, 'volume_adjust') + else + # sox -m will divide by number of inputs by default. But we purposefully leave out tracks that are mute/no volume (to save downloading/processing time in this job) + # so we need to tell sox to divide by how many tracks there are as a constant, because this is how the client works today + #sox -m -v 1/n file1 -v 1/n file2 out + cmd = "sox -m" + mix_divide = 1.0/@track_count + @track_settings.each do |track| + volumed_file = track[:volumed_file] + cmd << " -v #{mix_divide} \"#{volumed_file}\"" + end + + + cmd << " \"#{@mix_file}\"" + cmd(cmd, 'mix_adjust') end - @mix_file = File.join(tmp_dir, "mix.ogg") - - cmd << " \"#{@mix_file}\"" - cmd(cmd) end @@ -335,7 +350,7 @@ module JamRuby raise 'unknown file_type' if @mixdown_package.file_type != JamTrackMixdownPackage::FILE_TYPE_AAC - cmd("ffmpeg -i \"#{@speed_mix_file}\" -c:a libfdk_aac -b:a 128k \"#{output}\"") + cmd("ffmpeg -i \"#{@speed_mix_file}\" -c:a libfdk_aac -b:a 128k \"#{output}\"", 'convert_aac') output end end @@ -357,6 +372,25 @@ module JamRuby py_file = File.join(py_root, "jkcreate.py") version = @mixdown_package.version + right = @mixdown.jam_track.right_for_user(@mixdown.user) + + if @mixdown_package.sample_rate == 48 + private_key = right.private_key_48 + else + private_key = right.private_key_44 + end + + unless private_key + @error_reason = 'no_private_key' + @error_detail = 'user needs to generate JamTrack for given sample rate' + raise @error_reason + end + + private_key_file = File.join(tmp_dir, 'skey.pem') + File.open(private_key_file, 'w') {|f| f.write(private_key) } + + log.debug("PRIVATE KEY") + log.debug(private_key) log.info "Executing python source in #{py_file}, outputting to #{tmp_dir} (#{output})" cli = "python #{py_file} -D -k #{sku} -p #{Shellwords.escape(tmp_dir)}/pkey.pem -s #{Shellwords.escape(tmp_dir)}/skey.pem #{jam_file_opts} -o #{Shellwords.escape(output)} -t #{Shellwords.escape(title)} -V #{Shellwords.escape(version)}" @@ -370,12 +404,12 @@ module JamRuby raise ArgumentError, "Error calling python script: #{err}" if err.present? raise ArgumentError, "Error calling python script: #{out}" if out && (out.index("No track files specified") || out.index("Cannot find file")) - private_key = File.read("#{tmp_dir}/skey.pem") + private_key = File.read(private_key_file) end return output, private_key end - def cmd(cmd) + def cmd(cmd, type) log.debug("executing #{cmd}") @@ -386,6 +420,8 @@ module JamRuby if result_code == 0 output else + @error_reason = type + "_fail" + @error_detail = "#{cmd}, #{output}" raise "command `#{cmd}` failed." end end diff --git a/web/app/assets/javascripts/download_jamtrack.js.coffee b/web/app/assets/javascripts/download_jamtrack.js.coffee index 8890e6b87..c4727631d 100644 --- a/web/app/assets/javascripts/download_jamtrack.js.coffee +++ b/web/app/assets/javascripts/download_jamtrack.js.coffee @@ -190,7 +190,7 @@ context.JK.DownloadJamTrack = class DownloadJamTrack showDownloading: () => @logger.debug("showing #{@state.name}") # while downloading, we don't run the transition timer, because the download API is guaranteed to call success, or failure, eventually - context.jamClient.JamTrackDownload(@jamTrack.id, context.JK.currentUserId, + context.jamClient.JamTrackDownload(@jamTrack.id, null, context.JK.currentUserId, this.makeDownloadProgressCallback(), this.makeDownloadSuccessCallback(), this.makeDownloadFailureCallback()) diff --git a/web/app/assets/javascripts/fakeJamClient.js b/web/app/assets/javascripts/fakeJamClient.js index 8dfc2ace7..077ad4ced 100644 --- a/web/app/assets/javascripts/fakeJamClient.js +++ b/web/app/assets/javascripts/fakeJamClient.js @@ -505,6 +505,9 @@ return 0; } + function GetJamTrackSettings() { + return {tracks:[]} + } function SessionGetJamTracksPlayDurationMs() { return 60000; } @@ -1214,6 +1217,7 @@ this.TrackGetChatUsesMusic = TrackGetChatUsesMusic; this.TrackSetChatUsesMusic = TrackSetChatUsesMusic; + this.GetJamTrackSettings = GetJamTrackSettings; this.JamTrackStopPlay = JamTrackStopPlay; this.JamTrackPlay = JamTrackPlay; this.JamTrackIsPlayable = JamTrackIsPlayable; diff --git a/web/app/assets/javascripts/react-components.js b/web/app/assets/javascripts/react-components.js index 6b8ef7446..350dce7e9 100644 --- a/web/app/assets/javascripts/react-components.js +++ b/web/app/assets/javascripts/react-components.js @@ -1,6 +1,6 @@ //= require react-input-autosize //= require react-select -//= require react_rails_img +// //= require react_rails_img //= require_directory ./react-components/helpers //= require_directory ./react-components/actions //= require ./react-components/stores/AppStore diff --git a/web/app/assets/javascripts/react-components/MediaControls.js.jsx.coffee b/web/app/assets/javascripts/react-components/MediaControls.js.jsx.coffee index 34899a6cf..fed32bea6 100644 --- a/web/app/assets/javascripts/react-components/MediaControls.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/MediaControls.js.jsx.coffee @@ -38,9 +38,11 @@ mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged')) @state.controls.onPlayStopEvent() else if changes.playbackState == 'play_pause' @state.controls.onPlayPauseEvent(); - else if changes.positionUpdateChanged + if changes.positionUpdateChanged if @state.controls? @state.controls.executeMonitor(changes.positionMs, changes.durationMs, changes.isPlaying) + if changes.currentTimeChanged + @setState({time: changes.time}) onInputsChanged: (sessionMixers) -> @@ -69,8 +71,8 @@ mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged')) monitorControls: (controls, mediaSummary) -> - if mediaSummary.mediaOpen - if mediaSummary.jamTrackOpen + if mediaSummary.mediaOpen || mediaSummary.jamTrack? + if mediaSummary.jamTrack? controls.startMonitor(PLAYBACK_MONITOR_MODE.JAMTRACK) else if mediaSummary.backingTrackOpen controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE) @@ -163,7 +165,7 @@ mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged')) -
0:00
+
{this.state.time}
@@ -179,7 +181,7 @@ mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged')) getInitialState: () -> - {controls: null, mediaSummary: {}, initializedMetronomeControls: false} + {controls: null, mediaSummary: {}, initializedMetronomeControls: false, time: '0:00'} tryPrepareMetronome: (metro) -> if @state.mediaSummary.metronomeOpen && !@state.initializedMetronomeControls diff --git a/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee b/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee index 929496841..1794fea7d 100644 --- a/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee @@ -17,17 +17,16 @@ if window.opener? if accessOpener SessionActions = window.opener.SessionActions - MediaPlaybackStore = window.opener.MediaPlaybackStore MixerActions = window.opener.MixerActions + JamTrackActions = window.opener.JamTrackActions JamTrackMixdownActions = window.opener.JamTrackMixdownActions #JamTrackMixdownStore = window.opener.JamTrackMixdownStore JamTrackMixdown = window.opener.JamTrackMixdown JamTrackStore = window.opener.JamTrackStore MixerStore = window.opener.MixerStore + SessionStore = window.opener.SessionStore mixins.push(Reflux.listenTo(MixerStore, 'onMixersChanged')) -mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged')) -#mixins.push(Reflux.listenTo(JamTrackMixdownStore, 'onJamTrackMixdownChanged')) mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) @PopupMediaControls = React.createClass({ @@ -51,7 +50,7 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) recordingName: mixers.recordingName() jamTrackName: mixers.jamTrackName() - @setState(media: state) + @setState(media: state, downloadingJamTrack: session.downloadingJamTrack) onMediaStateChanged: (changes) -> if changes.currentTimeChanged && @root? @@ -70,7 +69,15 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) SessionActions.showNativeMetronomeGui() getInitialState: () -> - {media: @props.media, time: '0:00', mixdown: @props.mixdown, jamTrackState: @props.jamTrackState, creatingMixdown: false, createMixdownErrors: null} + { + media: @props.media, + mixdown: @props.mixdown, + jamTrackState: @props.jamTrackState, + creatingMixdown: false, + createMixdownErrors: null, + editingMixdownId: null, + downloadingJamTrack: @props.downloadingJamTrack + } close: () -> window.close() @@ -87,7 +94,7 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) mediaType = "Recording" mediaName = @state.media.recordedTracks[0].recordingName closeLinkText = 'close recording' - header = `

{mediaType}: {mediaName} ({this.state.time})

` + header = `

{mediaType}: {mediaName}

` else if @state.jamTrackState.jamTrack? jamTrack = @state.jamTrackState.jamTrack mediaType = "JamTrack" @@ -95,20 +102,41 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) closeLinkText = 'CLOSE JAMTRACK' - selectedMixdown = null - if jamTrack.last_mixdown_id - selectedMixdowns = jamTrack.mixdowns.filter((mixdown) -> jamTrack.last_mixdown_id == mixdown.id) - selectedMixdown = selectedMixdowns[0] if selectedMixdowns.length > 0 + selectedMixdown = jamTrack.activeMixdown + if selectedMixdown? jamTrackTypeHeader = 'Custom Mix' - customMixName = `
{selectedMixdown.name}
` + + disabled = true + if selectedMixdown.client_state? + switch selectedMixdown.client_state + when 'cant_open' + customMixName = `
L: {selectedMixdown.name}
` + when 'keying_timeout' + customMixName = `
K: {selectedMixdown.name}
` + when 'download_fail' + customMixName = `
D: {selectedMixdown.name}
` + when 'keying' + customMixName = `
K: Loading selected mix...
` + when 'downloading' + customMixName = `
D: Loading selected mix...
` + when 'ready' + customMixName = `
{selectedMixdown.name}
` + disabled = false + else + customMixName = `
Creating mixdown...
` + else - jamTrackTypeHeader = 'Full JamTrack' + logger.debug("STATE!", @state.downloadingJamTrack) + if SessionStore.downloadingJamTrack + downloader = `` + + jamTrackTypeHeader = `Full JamTrack {downloader}` header = `
-

{mediaType}: {mediaName} ({this.state.time})

+

{mediaType}: {mediaName}

{jamTrackTypeHeader}

{customMixName}
` @@ -116,19 +144,37 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) myMixes = null if @state.showMyMixes myMixdowns = [] + + boundPlayClick = this.jamTrackPlay.bind(this, jamTrack); + + active = jamTrack.last_mixdown_id == null + + myMixdowns.push ` +
+
+ Full JamTrack +
+
+ +
+
` + 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 - logger.debug("MY PACKAGE STATE", mixdown_package.signing_state) switch mixdown_package.signing_state when 'QUIET_TIMEOUT' action = `` @@ -149,22 +195,28 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) else action = `` + if editing + mixdownName = `` + editIcon = `` + else + mixdownName = mixdown.name + editIcon = `` myMixdowns.push `
- {mixdown.name} + {mixdownName}
{action} - + {editIcon} - +
` - myMixes = `
{myMixdowns}
` + myMixes = `
{myMixdowns}
` mixControls = null if @state.showCustomMixes @@ -172,7 +224,7 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) nameClassData = {field: true} if @state.createMixdownErrors? - errorHtml = context.JK.reactErrors(@state.createMixdownErrors, {name: 'Mix Name', settings: 'Settings'}) + 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 = ` @@ -278,7 +330,7 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) mediaType = "Audio File" mediaName = context.JK.getNameOfFile(@state.media.backingTracks[0].shortFilename) closeLinkText = 'CLOSE AUDIO FILE' - header = `

{mediaType}: {mediaName} ({this.state.time})

` + header = `

{mediaType}: {mediaName}

` extraControls = `
@@ -313,17 +365,57 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) toggleCustomMixes: (e) -> e.preventDefault() + @setState({showCustomMixes: !@state.showCustomMixes}) mixdownPlay: (mixdown, e) -> + @setState({editingMixdownId: null}) + e.preventDefault() + + if @disableLoading + $target = $(e.target) + context.JK.prodBubble($target, 'no-change-while-loading', {}, {positions:['left', 'top']}) + return + # make this package the active one JamTrackMixdownActions.openMixdown(mixdown) + jamTrackPlay: (jamtrack, e) -> + e.preventDefault() + # user wants to select the full track + + if @disableLoading + $target = $(e.target) + context.JK.prodBubble($target, 'no-change-while-loading', {}, {positions:['left', 'top']}) + return + + JamTrackActions.activateNoMixdown(jamtrack) + + onEditKeydown: (mixdown, e) -> + logger.debug("on edit keydown", e) + if e.keyCode == 13 # enter + @mixdownSave(mixdown, e) + else if e.keyCode == 27 # esc + @setState({editingMixdownId: null}) + mixdownEdit: (mixdown) -> + @setState({editingMixdownId: mixdown.id}) + + mixdownSave: (mixdown, e) -> + e.preventDefault() + $input = $(this.getDOMNode()).find('input.edit-name') + newValue = $input.val() + logger.debug("editing mixdown name to be: " + newValue) + JamTrackMixdownActions.editMixdown({id: mixdown.id, name: newValue}) + @setState({editingMixdownId: null}) mixdownDelete: (mixdown) -> - JamTrackAction.deleteMixdown(mixdown) + if confirm("Delete this custom mix?") + + @setState({editingMixdownId:null}) + JamTrackMixdownActions.deleteMixdown(mixdown) + mixdownError: (mixdown) -> alert("error") @@ -339,6 +431,10 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) speed = $root.find('select[name="mix-speed"]').val() pitch = $root.find('select[name="mix-pitch"]').val() + if @state.jamTrackState.jamTrack?.activeMixdown? + @setState({createMixdownErrors: {errors: {'Full JamTrack': ['must be selected']}}}) + return + if name == null || name == '' @setState({createMixdownErrors: {errors: {'Mix Name': ["can't be blank"]}}}) return @@ -353,7 +449,6 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) else pitch = parseInt(pitch) - mixdown = {jamTrackID: @state.jamTrackState.jamTrack.id, name: name, settings: {speed:speed, pitch: pitch}} package_settings = {file_type: 'ogg', encrypt_type: 'jkz'} @@ -419,4 +514,25 @@ mixins.push(Reflux.listenTo(JamTrackStore, 'onJamTrackChanged')) #offset += 25 window.resizeTo(width, height + offset) + + componentWillUpdate: (nextProps, nextState) -> + + @disableLoading = false + + return unless nextState? + + selectedMixdown = nextState?.jamTrackState?.jamTrack?.activeMixdown + + mixdownDownloading = false + if selectedMixdown? + switch selectedMixdown.client_state + when 'keying' + mixdownDownloading = true + when 'downloading' + mixdownDownloading = true + + + @disableLoading = SessionStore.downloadingJamTrack || mixdownDownloading + + }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee index f800302c8..afb9a13f2 100644 --- a/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionMediaTracks.js.jsx.coffee @@ -15,9 +15,18 @@ ChannelGroupIds = context.JK.ChannelGroupIds Reflux.listenTo(@JamTrackStore, "onJamTrackStateChanged")] onJamTrackStateChanged: (jamTrackState) -> - if jamTrackState.opened + if jamTrackState.fullTrackActivated || jamTrackState.opened && jamTrackState.jamTrack.activeMixdown == null @loadJamTrack(jamTrackState.jamTrack) else if jamTrackState.closed + logger.debug("SessionMediaTracks: jamtrack has been closed") + + if @state.downloadJamTrack? + logger.debug("closing DownloadJamTrack widget") + @state.downloadJamTrack.root.remove() + @state.downloadJamTrack.destroy() + SessionActions.downloadingJamTrack(false) + @setState({downloadJamTrack: null}) + SessionActions.closeMedia(true) #inputsChangedProcessed: (state) -> @@ -264,8 +273,8 @@ ChannelGroupIds = context.JK.ChannelGroupIds # All the JamTracks mediaTracks.push(``) - - if @state.metronome? + # show metronome only if it's a full jamtrack + if @state.metronome? && @state.jamTrackMixdown.id == null @state.metronome.mode = MIX_MODES.PERSONAL mediaTracks.push(``) @@ -338,7 +347,7 @@ ChannelGroupIds = context.JK.ChannelGroupIds unless @childWindow? logger.debug("opening media control window") @childWindow = window.open("/popups/media-controls", 'Media Controls', 'scrollbars=yes,toolbar=no,status=no,height=155,width=350') - @childWindow.PopupProps = {media: @state, jamTrackState: context.JamTrackStore.getState()} + @childWindow.PopupProps = {media: @state, jamTrackState: context.JamTrackStore.getState(), downloadingJamTrack: context.SessionStore.downloadingJamTrack } else if @childWindow? @childWindow.DontAutoCloseMedia = true diff --git a/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee index 63c02f9bb..e14db1c14 100644 --- a/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionTrackVolumeHover.js.jsx.coffee @@ -113,9 +113,10 @@ ptrCount = 0 context.JK.checkbox($checkbox) $checkbox.on('ifChanged', this.handleMuteCheckbox); + # using iCheck causes a 'ifChanged' event, so we need to swallow this up @iCheckMaint = true - if muteMixer.mute + if muteMixer?.mute $checkbox.iCheck('check').attr('checked', true) else $checkbox.iCheck('uncheck').attr('checked', false) @@ -139,7 +140,7 @@ ptrCount = 0 # using iCheck causes a 'ifChanged' event, so we need to swallow this up @iCheckMaint = true - if muteMixer.mute + if muteMixer?.mute $checkbox.iCheck('check').attr('checked', true) else $checkbox.iCheck('uncheck').attr('checked', false) diff --git a/web/app/assets/javascripts/react-components/actions/JamTrackActions.js.coffee b/web/app/assets/javascripts/react-components/actions/JamTrackActions.js.coffee index 0c122c4c6..1b5e6a9ae 100644 --- a/web/app/assets/javascripts/react-components/actions/JamTrackActions.js.coffee +++ b/web/app/assets/javascripts/react-components/actions/JamTrackActions.js.coffee @@ -3,6 +3,7 @@ context = window @JamTrackActions = Reflux.createActions({ open: {} close: {} + activateNoMixdown: {} requestSearch: {} requestFilter: {} }) diff --git a/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee b/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee index 634e7d419..d9d31fada 100644 --- a/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee +++ b/web/app/assets/javascripts/react-components/actions/SessionActions.js.coffee @@ -19,4 +19,5 @@ context = window broadcastFailure: {} broadcastSuccess: {} broadcastStopped: {} + mixdownActive: {} }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee b/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee index e4d46e2b6..c1fe26514 100644 --- a/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee +++ b/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee @@ -299,6 +299,7 @@ MIX_MODES = context.JK.MIX_MODES; jamTrackMixers = @jamTrackMixers.slice(); jamTracks = [] jamTrackName = null; + jamTrackMixdown = {id: null} if @session.isPlayingRecording() # only return managed mixers for recorded backing tracks @@ -308,6 +309,7 @@ MIX_MODES = context.JK.MIX_MODES; # only return un-managed (ad-hoc) mixers for normal backing tracks jamTracks = @session.jamTracks() jamTrackName = @session.jamTrackName() + jamTrackMixdown = @session.jamTrackMixdown() # pluck the 1st mixer, and assume that all other mixers in this group are of the same type (between JamTrack vs Peer) # if it's a locally opened track (JamTrackGroup), then we can say this person is the opener @@ -315,55 +317,90 @@ MIX_MODES = context.JK.MIX_MODES; if jamTracks noCorrespondingTracks = false - for jamTrack in jamTracks - mixer = null - preMasteredClass = "" - # find the track or tracks that correspond to the mixer - correspondingTracks = [] - for matchMixer in @jamTrackMixers - if matchMixer.id == jamTrack.id - correspondingTracks.push(jamTrack) - mixer = matchMixer - - if correspondingTracks.length == 0 + # Are we opening a mixdown, or a full track? + if jamTrackMixdown.id? + logger.debug("MixerHelper: mixdown is active. id: #{jamTrackMixdown.id}") + if jamTrackMixers.length == 0 noCorrespondingTracks = true - logger.error("could not correlate jam tracks", jamTrackMixers, jamTracks) + logger.error("could not correlate mixdown tracks", jamTrackMixers, jamTrackMixdown) @app.notify({ - title: "Unable to Open JamTrack", + title: "Unable to Open Custom Mix", text: "Could not correlate server and client tracks", icon_url: "/assets/content/icon_alert_big.png"}) return _jamTracks - - #jamTracks = $.grep(jamTracks, (value) => - # $.inArray(value, correspondingTracks) < 0 - #) - - # prune found mixers - jamTrackMixers.splice(mixer); - - oneOfTheTracks = correspondingTracks[0]; - instrumentIcon = context.JK.getInstrumentIcon24(oneOfTheTracks.instrument.id); - - part = oneOfTheTracks.part - - instrumentName = oneOfTheTracks.instrument.description - - if part? - trackName = "#{instrumentName}: #{part}" + else if jamTrackMixers.length > 1 + logger.warn("ignoring wrong amount of mixers for JamTrack in mixdown mode") + return _jamTracks else - trackName = instrumentName - data = - name: jamTrackName - trackName: trackName - part: part - isOpener: isOpener - instrumentIcon: instrumentIcon - track: oneOfTheTracks - mixers: @mediaMixers(mixer, isOpener) + instrumentIcon = context.JK.getInstrumentIcon24('other') + part = null + instrumentName = 'Custom Mix' + trackName = 'Custom Mix' - _jamTracks.push(data) + data = + name: jamTrackName + trackName: trackName + part: part + isOpener: isOpener + instrumentIcon: instrumentIcon + track: jamTrackMixdown + mixers: @mediaMixers(jamTrackMixers[0], isOpener) + + _jamTracks.push(data) + else + logger.debug("MixerHelper: full jamtrack is active") + + for jamTrack in jamTracks + mixer = null + preMasteredClass = "" + # find the track or tracks that correspond to the mixer + correspondingTracks = [] + + for matchMixer in @jamTrackMixers + if matchMixer.id == jamTrack.id + correspondingTracks.push(jamTrack) + mixer = matchMixer + + if correspondingTracks.length == 0 + noCorrespondingTracks = true + logger.error("could not correlate jam tracks", jamTrackMixers, jamTracks) + @app.notify({ + title: "Unable to Open JamTrack", + text: "Could not correlate server and client tracks", + icon_url: "/assets/content/icon_alert_big.png"}) + return _jamTracks + + #jamTracks = $.grep(jamTracks, (value) => + # $.inArray(value, correspondingTracks) < 0 + #) + + # prune found mixers + jamTrackMixers.splice(mixer); + + oneOfTheTracks = correspondingTracks[0]; + instrumentIcon = context.JK.getInstrumentIcon24(oneOfTheTracks.instrument.id); + + part = oneOfTheTracks.part + + instrumentName = oneOfTheTracks.instrument.description + + if part? + trackName = "#{instrumentName}: #{part}" + else + trackName = instrumentName + + data = + name: jamTrackName + trackName: trackName + part: part + isOpener: isOpener + instrumentIcon: instrumentIcon + track: oneOfTheTracks + mixers: @mediaMixers(mixer, isOpener) + + _jamTracks.push(data) _jamTracks diff --git a/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee b/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee index 1cb5023c7..e9d88c04a 100644 --- a/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee +++ b/web/app/assets/javascripts/react-components/helpers/SessionHelper.js.coffee @@ -75,6 +75,9 @@ context = window else null + jamTrackMixdown: () -> + { id: @session?.jam_track?.mixdown.id } + jamTrackName: () -> @session?.jam_track?.name diff --git a/web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee index 92dc025f7..6297c7be0 100644 --- a/web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee +++ b/web/app/assets/javascripts/react-components/mixins/SessionMediaTracksMixin.js.coffee @@ -31,6 +31,7 @@ logger = context.JK.logger mediaCategoryMixer: mediaCategoryMixer recordingName: mixers.recordingName() jamTrackName: mixers.jamTrackName() + jamTrackMixdown: session.jamTrackMixdown() @inputsChangedProcessed(state) if @inputsChangedProcessed? diff --git a/web/app/assets/javascripts/react-components/stores/JamTrackStore.js.coffee b/web/app/assets/javascripts/react-components/stores/JamTrackStore.js.coffee index 997f311d9..c92666e2e 100644 --- a/web/app/assets/javascripts/react-components/stores/JamTrackStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/JamTrackStore.js.coffee @@ -30,14 +30,12 @@ JamTrackActions = @JamTrackActions return unless @jamTrack? - sampleRate = context.jamClient.GetSampleRate() - sampleRate = if sampleRate == 48 then 48 else 44 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 + if mixdown_package.file_type == 'ogg' && mixdown_package.encrypt_type == 'jkz' && mixdown_package.sample_rate == @sampleRate myPackage = mixdown_package break @@ -95,22 +93,162 @@ JamTrackActions = @JamTrackActions @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' + @app.notify( + { + title: "Mixdown Can Not Open", + text: "Unable to open your JamTrack Mixdown. Please contact support@jamkazam.com" + } + , null, true) + + when 'unknown' + if @jamTrack.activeMixdown.client_state != 'downloading' + @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' + @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} + @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 @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: () -> @@ -138,7 +276,18 @@ JamTrackActions = @JamTrackActions requested onCreateMixdown: (mixdown, package_settings, 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, package_settings) + rest.createMixdown(mixdown) .done((created) => @@ -149,9 +298,7 @@ JamTrackActions = @JamTrackActions package_settings.id = created.id # we have to determine sample rate here, in the store, because child windows don't have access to jamClient - sampleRate = context.jamClient.GetSampleRate() - sampleRate = if sampleRate == 48 then 48 else 44 - package_settings.sample_rate = sampleRate + package_settings.sample_rate = @sampleRate rest.enqueueMixdown(package_settings) .done((enqueued) => @@ -160,7 +307,7 @@ JamTrackActions = @JamTrackActions done(enqueued) ) .fail((jqxhr) => - @app.layout.notify({title:'Unable to Package Mixdown', text: 'You can push the RETRY button.'}) + @app.layout.notify({title:'Unable to Create Custom Mix', text: 'You can push the RETRY button.'}) fail(jqxhr) ) ) @@ -171,9 +318,27 @@ JamTrackActions = @JamTrackActions 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) @@ -182,12 +347,35 @@ JamTrackActions = @JamTrackActions .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) @@ -208,6 +396,41 @@ JamTrackActions = @JamTrackActions 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 @@ -237,5 +460,50 @@ JamTrackActions = @JamTrackActions 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) -> + + if @jamTrack?.activeMixdown? + @jamTrack.activeMixdown.client_state = 'download_fail' + @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" + + } ) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee index f56828030..b0bbb0ac7 100644 --- a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee @@ -53,6 +53,11 @@ VideoActions = @VideoActions RecordingActions.initModel(@recordingModel) @helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack) + onMixdownActive: (mixdown) -> + if @currentSession?.jam_track? + @currentSession.jam_track.mixdown = mixdown + @issueChange() + onVideoChanged: (@videoState) -> @@ -241,6 +246,7 @@ VideoActions = @VideoActions rest.closeJamTrack({id: @currentSessionId}) .done(() => + @downloadingJamTrack = false @refreshCurrentSession(true) ) .fail((jqXHR) => diff --git a/web/app/assets/stylesheets/minimal/media_controls.css.scss b/web/app/assets/stylesheets/minimal/media_controls.css.scss index 4e16538c8..960f5b4ab 100644 --- a/web/app/assets/stylesheets/minimal/media_controls.css.scss +++ b/web/app/assets/stylesheets/minimal/media_controls.css.scss @@ -55,11 +55,29 @@ body.media-controls-popup.popup { margin-top:15px; font-size:12px; font-weight:normal; + + span { + vertical-align:middle; + } + img { + vertical-align:middle; + margin-left:5px; + height:16px; + } } h5 { font-size:12px; font-weight:normal; + + span { + vertical-align:middle; + } + img { + vertical-align:middle; + margin-left:5px; + height:16px; + } } } @@ -105,6 +123,7 @@ body.media-controls-popup.popup { background-color:#2c2c2c; @include border_box_sizing; border-spacing:7px; + text-align: left; &.active { background-color:#44423f; diff --git a/web/app/controllers/api_jam_track_mixdowns_controller.rb b/web/app/controllers/api_jam_track_mixdowns_controller.rb index ca830ef47..2da9c9ff3 100644 --- a/web/app/controllers/api_jam_track_mixdowns_controller.rb +++ b/web/app/controllers/api_jam_track_mixdowns_controller.rb @@ -18,22 +18,27 @@ class ApiJamTrackMixdownsController < ApiController end def show - JamTrack.find() @jam_track_mixdown = JamTrackMixdown.find(params[:id]) end - def update + def delete @jam_track_mixdown = JamTrackMixdown.find(params[:id]) - @jam_track_mixdown.name = params[:name] if params[:name] - @jam_track_mixdown.description = params[:description] if params[:description] - @jam_track_mixdown.save + @jam_track_mixdown.destroy + render json: {}, status:204 + end + + def update + @mixdown = JamTrackMixdown.find(params[:id]) + @mixdown.name = params[:name] if params[:name] + @mixdown.description = params[:description] if params[:description] + @mixdown.save if params[:active] - @jam_track_right.last_mixdown = @jam_track_mixdown + @jam_track_right.last_mixdown = @mixdown @jam_track_right.save end - if @jam_track_mixdown.errors.any? - respond_with_model(@jam_track_mixdown) + if @mixdown.errors.any? + respond_with_model(@mixdown) return else diff --git a/web/app/controllers/api_jam_tracks_controller.rb b/web/app/controllers/api_jam_tracks_controller.rb index 619ad3459..55fe58ce7 100644 --- a/web/app/controllers/api_jam_tracks_controller.rb +++ b/web/app/controllers/api_jam_tracks_controller.rb @@ -153,7 +153,10 @@ class ApiJamTracksController < ApiController end def keys + puts "Keys" + puts "--------------------------" jamtrack_holder = params[:jamtracks] + puts jamtrack_holder.inspect unless jamtrack_holder.kind_of?(Hash) render :json => {message: 'jamtracks parameter must be an hash'}, :status => 422 @@ -169,20 +172,49 @@ class ApiJamTracksController < ApiController # jamtracks come in the form id-44 or id-48, so we need to do a little extra parsing + # mixdowns come in the form id_mixid-44 or id_mixid-48, so we also need to handle that jamtrack_ids = Set.new jamtracks_fq_ids = Set.new + jamtrack_mixdowns = {} + jamtracks.each do |jamtrack| rindex = jamtrack.rindex('-') if rindex id = jamtrack[0..(rindex-1)] - jamtrack_ids << id + + # let's see if a mixid is in this ID + rindex = id.rindex('_') + + if rindex + # ok, this is id_mixid-44 format; so we need to parse again for the ID + just_id = jamtrack[0..(rindex-1)] + sample_rate = jamtrack[-2..-1] + + jamtrack_ids << just_id + + simulated_fq_id = "#{just_id}-#{sample_rate}" + mixdown_info = jamtrack_mixdowns[simulated_fq_id] + + unless mixdown_info + mixdown_info = [] + jamtrack_mixdowns[simulated_fq_id] = mixdown_info + end + mixdown_info << id + + else + jamtrack_ids << id + end + + jamtracks_fq_ids << jamtrack # includes sample rate end end @jam_tracks = JamTrackRight.list_keys(current_user, jamtrack_ids) @jamtracks_fq_ids = jamtracks_fq_ids + @jamtrack_mixdowns = jamtrack_mixdowns + puts "jamtrack_mixdowns #{jamtrack_mixdowns}" end private diff --git a/web/app/views/api_jam_track_mixdowns/show_package.rabl b/web/app/views/api_jam_track_mixdowns/show_package.rabl index 9813b688b..b4899b037 100644 --- a/web/app/views/api_jam_track_mixdowns/show_package.rabl +++ b/web/app/views/api_jam_track_mixdowns/show_package.rabl @@ -1,3 +1,3 @@ object @package -attributes :id, :jam_track_mixdown_id, :file_type, :sample_rate, :encrypt_type, :error_count, :error_reason, :error_detail, :signing_state, :packaging_steps, :current_packaging_step +attributes :id, :jam_track_mixdown_id, :file_type, :sample_rate, :encrypt_type, :error_count, :error_reason, :error_detail, :signing_state, :packaging_steps, :current_packaging_step, :version diff --git a/web/app/views/api_jam_tracks/keys.rabl b/web/app/views/api_jam_tracks/keys.rabl index a5cd9d471..2b482e44a 100644 --- a/web/app/views/api_jam_tracks/keys.rabl +++ b/web/app/views/api_jam_tracks/keys.rabl @@ -17,7 +17,35 @@ node do |jam_track| private: jam_track['private_key_48'], error: jam_track['private_key_48'] ? nil : ( jam_track['jam_track_right_id'] ? 'no_key' : 'not_purchased' ) } + end + + # now include mixdown info + mixdowns_44 = [] + mixdown_info = @jamtrack_mixdowns[id] + if mixdown_info + mixdown_info.each do |mixdown_id| + mixdowns_44 << { + id: mixdown_id + '-44', + private: jam_track['private_key_44'], + error: jam_track['private_key_44'] ? nil : ( jam_track['jam_track_right_id'] ? 'no_key' : 'not_purchased' ) + } end + end + result['mixdowns_44'] = mixdowns_44 + + # now include mixdown info + mixdowns_48 = [] + mixdown_info = @jamtrack_mixdowns[id + '-48'] + if mixdown_info + mixdown_info.each do |mixdown_id| + mixdowns_48 << { + id: mixdown_id + '-48', + private: jam_track['private_key_48'], + error: jam_track['private_key_48'] ? nil : ( jam_track['jam_track_right_id'] ? 'no_key' : 'not_purchased' ) + } + end + end + result['mixdowns_48'] = mixdowns_48 result end \ No newline at end of file diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl index 3472f18a3..d5afc2a02 100644 --- a/web/app/views/api_music_sessions/show.rabl +++ b/web/app/views/api_music_sessions/show.rabl @@ -81,6 +81,12 @@ else child({:jam_track => :jam_track}, :if => lambda { |music_session| music_session.users.exists?(current_user) }) { attributes :id, :name, :description + node :mixdown do |jam_track| + right = jam_track.right_for_user(User.find(@music_session.jam_track_initiator_id)) + + {id: right ? right.last_mixdown_id : nil} + end + child(:jam_track_tracks => :tracks) { attributes :id, :part, :instrument, :track_type } diff --git a/web/app/views/clients/_help.html.slim b/web/app/views/clients/_help.html.slim index 721a5f191..6823d0b45 100644 --- a/web/app/views/clients/_help.html.slim +++ b/web/app/views/clients/_help.html.slim @@ -349,4 +349,7 @@ script type="text/template" id="template-help-ftue-video-disable" li If you know you never want to see anyone else's video. li If you are experiencing technical problems with others send you video. +script type="text/template" id="template-help-no-change-while-loading" + span Certain actions are disabled while a track is being loaded. + diff --git a/web/config/routes.rb b/web/config/routes.rb index 7d08ac47d..cef2252c1 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -257,6 +257,7 @@ SampleApp::Application.routes.draw do match '/mixdowns/:id' => 'api_jam_track_mixdowns#show', :via => :get match '/mixdowns/:id' => 'api_jam_track_mixdowns#update', :via => :post match '/mixdowns' => 'api_jam_track_mixdowns#create', :via => :post + match '/mixdowns/:id' => 'api_jam_track_mixdowns#delete', :via => :delete match '/mixdown_packages/:id' => 'api_jam_track_mixdowns#show_package', :via => :get