Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 101 additions & 86 deletions packages/common/src/adapters/track.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
type full,
type CrossPlatformFile,
Genre,
type NativeFile,
type Track,
HashId,
Expand Down Expand Up @@ -34,6 +35,17 @@ import { repostFromSDK } from './repost'
import { userMetadataFromSDK } from './user'
import { transformAndCleanList } from './utils'

const VALID_GENRES = new Set<string>(Object.values(Genre))

function toSdkGenre(
value: string | undefined | ''
): (typeof Genre)[keyof typeof Genre] | undefined {
if (value === undefined || value === '') return undefined
return VALID_GENRES.has(value)
? (value as (typeof Genre)[keyof typeof Genre])
: undefined
}

export const trackSegmentFromSDK = ({
duration,
multihash
Expand Down Expand Up @@ -352,92 +364,95 @@ export const stemTrackMetadataFromSDK = (
}
}

export const trackMetadataForUploadToSdk = (input: TrackMetadataForUpload) => ({
...camelcaseKeys(
pick(input, [
'license',
'isrc',
'iswc',
'is_unlisted',
'is_premium',
'premium_conditions',
'is_stream_gated',
'stream_conditions',
'is_download_gated',
'is_downloadable',
'is_original_available',
'is_scheduled_release',
'bpm',
'is_custom_bpm',
'is_custom_musical_key',
'comments_disabled',
'ddex_release_ids',
'parental_warning_type'
])
),
trackId: OptionalId.parse(input.track_id),
title: input.title,
description: squashNewLines(input.description) ?? undefined,
mood: input.mood,
tags: input.tags ?? undefined,
genre: input.genre || undefined,
releaseDate: input.release_date ? new Date(input.release_date) : undefined,
previewStartSeconds: input.preview_start_seconds ?? undefined,
previewCid: input.preview_cid ?? '',
ddexApp: input.ddex_app ?? '',
audioUploadId: input.audio_upload_id ?? undefined,
duration: input.duration ?? undefined,
musicalKey: input.musical_key
? formatMusicalKey(input.musical_key)
: undefined,
trackCid: input.track_cid ?? '',
origFileCid: input.orig_file_cid ?? '',
origFilename: input.orig_filename ?? undefined,
fieldVisibility: input.field_visibility
? mapValues(
camelcaseKeys(input.field_visibility),
(value: Maybe<boolean>) => (value === null ? undefined : value)
)
: undefined,
downloadConditions: input.download_conditions
? accessConditionsToSDK(input.download_conditions)
: null,
streamConditions: input.stream_conditions
? accessConditionsToSDK(input.stream_conditions)
: null,
remixOf: input.remix_of
? {
tracks: input.remix_of.tracks.map((track) => ({
parentTrackId: Id.parse(track.parent_track_id)
}))
}
: undefined,
stemOf: input.stem_of
? {
category: input.stem_of.category,
parentTrackId: Id.parse(input.stem_of.parent_track_id)
}
: undefined,
copyrightLine: input.copyright_line
? camelcaseKeys(input.copyright_line)
: undefined,
producerCopyrightLine: input.producer_copyright_line
? camelcaseKeys(input.producer_copyright_line)
: undefined,
rightsController: input.rights_controller
? camelcaseKeys(input.rights_controller)
: undefined,
resourceContributors: input.resource_contributors
? input.resource_contributors.map((contributor) =>
camelcaseKeys(contributor)
)
: undefined,
indirectResourceContributors: input.indirect_resource_contributors
? input.indirect_resource_contributors.map((contributor) =>
camelcaseKeys(contributor)
)
: undefined
})
export const trackMetadataForUploadToSdk = (input: TrackMetadataForUpload) => {
const sdkGenre = toSdkGenre(input.genre)
return {
...camelcaseKeys(
pick(input, [
'license',
'isrc',
'iswc',
'is_unlisted',
'is_premium',
'premium_conditions',
'is_stream_gated',
'stream_conditions',
'is_download_gated',
'is_downloadable',
'is_original_available',
'is_scheduled_release',
'bpm',
'is_custom_bpm',
'is_custom_musical_key',
'comments_disabled',
'ddex_release_ids',
'parental_warning_type'
])
),
trackId: OptionalId.parse(input.track_id),
title: input.title,
description: squashNewLines(input.description) ?? undefined,
mood: input.mood,
tags: input.tags ?? undefined,
...(sdkGenre !== undefined ? { genre: sdkGenre } : {}),
releaseDate: input.release_date ? new Date(input.release_date) : undefined,
previewStartSeconds: input.preview_start_seconds ?? undefined,
previewCid: input.preview_cid ?? '',
ddexApp: input.ddex_app ?? '',
audioUploadId: input.audio_upload_id ?? undefined,
duration: input.duration ?? undefined,
musicalKey: input.musical_key
? formatMusicalKey(input.musical_key)
: undefined,
trackCid: input.track_cid ?? '',
origFileCid: input.orig_file_cid ?? '',
origFilename: input.orig_filename ?? undefined,
fieldVisibility: input.field_visibility
? mapValues(
camelcaseKeys(input.field_visibility),
(value: Maybe<boolean>) => (value === null ? undefined : value)
)
: undefined,
downloadConditions: input.download_conditions
? accessConditionsToSDK(input.download_conditions)
: null,
streamConditions: input.stream_conditions
? accessConditionsToSDK(input.stream_conditions)
: null,
remixOf: input.remix_of
? {
tracks: input.remix_of.tracks.map((track) => ({
parentTrackId: Id.parse(track.parent_track_id)
}))
}
: undefined,
stemOf: input.stem_of
? {
category: input.stem_of.category,
parentTrackId: Id.parse(input.stem_of.parent_track_id)
}
: undefined,
copyrightLine: input.copyright_line
? camelcaseKeys(input.copyright_line)
: undefined,
producerCopyrightLine: input.producer_copyright_line
? camelcaseKeys(input.producer_copyright_line)
: undefined,
rightsController: input.rights_controller
? camelcaseKeys(input.rights_controller)
: undefined,
resourceContributors: input.resource_contributors
? input.resource_contributors.map((contributor) =>
camelcaseKeys(contributor)
)
: undefined,
indirectResourceContributors: input.indirect_resource_contributors
? input.indirect_resource_contributors.map((contributor) =>
camelcaseKeys(contributor)
)
: undefined
}
}

export const fileToSdk = (
file: Blob | File | NativeFile,
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/api/tan-query/lineups/useTrending.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const useTrending = (

const { data: sdkResponse = [] } = await sdk.tracks.getTrendingTracks({
time: timeRange,
genre: (genre as string) || undefined,
genre: genre ?? undefined,
userId: OptionalId.parse(currentUserId),
limit: currentPageSize,
offset: pageParam
Expand Down
4 changes: 2 additions & 2 deletions packages/common/src/api/tan-query/tracks/useUpdateTrack.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Id, type CrossPlatformFile } from '@audius/sdk'
import { type UpdateTrackRequestBody, Id, type CrossPlatformFile } from '@audius/sdk'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useDispatch, useStore } from 'react-redux'

Expand Down Expand Up @@ -63,7 +63,7 @@ export const useUpdateTrack = () => {
imageFile,
trackId: Id.parse(trackId),
userId: Id.parse(userId),
metadata: sdkMetadata,
metadata: sdkMetadata as UpdateTrackRequestBody,
onProgress: (_, progress) => {
if (progress.key === 'audio') {
dispatch(
Expand Down
10 changes: 3 additions & 7 deletions packages/common/src/api/tan-query/upload/usePublishStems.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { HashId, Id, type UploadResponse } from '@audius/sdk'
import { useMutation, useQueryClient } from '@tanstack/react-query'

import {
StemCategory,
Name,
type StemUpload,
type TrackMetadata
} from '~/models'
import { StemCategory, Name, type StemUpload } from '~/models'
import { ProgressStatus, uploadActions } from '~/store'
import type { TrackMetadataForUpload } from '~/store/upload/types'

import { getStemsQueryKey } from '../tracks/useStems'
import { useCurrentUserId } from '../users/account/useCurrentUserId'
Expand All @@ -27,7 +23,7 @@ type PublishStemsContext = Pick<
type PublishStemsParams = {
clientId: string
parentTrackId: number
parentMetadata: Omit<TrackMetadata, 'artwork' | 'track_id'>
parentMetadata: TrackMetadataForUpload
stems: {
metadata: StemUpload
audioUploadResponse: UploadResponse
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/api/tan-query/upload/useUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export const useUpload = (
? (t.metadata.artwork.source as 'unsplash' | 'original')
: 'original',
trackId: t.metadata.track_id!,
genre: t.metadata.genre,
genre: t.metadata.genre ?? '',
mood: t.metadata.mood ?? undefined,
size: t.file.size ?? -1,
fileType: t.file.type ?? '',
Expand Down
11 changes: 3 additions & 8 deletions packages/common/src/store/pages/trending/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
TRENDING_MONTH_PREFIX,
TRENDING_ALL_TIME_PREFIX
} from '~/store/pages/trending/lineup/actions'
import { ALL_GENRES, GENRES, Genre } from '~/utils/genres'
import { parseTrendingGenreFromUrl } from '~/utils/genres'

import { TimeRange, Track } from '../../../models'

Expand Down Expand Up @@ -94,20 +94,15 @@ const reducer =

if (history) {
const urlParams = new URLSearchParams(history.location.search)
const genre = urlParams.get('genre') as Genre | null
const genreParam = urlParams.get('genre')
const timeRange = urlParams.get('timeRange') as TimeRange | null
return {
...initialState,
trendingTimeRange:
timeRange && Object.values(TimeRange).includes(timeRange)
? timeRange
: TimeRange.WEEK,
trendingGenre:
genre === ALL_GENRES
? null
: genre && GENRES.includes(genre)
? genre
: null
trendingGenre: parseTrendingGenreFromUrl(genreParam)
}
}

Expand Down
3 changes: 2 additions & 1 deletion packages/common/src/store/upload/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export const isTrackForUpload = (
*/
export interface TrackMetadataForUpload
extends Omit<TrackMetadata, 'artwork' | 'track_id' | 'genre' | 'mood'> {
genre?: Genre | ''
/** API tracks use genre: string; form empty state is ''. */
genre?: Genre | '' | string
mood?: Mood | null
artwork?:
| Nullable<{
Expand Down
33 changes: 29 additions & 4 deletions packages/common/src/utils/genres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,35 @@ export const GENRES = [
...Object.values(ELECTRONIC_SUBGENRES)
] as const

export const convertGenreLabelToValue = (
genreLabel: (typeof GENRES)[number]
) => {
return genreLabel.replace(ELECTRONIC_PREFIX, '')
export type GenreLabel = (typeof GENRES)[number]

export const convertGenreLabelToValue = (genreLabel: GenreLabel): SDKGenre => {
return genreLabel.replace(ELECTRONIC_PREFIX, '') as SDKGenre
}

/**
* Converts a string from the trending genre UI (e.g. from URL or genre list)
* into Genre | null for Redux state. Returns null for null, empty, or ALL_GENRES.
*/
export const parseTrendingGenreFromUrl = (param: string | null): SDKGenre | null => {
if (param === null || param === '' || param === ALL_GENRES) return null
const genresList = GENRES as readonly string[]
if (!genresList.includes(param)) return null
const trimmed = param.startsWith(ELECTRONIC_PREFIX)
? param.slice(ELECTRONIC_PREFIX.length)
: param
return trimmed as SDKGenre
}

/**
* Converts a genre string from UI (e.g. from GenreSelectionList) to Genre | null
* for setTrendingGenre. Use when the value is known to come from GENRES.
*/
export const toTrendingGenre = (value: string | null): SDKGenre | null => {
if (value === null || value === '' || value === ALL_GENRES) return null
const genresList = GENRES as readonly string[]
if (!genresList.includes(value)) return null
return convertGenreLabelToValue(value as GenreLabel)
}

const NEWLY_ADDED_GENRES: string[] = []
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/utils/isLongFormContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import { Maybe } from './typeUtils'

export const isLongFormContent = (
track: Maybe<Pick<TrackMetadata, 'genre'> | null>
) => track?.genre === Genre.PODCASTS || track?.genre === Genre.AUDIOBOOKS
) => track?.genre === Genre.Podcasts || track?.genre === Genre.Audiobooks
6 changes: 4 additions & 2 deletions packages/common/src/utils/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import qs from 'query-string'
import { ID, SearchCategory, SearchFilters } from '~/models'

import { encodeUrlName, formatTickerForUrl } from './formatUtil'
import { convertGenreLabelToValue, Genre } from './genres'
import { convertGenreLabelToValue, type GenreLabel } from './genres'

// External Routes
export const PRIVACY_POLICY = '/legal/privacy-policy'
Expand Down Expand Up @@ -441,7 +441,9 @@ export const searchPage = (searchOptions: SearchOptions) => {
const { category, ...searchParams } = searchOptions

if (searchParams.genre) {
searchParams.genre = convertGenreLabelToValue(searchParams.genre) as Genre
searchParams.genre = convertGenreLabelToValue(
searchParams.genre as GenreLabel
)
}

// Build the search path - category is optional
Expand Down
Loading
Loading