diff --git a/packages/web/src/containers/upload-page/store/sagas.js b/packages/web/src/containers/upload-page/store/sagas.js index cacd703ecf..e6065e6660 100644 --- a/packages/web/src/containers/upload-page/store/sagas.js +++ b/packages/web/src/containers/upload-page/store/sagas.js @@ -42,6 +42,7 @@ import { Name } from 'services/analytics' import { getStems } from 'containers/upload-page/store/selectors' import { updateAndFlattenStems } from 'containers/upload-page/store/utils/stems' import { trackNewRemixEvent } from 'store/cache/tracks/sagas' +import { reportSuccessAndFailureEvents } from './utils/sagaHelpers' const MAX_CONCURRENT_UPLOADS = 4 const MAX_CONCURRENT_TRACK_SIZE_BYTES = 40 /* MB */ * 1024 * 1024 @@ -341,7 +342,12 @@ function* uploadWorker(requestChan, respChan, progressChan) { * * tracks is of type [{ track: ..., metadata: ... }] */ -export function* handleUploads(tracks, isCollection, isStem = false) { +export function* handleUploads({ + tracks, + isCollection, + isStem = false, + isAlbum = false +}) { const numWorkers = getNumWorkers(tracks.map(t => t.track.file)) // Map of shape {[trackId]: { track: track, metadata: object, artwork?: file, index: number }} @@ -426,6 +432,7 @@ export function* handleUploads(tracks, isCollection, isStem = false) { // Now wait for our workers to finish or error console.debug('Awaiting workers') let numOutstandingRequests = tracks.length + let numSuccessRequests = 0 // Technically not needed, but adding defensively const trackIds = [] const creatorNodeMetadata = [] const failedRequests = [] // Array of shape [{ id, timeout, message }] @@ -478,8 +485,9 @@ export function* handleUploads(tracks, isCollection, isStem = false) { trackIds[trackObj.index] = newId } - // Finally, decrement the request count + // Finally, decrement the request count and increase success count numOutstandingRequests -= 1 + numSuccessRequests += 1 } // Regardless of whether we stopped due to finishing @@ -488,6 +496,21 @@ export function* handleUploads(tracks, isCollection, isStem = false) { yield all(workerTasks.map(t => cancel(t))) let returnVal = { trackIds } + + // Report success + failure events + const uploadType = isCollection + ? isAlbum + ? 'album' + : 'playlist' + : 'multi_track' + yield reportSuccessAndFailureEvents({ + // Don't report non-uploaded tracks due to playlist upload abort + numSuccess: numSuccessRequests, + numFailure: failedRequests.length, + errors: failedRequests.map(r => r.message), + uploadType + }) + if (isCollection) { // If this was a collection and we didn't error, // now we go write all this out to chain @@ -603,7 +626,11 @@ function* uploadCollection(tracks, userId, collectionMetadata, isAlbum) { metadata } }) - const { trackIds, error } = yield call(handleUploads, tracksWithMetadata) + const { trackIds, error } = yield call(handleUploads, { + tracks: tracksWithMetadata, + isCollection: true, + isAlbum + }) // If we errored, return early if (error) { @@ -819,6 +846,14 @@ function* uploadSingleTrack(track) { ) const { confirmedTrack, error } = yield take(responseChan) + + yield reportSuccessAndFailureEvents({ + numSuccess: error ? 0 : 1, + numFailure: error ? 1 : 0, + uploadType: 'single_track', + errors: error ? [error] : [] + }) + if (error) { return } @@ -876,7 +911,11 @@ function* uploadSingleTrack(track) { export function* uploadStems({ parentTrackIds, stems }) { const updatedStems = updateAndFlattenStems(stems, parentTrackIds) - const uploadedTracks = yield call(handleUploads, updatedStems, false, true) + const uploadedTracks = yield call(handleUploads, { + tracks: updatedStems, + isCollection: false, + isStem: true + }) if (uploadedTracks.trackIds) { for (let i = 0; i < uploadedTracks.trackIds.length; i += 1) { const trackId = uploadedTracks.trackIds[i] @@ -898,7 +937,10 @@ function* uploadMultipleTracks(tracks) { metadata: track.metadata })) - const { trackIds } = yield call(handleUploads, tracksWithMetadata) + const { trackIds } = yield call(handleUploads, { + tracks: tracksWithMetadata, + isCollection: false + }) const stems = yield select(getStems) if (stems.length) { yield call(uploadStems, { diff --git a/packages/web/src/containers/upload-page/store/utils/sagaHelpers.ts b/packages/web/src/containers/upload-page/store/utils/sagaHelpers.ts new file mode 100644 index 0000000000..89416c18c8 --- /dev/null +++ b/packages/web/src/containers/upload-page/store/utils/sagaHelpers.ts @@ -0,0 +1,40 @@ +import { all, put, select } from 'redux-saga/effects' +import { range } from 'lodash' +import { make } from 'store/analytics/actions' +import { Name } from 'services/analytics' +import { getAccountUser } from 'store/account/selectors' + +export function* reportSuccessAndFailureEvents({ + numSuccess, + numFailure, + uploadType, + errors +}: { + numSuccess: number + numFailure: number + uploadType: 'single_track' | 'multi_track' | 'album' | 'playlist' + errors: string[] +}) { + const accountUser: ReturnType = yield select( + getAccountUser + ) + if (!accountUser) return + const primary = accountUser.creator_node_endpoint?.split(',')[0] + if (!primary) return + const successEvents = range(numSuccess).map(_ => + make(Name.TRACK_UPLOAD_SUCCESS, { + endpoint: primary, + kind: uploadType + }) + ) + + const failureEvents = range(numFailure).map(i => + make(Name.TRACK_UPLOAD_FAILURE, { + endpoint: primary, + kind: uploadType, + error: errors[i] + }) + ) + + yield all([...successEvents, ...failureEvents].map(e => put(e))) +} diff --git a/packages/web/src/services/analytics.ts b/packages/web/src/services/analytics.ts index 661d13b4bc..acc5f4303b 100644 --- a/packages/web/src/services/analytics.ts +++ b/packages/web/src/services/analytics.ts @@ -59,6 +59,8 @@ export enum Name { TRACK_UPLOAD_COPY_LINK = 'Track Upload: Copy Link', TRACK_UPLOAD_SHARE_WITH_FANS = 'Track Upload: Share with your fans', TRACK_UPLOAD_VIEW_TRACK_PAGE = 'Track Upload: View Track page', + TRACK_UPLOAD_SUCCESS = 'Track Upload: Success', + TRACK_UPLOAD_FAILURE = 'Track Upload: Failure', TRENDING_CHANGE_VIEW = 'Trending: Change view', TRENDING_PAGINATE = 'Trending: Fetch next page', FEED_CHANGE_VIEW = 'Feed: Change view', @@ -379,6 +381,19 @@ type TrackUploadCompleteUpload = { kind: 'tracks' | 'album' | 'playlist' } +type TrackUploadSuccess = { + eventName: Name.TRACK_UPLOAD_SUCCESS + endpoint: string + kind: 'single_track' | 'multi_track' | 'album' | 'playlist' +} + +type TrackUploadFailure = { + eventName: Name.TRACK_UPLOAD_FAILURE + endpoint: string + kind: 'single_track' | 'multi_track' | 'album' | 'playlist' + error?: string +} + type TrackUploadCopyLink = { eventName: Name.TRACK_UPLOAD_COPY_LINK uploadType: string @@ -693,6 +708,8 @@ export type AllTrackingEvents = | TrackUploadStartUploading | TrackUploadTrackUploading | TrackUploadCompleteUpload + | TrackUploadSuccess + | TrackUploadFailure | TrackUploadCopyLink | TrackUploadShareWithFans | TrackUploadViewTrackPage diff --git a/packages/web/src/store/application/ui/stemsUpload/sagas.ts b/packages/web/src/store/application/ui/stemsUpload/sagas.ts index fff9389d40..70f89db578 100644 --- a/packages/web/src/store/application/ui/stemsUpload/sagas.ts +++ b/packages/web/src/store/application/ui/stemsUpload/sagas.ts @@ -29,7 +29,11 @@ function* watchUploadStems() { } } }) - const { trackIds } = yield call(handleUploads, stemTracks, false, true) + const { trackIds } = yield call(handleUploads, { + tracks: stemTracks, + isCollection: false, + isStem: true + }) yield put(stemUploadsSucceeded({ parentId, batchUID }))