806 lines
29 KiB
CoffeeScript
806 lines
29 KiB
CoffeeScript
context = window
|
|
logger = context.JK.logger
|
|
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|
rest = context.JK.Rest()
|
|
|
|
mixins = []
|
|
|
|
|
|
|
|
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
|
# this check ensures we attempt to listen if this component is created in a popup
|
|
reactContext = if window.opener? then window.opener else window
|
|
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
|
if window.opener?
|
|
try
|
|
m = window.opener.MixerStore
|
|
catch e
|
|
reactContext = window
|
|
|
|
if true # /iPhone|iPad|iPod|android/i.test(navigator.userAgent)
|
|
# iPad or iPhone
|
|
reactContext = window
|
|
|
|
AppActions = reactContext.AppActions
|
|
JamTrackPlayerActions = reactContext.JamTrackPlayerActions
|
|
JamTrackPlayerStore = reactContext.JamTrackPlayerStore
|
|
|
|
mixins.push(Reflux.listenTo(JamTrackPlayerStore, 'onJamTrackPlayerStoreChanged'))
|
|
|
|
|
|
@PopupJamTrackPlayer = React.createClass({
|
|
|
|
mixins: mixins
|
|
|
|
computeWeight: (jam_track_track) ->
|
|
weight = switch
|
|
when jam_track_track.track_type == 'Master' then 0
|
|
when jam_track_track.track_type == 'Click' then 10000
|
|
else jam_track_track.position
|
|
|
|
onJamTrackPlayerStoreChanged: (changes) ->
|
|
#logger.debug("PopupMediaControls: jamtrack changed", changes)
|
|
@setState({jamTrackState: changes})
|
|
|
|
showMetronome: (e) ->
|
|
e.preventDefault()
|
|
|
|
SessionActions.showNativeMetronomeGui()
|
|
|
|
getInitialState: () ->
|
|
state = {}
|
|
state.jamTrackState = JamTrackPlayerStore.getState()
|
|
state.showCustomMixes = true
|
|
state.showMyMixes = true
|
|
return state
|
|
|
|
close: () ->
|
|
window.close()
|
|
|
|
help: (e) ->
|
|
e.preventDefault()
|
|
|
|
AppActions.openExternalUrl($(e.target).attr('href'))
|
|
|
|
render: () ->
|
|
|
|
closeLinkText = null
|
|
header = null
|
|
extraControls = null
|
|
|
|
jamTrack = @state.jamTrackState?.jamTrack
|
|
mediaType = "JamTrack"
|
|
mediaName = jamTrack?.name
|
|
closeLinkText = 'CLOSE JAMTRACK'
|
|
helpLink = 'https://jamkazam.desk.com/customer/portal/articles/2138903-using-custom-mixes-to-slow-tempo-change-pitch'
|
|
|
|
selectedMixdown = jamTrack?.activeMixdown
|
|
selectedStem = jamTrack?.activeStem
|
|
|
|
if selectedMixdown?
|
|
jamTrackTypeHeader = 'Custom Mix'
|
|
|
|
disabled = true
|
|
if selectedMixdown.client_state?
|
|
switch selectedMixdown.client_state
|
|
when 'download_fail'
|
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></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
|
|
if selectedMixdown.myPackage
|
|
customMixName = `<h5>Creating mixdown... <img src="/assets/shared/spinner.gif" /></h5>`
|
|
else
|
|
customMixName = `<h5>{selectedMixdown.name}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
|
|
|
else if selectedStem?
|
|
if selectedStem.instrument
|
|
instrumentId = selectedStem.instrument.id
|
|
instrumentDescription = selectedStem.instrument.description
|
|
part = "(#{selectedStem.part})" if selectedStem.part? && selectedStem.part != instrumentDescription
|
|
part = '' unless part
|
|
|
|
jamTrackTypeHeader = 'Track'
|
|
|
|
trackName = "#{instrumentDescription} #{part}"
|
|
|
|
disabled = true
|
|
if selectedStem.client_state?
|
|
switch selectedStem.client_state
|
|
when 'downloading'
|
|
customMixName = `<h5>Loading {trackName}... <img src="/assets/shared/spinner.gif" /></h5>`
|
|
when 'download_fail'
|
|
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
|
when 'ready'
|
|
customMixName = `<h5>{trackName}</h5>`
|
|
disabled = false
|
|
else
|
|
customMixName = `<h5>{trackName}<img src="/assets/content/icon-mix-fail@2X.png" /></h5>`
|
|
|
|
else
|
|
if jamTrack?.client_state == 'downloading'
|
|
downloader = `<img src="/assets/shared/spinner.gif" />`
|
|
else if jamTrack?.client_state == 'download_fail'
|
|
downloader = `<img src="/assets/content/icon-mix-fail@2X.png" />`
|
|
|
|
jamTrackTypeHeader = `<span>Full JamTrack {downloader}</span>`
|
|
|
|
header = `
|
|
<div className="header">
|
|
<h3>{mediaType}: {mediaName}</h3>
|
|
<h4>{jamTrackTypeHeader}</h4>
|
|
{customMixName}
|
|
</div>`
|
|
|
|
myMixes = null
|
|
if @state.showMyMixes
|
|
|
|
if jamTrack?
|
|
myMixdowns = []
|
|
|
|
boundPlayClick = this.jamTrackPlay.bind(this, jamTrack);
|
|
boundDownloadClick = this.jamTrackDownload.bind(this, jamTrack);
|
|
|
|
active = jamTrack.last_mixdown_id == null && jamTrack.last_stem_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}/>
|
|
<a href={this.createJamTrackUrl(jamTrack)} target="_blank" onClick={boundDownloadClick}><img src="/assets/content/icon_download@2X.png" className="mixdown-download"/></a>
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete" onClick={false} />
|
|
</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);
|
|
|
|
boundDownloadNotReadyClick = this.downloadNotReady.bind(this, mixdown)
|
|
boundDownloadReadyClick = this.downloadMixdownReady.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}/>`
|
|
|
|
# create hidden objects to deal with alginment issues using a table
|
|
if !editIcon
|
|
editIcon = `<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />`
|
|
|
|
download = `<a onClick={boundDownloadReadyClick} href={this.downloadMixdownUrl(mixdown)} target="_blank"><img src="/assets/content/icon_download@2X.png" className="mixdown-download" /></a>`
|
|
|
|
myMixdowns.push `
|
|
<div key={mixdown.id} className={classNames({'mixdown-display': true, 'active' : active})}>
|
|
<div className="mixdown-name">
|
|
{mixdownName}
|
|
</div>
|
|
<div className="mixdown-actions">
|
|
{action}
|
|
{download}
|
|
{editIcon}
|
|
|
|
<img src ="/assets/content/icon-delete@2X.png" className="mixdown-delete" onClick={boundDeleteClick} />
|
|
</div>
|
|
</div>`
|
|
|
|
active = jamTrack.last_stem_id?
|
|
trackOptions = []
|
|
|
|
jamTrack.tracks.sort((a, b) =>
|
|
aWeight = @computeWeight(a)
|
|
bWeight = @computeWeight(b)
|
|
return aWeight - bWeight
|
|
)
|
|
|
|
|
|
for track in jamTrack.tracks
|
|
if track.track_type == 'Track' || track.track_type == 'Click'
|
|
|
|
if track.instrument
|
|
instrumentId = track.instrument.id
|
|
instrumentDescription = track.instrument.description
|
|
if track.part? && track.part != instrumentDescription
|
|
part = "(#{track.part})"
|
|
else
|
|
part = ''
|
|
trackOptions.push(`<option key={track.id} value={track.id}>{instrumentDescription} {part}</option>`)
|
|
|
|
boundStemActivateClick = this.activateStem.bind(this)
|
|
boundStemPlayClick = this.downloadStem.bind(this)
|
|
boundStemChange = this.stemChanged.bind(this)
|
|
|
|
myMixdowns.push `
|
|
<div key={track.id} className={classNames({'stem-track' : true, 'mixdown-display': true, 'active' : active})}>
|
|
<div className="mixdown-name">
|
|
<select className="active-stem-select" name="active-stem-select" onChange={boundStemChange} defaultValue={jamTrack.last_stem_id}>
|
|
<option key="null" value="">or select a track</option>
|
|
{trackOptions}
|
|
</select>
|
|
</div>
|
|
<div className="mixdown-actions">
|
|
<img src="/assets/content/icon_open@2X.png" className="mixdown-play" onClick={boundStemActivateClick}/>
|
|
<a href={this.createStemUrl(jamTrack.id, jamTrack.last_stem_id)} target="_blank" onClick={boundStemPlayClick}><img src="/assets/content/icon_download@2X.png" className="mixdown-download" /></a>
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-edit" onClick={false} />
|
|
<img src ="/assets/content/icon-delete@2X.png" style={{visibility:'hidden'}} className="mixdown-delete" onClick={false} />
|
|
</div>
|
|
</div>`
|
|
|
|
myMixes = `<div key="my-mixes" className="my-mixes">{myMixdowns}</div>`
|
|
else
|
|
# nothing
|
|
|
|
mixControls = null
|
|
|
|
if @state.showCustomMixes
|
|
|
|
tracks = []
|
|
if jamTrack?
|
|
for track in jamTrack.tracks
|
|
if track.track_type == 'Track' || track.track_type == 'Click'
|
|
if track.track_type == 'Click'
|
|
instrumentId = track.instrument.id
|
|
instrumentDescription = 'Clicktrack'
|
|
part = ''
|
|
else if track.instrument
|
|
instrumentId = track.instrument.id
|
|
instrumentDescription = track.instrument.description
|
|
if track.part? && track.part != instrumentDescription
|
|
part = "(#{track.part})"
|
|
else
|
|
part = ''
|
|
|
|
|
|
tracks.push(`
|
|
<tr className="stem">
|
|
<td><img src={context.JK.getInstrumentIcon24(instrumentId)} className="instrument-icon" /> {instrumentDescription} {part}</td>
|
|
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id={track.id} defaultChecked={track.track_type == 'Click'}/></td>
|
|
</tr>`)
|
|
|
|
if jamTrack?.jmep?.Events? && jamTrack.jmep.Events[0].metronome?
|
|
# tap-in detected; show user tap-in option
|
|
tracks.push(`
|
|
<tr className="stem">
|
|
<td><img src={context.JK.getInstrumentIcon24('computer')} className="instrument-icon" /> Count-in</td>
|
|
<td className="mute"><input type="checkbox" className="stem-mute" data-stem-id="count-in" /></td>
|
|
</tr>`)
|
|
|
|
stems = `<div key="stems" className="stems">
|
|
<table>
|
|
<thead>
|
|
<col align="left" /><col align="right"/>
|
|
<tr><th>TRACKS</th><th className="mute">mute</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{tracks}
|
|
</tbody>
|
|
</table>
|
|
</div>`
|
|
|
|
|
|
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>Mute or unmute any tracks 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.</p>
|
|
{stems}
|
|
<div className="field">
|
|
<label>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>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 className="show-hide-my-mixes" onClick={this.toggleMyMixes}>hide my mixes <div className="details-arrow arrow-up" /></a>`
|
|
else
|
|
showMyMixesText = `<a className="show-hide-my-mixes" onClick={this.toggleMyMixes}>show my mixes <div className="details-arrow arrow-down" /></a>`
|
|
|
|
if @state.showCustomMixes
|
|
showMixControlsText = `<a className="show-hide-mix-controls" onClick={this.toggleCustomMixes}>hide mix controls <div className="details-arrow arrow-up" /></a>`
|
|
else
|
|
showMixControlsText = `<a className="show-hide-mix-controls" 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={false}>
|
|
{mixControls}
|
|
</ReactCSSTransitionGroup>
|
|
</div>`
|
|
|
|
|
|
|
|
if helpLink?
|
|
helpButton = `<a className="help-link button-grey" href={helpLink} target="_blank">HELP</a>`
|
|
|
|
`<div className="media-controls-popup">
|
|
{header}
|
|
<BrowserMediaControls disabled={this.disableLoading}/>
|
|
{extraControls}
|
|
<div className="actions">
|
|
{helpButton}
|
|
</div>
|
|
</div>`
|
|
|
|
# <a className="close-link button-orange" onClick={this.close}>{closeLinkText}</a>
|
|
windowUnloaded: () ->
|
|
JamTrackPlayerActions.windowUnloaded() unless window.DontAutoCloseMedia
|
|
|
|
toggleMyMixes: (e) ->
|
|
e.preventDefault()
|
|
@setState({showMyMixes: !@state.showMyMixes})
|
|
|
|
toggleCustomMixes: (e) ->
|
|
e.preventDefault()
|
|
|
|
|
|
@setState({showCustomMixes: !@state.showCustomMixes})
|
|
|
|
downloadNotReady: (mixdown, e) ->
|
|
alert("This mix is not yet ready to download.")
|
|
e.preventDefault()
|
|
|
|
downloadMixdownReady: (mixdown, e) ->
|
|
|
|
|
|
if mixdown.myPackage?.signing_state == 'SIGNED'
|
|
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
# fall through
|
|
else
|
|
e.preventDefault()
|
|
iframe = document.createElement("iframe")
|
|
iframe.src = @downloadMixdownUrl(mixdown)
|
|
iframe.style.display = "none"
|
|
document.body.appendChild(iframe);
|
|
else
|
|
alert("The mix is not yet ready to download")
|
|
|
|
downloadMixdownUrl: (mixdown) ->
|
|
window.location.protocol + '//' + window.location.host + "/api/mixdowns/#{mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1"
|
|
|
|
activateStem: (e) ->
|
|
e.preventDefault()
|
|
|
|
$select = $(this.getDOMNode()).find('.active-stem-select')
|
|
|
|
selectedTrackId = $select.val()
|
|
|
|
if !selectedTrackId? || selectedTrackId == ''
|
|
alert("You must pick a track from the dropdown in order to play it.")
|
|
else
|
|
|
|
@setState({editingMixdownId: null})
|
|
|
|
e.preventDefault()
|
|
|
|
if @disableLoading
|
|
alert('Certain actions are disabled while a track is being loaded.')
|
|
return
|
|
|
|
# make this package the active one
|
|
JamTrackPlayerActions.openStem(selectedTrackId)
|
|
|
|
downloadStem: (e) ->
|
|
|
|
$select = $(this.getDOMNode()).find('.active-stem-select')
|
|
|
|
selectedTrackId = $select.val()
|
|
|
|
if !selectedTrackId? || selectedTrackId == ''
|
|
e.preventDefault()
|
|
alert("You must select a track in order to download and also click the folder icon.")
|
|
else
|
|
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
if @state.jamTrackState.jamTrack?.last_stem_id
|
|
# fall through
|
|
else
|
|
e.preventDefault()
|
|
alert("You must select a track in order to download and also click the folder icon.")
|
|
else
|
|
e.preventDefault()
|
|
|
|
iframe = document.createElement("iframe")
|
|
iframe.src = @createStemUrl(@state.jamTrackState.jamTrack.id, selectedTrackId)
|
|
iframe.style.display = "none"
|
|
document.body.appendChild(iframe);
|
|
|
|
createStemUrl: (jamTrackId, stemId) ->
|
|
window.location.protocol + '//' + window.location.host + "/api/jamtracks/#{jamTrackId}/stems/#{stemId}/download.mp3?file_type=mp3&download=1"
|
|
|
|
stemChanged: () ->
|
|
|
|
mixdownPlay: (mixdown, e) ->
|
|
@setState({editingMixdownId: null})
|
|
|
|
e.preventDefault()
|
|
|
|
if @disableLoading
|
|
alert('Certain actions are disabled while a track is being loaded.')
|
|
return
|
|
|
|
# make this package the active one
|
|
JamTrackPlayerActions.openMixdown(mixdown)
|
|
|
|
jamTrackPlay: (jamtrack, e) ->
|
|
e.preventDefault()
|
|
# user wants to select the full track
|
|
|
|
if @disableLoading
|
|
alert('Certain actions are disabled while a track is being loaded.')
|
|
return
|
|
|
|
JamTrackPlayerActions.activateNoMixdown(jamtrack)
|
|
|
|
jamTrackDownload: (jamTrack, e) ->
|
|
|
|
if /iPhone|iPad|iPod/i.test(navigator.userAgent)
|
|
# fall through
|
|
|
|
else
|
|
e.preventDefault()
|
|
|
|
iframe = document.createElement("iframe")
|
|
iframe.src = @createJamTrackUrl(jamTrack)
|
|
iframe.style.display = "none"
|
|
document.body.appendChild(iframe);
|
|
|
|
createJamTrackUrl: (jamTrack) ->
|
|
window.location.protocol + '//' + window.location.host + "/api/jamtracks/#{jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1"
|
|
|
|
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)
|
|
JamTrackPlayerActions.editMixdown({id: mixdown.id, name: newValue})
|
|
@setState({editingMixdownId: null})
|
|
|
|
mixdownDelete: (mixdown) ->
|
|
if @state.editingMixdownId?
|
|
@setState({editingMixdownId:null})
|
|
return
|
|
|
|
if confirm("Delete this custom mix?")
|
|
JamTrackPlayerActions.deleteMixdown(mixdown)
|
|
|
|
|
|
mixdownError: (mixdown) ->
|
|
|
|
myPackage = mixdown.myPackage
|
|
|
|
if myPackage?
|
|
switch myPackage.signing_state
|
|
when 'QUIET_TIMEOUT'
|
|
action = 'Custom mix never got created. Retry?'
|
|
when 'QUEUED_TIMEOUT'
|
|
action = 'Custom mix was never built. Retry?'
|
|
when 'SIGNING_TIMEOUT'
|
|
action = 'Custom mix took took long to build. Retry?'
|
|
when 'ERROR'
|
|
action = 'Custom mix failed to build. Retry?'
|
|
else
|
|
action = 'Custom mix never got created. Retry?'
|
|
|
|
return unless action?
|
|
|
|
if confirm(action)
|
|
JamTrackPlayerActions.enqueueMixdown(mixdown, @enqueueDone)
|
|
|
|
enqueueDone: (enqueued) ->
|
|
@promptEstimate(enqueued)
|
|
|
|
promptEstimate: (enqueued) ->
|
|
time = enqueued.queue_time
|
|
|
|
if time == 0
|
|
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 about #{msg} to be created.")
|
|
|
|
|
|
createMix: (e) ->
|
|
e.preventDefault()
|
|
|
|
return if @state.creatingMix
|
|
|
|
$root = $(@getDOMNode())
|
|
|
|
name = $root.find('input[name="mix-name"]').val()
|
|
speed = $root.find('select[name="mix-speed"]').val()
|
|
pitch = $root.find('select[name="mix-pitch"]').val()
|
|
|
|
if name == null || name == ''
|
|
@setState({createMixdownErrors: {errors: {'Mix Name': ["can't be blank"]}}})
|
|
return
|
|
|
|
# sanitize junk out of speed/pitch
|
|
if speed == '' || speed.indexOf('separator') > -1
|
|
speed = undefined
|
|
else
|
|
speed = parseInt(speed)
|
|
if pitch == '' || pitch.indexOf('separator') > -1
|
|
pitch = undefined
|
|
else
|
|
pitch = parseInt(pitch)
|
|
|
|
count_in = false
|
|
|
|
# get mute state of all tracks
|
|
$mutes = $(@getDOMNode()).find('.stems .stem .stem-mute')
|
|
|
|
tracks = []
|
|
$mutes.each((i, mute) =>
|
|
$mute = $(mute)
|
|
stemId = $mute.attr('data-stem-id')
|
|
muted = $mute.is(':checked')
|
|
|
|
if stemId == 'count-in'
|
|
count_in = !muted
|
|
else
|
|
tracks.push({id: stemId, mute: muted})
|
|
)
|
|
|
|
mixdown = {jamTrackID: @state.jamTrackState.jamTrack.id, name: name, settings: {speed:speed, pitch: pitch, "count-in": count_in, tracks:tracks}}
|
|
|
|
JamTrackPlayerActions.createMixdown(mixdown, @createMixdownDone, @createMixdownFail)
|
|
|
|
@setState({creatingMixdown: true, createMixdownErrors: null})
|
|
|
|
createMixdownDone: (created) ->
|
|
logger.debug("created (within PopupMediaControls)", created)
|
|
@setState({creatingMixdown: false, showCustomMixes: true, showMyMixes: true})
|
|
|
|
@promptEstimate(created)
|
|
|
|
createMixdownFail: (jqXHR) ->
|
|
logger.debug("create mixdown fail (within PopupMediaControls)", jqXHR.status)
|
|
@setState({creatingMixdown: false})
|
|
if jqXHR.status == 422
|
|
response = JSON.parse(jqXHR.responseText)
|
|
logger.warn("failed to create mixdown", response, jqXHR.responseText)
|
|
|
|
@setState({createMixdownErrors: response})
|
|
|
|
fetchJamTrackInfo: () ->
|
|
rest.getJamTrack({plan_code: gon.jamtrack_id})
|
|
.done((response) =>
|
|
JamTrackPlayerActions.open(response, false);
|
|
@fetchUserInfo()
|
|
)
|
|
.fail((jqXHR) =>
|
|
alert("Unable to fetch JamTrack information. Try logging in.")
|
|
)
|
|
|
|
fetchUserInfo: () ->
|
|
rest.getUserDetail()
|
|
.done((response) =>
|
|
|
|
rest.postUserEvent({name: 'jamtrack_web_player_open'})
|
|
context.stats.write('web.jamtrack_web_player.open', {
|
|
value: 1,
|
|
user_id: context.JK.currentUserId,
|
|
user_name: context.JK.currentUserName
|
|
})
|
|
|
|
if !response.first_opened_jamtrack_web_player
|
|
setTimeout((() =>
|
|
logger.debug("first time user")
|
|
rest.userOpenedJamTrackWebPlayer()
|
|
$root = $(@getDOMNode())
|
|
#context.JK.prodBubble($root.find('.create-mix-btn'), 'first-time-jamtrack-web-player', {}, {positions:['left'], offsetParent: $root})
|
|
), 1500)
|
|
)
|
|
|
|
|
|
|
|
componentDidMount: () ->
|
|
|
|
$(window).unload(@windowUnloaded)
|
|
|
|
@root = jQuery(this.getDOMNode())
|
|
|
|
$loop = @root.find('input[name="loop"]')
|
|
context.JK.checkbox($loop)
|
|
|
|
$loop.on('ifChecked', () =>
|
|
# it doesn't matter if you do personal or master, because backend just syncs both
|
|
MixerActions.loopChanged(@state.media.backingTracks[0].mixers.personal.mixer, true)
|
|
)
|
|
$loop.on('ifUnchecked', () =>
|
|
# it doesn't matter if you do personal or master, because backend just syncs both
|
|
MixerActions.loopChanged(@state.media.backingTracks[0].mixers.personal.mixer, false)
|
|
)
|
|
|
|
@resizeWindow()
|
|
|
|
# this is necessary due to whatever the client's rendering behavior is.
|
|
setTimeout(@resizeWindow, 300)
|
|
|
|
@fetchJamTrackInfo()
|
|
|
|
componentDidUpdate: () ->
|
|
@resizeWindow()
|
|
setTimeout(@resizeWindow, 1000)
|
|
|
|
resizeWindow: () =>
|
|
return
|
|
$container = $('#minimal-container')
|
|
width = $container.width()
|
|
height = $container.height()
|
|
|
|
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
|
#mysteryTopMargin = 20
|
|
mysteryTopMargin = 0
|
|
# deal with chrome in real browsers
|
|
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
|
|
|
# handle native client chrome that the above outer-inner doesn't catch
|
|
#if navigator.userAgent.indexOf('JamKazam') > -1
|
|
|
|
#offset += 25
|
|
|
|
window.resizeTo(450, height + offset)
|
|
|
|
computeDisableLoading: (state) ->
|
|
@disableLoading = false
|
|
|
|
return unless nextState?
|
|
|
|
selectedMixdown = state?.jamTrackState?.jamTrack?.activeMixdown
|
|
|
|
mixdownDownloading = false
|
|
if selectedMixdown?
|
|
switch selectedMixdown.client_state
|
|
when 'downloading'
|
|
mixdownDownloading = true
|
|
|
|
selectedStem = state?.jamTrackState?.jamTrack?.activeStem
|
|
|
|
stemDownloading = false
|
|
if selectedStem?
|
|
switch selectedStem.client_state
|
|
when 'downloading'
|
|
stemDownloading = true
|
|
|
|
@disableLoading = state?.jamTrackState?.jamTrack?.client_state == 'downloading' || mixdownDownloading || stemDownloading
|
|
|
|
componentWillMount: () ->
|
|
@computeDisableLoading(@state)
|
|
|
|
componentWillUpdate: (nextProps, nextState) ->
|
|
@computeDisableLoading(nextState)
|
|
|
|
|
|
}) |