work on JamTrack player
This commit is contained in:
parent
cbaf1ea4f9
commit
08fafbf2de
|
|
@ -185,7 +185,7 @@ const JKCreateCustomMix = () => {
|
|||
<thead className="bg-200 text-900">
|
||||
<tr>
|
||||
<th>Tracks {tracks.length > 0 && <>({tracks.length})</>}</th>
|
||||
<th class="text-center">Mute</th>
|
||||
<th className="text-center">Mute</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -196,8 +196,8 @@ const JKCreateCustomMix = () => {
|
|||
<td>
|
||||
<span>{trackName(track)}</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<input type="checkbox" value={track.id} onClick={toggleTrack} checked={ selectedTracks.includes(track.id)} disabled={hasExceededMax} />
|
||||
<td className="text-center">
|
||||
<input type="checkbox" value={track.id} onChange={toggleTrack} checked={ selectedTracks.includes(track.id)} disabled={hasExceededMax} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import React, { useState, useEffect, useRef } from 'react';
|
|||
import Select from 'react-select';
|
||||
import { Row, Col } from 'reactstrap';
|
||||
import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import JKModalDialog from '../common/JKModalDialog';
|
||||
import { enqueueMyMixdown } from '../../store/features/jamTrackSlice';
|
||||
import { set } from 'react-hook-form';
|
||||
|
||||
const JKJamTrackPlayer = () => {
|
||||
const [options, setOptions] = useState([]);
|
||||
|
|
@ -11,10 +14,17 @@ const JKJamTrackPlayer = () => {
|
|||
const [audioUrl, setAudioUrl] = useState(null);
|
||||
const audioRef = useRef(null);
|
||||
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
|
||||
const mixdownsLoadingStatus = useSelector(state => state.jamTrack.mixdownsLoadingStatus);
|
||||
const SAMPLE_RATE = 48;
|
||||
const [retryMessage, setRetryMessage] = useState('');
|
||||
const [showRetry, setShowRetry] = useState(false);
|
||||
const [retryDownload, setRetryDownload] = useState(false);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (jamTrack) {
|
||||
const opts = jamTrack.mixdowns.map(mix => ({ value: mix.id, label: mix.name })).filter(mix => mix.value !== 'temp');
|
||||
const opts = jamTrack.mixdowns.filter(mix => mix.id !== 'temp').map(mix => ({ value: mix.id, label: mix.name }));
|
||||
opts.unshift({ value: 'original', label: 'Original' });
|
||||
setOptions(opts);
|
||||
if (jamTrack.last_mixdown_id) {
|
||||
|
|
@ -25,6 +35,16 @@ const JKJamTrackPlayer = () => {
|
|||
}
|
||||
}, [jamTrack]);
|
||||
|
||||
const audioFileExtension = url => {
|
||||
try {
|
||||
const u = new URL(url);
|
||||
const ext = u.pathname.split('.').pop();
|
||||
return `audio/${ext}`;
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnChange = selectedOption => {
|
||||
const option = options.find(opt => opt.value === selectedOption.value);
|
||||
setSelectedOption(option);
|
||||
|
|
@ -35,22 +55,76 @@ const JKJamTrackPlayer = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log('selectedOption', selectedOption);
|
||||
if (selectedOption.value === 'original') {
|
||||
const audioUrl = getMasterTrack();
|
||||
const audioUrl = getOriginalTrackUrl();
|
||||
setAudioUrl(audioUrl);
|
||||
if(audioRef.current)
|
||||
if (audioRef.current) {
|
||||
audioRef.current.load();
|
||||
}
|
||||
} else {
|
||||
//it's a mixdown
|
||||
getMixdown().then(audioUrl => {
|
||||
//see if there is a myPackage avaialble. if it is, it means the mixdown is ready
|
||||
//if not, it means the mixdown is still being processed
|
||||
|
||||
const myPackage = jamTrack.mixdowns
|
||||
.find(mix => mix.id === selectedOption.value)
|
||||
?.packages.find(p => p.file_type === 'mp3' && p.encrypt_type === null && p.sample_rate === SAMPLE_RATE);
|
||||
|
||||
console.log('_DEBUG_ myPackage', myPackage);
|
||||
|
||||
let retry = false;
|
||||
if (myPackage) {
|
||||
//let see if the mixdown is signed
|
||||
switch (myPackage.signing_state) {
|
||||
case 'QUIET_TIMEOUT':
|
||||
setRetryMessage('Custom mix never got created. Retry?');
|
||||
retry = true;
|
||||
break;
|
||||
case 'QUEUED_TIMEOUT':
|
||||
setRetryMessage('Custom mix was never built. Retry?');
|
||||
retry = true;
|
||||
break;
|
||||
case 'SIGNING_TIMEOUT':
|
||||
setRetryMessage('Custom mix took took long to build. Retry?');
|
||||
retry = true;
|
||||
break;
|
||||
case 'ERROR':
|
||||
setRetryMessage('Custom mix failed to build. Retry?');
|
||||
retry = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
setRetryMessage('Custom mix never got created. Retry?');
|
||||
retry = true;
|
||||
setAudioUrl(null);
|
||||
}
|
||||
|
||||
if (retry) {
|
||||
setShowRetry(true);
|
||||
return;
|
||||
}
|
||||
|
||||
getActiveMixdownUrl().then(audioUrl => {
|
||||
console.log('_DEBUG_ audioUrl', audioUrl);
|
||||
setAudioUrl(audioUrl);
|
||||
if(audioRef.current)
|
||||
if (audioRef.current) {
|
||||
audioRef.current.load();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [selectedOption]);
|
||||
|
||||
const getMasterTrack = () => {
|
||||
useEffect(() => {
|
||||
if (retryDownload && selectedOption) {
|
||||
const options = { id: selectedOption.value, file_type: 'mp3', encrypt_type: null, sample_rate: SAMPLE_RATE };
|
||||
dispatch(enqueueMyMixdown(options));
|
||||
}
|
||||
}, [retryDownload]);
|
||||
|
||||
const getOriginalTrackUrl = () => {
|
||||
const masterTrack = jamTrack.tracks.find(track => track.track_type === 'Master');
|
||||
if (masterTrack) {
|
||||
const audioUrl = masterTrack.preview_mp3_url;
|
||||
|
|
@ -58,14 +132,13 @@ const JKJamTrackPlayer = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const getMixdown = async () => {
|
||||
const getActiveMixdownUrl = async () => {
|
||||
const activeMix = jamTrack.mixdowns.find(mix => mix.id === selectedOption.value);
|
||||
const fp = await fpPromise;
|
||||
const result = await fp.get();
|
||||
const audioUrl =
|
||||
process.env.REACT_APP_API_BASE_URL +
|
||||
`/mixdowns/${activeMix.id}/download.mp3?file_type=mp3&sample_rate=48&mark=${result.visitorId}`;
|
||||
console.log('audioUrl', audioUrl);
|
||||
return audioUrl;
|
||||
};
|
||||
|
||||
|
|
@ -77,14 +150,35 @@ const JKJamTrackPlayer = () => {
|
|||
{audioUrl && (
|
||||
<figure>
|
||||
<audio controls style={{ width: '100%' }} ref={audioRef}>
|
||||
<source src={audioUrl} type={`audio/${audioUrl.split('.').pop()}`} />
|
||||
<source src={audioUrl} type={audioFileExtension(audioUrl)} />
|
||||
</audio>
|
||||
</figure>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<JKModalDialog
|
||||
show={showRetry}
|
||||
onToggle={() => setRetryDownload(!showRetry)}
|
||||
title="Retry Download?"
|
||||
showFooter={false}
|
||||
>
|
||||
<div className="d-flex flex-column">
|
||||
<p>{retryMessage}</p>
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={() => {
|
||||
setRetryDownload(true);
|
||||
setShowRetry(false);
|
||||
setRetryMessage('');
|
||||
}}
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
</div>
|
||||
</JKModalDialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JKJamTrackPlayer;
|
||||
export default JKJamTrackPlayer;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const JKMyJamTrackMixes = () => {
|
|||
const mixdownsLoadingStatus = useSelector(state => state.jamTrack.mixdownsLoadingStatus);
|
||||
const deleteMixdownStatus = useSelector(state => state.jamTrack.deleteMixdownStatus);
|
||||
const tempMixdownLoadingStatus = useSelector(state => state.jamTrack.tempMixdownLoadingStatus);
|
||||
const buildRetries = useSelector(state => state.jamTrack.buildRetries);
|
||||
|
||||
useEffect(() => {
|
||||
if (!jamTrack) {
|
||||
|
|
@ -74,6 +75,11 @@ const JKMyJamTrackMixes = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const isMixdownBuilding = mix => {
|
||||
const retry = buildRetries.find(retry => retry.mixdownId === mix.id);
|
||||
return mix.id === 'temp' || (retry && ['queued', 'enqueuing'].includes(retry.state))
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
|
|
@ -84,13 +90,13 @@ const JKMyJamTrackMixes = () => {
|
|||
<thead className="bg-200 text-900">
|
||||
<tr>
|
||||
<th>Mix</th>
|
||||
<th class="text-center">Actions</th>
|
||||
<th className="text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Full JamTrack</td>
|
||||
<td class="text-center">
|
||||
<td className="text-center">
|
||||
<a onClick={downloadJamTrack}>
|
||||
<FontAwesomeIcon icon="download" size="lg" className="mr-3" />
|
||||
</a>
|
||||
|
|
@ -99,8 +105,8 @@ const JKMyJamTrackMixes = () => {
|
|||
{mixes.map(mix => (
|
||||
<tr key={mix.id}>
|
||||
<td>{mix.name}</td>
|
||||
<td class="text-center">
|
||||
{mix.id === 'temp' ? (
|
||||
<td className="text-center">
|
||||
{isMixdownBuilding(mix) ? (
|
||||
<FontAwesomeIcon icon="spinner" size="lg" />
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -112,7 +118,7 @@ const JKMyJamTrackMixes = () => {
|
|||
disabled={deleteMixdownStatus === 'loading'}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<FontAwesomeIcon icon="trash" size="xl" />
|
||||
<FontAwesomeIcon icon="trash" size="lg" />
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -576,6 +576,18 @@ export const createMixdown = options => {
|
|||
});
|
||||
}
|
||||
|
||||
export const enqueueMixdown = options => {
|
||||
const { id, ...rest } = options;
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch(`/mixdowns/${id}/enqueue`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(rest)
|
||||
})
|
||||
.then(response => resolve(response))
|
||||
.catch(error => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
export const deleteMixdown = id => {
|
||||
return new Promise((resolve, reject) => {
|
||||
apiFetch(`/mixdowns/${id}`, {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { deleteMixdown, getJamTrack, createMixdown } from '../../helpers/rest';
|
||||
import { deleteMixdown, getJamTrack, createMixdown, enqueueMixdown } from '../../helpers/rest';
|
||||
|
||||
const initialState = {
|
||||
jamTrack: {},
|
||||
|
|
@ -8,27 +8,33 @@ const initialState = {
|
|||
deleteMixdownStatus: 'idle',
|
||||
newMixdownLoadingStatus: 'idle',
|
||||
tempMixdownLoadingStatus: 'idle',
|
||||
|
||||
enqueueLoadingStatus: 'idle',
|
||||
buildRetries: [],
|
||||
error: null
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchJamTrack = createAsyncThunk('jamTracks/fetchJamTrack', async(options, thunkAPI) => {
|
||||
const response = await getJamTrack(options)
|
||||
export const fetchJamTrack = createAsyncThunk('jamTracks/fetchJamTrack', async (options, thunkAPI) => {
|
||||
const response = await getJamTrack(options);
|
||||
return response.json();
|
||||
});
|
||||
|
||||
export const createMyMixdown = createAsyncThunk('jamTracks/createMixdown', async(options, thunkAPI) => {
|
||||
const response = await createMixdown(options)
|
||||
export const createMyMixdown = createAsyncThunk('jamTracks/createMixdown', async (options, thunkAPI) => {
|
||||
const response = await createMixdown(options);
|
||||
return response.json();
|
||||
});
|
||||
|
||||
export const removeMixdown = createAsyncThunk('jamTracks/removeMixdown', async(options, thunkAPI) => {
|
||||
export const removeMixdown = createAsyncThunk('jamTracks/removeMixdown', async (options, thunkAPI) => {
|
||||
console.log('removeMixdown', options);
|
||||
const { id } = options;
|
||||
const response = await deleteMixdown(id)
|
||||
const response = await deleteMixdown(id);
|
||||
return { id };
|
||||
});
|
||||
|
||||
export const enqueueMyMixdown = createAsyncThunk('jamTracks/enqueueMixdown', async (options, thunkAPI) => {
|
||||
const response = await enqueueMixdown(options);
|
||||
return response.json();
|
||||
});
|
||||
|
||||
export const jamTrackSlice = createSlice({
|
||||
name: 'jamTrack',
|
||||
initialState,
|
||||
|
|
@ -40,62 +46,101 @@ export const jamTrackSlice = createSlice({
|
|||
state.jamTrack.mixdowns = [...jamTrack.mixdowns, payload];
|
||||
state.tempMixdownLoadingStatus = 'succeeded';
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
extraReducers: builder => {
|
||||
builder
|
||||
.addCase(fetchJamTrack.pending, (state, action) => {
|
||||
state.jamTrackLoadingStatus = 'loading'
|
||||
state.mixdownsLoadingStatus = 'loading'
|
||||
state.jamTrackLoadingStatus = 'loading';
|
||||
state.mixdownsLoadingStatus = 'loading';
|
||||
})
|
||||
.addCase(fetchJamTrack.fulfilled, (state, action) => {
|
||||
state.jamTrack = action.payload
|
||||
state.jamTrackLoadingStatus = 'succeeded'
|
||||
state.jamTrack = action.payload;
|
||||
state.jamTrackLoadingStatus = 'succeeded';
|
||||
|
||||
if (action.payload.mixdowns) {
|
||||
state.mixdownsLoadingStatus = 'succeeded'
|
||||
state.mixdownsLoadingStatus = 'succeeded';
|
||||
}
|
||||
})
|
||||
.addCase(fetchJamTrack.rejected, (state, action) => {
|
||||
state.status = 'failed'
|
||||
state.jamTrackLoadingStatus = 'failed'
|
||||
state.mixdownsLoadingStatus = 'failed'
|
||||
state.status = 'failed';
|
||||
state.jamTrackLoadingStatus = 'failed';
|
||||
state.mixdownsLoadingStatus = 'failed';
|
||||
state.error = action.error.message;
|
||||
})
|
||||
.addCase(createMyMixdown.pending, (state, action) => {
|
||||
state.newMixdownLoadingStatus = 'loading'
|
||||
state.mixdownsLoadingStatus = 'loading'
|
||||
state.newMixdownLoadingStatus = 'loading';
|
||||
state.mixdownsLoadingStatus = 'loading';
|
||||
})
|
||||
.addCase(createMyMixdown.fulfilled, (state, action) => {
|
||||
state.jamTrack.mixdowns = [...state.jamTrack.mixdowns, action.payload];
|
||||
state.newMixdownLoadingStatus = 'succeeded'
|
||||
state.mixdownsLoadingStatus = 'succeeded'
|
||||
state.tempMixdownLoadingStatus = 'idle'
|
||||
state.newMixdownLoadingStatus = 'succeeded';
|
||||
state.mixdownsLoadingStatus = 'succeeded';
|
||||
state.tempMixdownLoadingStatus = 'idle';
|
||||
})
|
||||
.addCase(createMyMixdown.rejected, (state, action) => {
|
||||
state.error = action.error.message;
|
||||
state.newMixdownLoadingStatus = 'failed'
|
||||
state.tempMixdownLoadingStatus = 'idle'
|
||||
state.newMixdownLoadingStatus = 'failed';
|
||||
state.tempMixdownLoadingStatus = 'idle';
|
||||
})
|
||||
.addCase(removeMixdown.pending, (state, action) => {
|
||||
state.mixdownsLoadingStatus = 'loading'
|
||||
state.deleteMixdownStatus = 'loading'
|
||||
state.mixdownsLoadingStatus = 'loading';
|
||||
state.deleteMixdownStatus = 'loading';
|
||||
})
|
||||
.addCase(removeMixdown.fulfilled, (state, action) => {
|
||||
console.log('mixdown removed', action.payload)
|
||||
console.log('mixdown removed', action.payload);
|
||||
const mixdowns = state.jamTrack.mixdowns.filter(mix => mix.id !== action.payload.id);
|
||||
state.jamTrack.mixdowns = mixdowns;
|
||||
state.mixdowns = mixdowns;
|
||||
state.mixdownsLoadingStatus = 'succeeded'
|
||||
state.deleteMixdownStatus = 'succeeded'
|
||||
state.mixdownsLoadingStatus = 'succeeded';
|
||||
state.deleteMixdownStatus = 'succeeded';
|
||||
})
|
||||
.addCase(removeMixdown.rejected, (state, action) => {
|
||||
state.error = action.error.message;
|
||||
state.mixdownsLoadingStatus = 'failed'
|
||||
state.deleteMixdownStatus = 'failed'
|
||||
state.mixdownsLoadingStatus = 'failed';
|
||||
state.deleteMixdownStatus = 'failed';
|
||||
})
|
||||
.addCase(enqueueMyMixdown.pending, (state, action) => {
|
||||
const estimate = { mixdownId: action.meta.arg.id, state: 'enqueuing' };
|
||||
if (!state.buildRetries.find(estimate => estimate.mixdownId === action.meta.arg.id)) {
|
||||
state.buildRetries = [...state.buildRetries, estimate];
|
||||
} else {
|
||||
state.buildRetries = state.buildRetries.map(estimate => {
|
||||
if (estimate.mixdownId === action.meta.arg.id) {
|
||||
return { ...estimate, state: 'enqueuing' };
|
||||
}
|
||||
return estimate;
|
||||
});
|
||||
}
|
||||
})
|
||||
.addCase(enqueueMyMixdown.fulfilled, (state, action) => {
|
||||
const estimate = { mixdownId: action.payload.id, time: action.payload.queue_time, state: 'queued' };
|
||||
if (!state.buildRetries.find(estimate => estimate.mixdownId === action.payload.id)) {
|
||||
state.buildRetries = [...state.buildRetries, estimate];
|
||||
} else {
|
||||
state.buildRetries = state.buildRetries.map(estimate => {
|
||||
if (estimate.mixdownId === action.payload.id) {
|
||||
return { ...estimate, ...action.payload, state: 'queued' };
|
||||
}
|
||||
return estimate;
|
||||
});
|
||||
}
|
||||
})
|
||||
.addCase(enqueueMyMixdown.rejected, (state, action) => {
|
||||
const estimate = { mixdownId: action.meta.arg.id, state: 'failed' };
|
||||
if (!state.buildRetries.find(estimate => estimate.mixdownId === action.meta.arg.id)) {
|
||||
state.buildRetries = [...state.buildRetries, estimate];
|
||||
} else {
|
||||
state.buildRetries = state.buildRetries.map(estimate => {
|
||||
if (estimate.mixdownId === action.meta.arg.id) {
|
||||
return { ...estimate, state: 'failed' };
|
||||
}
|
||||
return estimate;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const { addMixdown } = jamTrackSlice.actions;
|
||||
export default jamTrackSlice.reducer;
|
||||
export default jamTrackSlice.reducer;
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ class ApiJamTrackMixdownsController < ApiController
|
|||
end
|
||||
|
||||
def enqueue
|
||||
debugger
|
||||
if @jam_track_right.valid?
|
||||
|
||||
begin
|
||||
|
|
|
|||
Loading…
Reference in New Issue