Files
musicland/src/utils/projectStages.js
T
2026-09-04 13:04:27 +02:00

271 lines
7.4 KiB
JavaScript

import { Routes } from '../navigation/Routes'
export const CREATION_STAGE_KEYS = ['songwriter', 'beatmaker', 'director']
const getSectionLyrics = (section) => {
if (typeof section === 'string') {
return section.trim()
}
if (section && typeof section === 'object') {
const value = typeof section.lyrics === 'string' ? section.lyrics.trim() : ''
return value
}
return ''
}
const getLyricsCount = (project) => {
if (!project) return 0
const { lyrics } = project
if (Array.isArray(lyrics)) {
return lyrics.reduce((count, section) => (getSectionLyrics(section) ? count + 1 : count), 0)
}
if (lyrics && typeof lyrics === 'object') {
return Object.values(lyrics).reduce(
(count, value) => (getSectionLyrics(value) ? count + 1 : count),
0
)
}
return getSectionLyrics(lyrics) ? 1 : 0
}
const getStageMetadata = (project) => {
const lyricsCount = getLyricsCount(project)
const hasLyrics = !!(project?.hasLyrics || lyricsCount > 0)
const hasSongUrl = !!project?.songUrl
const cover = project?.cover || {}
const hasCoverUrl = !!project?.coverUrl
const hasPlaybackAsset = !!project?.playbackUrl
const hasPlayback = hasPlaybackAsset || hasSongUrl
const musicUrls = Array.isArray(project?.musicUrls) ? project.musicUrls.filter(Boolean) : []
const musicStatus = project?.musicStatus || null
const coverStatus = project?.coverStatus || null
const playbackStatus = project?.playbackStatus || null
const isPlaybackGenerating =
project?.playbackGenerating === true ||
project?.isPlaybackGenerating === true ||
playbackStatus === 'GENERATING'
const isPlaybackDraftReady = playbackStatus === 'DRAFT_READY'
return {
lyricsCount,
hasLyrics,
hasSongUrl,
hasCover: hasCoverUrl,
hasPlayback,
hasPlaybackAsset,
hasMusicDraft: musicUrls.length > 0,
musicStatus,
coverStatus,
playbackStatus,
isPlaybackDraftReady,
isMusicGenerating: musicStatus === 'GENERATING',
isCoverGenerating: coverStatus === 'GENERATING',
isPlaybackGenerating,
}
}
const getStageLockState = (key, metadata) => {
switch (key) {
case 'songwriter':
return metadata.hasSongUrl
case 'beatmaker':
if (metadata.hasCover) {
return true
}
return metadata.lyricsCount <= 0
case 'director':
return !metadata.hasCover
default:
return true
}
}
const getStageDescription = (key, metadata) => {
const {
hasLyrics,
hasSongUrl,
hasCover,
hasPlaybackAsset,
hasMusicDraft,
isMusicGenerating,
isPlaybackGenerating,
isPlaybackDraftReady,
} = metadata
switch (key) {
case 'songwriter':
if (!hasLyrics) {
return 'Commencer à créer des paroles pour votre chanson'
}
if (!hasSongUrl) {
return 'Continuer la création de paroles'
}
return 'Modifier les paroles créées'
case 'beatmaker':
if (metadata.hasCover) {
return 'Impossible de modifier la cover ou la production musicale'
}
if (!hasLyrics) {
return 'Écrivez vos paroles pour débloquer la musique'
}
if (isMusicGenerating) {
return 'Votre morceau est en cours de création'
}
if (hasSongUrl) {
if (!hasCover) {
return 'Créer ou modifier la cover de votre morceau'
}
return 'Modifier la cover ou la production musicale'
}
if (hasMusicDraft) {
return 'Modifier la production musicale'
}
return 'Commencer la création de votre morceau'
case 'director':
if (!hasCover) {
return 'Validez votre pochette pour débloquer le playback'
}
if (isPlaybackGenerating) {
return 'Votre playback est en cours de préparation'
}
if (isPlaybackDraftReady) {
return 'Votre playback IA est prêt à être validé'
}
if (hasPlaybackAsset) {
return 'Télécharger ou publier votre playback'
}
return 'Commencer la création de votre playback'
default:
return ''
}
}
const getStageLockedDescription = (key, metadata) => {
switch (key) {
case 'songwriter':
return metadata.hasSongUrl ? 'Impossible de modifier les paroles' : undefined
case 'beatmaker':
if (metadata.hasCover) {
return 'Impossible de modifier la cover ou la production musicale'
}
return metadata.lyricsCount > 0 ? undefined : 'Créez vos paroles pour débloquer le studio'
case 'director':
return undefined
default:
return undefined
}
}
const getStageCompletionState = (key, metadata) => {
switch (key) {
case 'songwriter':
return metadata.hasLyrics
case 'beatmaker':
return metadata.hasSongUrl || metadata.musicStatus === 'GENERATED'
case 'director':
return metadata.hasPlaybackAsset
default:
return false
}
}
export const getCreationStageStates = (project) => {
const metadata = getStageMetadata(project)
return CREATION_STAGE_KEYS.map((key, index) => {
const isLocked = getStageLockState(key, metadata)
const description = getStageDescription(key, metadata)
const lockedDescription = getStageLockedDescription(key, metadata)
return {
key,
index,
isLocked,
isCompleted: getStageCompletionState(key, metadata),
description: isLocked && lockedDescription ? lockedDescription : description,
lockedDescription,
}
})
}
export const getStageAction = (key, project) => {
switch (key) {
case 'songwriter':
return {
route: getLyricsCount(project) > 0 ? Routes.Lyrics : Routes.CreateLyricsWithAi,
}
case 'beatmaker': {
if (!project) {
return { route: Routes.ComposeSong }
}
const musicStatus = project?.musicStatus || null
const songUrl = project?.songUrl || null
const coverStatus = project?.coverStatus || null
const cover = project?.cover || {}
const hasCoverResult = !!cover?.result
const hasFinalCover = !!project?.coverUrl
if (musicStatus === 'GENERATING') {
return { route: Routes.GeneratingSong }
}
if (!songUrl) {
if (musicStatus === 'GENERATED') {
return { route: Routes.SongReady }
}
return { route: Routes.ComposeSong }
}
if (coverStatus === 'GENERATING') {
return { route: Routes.PouchReady }
}
if (!hasCoverResult) {
return { route: Routes.ChooseCoverType }
}
return { route: hasFinalCover ? Routes.ChooseCoverType : Routes.PouchReady }
}
case 'director':
if (project?.playbackUrl) {
return {
route: Routes.PlaybackDownload,
params: {
action: 'playback',
project,
},
}
}
return {
route: Routes.Playback,
params: project ? { project } : undefined,
}
default:
return {}
}
}
export const findFirstUnlockedStageIndex = (project) => {
const stages = getCreationStageStates(project)
if (!stages.length) {
return 0
}
const actionableIndex = stages.findIndex((stage) => !stage.isCompleted && !stage.isLocked)
if (actionableIndex !== -1) {
return actionableIndex
}
const firstIncomplete = stages.findIndex((stage) => !stage.isCompleted)
if (firstIncomplete !== -1) {
return firstIncomplete
}
const firstUnlocked = stages.findIndex((stage) => !stage.isLocked)
if (firstUnlocked !== -1) {
return firstUnlocked
}
return stages.length - 1
}