wip jamtrack page creating and playing custom jamtracks

This commit is contained in:
Nuwan 2024-12-17 16:22:31 +05:30
parent 08fafbf2de
commit c3563a9197
13 changed files with 400 additions and 186 deletions

View File

@ -160,6 +160,7 @@ function JKDashboardMain() {
registerFriendRequest();
registerFriendRequestAccepted();
registerChatMessageCallback();
registerSubscriptionCallback();
scriptLoaded.current = true;
};
@ -242,6 +243,14 @@ function JKDashboardMain() {
});
};
const registerSubscriptionCallback = () => {
window.JK.JamServer.registerMessageCallback(window.JK.MessageType.SUBSCRIPTION_MESSAGE, function(header, payload) {
console.log('registerSubscriptionCallback payload', payload);
console.log('registerSubscriptionCallback header', header);
handleSubscriptionMessage(payload);
});
};
const handleNotification = (payload, type) => {
console.log('handleNotification', payload, type);
const notification = {
@ -266,6 +275,10 @@ function JKDashboardMain() {
}
};
const handleSubscriptionMessage = payload => {
}
useScript(`${process.env.REACT_APP_CLIENT_BASE_URL}/client_scripts`, initJKScripts);
return (

View File

@ -1,19 +1,30 @@
import React, { useState, useEffect, useRef } from 'react';
import { Table, Row, Col, Input, Button } from 'reactstrap';
import Select from 'react-select';
import { useForm, Controller } from 'react-hook-form';
import { createMyMixdown, addMixdown } from '../../store/features/jamTrackSlice';
import { useForm, Controller, set } from 'react-hook-form';
import {
createMyMixdown,
enqueueMyMixdown,
addWatchedMixdown,
removeWatchedMixdown
} from '../../store/features/jamTrackSlice';
import { useDispatch, useSelector } from 'react-redux';
import { Scrollbar } from 'react-scrollbars-custom';
import { SAMPLE_RATE } from '../../helpers/jamTracks';
import JKModalDialog from '../common/JKModalDialog';
import { createAlert } from '../../helpers/rest';
import { useAuth } from '../../context/UserAuth';
const JKCreateCustomMix = () => {
const MAX_MIXDOWNS = 5;
const [tracks, setTracks] = useState([]);
const [mixdowns, setMixdowns] = useState([]);
const [selectedTracks, setSelectedTracks] = useState([]);
const [showQueueTime, setShowQueueTime] = useState(false);
const [enqueueTimeMessage, setEnqueueTimeMessage] = useState('');
const dispatch = useDispatch();
const scrollbar = useRef();
const { currentUser} = useAuth();
const TEMPO_OPTIONS = [
{ value: '0', label: 'Original tempo' },
@ -68,7 +79,12 @@ const JKCreateCustomMix = () => {
];
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
const mixdowns = useSelector(state => state.jamTrack.mixdowns);
const newMixdownLoadingStatus = useSelector(state => state.jamTrack.newMixdownLoadingStatus);
const awaitingMixdown = useSelector(state => state.jamTrack.awaitingMixdown);
const enqueuedMixdown = useSelector(state => state.jamTrack.enqueuedMixdown);
const enqueuedMixdowns = useSelector(state => state.jamTrack.enqueuedMixdowns);
const watchedMixdowns = useSelector(state => state.jamTrack.watchedMixdowns);
const {
control,
@ -114,11 +130,16 @@ const JKCreateCustomMix = () => {
const mixData = {
jamTrackID: jamTrack.id,
name: data.mixName,
settings: { speed: parseInt(data.tempo.value), pitch: parseInt(data.pitch.value), 'count-in': countIn, tracks: _tracks }
settings: {
speed: parseInt(data.tempo.value),
pitch: parseInt(data.pitch.value),
'count-in': countIn,
tracks: _tracks
}
};
const tempMixdown = {...mixData, id: 'temp', jam_track_id: jamTrack.id};
dispatch(addMixdown(tempMixdown));
//const tempMixdown = { ...mixData, id: 'temp', jam_track_id: jamTrack.id };
//dispatch(addMixdown(tempMixdown));
dispatch(createMyMixdown(mixData));
};
@ -137,23 +158,133 @@ const JKCreateCustomMix = () => {
useEffect(() => {
if (jamTrack) {
setTracks(jamTrack.tracks.filter(track => track.track_type === 'Track' || track.track_type === 'Click'));
setMixdowns(jamTrack.mixdowns);
}
//reset watched mixdowns on unload
return () => {
watchedMixdowns.forEach(subscription => {
window.JK.SubscriptionUtils.unsubscribe(subscription.type, subscription.id);
});
};
}, [jamTrack]);
useEffect(() => {
if (jamTrack) {
if(newMixdownLoadingStatus === 'succeeded') {
setValue('mixName', '');
setValue('tempo', TEMPO_OPTIONS[0]);
setValue('pitch', PITCH_OPTIONS[0]);
setValue('mixdownTracks', []);
setSelectedTracks([]);
setMixdowns(jamTrack.mixdowns);
}
if (newMixdownLoadingStatus === 'succeeded') {
setValue('mixName', '');
setValue('tempo', TEMPO_OPTIONS[0]);
setValue('pitch', PITCH_OPTIONS[0]);
setValue('mixdownTracks', []);
setSelectedTracks([]);
}
}, [newMixdownLoadingStatus]);
useEffect(() => {
if (awaitingMixdown) {
//enqueue the mixdown
console.log('Enqueueing mixdown', awaitingMixdown);
const options = { id: awaitingMixdown.id, file_type: 'mp3', encrypt_type: null, sample_rate: SAMPLE_RATE };
dispatch(enqueueMyMixdown(options));
}
}, [awaitingMixdown]);
useEffect(() => {
if (enqueuedMixdown) {
showEstimatedTime();
manageWatchedMixdowns();
}
}, [enqueuedMixdown]);
const showEstimatedTime = () => {
console.log('Enqueued mixdown', enqueuedMixdown);
const time = enqueuedMixdown.queue_time;
if (time === 0) {
setEnqueueTimeMessage('Your custom mix will take about 1 minute to be created.');
} else {
const guess = Math.ceil(time / 60.0);
if (guess === 1) {
setEnqueueTimeMessage('Your custom mix will take about 1 minute to be created.');
} else {
setEnqueueTimeMessage(`Your custom mix will take about ${guess} minutes to be created.`);
}
}
setShowQueueTime(true);
};
const manageWatchedMixdowns = () => {
console.log('Managing watched mixdowns');
mixdowns.forEach(mixdown => {
if (mixdown.oggPackage) {
if (mixdown.oggPackage.signing_state === 'SIGNED') {
console.log('unsubscribing to mixdown', mixdown);
unsubscribe(mixdown.oggPackage);
} else {
console.log('subscribing to mixdown', mixdown);
subscribe(mixdown.oggPackage);
}
}
});
};
const subscriptionKey = mixdown_package => {
return `mixdown-${mixdown_package.id}`;
};
const subscribe = mixdown_package => {
const key = subscriptionKey(mixdown_package);
console.log('watchedMixdowns', watchedMixdowns);
if (!watchedMixdowns[key]) {
window.JK.SubscriptionUtils.subscribe('mixdown', mixdown_package.id).on(
window.JK.EVENTS.SUBSCRIBE_NOTIFICATION,
onMixdownSubscriptionEvent
);
dispatch(addWatchedMixdown({ type: 'mixdown', id: mixdown_package.id }));
}
};
const unsubscribe = mixdown_package => {
const key = subscriptionKey(mixdown_package);
if (watchedMixdowns[key]) {
window.JK.SubscriptionUtils.unsubscribe('mixdown', mixdown_package.id);
dispatch(removeWatchedMixdown({ type: 'mixdown', id: mixdown_package.id }));
}
};
const onMixdownSubscriptionEvent = (e, data) => {
console.log("JamTrackStore: subscription notification received: type:" + data.type, data)
const mixdown_package_id = data.id;
mixdowns.forEach(mixdown => {
const mixdown_package = mixdown.packages.find(p => p.id === mixdown_package_id);
if (mixdown_package) {
mixdown_package.signing_state = data.body.signing_state
mixdown_package.packaging_steps = data.body.packaging_steps
mixdown_package.current_packaging_step = data.body.current_packaging_step
console.log("updated package with subscription notification event")
if(mixdown_package.signing_state === 'SIGNING_TIMEOUT' || mixdown_package.signing_state === 'QUEUED_TIMEOUT' || mixdown_package.signing_state === 'QUIET_TIMEOUT' || mixdown_package.signing_state === 'ERROR'){
reportError(mixdown)
}
}
})
}
const reportError = (mixdown) => {
const enqueued = enqueuedMixdowns[mixdown?.id]
if (!enqueued || enqueued.marked) {
return
}
enqueued.marked = true
const data = {
value: 1,
user_id: currentUser.id,
user_name: currentUser.name,
result: `signing state: ${mixdown.oggPackage?.signing_state}, client state: ${mixdown.client_state}`,
mixdown: mixdown.id,
package: mixdown.oggPackage?.id,
detail: mixdown.oggPackage?.error_reason
}
createAlert(`Mixdown Sync failed for ${currentUser.name}`, data)
}
const trackName = track => {
if (track.track_type === 'Track' || track.track_type === 'Click') {
if (track.track_type === 'Click') {
@ -180,30 +311,34 @@ const JKCreateCustomMix = () => {
<form onSubmit={handleSubmit(onSubmit)}>
<Row>
<Col>
<Scrollbar ref={scrollbar} style={{ width: '100%', height: 300 }} mobileNative={true}>
<Table striped bordered className="fs--1 mb-0">
<thead className="bg-200 text-900">
<tr>
<th>Tracks {tracks.length > 0 && <>({tracks.length})</>}</th>
<th className="text-center">Mute</th>
</tr>
</thead>
<tbody>
{tracks &&
tracks.map((track, index) => (
<tr key={index}>
<td>
<span>{trackName(track)}</span>
</td>
<td className="text-center">
<input type="checkbox" value={track.id} onChange={toggleTrack} checked={ selectedTracks.includes(track.id)} disabled={hasExceededMax} />
</td>
</tr>
))}
</tbody>
</Table>
<Scrollbar ref={scrollbar} style={{ width: '100%', height: 300 }} mobileNative={true}>
<Table striped bordered className="fs--1 mb-0">
<thead className="bg-200 text-900">
<tr>
<th>Tracks {tracks.length > 0 && <>({tracks.length})</>}</th>
<th className="text-center">Mute</th>
</tr>
</thead>
<tbody>
{tracks &&
tracks.map((track, index) => (
<tr key={index}>
<td>
<span>{trackName(track)}</span>
</td>
<td className="text-center">
<input
type="checkbox"
value={track.id}
onChange={toggleTrack}
checked={selectedTracks.includes(track.id)}
disabled={hasExceededMax}
/>
</td>
</tr>
))}
</tbody>
</Table>
</Scrollbar>
<Controller
name="mixdownTracks"
@ -211,7 +346,7 @@ const JKCreateCustomMix = () => {
rules={{
required: 'Select at least one track to create a mix'
}}
render={({ field }) => <Input type='hidden' {...field} />}
render={({ field }) => <Input type="hidden" {...field} />}
/>
{errors.mixdownTracks && (
<div className="text-danger">
@ -222,7 +357,9 @@ const JKCreateCustomMix = () => {
</Row>
<Row className="mb-3 mt-3">
<Col sm={6} md={4} lg={3}>Tempo</Col>
<Col sm={6} md={4} lg={3}>
Tempo
</Col>
<Col>
<Controller
name="tempo"
@ -232,7 +369,9 @@ const JKCreateCustomMix = () => {
</Col>
</Row>
<Row className="mb-3">
<Col sm={6} md={4} lg={3}>Pitch</Col>
<Col sm={6} md={4} lg={3}>
Pitch
</Col>
<Col>
<Controller
name="pitch"
@ -242,7 +381,9 @@ const JKCreateCustomMix = () => {
</Col>
</Row>
<Row className="mb-3">
<Col sm={6} md={4} lg={3}>Mix Name</Col>
<Col sm={6} md={4} lg={3}>
Mix Name
</Col>
<Col>
<Controller
name="mixName"
@ -260,13 +401,23 @@ const JKCreateCustomMix = () => {
</Col>
</Row>
<Row>
<Col className='d-flex justify-content-end'>
<Button color="primary" disabled={newMixdownLoadingStatus === 'loading' || hasExceededMax }>
<Col className="d-flex justify-content-end">
<Button color="primary" disabled={newMixdownLoadingStatus === 'loading' || hasExceededMax}>
{newMixdownLoadingStatus === 'loading' ? 'Creating Mix...' : 'Create Mix'}
</Button>
</Col>
</Row>
</form>
<JKModalDialog
show={showQueueTime}
onToggle={() => setShowQueueTime(!showQueueTime)}
title="Mixdown Queued"
showFooter={true}
>
<div className="d-flex flex-column">
<p>{enqueueTimeMessage}</p>
</div>
</JKModalDialog>
</>
);
};

View File

@ -8,14 +8,13 @@ import { getUserDetail, postUserEvent, userOpenedJamTrackWebPlayer } from '../..
import JKJamTrackPlayer from './JKJamTrackPlayer';
import JKMyJamTrackMixes from './JKMyJamTrackMixes';
import JKCreateCustomMix from './JKCreateCustomMix';
import JKJamTrackResourceLinks from './JKJamTrackResourceLinks';
import { useAuth } from '../../context/UserAuth';
import { fetchJamTrack } from '../../store/features/jamTrackSlice';
import { useDispatch, useSelector } from 'react-redux';
const JKJamTrack = () => {
console.log('JKJamTrack rendering');
const { t } = useTranslation('jamtracks');
const { greaterThan } = useResponsive();
const { id } = useParams();
@ -25,7 +24,7 @@ const JKJamTrack = () => {
const dispatch = useDispatch();
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
const jamTrackLoadingStatus = useSelector(state => state.jamTrack.status);
const jamTrackLoadingStatus = useSelector(state => state.jamTrack.jamTrackLoadingStatus);
const fetchJamTrackRecord = () => {
dispatch(fetchJamTrack({ id }));
@ -62,7 +61,7 @@ const JKJamTrack = () => {
<>
{jamTrackLoadingStatus === 'loading' || jamTrackLoadingStatus == 'idel' ? (
<div>Loading...</div>
) : Object.keys(jamTrack).length ? (
) : jamTrack ? (
<Row>
<Col sm={12} md={4}>
<Card className="mx-auto mb-4">
@ -84,41 +83,7 @@ const JKJamTrack = () => {
<Card className="mx-auto">
<FalconCardHeader title={t('jamtrack.help_resources.title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3">
<div className="mb-3">
<div>
<a target="_blank" href="https://jamkazam.freshdesk.com/support/solutions/articles/66000501472">
{t('jamtrack.help_resources.using_overview.title')}
</a>
</div>
{t('jamtrack.help_resources.using_overview.description')}
</div>
<div className="mb-3">
<div>
<a target="_blank" href="https://jamkazam.freshdesk.com/support/solutions/articles/66000125792">
{t('jamtrack.help_resources.using_mac_windows.title')}
</a>
</div>
{t('jamtrack.help_resources.using_mac_windows.description')}
</div>
<div className="mb-3">
<div>
<a target="_blank" href="https://www.jamkazam.com/client#/jamtrack">
{t('jamtrack.help_resources.jamtracks_home.title')}
</a>
</div>
{t('jamtrack.help_resources.jamtracks_home.description')}
</div>
<div className="mb-3">
<div>
<a target="_blank" href={`https://www.jamkazam.com/client?artist=${encodeURIComponent(
jamTrack.original_artist
)}#/jamtrack/search`}
>
{t('jamtrack.help_resources.see_more.title')}
</a>
</div>
{t('jamtrack.help_resources.see_more.description')}
</div>
<JKJamTrackResourceLinks jamTrack={jamTrack} />
</CardBody>
</Card>
</Col>

View File

@ -5,7 +5,7 @@ import FingerprintJS from '@fingerprintjs/fingerprintjs';
import { useSelector, useDispatch } from 'react-redux';
import JKModalDialog from '../common/JKModalDialog';
import { enqueueMyMixdown } from '../../store/features/jamTrackSlice';
import { set } from 'react-hook-form';
import { SAMPLE_RATE } from '../../helpers/jamTracks';
const JKJamTrackPlayer = () => {
const [options, setOptions] = useState([]);
@ -15,7 +15,6 @@ const JKJamTrackPlayer = () => {
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);
@ -24,7 +23,7 @@ const JKJamTrackPlayer = () => {
useEffect(() => {
if (jamTrack) {
const opts = jamTrack.mixdowns.filter(mix => mix.id !== 'temp').map(mix => ({ value: mix.id, label: mix.name }));
const opts = jamTrack.mixdowns.map(mix => ({ value: mix.id, label: mix.name }));
opts.unshift({ value: 'original', label: 'Original' });
setOptions(opts);
if (jamTrack.last_mixdown_id) {
@ -55,7 +54,7 @@ const JKJamTrackPlayer = () => {
return;
}
console.log('selectedOption', selectedOption);
if (selectedOption.value === 'original') {
const audioUrl = getOriginalTrackUrl();
setAudioUrl(audioUrl);
@ -64,19 +63,17 @@ const JKJamTrackPlayer = () => {
}
} else {
//it's a mixdown
//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);
console.log('_selectedOption', jamTrack);
let retry = false;
if (myPackage) {
//let see if the mixdown is signed
switch (myPackage.signing_state) {
if (jamTrack.mp3Package && jamTrack.mp3Package.signing_state) {
// SIGNED - the package is ready to be downloaded
// ERROR - the package was built unsuccessfully
// SIGNING_TIMEOUT - the package was kicked off to be signed, but it seems to have hung
// SIGNING - the package is currently signing
// QUEUED_TIMEOUT - the package signing job (JamTrackBuilder) was queued, but never executed
// QUEUED - the package is queued to sign
// QUIET - the jam_track_right exists, but no job has been kicked off; a job needs to be enqueued
switch (jamTrack.mp3Package.signing_state) {
case 'QUIET_TIMEOUT':
setRetryMessage('Custom mix never got created. Retry?');
retry = true;
@ -86,13 +83,17 @@ const JKJamTrackPlayer = () => {
retry = true;
break;
case 'SIGNING_TIMEOUT':
setRetryMessage('Custom mix took took long to build. Retry?');
setRetryMessage('Custom mix took long to build. Retry?');
retry = true;
break;
case 'ERROR':
setRetryMessage('Custom mix failed to build. Retry?');
retry = true;
break;
case 'QUIET':
setRetryMessage('Custom mix never got created. Retry?');
retry = true;
break;
default:
break;
}
@ -102,6 +103,7 @@ const JKJamTrackPlayer = () => {
setAudioUrl(null);
}
if (retry) {
setShowRetry(true);
return;
@ -120,6 +122,7 @@ const JKJamTrackPlayer = () => {
useEffect(() => {
if (retryDownload && selectedOption) {
const options = { id: selectedOption.value, file_type: 'mp3', encrypt_type: null, sample_rate: SAMPLE_RATE };
console.log('enqueueMyMixdown', options);
dispatch(enqueueMyMixdown(options));
}
}, [retryDownload]);

View File

@ -0,0 +1,49 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
const JKJamTrackResourceLinks = ({jamTrack}) => {
const { t } = useTranslation('jamtracks');
return (
<>
<div className="mb-3">
<div>
<a target="_blank" href="https://jamkazam.freshdesk.com/support/solutions/articles/66000501472">
{t('jamtrack.help_resources.using_overview.title')}
</a>
</div>
{t('jamtrack.help_resources.using_overview.description')}
</div>
<div className="mb-3">
<div>
<a target="_blank" href="https://jamkazam.freshdesk.com/support/solutions/articles/66000125792">
{t('jamtrack.help_resources.using_mac_windows.title')}
</a>
</div>
{t('jamtrack.help_resources.using_mac_windows.description')}
</div>
<div className="mb-3">
<div>
<a target="_blank" href="https://www.jamkazam.com/client#/jamtrack">
{t('jamtrack.help_resources.jamtracks_home.title')}
</a>
</div>
{t('jamtrack.help_resources.jamtracks_home.description')}
</div>
<div className="mb-3">
<div>
<a
target="_blank"
href={`https://www.jamkazam.com/client?artist=${encodeURIComponent(
jamTrack.original_artist
)}#/jamtrack/search`}
>
{t('jamtrack.help_resources.see_more.title')}
</a>
</div>
{t('jamtrack.help_resources.see_more.description')}
</div>
</>
);
};
export default JKJamTrackResourceLinks;

View File

@ -6,30 +6,13 @@ import { useDispatch, useSelector } from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const JKMyJamTrackMixes = () => {
const [mixes, setMixes] = useState([]);
const fpPromise = FingerprintJS.load();
const dispatch = useDispatch();
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
const mixdowns = useSelector(state => state.jamTrack.mixdowns);
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) {
return;
}
if (mixdownsLoadingStatus === 'succeeded') {
setMixes(jamTrack.mixdowns.filter(m => m.id !== 'temp'));
}
}, [mixdownsLoadingStatus]);
useEffect(() => {
if (tempMixdownLoadingStatus === 'succeeded') {
setMixes(jamTrack.mixdowns);
}
}, [tempMixdownLoadingStatus]);
const downloadJamTrack = async () => {
console.log('Downloading JamTrack');
@ -47,7 +30,7 @@ const JKMyJamTrackMixes = () => {
const downloadMix = async mixId => {
console.log('Download mixdown');
const mixdown = mixes.find(m => m.id === mixId);
const mixdown = mixdowns.find(m => m.id === mixId);
const mixdownPackage = mixdown.packages.find(p => p.file_type === 'mp3');
if (mixdownPackage?.signing_state == 'SIGNED') {
const fp = await fpPromise;
@ -56,7 +39,7 @@ const JKMyJamTrackMixes = () => {
mixdown.id
}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=${result.visitorId}`;
openDownload(src);
}else{
} else {
console.log('Mixdown not signed');
}
};
@ -75,10 +58,17 @@ const JKMyJamTrackMixes = () => {
}
};
const isMixdownBuilding = mix => {
const retry = buildRetries.find(retry => retry.mixdownId === mix.id);
return mix.id === 'temp' || (retry && ['queued', 'enqueuing'].includes(retry.state))
}
const mixdownPackage = mixdown => {
if (!mixdown.packages || mixdown.packages.length === 0) {
return null;
}
return mixdown.packages[0];
};
const isMixdownPackageReady = mixdown => {
const pkg = mixdownPackage(mixdown);
return pkg && pkg.signing_state === 'SIGNED';
};
return (
<>
@ -102,25 +92,36 @@ const JKMyJamTrackMixes = () => {
</a>
</td>
</tr>
{mixes.map(mix => (
<tr key={mix.id}>
<td>{mix.name}</td>
{mixdowns.map(mixdown => (
<tr key={mixdown.id}>
<td>
{mixdown.name} <br />
{mixdown.id} <br />
pkg-{mixdownPackage(mixdown)?.signing_state}
</td>
<td className="text-center">
{isMixdownBuilding(mix) ? (
<FontAwesomeIcon icon="spinner" size="lg" />
) : (
{isMixdownPackageReady(mixdown) ? (
<>
<a onClick={() => downloadMix(mix.id)} style={{ cursor: 'pointer' }}>
<a onClick={() => downloadMix(mixdown.id)} style={{ cursor: 'pointer' }}>
<FontAwesomeIcon icon="download" size="lg" className="mr-3" />
</a>
<a
onClick={() => deleteMix(mix.id)}
onClick={() => deleteMix(mixdown.id)}
disabled={deleteMixdownStatus === 'loading'}
style={{ cursor: 'pointer' }}
>
<FontAwesomeIcon icon="trash" size="lg" />
</a>
</>
) : (
<>
{mixdown.signing_state === 'QUEUED' || mixdown.signing_state === 'SIGNING' ? (
<FontAwesomeIcon icon="spinner" size="lg" className="mr-3" />
) : (
<FontAwesomeIcon icon="exclamation-circle" size="lg" className="mr-3" />
)}
</>
)}
</td>
</tr>

View File

@ -0,0 +1 @@
export const SAMPLE_RATE = 48;

View File

@ -1,4 +1,3 @@
import { error } from 'is_js';
import apiFetch from './apiFetch';
export const getMusicians = page => {
@ -622,4 +621,15 @@ export const deleteAvatar = id => {
.then(response => resolve(response))
.catch(error => reject(error));
});
}
}
export const createAlert = (subject, data) => {
return new Promise((resolve, reject) => {
apiFetch(`/alerts`, {
method: 'POST',
body: JSON.stringify({subject, data})
})
.then(response => resolve(response))
.catch(error => reject(error));
});
};

View File

@ -1,15 +1,19 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { deleteMixdown, getJamTrack, createMixdown, enqueueMixdown } from '../../helpers/rest';
import { SAMPLE_RATE } from '../../helpers/jamTracks';
const initialState = {
jamTrack: {},
jamTrack: null,
mixdowns: [],
awaitingMixdown: null,
enqueuedMixdown: null,
enqueuedMixdowns: [],
watchedMixdowns: [],
jamTrackLoadingStatus: 'idle',
mixdownsLoadingStatus: 'idle',
deleteMixdownStatus: 'idle',
newMixdownLoadingStatus: 'idle',
tempMixdownLoadingStatus: 'idle',
enqueueLoadingStatus: 'idle',
buildRetries: [],
error: null
};
@ -24,7 +28,6 @@ export const createMyMixdown = createAsyncThunk('jamTracks/createMixdown', async
});
export const removeMixdown = createAsyncThunk('jamTracks/removeMixdown', async (options, thunkAPI) => {
console.log('removeMixdown', options);
const { id } = options;
const response = await deleteMixdown(id);
return { id };
@ -35,6 +38,10 @@ export const enqueueMyMixdown = createAsyncThunk('jamTracks/enqueueMixdown', asy
return response.json();
});
export const jamTrackSlice = createSlice({
name: 'jamTrack',
initialState,
@ -46,6 +53,26 @@ export const jamTrackSlice = createSlice({
state.jamTrack.mixdowns = [...jamTrack.mixdowns, payload];
state.tempMixdownLoadingStatus = 'succeeded';
}
},
addWatchedMixdown: (state, action) => {
const payload = action.payload;
state.watchedMixdowns = [...state.watchedMixdowns, payload];
},
removeWatchedMixdown: (state, action) => {
const payload = action.payload;
state.watchedMixdowns = state.watchedMixdowns.filter(mix => mix.id !== payload.id);
},
updateMixdown: (state, action) => {
const payload = action.payload;
const jamTrack = state.jamTrack;
if (jamTrack) {
state.jamTrack.mixdowns = jamTrack.mixdowns.map(mix => {
if (mix.id === payload.id) {
return payload;
}
return mix;
});
}
}
},
extraReducers: builder => {
@ -57,23 +84,26 @@ export const jamTrackSlice = createSlice({
.addCase(fetchJamTrack.fulfilled, (state, action) => {
state.jamTrack = action.payload;
state.jamTrackLoadingStatus = 'succeeded';
if (action.payload.mixdowns) {
state.mixdowns = action.payload.mixdowns;
assignPackages(state);
state.mixdownsLoadingStatus = 'succeeded';
}
})
.addCase(fetchJamTrack.rejected, (state, action) => {
state.status = 'failed';
state.jamTrackLoadingStatus = 'failed';
state.jamTrack = null;
state.mixdownsLoadingStatus = 'failed';
state.mixdowns = [];
state.error = action.error.message;
})
.addCase(createMyMixdown.pending, (state, action) => {
state.newMixdownLoadingStatus = 'loading';
state.mixdownsLoadingStatus = 'loading';
state.tempMixdownLoadingStatus = 'loading';
})
.addCase(createMyMixdown.fulfilled, (state, action) => {
state.jamTrack.mixdowns = [...state.jamTrack.mixdowns, action.payload];
state.mixdowns = [...state.mixdowns, action.payload];
state.awaitingMixdown = action.payload;
state.newMixdownLoadingStatus = 'succeeded';
state.mixdownsLoadingStatus = 'succeeded';
state.tempMixdownLoadingStatus = 'idle';
@ -88,9 +118,7 @@ export const jamTrackSlice = createSlice({
state.deleteMixdownStatus = 'loading';
})
.addCase(removeMixdown.fulfilled, (state, action) => {
console.log('mixdown removed', action.payload);
const mixdowns = state.jamTrack.mixdowns.filter(mix => mix.id !== action.payload.id);
state.jamTrack.mixdowns = mixdowns;
const mixdowns = state.mixdowns.filter(mix => mix.id !== action.payload.id);
state.mixdowns = mixdowns;
state.mixdownsLoadingStatus = 'succeeded';
state.deleteMixdownStatus = 'succeeded';
@ -100,47 +128,36 @@ export const jamTrackSlice = createSlice({
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;
});
}
console.log('enqueueMyMixdown.fulfilled', action.payload);
const enqueue = { queue_time: action.payload.queue_time, signing_state: action.payload.signing_state };
state.enqueuedMixdown = enqueue;
state.enqueuedMixdowns = [...state.enqueuedMixdowns, enqueue];
state.mixdowns = state.mixdowns.map(mix => {
if (mix.id === action.payload.jam_track_mixdown_id) {
return { ...mix, ...enqueue };
}
return mix;
});
assignPackages(state);
})
.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;
//help functions
const pickMp3Package = (mixdown) => {
return mixdown.packages.find(p => p.file_type === 'mp3' && p.encrypt_type === null && p.sample_rate === SAMPLE_RATE);
};
const pickOggPackage = (mixdown) => {
return mixdown.packages.find(p => p.file_type === 'ogg' && p.encrypt_type === null && p.sample_rate === SAMPLE_RATE);
};
const assignPackages = (state) => {
state.jamTrack.mp3Package = state.mixdowns.map(pickMp3Package).filter(p => p);
state.jamTrack.oggPackage = state.mixdowns.map(pickOggPackage).filter(p => p);
};
export const { addMixdown, addWatchedMixdown, removeWatchedMixdown } = jamTrackSlice.actions;
export default jamTrackSlice.reducer;

View File

@ -627,6 +627,7 @@
}
server.registerMessageCallback = function (messageType, callback) {
console.log("_DEBUG_ jamserver server.registerMessageCallback", messageType, callback)
if (server.dispatchTable[messageType] === undefined) {
server.dispatchTable[messageType] = [];
}
@ -818,6 +819,8 @@
var jsMessage = JSON.stringify(message);
console.log("server.send(" + jsMessage + ")");
if( isLatencyTester() && (message.type == context.JK.MessageType.HEARTBEAT || message.type == context.JK.MessageType.PEER_MESSAGE)) {
logger.info("latency-tester: server.send(" + jsMessage + ")")
}

View File

@ -607,6 +607,7 @@
}
server.registerMessageCallback = function (messageType, callback) {
console.log("_DEBUG_ jamserver_copy server.registerMessageCallback", messageType, callback)
if (server.dispatchTable[messageType] === undefined) {
server.dispatchTable[messageType] = [];
}

View File

@ -72,6 +72,7 @@ class SubscriptionUtils
$server.on(@events.CONNECTION_DOWN, this.onConnectionDown)
onSubscriptionMessage: (header, payload) =>
console.log("onSubscriptionMessage", payload, header)
key = this.genKey(payload.type, payload.id)
watch = @subscriptions[key]

View File

@ -118,7 +118,6 @@ class ApiJamTrackMixdownsController < ApiController
end
def enqueue
debugger
if @jam_track_right.valid?
begin