jamtrack player, cusmtom mixes and download mixes

This commit is contained in:
Nuwan 2024-08-12 11:48:04 +05:30
parent f904bdfc1c
commit 3b451427fc
13 changed files with 1014 additions and 166 deletions

View File

@ -23,7 +23,7 @@ describe('JamTracks Page', () => {
cy.get('input[type="search"]').should('exist');
});
describe('search artists', () => {
describe('search', () => {
beforeEach(() => {
//http://www.jamkazam.local:3000/api/jamtracks/autocomplete?match=ac+&limit=5
// cy.intercept('GET', /S+\/jamtracks\/autocomplete\?match=ac\S+/, {

View File

@ -0,0 +1,41 @@
/// <reference types="cypress" />
import makeFakeUser from '../../factories/user';
describe('JamTracks Page', () => {
beforeEach(() => {
const currentUser = makeFakeUser();
cy.stubAuthenticate({ id: currentUser.id });
cy.visit('/my-jamtracks');
});
it('should display the My JamTracks page', () => {
cy.get('.card-header h5').should('contain', 'My JamTracks');
});
it('should display the search bar', () => {
cy.get('input[type="search"]').should('exist');
});
describe('filter', () => {
beforeEach(() => {
cy.intercept('GET', /\S+\/jamtracks\/purchased\?page=1\&\S+/, { fixture: 'my_jamtracks_page1' }).as('getMyJamTracks_page1');
});
it('should display the JamTracks', () => {
cy.get('input[type="search"]').type('ba');
cy.wait('@getMyJamTracks_page1');
cy.get('[data-testid=myJamTrackList]').should('contain', 'Back in Black by AC DC');
});
it('clicking on a JamTrack should navigate to the JamTrack page', () => {
cy.get('input[type="search"]').type('ba');
cy.wait('@getMyJamTracks_page1');
cy.get('[data-testid=myJamTrackList] a').first().click();
cy.url().should('include', '/jamtracks/1');
});
})
});

View File

@ -0,0 +1,562 @@
{
"next": 10,
"count": 3756,
"jamtracks": [
{
"id": "1",
"name": "Back in Black",
"description": "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the AC DC song \"Back in Black\".",
"recording_type": "Cover",
"original_artist": "AC DC",
"songwriter": null,
"publisher": null,
"sales_region": "Worldwide",
"price": "1.99",
"version": "0",
"duration": 221,
"year": null,
"plan_code": "jamtrack-acdc-backinblack",
"allow_free": true,
"download_price": "2.99",
"upgrade_price": "1.0",
"tracks": [
{
"id": "103dea4d-f2a3-4414-8efe-d2ca378dda60",
"part": "Master Mix",
"instrument": {
"id": "computer",
"description": "Computer",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Master",
"position": 1000,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Master%20Mix-44100-preview-e9a5a63f34b4d523ee1842fff31f88ce.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Master%20Mix-44100-preview-25fcba7ace7086e3cb6b97d7e33ba72e.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Master%20Mix-44100-preview-9f0b072ed9f4b546e170fcdfb302137e.mp3"
},
{
"id": "2755cbdd-0476-4f3b-9ba1-e2da561ddb4e",
"part": "Lead",
"instrument": {
"id": "voice",
"description": "Voice",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 1,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Vocal%20-%20Lead-44100-preview-d35c328fc3936dad9a79fe102dc72950.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Vocal%20-%20Lead-44100-preview-b97b37651eae352fae3b3060918c7bcb.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Vocal%20-%20Lead-44100-preview-d35c328fc3936dad9a79fe102dc72950.aac"
},
{
"id": "0db7c4e1-5e8d-43fe-bd35-98acd8f68b26",
"part": "Drums",
"instrument": {
"id": "drums",
"description": "Drums",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 2,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Drums-44100-preview-03aadceb966caf40b96334bdd00234f6.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Drums-44100-preview-854914e3e0d6fdc5f0794325b0ecaead.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Drums-44100-preview-03aadceb966caf40b96334bdd00234f6.aac"
},
{
"id": "2cc79ab6-dab8-4905-85e6-0df5f8e087f1",
"part": "Bass",
"instrument": {
"id": "bass guitar",
"description": "Bass Guitar",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 3,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Bass-44100-preview-61c334ac87f811bd010ed3a910764c2e.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Bass-44100-preview-4066dafd7b72e9993b0c0fe1dba2b332.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Bass-44100-preview-61c334ac87f811bd010ed3a910764c2e.aac"
},
{
"id": "ed1d3487-3b32-442f-9c76-8a36fe3bb643",
"part": "Solo",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 4,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Solo-44100-preview-e9fe8572a9ac1022762642cbd92b3c34.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Solo-44100-preview-5fb058042254206cfa9fb4dcb0310b2c.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Solo-44100-preview-e9fe8572a9ac1022762642cbd92b3c34.aac"
},
{
"id": "f4ce7c91-7542-4e03-8fc2-68b31683d33e",
"part": "Rhythm 1",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 5,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%201-44100-preview-6b498479823d4131a01fa535817d5eab.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%201-44100-preview-f4cbb31dbde3e1a3e6012730a7e0e10f.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%201-44100-preview-6b498479823d4131a01fa535817d5eab.aac"
},
{
"id": "2d96c7ec-59f1-4d56-8a7f-7f4c75a0ccef",
"part": "Rhythm 2",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 6,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%202-44100-preview-a626d7c632560f6737e1b6024141289e.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%202-44100-preview-06a0e5af451f001f3465992efcd34ec0.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%202-44100-preview-a626d7c632560f6737e1b6024141289e.aac"
},
{
"id": "fce018ca-c897-4137-aa10-ef56a8e1831f",
"part": "Intro Scrapes",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 7,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Intro%20Scrapes-44100-preview-0ddfaa7154e9ba35d05d60477d5dd3e9.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Intro%20Scrapes-44100-preview-f53ce3c5f9dcf81af51560f52635fbb0.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Intro%20Scrapes-44100-preview-0ddfaa7154e9ba35d05d60477d5dd3e9.aac"
},
{
"id": "c9b3e0a8-4db0-4d0f-9769-398a6d56506e",
"part": "Main",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 8,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Main-44100-preview-234a224f75a97d7ff8f55442ece6fcde.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Main-44100-preview-828c9691f5435dea1c90182fa2618c9b.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Electric%20Guitar%20-%20Main-44100-preview-234a224f75a97d7ff8f55442ece6fcde.aac"
},
{
"id": "28c3df07-2a88-45a9-9ae6-3399a5d2eb20",
"part": "Sound FX",
"instrument": {
"id": "computer",
"description": "Computer",
"created_at": "2021-02-02T23:16:46.168Z",
"updated_at": "2021-02-02T23:16:46.168Z",
"popularity": 3
},
"track_type": "Track",
"position": 9,
"preview_mp3_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Sound%20Effects-44100-preview-6c859c73036cd55bceb65f19f2d2f2f3.mp3",
"preview_ogg_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Sound%20Effects-44100-preview-f840d8df4c7388f776477139025ee712.ogg",
"preview_aac_url": "https://jamkazam-dev-public.s3.amazonaws.com/jam_track_previews/AC%20DC/Back%20in%20Black/Back%20in%20Black%20Stem%20-%20Sound%20Effects-44100-preview-6c859c73036cd55bceb65f19f2d2f2f3.aac"
}
],
"licensor": null,
"genres": ["Rock", "Pop"],
"added_cart": true,
"can_download": true,
"purchased": true
},
{
"id": "531",
"name": "1234",
"description": "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the Feist song \"1234\".",
"recording_type": "Cover",
"original_artist": "Feist",
"songwriter": null,
"publisher": null,
"sales_region": "Worldwide",
"price": "1.99",
"version": "1",
"duration": 184,
"year": 2007,
"plan_code": "jamtrack-feist-1234",
"allow_free": true,
"download_price": "2.99",
"upgrade_price": "1.0",
"tracks": [
{
"id": "b834ce9c-2624-4977-a079-0b1b5d90ad6e",
"part": "Clicktrack",
"instrument": {
"id": "computer",
"description": "Computer",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Click",
"position": 10000,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234%20-%20Tency%20Music/1234%20Clicktrack-44100-preview-90bed87ea6402ab0f8d283ffe094c95f.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234%20-%20Tency%20Music/1234%20Clicktrack-44100-preview-7198bae1519e40827aff0bd704e2066b.ogg",
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234%20-%20Tency%20Music/1234%20Clicktrack-44100-preview-90bed87ea6402ab0f8d283ffe094c95f.aac"
},
{
"id": "b5a67d64-f94f-453e-9dab-691304275bc2",
"part": "Master Mix",
"instrument": {
"id": "computer",
"description": "Computer",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Master",
"position": 1000,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1%202%203%204%20Master%20Mix-44100-preview-5fe2a923a614f17dd9c6440b65c1e884.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1%202%203%204%20Master%20Mix-44100-preview-aaefd823d7deaa1bc862eb8ec1e53fe3.ogg",
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1%202%203%204%20Master%20Mix-44100-preview-6d3edecd174080e88e611984969f1b2d.aac"
},
{
"id": "27353eb1-9f2b-487b-9047-93f49446b4db",
"part": "Lead",
"instrument": {
"id": "voice",
"description": "Voice",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 1,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Lead-44100-preview-426d923380d10fbdf2d5a4e4108b0de2.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Lead-44100-preview-30eaef3aea1499493cbad76c5c3c8a50.ogg",
"preview_aac_url": null
},
{
"id": "78736d23-e657-4922-b145-9f7db40bb36e",
"part": "Backing",
"instrument": {
"id": "voice",
"description": "Voice",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 2,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Backing-44100-preview-c0fb7721f3c8cd3253c8c5a6d6b034dd.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Vocal%20-%20Backing-44100-preview-b7145437bbb6b01b566f10ef9009d001.ogg",
"preview_aac_url": null
},
{
"id": "1978c439-115b-4214-85a6-beec780310a1",
"part": "Drums",
"instrument": {
"id": "drums",
"description": "Drums",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 3,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Drums%20-%20Drums-44100-preview-da3d7e78b9a7b50bea7bcc8f13a7d4b0.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Drums%20-%20Drums-44100-preview-d320dc7600c6d9c08af19b813d6d8176.ogg",
"preview_aac_url": null
},
{
"id": "8457c55d-924d-4048-8c5f-46b5c7668552",
"part": "Bass",
"instrument": {
"id": "bass guitar",
"description": "Bass Guitar",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 4,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Bass%20Guitar%20-%20Bass-44100-preview-c4b1ce442a9645f6ead0f078afe48d3d.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Bass%20Guitar%20-%20Bass-44100-preview-8a0b0d664802015a219f3320d4d5c0cd.ogg",
"preview_aac_url": null
},
{
"id": "ca5d5597-1a19-45b1-9d1b-478f6b6c4b19",
"part": "Piano",
"instrument": {
"id": "piano",
"description": "Piano",
"created_at": "2014-02-16T13:10:07.059Z",
"updated_at": "2014-02-16T13:10:07.059Z",
"popularity": 2
},
"track_type": "Track",
"position": 5,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Piano%20-%20Piano-44100-preview-208fb9e053bb445c1d55ec443809211c.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Piano%20-%20Piano-44100-preview-8940844b3972b39ad9b786805496bc1b.ogg",
"preview_aac_url": null
},
{
"id": "755c6c48-fc6c-4dc8-bbf2-9dc0d0a5f2a8",
"part": "Acoustic",
"instrument": {
"id": "acoustic guitar",
"description": "Acoustic Guitar",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 6,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Acoustic%20Guitar%20-%20Acoustic-44100-preview-11bde0963b7a833072a49c1096a0b6ef.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Acoustic%20Guitar%20-%20Acoustic-44100-preview-92c44a32feac702880796fd9d6d5f883.ogg",
"preview_aac_url": null
},
{
"id": "508bcbaa-4d29-44c0-8cdd-f57fd3fee717",
"part": "Banjo",
"instrument": {
"id": "banjo",
"description": "Banjo",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 2
},
"track_type": "Track",
"position": 7,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Banjo%20-%20Banjo-44100-preview-6ed9db0acb0616651e297b0c057f453d.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Banjo%20-%20Banjo-44100-preview-1e18e0b352e4b05fefa35ce7fd9c84a1.ogg",
"preview_aac_url": null
},
{
"id": "6ef8af53-6615-450c-b78b-4310fb0e0395",
"part": "Strings",
"instrument": {
"id": "orchestra",
"description": "Orchestra",
"created_at": "2015-08-11T16:08:58.806Z",
"updated_at": "2015-08-11T16:08:58.806Z",
"popularity": 1
},
"track_type": "Track",
"position": 8,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Orchestra%20-%20Strings-44100-preview-7017822e5edaedbe91fea259cc29666c.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Orchestra%20-%20Strings-44100-preview-70f5d96ed08b1d6a625f21c8be3f691c.ogg",
"preview_aac_url": null
},
{
"id": "91427115-63be-49e6-adb1-078f4a7f7ae8",
"part": "Trumpet",
"instrument": {
"id": "trumpet",
"description": "Trumpet",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 2
},
"track_type": "Track",
"position": 9,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Trumpet%20-%20Trumpet-44100-preview-74d7ab8c5af40dc9768176df2afd691a.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Feist/1234/1234%20Stem%20-%20Trumpet%20-%20Trumpet-44100-preview-df093dfebc96d3c22c13ed8d14b44ed0.ogg",
"preview_aac_url": null
}
],
"licensor": { "id": "027d90a1-b126-4d5a-8af6-3167296dfb04", "name": "Tency Music" },
"genres": ["Folk", "Alternative Rock"],
"added_cart": false,
"can_download": false,
"purchased": false
},
{
"id": "1437",
"name": "18 And Life",
"description": "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the Skid Row song \"18 And Life\".",
"recording_type": "Cover",
"original_artist": "Skid Row",
"songwriter": null,
"publisher": null,
"sales_region": "Worldwide",
"price": "1.99",
"version": "1",
"duration": 227,
"year": 1989,
"plan_code": "jamtrack-skidrow-18andlife",
"allow_free": true,
"download_price": "2.99",
"upgrade_price": "1.0",
"tracks": [
{
"id": "7c515f02-bebd-4fdd-b30a-81b72ec277b9",
"part": "Clicktrack",
"instrument": {
"id": "computer",
"description": "Computer",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Click",
"position": 10000,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life%20-%20Tency%20Music/click-44100-preview-424086caaa67c89532635ef0970b2d75.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life%20-%20Tency%20Music/click-44100-preview-d30de7c1353826896110d3ce5f459b23.ogg",
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life%20-%20Tency%20Music/click-44100-preview-424086caaa67c89532635ef0970b2d75.aac"
},
{
"id": "db0a34e4-71e9-4342-b04a-e7c7f068b4fb",
"part": "Master Mix",
"instrument": {
"id": "computer",
"description": "Computer",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Master",
"position": 1000,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Master%20Mix-44100-preview-5bf5b2872e898dc43875f829e238b62b.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Master%20Mix-44100-preview-3ba1eb2b8ab936212cc299cfb8db34ac.ogg",
"preview_aac_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Master%20Mix-44100-preview-146e9a850f83a2f76dc31dc9489dc3aa.aac"
},
{
"id": "cc237bfd-5d87-413b-a460-55d17594f785",
"part": "Lead",
"instrument": {
"id": "voice",
"description": "Voice",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 1,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/ld-44100-preview-04a953ee8607a97e8533ea5adf4560a1.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/ld-44100-preview-3486ef50eb889b71208040a0a13de52f.ogg",
"preview_aac_url": null
},
{
"id": "a182cf33-1d61-41a8-8e9b-fd23d501885c",
"part": "Drums",
"instrument": {
"id": "drums",
"description": "Drums",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 2,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/drums-44100-preview-7ec143f7aa0dc019a2af48c6861c400b.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/drums-44100-preview-6fd8eacb6a8bd35a85e0d4a3a8ec120c.ogg",
"preview_aac_url": null
},
{
"id": "406c1f87-c6c1-406f-85a9-05768267ff46",
"part": "Bass",
"instrument": {
"id": "bass guitar",
"description": "Bass Guitar",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 3,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/bass-44100-preview-37326a28b8d55f4b61a00eb7406a4da9.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/bass-44100-preview-ff2e87a89ae7693fa8f15a3d065c5833.ogg",
"preview_aac_url": null
},
{
"id": "fa46fcd7-0647-4f94-93fd-bddb403abc8b",
"part": "Lead",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 4,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Lead-44100-preview-7b8ac822d2a0ccf341c4607919e5125a.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Lead-44100-preview-3d13650573ebe20f939a7e2d661f9fab.ogg",
"preview_aac_url": null
},
{
"id": "42e6c71e-715c-48b9-b000-fb7fdabd3fe3",
"part": "Rhythm Distorted",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 5,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%20Distorted-44100-preview-4f0312e6d429fa211c873ecd671c2629.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Rhythm%20Distorted-44100-preview-36e75c706ff27930a54be1159afafd9a.ogg",
"preview_aac_url": null
},
{
"id": "a16a69b1-2b89-4bca-8039-bb927c7f517f",
"part": "Arpeggios Clean",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 6,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Clean-44100-preview-f1f3df4288f9ac9bd8183303099f1a40.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Clean-44100-preview-f8da9761dbed9afd32d636fb0ba2f757.ogg",
"preview_aac_url": null
},
{
"id": "8246f3fb-6d35-4a31-9bcd-ef81fd39c66f",
"part": "Arpeggios Distorted",
"instrument": {
"id": "electric guitar",
"description": "Electric Guitar",
"created_at": "2013-01-03T01:57:43.040Z",
"updated_at": "2013-01-03T01:57:43.040Z",
"popularity": 3
},
"track_type": "Track",
"position": 7,
"preview_mp3_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Distorted-44100-preview-2bf523d0f429eaff366691fff0dbcb5f.mp3",
"preview_ogg_url": "https://jamkazam-public.s3.amazonaws.com/jam_track_previews/Skid%20Row/18%20And%20Life/18%20And%20Life%20Stem%20-%20Electric%20Guitar%20-%20Arpeggios%20Distorted-44100-preview-e25718a81d9d0a011746def84547f1e9.ogg",
"preview_aac_url": null
}
],
"licensor": { "id": "027d90a1-b126-4d5a-8af6-3167296dfb04", "name": "Tency Music" },
"genres": ["Metal", "Rock", "Hard Rock"],
"added_cart": false,
"can_download": false,
"purchased": false
}
]
}

View File

@ -1,13 +1,19 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { Table, Row, Col, Input, Button } from 'reactstrap';
import JKInstrumentIcon from '../profile/JKInstrumentIcon';
import Select from 'react-select';
import { useForm, Controller } from 'react-hook-form';
import { createMixdown } from '../../helpers/rest';
import { createMyMixdown, addMixdown } from '../../store/features/jamTrackSlice';
import { useDispatch, useSelector } from 'react-redux';
import { Scrollbar } from 'react-scrollbars-custom';
const JKCreateCustomMix = () => {
const MAX_MIXDOWNS = 5;
const JKCreateCustomMix = ({ jamTrack }) => {
const [tracks, setTracks] = useState([]);
//const [selectedTracks, setSelectedTracks] = useState([]);
const [mixdowns, setMixdowns] = useState([]);
const [selectedTracks, setSelectedTracks] = useState([]);
const dispatch = useDispatch();
const scrollbar = useRef();
const TEMPO_OPTIONS = [
{ value: '0', label: 'Original tempo' },
@ -61,6 +67,9 @@ const JKCreateCustomMix = ({ jamTrack }) => {
{ value: '12', label: 'Up 12 semitone' }
];
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
const newMixdownLoadingStatus = useSelector(state => state.jamTrack.newMixdownLoadingStatus);
const {
control,
handleSubmit,
@ -81,13 +90,13 @@ const JKCreateCustomMix = ({ jamTrack }) => {
mixdownTracks: []
}
});
const onSubmit = data => {
console.log('data', data);
const _tracks = [];
let countIn = false;
const selectedTracks = getValues('mixdownTracks');
const selected = getValues('mixdownTracks');
tracks.forEach(track => {
const muted = selectedTracks.includes(track.id);
const muted = selected.includes(track.id);
if (track.id === 'count-in') {
if (countIn === false) {
countIn = !muted;
@ -95,7 +104,7 @@ const JKCreateCustomMix = ({ jamTrack }) => {
} else {
_tracks.push({
id: track.id,
mute: selectedTracks.includes(track.id)
mute: selected.includes(track.id)
});
}
});
@ -105,39 +114,46 @@ const JKCreateCustomMix = ({ jamTrack }) => {
const mixData = {
jamTrackID: jamTrack.id,
name: data.mixName,
settings: { speed: data.tempo.value, pitch: data.pitch.value, 'count-in': countIn, tracks: _tracks }
settings: { speed: parseInt(data.tempo.value), pitch: parseInt(data.pitch.value), 'count-in': countIn, tracks: _tracks }
};
console.log('mixData', mixData);
const tempMixdown = {...mixData, id: 'temp', jam_track_id: jamTrack.id};
dispatch(addMixdown(tempMixdown));
createMixdown(mixData)
.then(response => {
console.log('mixdown created', response);
//TODO: add this mixdown to global state of jamtrack mixdowns
})
.catch(error => {
console.error('mixdown create error', error);
});
dispatch(createMyMixdown(mixData));
};
const toggleTrack = e => {
const trackId = e.target.value;
const selectedTracks = getValues('mixdownTracks');
if (selectedTracks.includes(trackId)) {
//setSelectedTracks(selectedTracks.filter(track => track !== trackId));
const selected = getValues('mixdownTracks');
if (selected.includes(trackId)) {
setValue('mixdownTracks', selectedTracks.filter(track => track !== trackId));
} else {
//setSelectedTracks([...selectedTracks, trackId]);
setValue('mixdownTracks', [...selectedTracks, trackId]);
}
setSelectedTracks(getValues('mixdownTracks'));
};
useEffect(() => {
if (jamTrack) {
setTracks(jamTrack.tracks.filter(track => track.track_type === 'Track' || track.track_type === 'Click'));
setMixdowns(jamTrack.mixdowns);
}
}, [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);
}
}
}, [newMixdownLoadingStatus]);
const trackName = track => {
if (track.track_type === 'Track' || track.track_type === 'Click') {
if (track.track_type === 'Click') {
@ -153,38 +169,42 @@ const JKCreateCustomMix = ({ jamTrack }) => {
}
};
const hasExceededMax = mixdowns.length >= MAX_MIXDOWNS;
return (
<>
<p>
Mute any tracks you like. Adjust the pitch or tempo of playback. Then give your mix a descriptive name, and
click the Create Mix button. It will take few minutes for us to create your custom mix.
</p>
<form onSubmit={handleSubmit(onSubmit)}>
<Row>
<Col>
<Table striped bordered className="fs--1">
<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>Mute</th>
<th class="text-center">Mute</th>
</tr>
</thead>
<tbody>
{tracks &&
tracks.map((track, index) => (
<tr key={index}>
<td>
<JKInstrumentIcon instrumentId={track.instrumentId} instrumentName={trackName(track)} />
<span className="ml-1">{trackName(track)}</span>
<span>{trackName(track)}</span>
</td>
<td>
<input type="checkbox" value={track.id} onClick={toggleTrack} />
<td class="text-center">
<input type="checkbox" value={track.id} onClick={toggleTrack} checked={ selectedTracks.includes(track.id)} disabled={hasExceededMax} />
</td>
</tr>
))}
</tbody>
</Table>
</Scrollbar>
<Controller
name="mixdownTracks"
control={control}
@ -201,28 +221,28 @@ const JKCreateCustomMix = ({ jamTrack }) => {
</Col>
</Row>
<Row className="mb-1">
<Col>Tempo</Col>
<Row className="mb-3 mt-3">
<Col sm={6} md={4} lg={3}>Tempo</Col>
<Col>
<Controller
name="tempo"
control={control}
render={({ field }) => <Select {...field} options={TEMPO_OPTIONS} />}
render={({ field }) => <Select {...field} options={TEMPO_OPTIONS} isDisabled={hasExceededMax} />}
/>
</Col>
</Row>
<Row className="mb-1">
<Col>Pitch</Col>
<Row className="mb-3">
<Col sm={6} md={4} lg={3}>Pitch</Col>
<Col>
<Controller
name="pitch"
control={control}
render={({ field }) => <Select {...field} options={PITCH_OPTIONS} />}
render={({ field }) => <Select {...field} options={PITCH_OPTIONS} isDisabled={hasExceededMax} />}
/>
</Col>
</Row>
<Row className="mb-1">
<Col>Mix Name</Col>
<Row className="mb-3">
<Col sm={6} md={4} lg={3}>Mix Name</Col>
<Col>
<Controller
name="mixName"
@ -230,7 +250,7 @@ const JKCreateCustomMix = ({ jamTrack }) => {
rules={{
required: 'Mix name is required'
}}
render={({ field }) => <Input {...field} />}
render={({ field }) => <Input {...field} disabled={hasExceededMax} />}
/>
{errors.mixName && (
<div className="text-danger">
@ -240,8 +260,10 @@ const JKCreateCustomMix = ({ jamTrack }) => {
</Col>
</Row>
<Row>
<Col>
<Button>Create Mix</Button>
<Col className='d-flex justify-content-end'>
<Button color="primary" disabled={newMixdownLoadingStatus === 'loading' || hasExceededMax }>
{newMixdownLoadingStatus === 'loading' ? 'Creating Mix...' : 'Create Mix'}
</Button>
</Col>
</Row>
</form>

View File

@ -1,37 +1,34 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useResponsive } from '@farfetch/react-context-responsive';
import { useParams } from 'react-router-dom';
import { Card, CardBody, Row, Col, Progress } from 'reactstrap';
import { Card, CardBody, Row, Col } from 'reactstrap';
import FalconCardHeader from '../common/FalconCardHeader';
import { getJamTrack, getUserDetail, postUserEvent, userOpenedJamTrackWebPlayer } from '../../helpers/rest';
import { getUserDetail, postUserEvent, userOpenedJamTrackWebPlayer } from '../../helpers/rest';
import JKJamTrackPlayer from './JKJamTrackPlayer';
import JKMyJamTrackMixes from './JKMyJamTrackMixes';
import JKCreateCustomMix from './JKCreateCustomMix';
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();
const [jamTrack, setJamTrack] = useState(null);
const [loading, setLoading] = useState(false);
const { currentUser } = useAuth();
const fetchJamTrack = async () => {
console.log('fetching jam track', id);
try {
setLoading(true);
const resp = await getJamTrack({ id });
const data = await resp.json();
console.log('jam track 123', data);
setJamTrack(data);
} catch (error) {
console.log('Error when fetching jam track', error);
} finally {
setLoading(false);
}
const dispatch = useDispatch();
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
const jamTrackLoadingStatus = useSelector(state => state.jamTrack.status);
const fetchJamTrackRecord = () => {
dispatch(fetchJamTrack({ id }));
};
const fetchUserDetail = async () => {
@ -58,31 +55,36 @@ const JKJamTrack = () => {
}, [currentUser, jamTrack]);
useEffect(() => {
fetchJamTrack();
fetchJamTrackRecord();
}, [id]);
return (
<Row>
<Col>
<Card className="mx-auto mb-4">
<FalconCardHeader title={t('jamtrack.player.title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3">{jamTrack && <JKJamTrackPlayer jamTrack={jamTrack} />}</CardBody>
</Card>
<Card className="mx-auto">
<FalconCardHeader title={t('jamtrack.my_mixes.title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3">{jamTrack && <JKMyJamTrackMixes jamTrack={jamTrack} />}</CardBody>
</Card>
</Col>
<Col>
<Card className="mx-auto">
<FalconCardHeader title={t('jamtrack.create_mix.title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3">
{ jamTrack && <JKCreateCustomMix jamTrack={jamTrack} /> }
</CardBody>
</Card>
</Col>
<Col />
</Row>
<>
{jamTrackLoadingStatus === 'loading' || jamTrackLoadingStatus == 'idel' ? (
<div>Loading...</div>
) : Object.keys(jamTrack).length ? (
<Row>
<Col sm={12} md={4}>
<Card className="mx-auto mb-4">
<FalconCardHeader title={t('jamtrack.player.title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3">{jamTrack && <JKJamTrackPlayer />}</CardBody>
</Card>
<Card className="mx-auto">
<FalconCardHeader title={t('jamtrack.my_mixes.title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3">{jamTrack && <JKMyJamTrackMixes />}</CardBody>
</Card>
</Col>
<Col sm={12} md={4} className={ greaterThan.sm ? null : 'mt-4' }>
<Card className="mx-auto">
<FalconCardHeader title={t('jamtrack.create_mix.title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3">{jamTrack && <JKCreateCustomMix />}</CardBody>
</Card>
</Col>
<Col />
</Row>
) : null}
</>
);
};

View File

@ -1,22 +1,20 @@
import React, { useState, useEffect, useRef } from 'react';
import Select from 'react-select';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Row, Col } from 'reactstrap';
import PropTypes from 'prop-types';
import { markMixdownActive } from '../../helpers/rest';
import FingerprintJS from '@fingerprintjs/fingerprintjs';
import { useSelector } from 'react-redux';
const JKJamTrackPlayer = ({ jamTrack }) => {
const JKJamTrackPlayer = () => {
const [options, setOptions] = useState([]);
const [selectedOption, setSelectedOption] = useState(null);
const fpPromise = FingerprintJS.load();
const [audioUrl, setAudioUrl] = useState(null);
const audioRef = useRef(null);
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
useEffect(() => {
if (jamTrack) {
console.log('_JamTrackPlayer_ jamTrack', jamTrack);
const opts = jamTrack.mixdowns.map(mix => ({ value: mix.id, label: mix.name }));
const opts = jamTrack.mixdowns.map(mix => ({ value: mix.id, label: mix.name })).filter(mix => mix.value !== 'temp');
opts.unshift({ value: 'original', label: 'Original' });
setOptions(opts);
if (jamTrack.last_mixdown_id) {
@ -37,8 +35,6 @@ const JKJamTrackPlayer = ({ jamTrack }) => {
return;
}
console.log('_JamTrackPlayer_ selectedOption', selectedOption);
if (selectedOption.value === 'original') {
const audioUrl = getMasterTrack();
setAudioUrl(audioUrl);
@ -56,7 +52,6 @@ const JKJamTrackPlayer = ({ jamTrack }) => {
const getMasterTrack = () => {
const masterTrack = jamTrack.tracks.find(track => track.track_type === 'Master');
console.log('_JamTrackPlayer_ master', masterTrack);
if (masterTrack) {
const audioUrl = masterTrack.preview_mp3_url;
return audioUrl;
@ -91,8 +86,4 @@ const JKJamTrackPlayer = ({ jamTrack }) => {
);
};
JKJamTrackPlayer.propTypes = {
jamTrack: PropTypes.object.isRequired
};
export default JKJamTrackPlayer;
export default JKJamTrackPlayer;

View File

@ -1,71 +1,121 @@
import React, { useState, useEffect } from 'react';
import { Table } from 'reactstrap';
import FingerprintJS from '@fingerprintjs/fingerprintjs';
import { removeMixdown } from '../../store/features/jamTrackSlice';
import { useDispatch, useSelector } from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const JKMyJamTrackMixes = ({ jamTrack }) => {
const JKMyJamTrackMixes = () => {
const [mixes, setMixes] = useState([]);
const fpPromise = FingerprintJS.load();
const dispatch = useDispatch();
const jamTrack = useSelector(state => state.jamTrack.jamTrack);
const mixdownsLoadingStatus = useSelector(state => state.jamTrack.mixdownsLoadingStatus);
const deleteMixdownStatus = useSelector(state => state.jamTrack.deleteMixdownStatus);
const tempMixdownLoadingStatus = useSelector(state => state.jamTrack.tempMixdownLoadingStatus);
useEffect(() => {
if(!jamTrack) {
if (!jamTrack) {
return;
}
setMixes(jamTrack.mixdowns)
}, []);
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');
if(!jamTrack.can_download) {
if (!jamTrack.can_download) {
console.log('Cannot download JamTrack');
return
return;
}
const fp = await fpPromise;
const result = await fp.get();
const redirectTo = `${process.env.REACT_APP_API_BASE_URL}/jamtracks/${jamTrack.id}/stems/master/download.mp3?file_type=mp3&download=1&mark=${result.visitorId}`;
window.open(redirectTo, '_blank');
}
const src = `${process.env.REACT_APP_API_BASE_URL}/jamtracks/${
jamTrack.id
}/stems/master/download.mp3?file_type=mp3&download=1&mark=${result.visitorId}`;
openDownload(src);
};
const downloadMix = async (mixId) => {
console.log('Download mixdown')
const downloadMix = async mixId => {
console.log('Download mixdown');
const mixdown = mixes.find(m => m.id === mixId);
const mixdownPackage = mixdown.packages.find(p => p.file_type === 'mp3');
if(mixdownPackage?.signing_state == 'SIGNED'){
if (mixdownPackage?.signing_state == 'SIGNED') {
const fp = await fpPromise;
const result = await fp.get();
const redirectTo = `${process.env.REACT_APP_API_BASE_URL}/mixdowns/${mixdown.id}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=${result.visitorId}`
window.open(redirectTo, '_blank');
const src = `${process.env.REACT_APP_API_BASE_URL}/mixdowns/${
mixdown.id
}/download.mp3?file_type=mp3&sample_rate=48&download=1&mark=${result.visitorId}`;
openDownload(src);
}else{
console.log('Mixdown not signed');
}
}
};
const deleteMix = (mixId) => {
if(window.confirm("Delete this custom mix?")){
console.log("Deleting mixdown", mixId)
const openDownload = async src => {
const iframe = document.createElement('iframe');
iframe.src = src;
iframe.style.display = 'none';
document.body.appendChild(iframe);
};
const deleteMix = mixId => {
if (window.confirm('Delete this custom mix?')) {
console.log('Deleting mixdown', mixId);
dispatch(removeMixdown({ id: mixId }));
}
}
};
return (
<>
<p>You can save a maximum of 5 mixes on JamKazam. If you need to make more mixes, download a mix to save it, then delete it to make more room</p >
<Table striped bordered className="fs--1" >
<p>
You can save a <strong>maximum of 5 mixes</strong> on JamKazam. If you need to make more mixes, download a mix
to save it, then delete it to make more room
</p>
<Table striped bordered className="fs--1">
<thead className="bg-200 text-900">
<tr>
<th>Mix</th>
<th>Actions</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>Full JamTrack</td>
<td>
<button onClick={downloadJamTrack}>Download</button>
<td class="text-center">
<a onClick={downloadJamTrack}>
<FontAwesomeIcon icon="download" size="lg" className="mr-3" />
</a>
</td>
</tr>
{mixes.map(mix => (
<tr key={mix.id}>
<td>{mix.name}</td>
<td>
<button onClick={() => downloadMix(mix.id)}>Download</button>
<button onClick={() => deleteMix(mix.id)}>Delete</button>
<td class="text-center">
{mix.id === 'temp' ? (
<FontAwesomeIcon icon="spinner" size="lg" />
) : (
<>
<a onClick={() => downloadMix(mix.id)} style={{ cursor: 'pointer' }}>
<FontAwesomeIcon icon="download" size="lg" className="mr-3" />
</a>
<a
onClick={() => deleteMix(mix.id)}
disabled={deleteMixdownStatus === 'loading'}
style={{ cursor: 'pointer' }}
>
<FontAwesomeIcon icon="trash" size="xl" />
</a>
</>
)}
</td>
</tr>
))}

View File

@ -3,33 +3,35 @@ import { Card, CardBody, ListGroup, ListGroupItem, FormGroup, Input, InputGroup,
import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next';
import { useResponsive } from '@farfetch/react-context-responsive';
import { getPurchasedJamTracks } from '../../helpers/rest';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import useOnScreen from '../../hooks/useOnScreen';
import { Link } from 'react-router-dom';
import { fetchMyJamTracks } from '../../store/features/myJamTracksSlice';
import { useDispatch, useSelector } from 'react-redux';
const JKMyJamTracks = () => {
const { t } = useTranslation('jamtracks');
const { greaterThan } = useResponsive();
const [jamTracks, setJamTracks] = useState([]);
const [loading, setLoading] = useState(false);
const [inputValue, setInputValue] = useState('');
const inputRef = React.createRef();
const containerRef = useRef(null);
const [lastJamTrackRef, setLastJamTrackRef] = useState(null);
const isIntersecting = useOnScreen({ current: lastJamTrackRef });
const [nextPage, setNextPage] = useState(1);
const dispatch = useDispatch();
const jamTracks = useSelector(state => state.myJamTrack.jamTracks);
const loadingStatus = useSelector(state => state.myJamTrack.status);
const nextPage = useSelector(state => state.myJamTrack.next);
const handleInputChange = e => {
const val = e.target.value;
setInputValue(val);
// const params = { page: 1, search: val };
// fetchJamTracks(params);
};
useEffect(() => {
const getMyJamTracks = setTimeout(() => {
fetchJamTracks({ page: 1, search: inputValue});
const getMyJamTracks = setTimeout(async () => {
await fetchJamTracks({ page: 1, search: inputValue});
}, 1000);
return () => clearTimeout(getMyJamTracks);
}, [inputValue]);
@ -37,37 +39,23 @@ const JKMyJamTracks = () => {
const fetchJamTracks = async (params) => {
const { page } = params;
try {
setLoading(true);
const resp = await getPurchasedJamTracks(params);
const data = await resp.json();
if (page === 1) {
setJamTracks(data.jamtracks);
}else{
setJamTracks(prev => [...prev, ...data.jamtracks]);
}
setNextPage(data.next);
dispatch(fetchMyJamTracks(params));
} catch (error) {
console.log('Error when fetching jam tracks', error);
} finally {
setLoading(false);
//setLoading(false);
}
};
useEffect(() => {
if (isIntersecting) {
if (nextPage && !loading && nextPage !== 1) {
if (nextPage && loadingStatus !== 'loading' && nextPage !== 1) {
const params = { page: nextPage };
fetchJamTracks(params);
}
}
}, [isIntersecting]);
useEffect(() => {
const params = { page: nextPage };
fetchJamTracks(params);
}, []);
const containerStyle = {
display: 'flex',
flexDirection: 'column',
@ -83,7 +71,7 @@ const JKMyJamTracks = () => {
<div className="d-flex align-items-center">
<InputGroup>
<InputGroupText style={{ borderRadius: '0', borderRight: '0' }}>
{loading ? (
{loadingStatus === 'loading' ? (
<span className="spinner-grow spinner-grow-sm" aria-hidden="true" />
) : (
<FontAwesomeIcon icon="search" transform="shrink-4 down-1" />
@ -102,18 +90,25 @@ const JKMyJamTracks = () => {
</div>
</FormGroup>
<div style={containerStyle} ref={containerRef}>
<ListGroup className="mt-1">
{jamTracks.map((jamTrack, index) => (
<div key={jamTrack.id} ref={ref => (jamTracks.length - 1 === index ? setLastJamTrackRef(ref) : null)}>
<ListGroupItem >
<Link to={`/jamtracks/${jamTrack.id}`}>{jamTrack.name}</Link>
{jamTrack.original_artist && ` by ${jamTrack.original_artist}`}
</ListGroupItem>
</div>
))}
</ListGroup>
{ loading && <div className="d-flex justify-content-center"> Loading... </div>}
{ loadingStatus === 'loading' ? (
<div className="d-flex justify-content-center"> Loading... </div>
) : loadingStatus === 'failed' ? (
<div className="d-flex justify-content-center"> Error loading jam tracks </div>
) : loadingStatus === 'succeeded' ? (
<ListGroup className="mt-1" data-testid="myJamTrackList">
{jamTracks && jamTracks.map((jamTrack, index) => (
<div key={jamTrack.id} ref={ref => (jamTracks.length - 1 === index ? setLastJamTrackRef(ref) : null)}>
<ListGroupItem >
<Link to={`/jamtracks/${jamTrack.id}`}>{jamTrack.name}</Link>
{jamTrack.original_artist && ` by ${jamTrack.original_artist}`}
</ListGroupItem>
</div>
))}
</ListGroup>
) : null }
</div>
</CardBody>
</Card>

View File

@ -35,8 +35,11 @@ import {
faPaperPlane as farPaperPlane,
faQuestionCircle as farQuestionCircle,
faSmileBeam as farSmileBeam,
faStar as farStar
faStar as farStar,
faMinus as farMinus,
far,
} from '@fortawesome/free-regular-svg-icons';
import {
faAlignLeft,
faAlignRight,
@ -164,7 +167,9 @@ import {
faPlayCircle,
faPauseCircle,
faStopCircle,
faInfoCircle
faInfoCircle,
faDownload,
} from '@fortawesome/free-solid-svg-icons';
//import { faAcousticGuitar } from "../icons";
@ -339,6 +344,7 @@ library.add(
farCircle,
farCopy,
farComment,
faMinus,
faDownload,
//faAcousticGuitar,
);

View File

@ -389,6 +389,7 @@ export const autocompleteJamTracks = (input, limit) => {
};
export const getPurchasedJamTracks = (options = {}) => {
options = { ...options, show_purchased_only: true };
return new Promise((resolve, reject) => {
apiFetch(`/jamtracks/purchased?${new URLSearchParams(options)}`)
.then(response => resolve(response))
@ -482,7 +483,7 @@ export const placeOrder = () => {
export const postUserEvent = (options = {}) => {
return new Promise((resolve, reject) => {
apiFetch(`users/event/record`, {
apiFetch(`/users/event/record`, {
method: 'POST',
body: JSON.stringify(options)
})
@ -493,7 +494,7 @@ export const postUserEvent = (options = {}) => {
export const userOpenedJamTrackWebPlayer = () => {
return new Promise((resolve, reject) => {
apiFetch(`/api/users/progression/opened_jamtrack_web_player`, {
apiFetch(`/users/progression/opened_jamtrack_web_player`, {
method: 'POST'
})
.then(response => resolve(response))
@ -530,3 +531,13 @@ export const createMixdown = options => {
.catch(error => reject(error));
});
}
export const deleteMixdown = id => {
return new Promise((resolve, reject) => {
apiFetch(`/mixdowns/${id}`, {
method: 'DELETE'
})
.then(response => resolve(response))
.catch(error => reject(error));
});
}

View File

@ -0,0 +1,101 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { deleteMixdown, getJamTrack, createMixdown } from '../../helpers/rest';
const initialState = {
jamTrack: {},
jamTrackLoadingStatus: 'idle',
mixdownsLoadingStatus: 'idle',
deleteMixdownStatus: 'idle',
newMixdownLoadingStatus: 'idle',
tempMixdownLoadingStatus: 'idle',
error: null
}
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)
return response.json();
});
export const removeMixdown = createAsyncThunk('jamTracks/removeMixdown', async(options, thunkAPI) => {
console.log('removeMixdown', options);
const { id } = options;
const response = await deleteMixdown(id)
return { id };
});
export const jamTrackSlice = createSlice({
name: 'jamTrack',
initialState,
reducers: {
addMixdown: (state, action) => {
const payload = action.payload;
const jamTrack = state.jamTrack;
if (jamTrack) {
state.jamTrack.mixdowns = [...jamTrack.mixdowns, payload];
state.tempMixdownLoadingStatus = 'succeeded';
}
},
},
extraReducers: builder => {
builder
.addCase(fetchJamTrack.pending, (state, action) => {
state.jamTrackLoadingStatus = 'loading'
state.mixdownsLoadingStatus = 'loading'
})
.addCase(fetchJamTrack.fulfilled, (state, action) => {
state.jamTrack = action.payload
state.jamTrackLoadingStatus = 'succeeded'
if (action.payload.mixdowns) {
state.mixdownsLoadingStatus = 'succeeded'
}
})
.addCase(fetchJamTrack.rejected, (state, action) => {
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'
})
.addCase(createMyMixdown.fulfilled, (state, action) => {
state.jamTrack.mixdowns = [...state.jamTrack.mixdowns, action.payload];
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'
})
.addCase(removeMixdown.pending, (state, action) => {
state.mixdownsLoadingStatus = 'loading'
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;
state.mixdowns = mixdowns;
state.mixdownsLoadingStatus = 'succeeded'
state.deleteMixdownStatus = 'succeeded'
})
.addCase(removeMixdown.rejected, (state, action) => {
state.error = action.error.message;
state.mixdownsLoadingStatus = 'failed'
state.deleteMixdownStatus = 'failed'
})
}
});
export const { addMixdown } = jamTrackSlice.actions;
export default jamTrackSlice.reducer;

View File

@ -0,0 +1,63 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { getPurchasedJamTracks } from '../../helpers/rest';
const initialState = {
jamTracks: [],
status: 'idle',
error: null,
next: null,
};
export const fetchMyJamTracks = createAsyncThunk('jamTracks/fetchMyJamTracks', async (options, thunkAPI) => {
const response = await getPurchasedJamTracks(options);
return response.json();
});
export const myJamTracksSlice = createSlice({
name: 'jamTracks',
initialState,
reducers: {
addJamTrack: (state, action) => {
state.jamTracks.push(action.payload);
},
// updateJamTrack: (state, action) => {
// const { id, name } = action.payload;
// const existingJamTrack = state.jamTracks.find(jamTrack => jamTrack.id === id);
// if (existingJamTrack) {
// existingJamTrack.name = name;
// }
// },
deleteJamTrack: (state, action) => {
const { id } = action.payload;
state.jamTracks = state.jamTracks.filter(jamTrack => jamTrack.id !== id);
}
},
extraReducers: builder => {
builder
.addCase(fetchMyJamTracks.pending, (state, action) => {
state.status = 'loading';
})
.addCase(fetchMyJamTracks.fulfilled, (state, action) => {
state.status = 'succeeded';
state.jamTracks = action.payload.jamtracks;
//--- amend the state to include only unique jamTracks
// const records = new Set([...state.jamTracks, ...action.payload.jamtracks]);
// const unique = [];
// records.map(x => (unique.filter(a => a.id === x.id).length > 0 ? null : unique.push(x)));
// state.jamTracks = unique;
// state.next = action.payload.next;
// state.status = 'succeeded';
//---
})
.addCase(fetchMyJamTracks.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
}
});
export const { addJamTrack, deleteJamTrack } = myJamTracksSlice.actions;
export default myJamTracksSlice.reducer;

View File

@ -8,6 +8,8 @@ import notificationReducer from './features/notificationSlice'
import latencyReducer from "./features/latencySlice"
import friendReducer from "./features/friendsSlice"
import sessionsHistoryReducer from "./features/sessionsHistorySlice"
import myJamTracksSlice from "./features/myJamTracksSlice"
import jamTrackSlice from "./features/jamTrackSlice"
export default configureStore({
reducer: {
@ -20,5 +22,7 @@ export default configureStore({
lobbyChat: lobbyChatMessagesReducer,
friend: friendReducer,
sessionsHistory: sessionsHistoryReducer, // this is the slice that holds the sessions history
myJamTrack: myJamTracksSlice,
jamTrack: jamTrackSlice
}
})