import {defineArrayMember, defineField, defineType} from 'sanity' import {PlayIcon} from '@sanity/icons' import {TrackDisplayTitleInput} from '../components/TrackDisplayTitleInput' export const releaseType = defineType({ name: 'release', title: 'Release', type: 'document', icon: PlayIcon, groups: [ {name: 'main', title: 'Main', default: true}, {name: 'text', title: 'Text'}, {name: 'media', title: 'Media'}, {name: 'tracklist', title: 'Tracklist'}, {name: 'links', title: 'Streaming Links'}, {name: 'references', title: 'Tags'}, {name: 'reviews', title: 'Reviews'}, {name: 'creditsSpecs', title: 'Credits & Specs'}, {name: 'medusa', title: 'Medusa'}, ], fieldsets: [ {name: 'main', title: 'Release Information', options: {columns: 2}}, {name: 'links', title: 'Streaming Links', options: {columns: 2}}, ], fields: [ defineField({ name: 'name', title: 'Title', type: 'string', group: 'main', validation: (Rule) => Rule.required(), }), defineField({ name: 'albumArtist', title: 'Album Artist', type: 'string', group: 'main', validation: (Rule) => Rule.required(), }), defineField({ name: 'catalogNo', title: 'Catalog #', type: 'string', group: 'main', fieldset: 'main', validation: (Rule) => Rule.required(), }), defineField({ name: 'slug', title: 'Slug', type: 'slug', options: { source: (doc: Record) => [doc.albumArtist, doc.name].filter(Boolean).join(' '), maxLength: 200, }, validation: (Rule) => Rule.required(), group: 'main', fieldset: 'main', }), defineField({name: 'upc', title: 'UPC/EAN', type: 'string', group: 'main', fieldset: 'main'}), defineField({ name: 'releaseDate', title: 'Release Date', type: 'date', group: 'main', fieldset: 'main', validation: (Rule) => Rule.required(), }), defineField({ name: 'format', title: 'Format', type: 'string', group: 'main', fieldset: 'main', options: { list: [ {title: 'Single', value: 'single'}, {title: 'EP', value: 'ep'}, {title: 'Album', value: 'album'}, {title: 'Boxset', value: 'boxset'}, ], }, }), defineField({ name: 'label', title: 'Label', type: 'string', group: 'main', fieldset: 'main', options: { list: [ {title: 'TRPTK', value: 'TRPTK'}, {title: 'Other', value: 'other'}, ], }, }), defineField({ name: 'shortDescription', title: 'Short Description', type: 'text', rows: 4, group: 'text', }), defineField({ name: 'description', title: 'Description', type: 'array', of: [defineArrayMember({type: 'block'})], group: 'text', }), defineField({ name: 'albumCover', title: 'Album Cover', type: 'image', group: 'media', validation: (Rule) => Rule.required(), }), defineField({ name: 'bookletPdf', title: 'Booklet PDF', type: 'file', options: {accept: 'application/pdf'}, group: 'media', }), defineField({ name: 'tracks', title: 'Tracklist', group: 'tracklist', type: 'array', of: [ defineArrayMember({ name: 'track', title: 'Track', type: 'object', fields: [ defineField({ name: 'work', title: 'Work', type: 'reference', to: [{type: 'work'}], options: {disableNew: true}, }), defineField({ name: 'movement', title: 'Movement', type: 'string', }), defineField({ name: 'displayTitle', title: 'Display title', type: 'string', description: 'Use only when Work and Movement are empty.', components: { input: TrackDisplayTitleInput, }, }), defineField({ name: 'artist', title: 'Artist', description: 'If left empty, Album Artist will be used.', type: 'string', }), defineField({ name: 'duration', title: 'Duration', description: 'm:ss, mm:ss, h:mm:ss, or hh:mm:ss', type: 'string', validation: (Rule) => Rule.required().custom((value) => { if (typeof value !== 'string') return 'Duration is required' const trimmed = value.trim() const regex = /^(?:\d{1,2}:[0-5]\d:[0-5]\d|[0-5]?\d:[0-5]\d)$/ return regex.test(trimmed) ? true : 'Use m:ss, mm:ss, h:mm:ss, or hh:mm:ss (e.g. 3:07, 03:07, 1:03:07, 12:03:07)' }), }), defineField({ name: 'previewMp3', title: '30s preview (MP3)', type: 'file', options: {accept: 'audio/mpeg'}, }), ], preview: { select: { storedTitle: 'displayTitle', workTitle: 'work.title', movement: 'movement', mp3: 'previewMp3', composerName: 'work.composer.name', arrangerName: 'work.arranger.name', artist: 'artist', albumArtist: 'albumArtist', }, prepare({ storedTitle, workTitle, movement, mp3, composerName, arrangerName, artist, albumArtist, }) { const wt = (workTitle || '').trim() const mv = (movement || '').trim() const computed = wt && mv ? `${wt}: ${mv}` : wt || mv const title = (storedTitle && storedTitle.trim()) || computed || '(Untitled track)' const subtitleParts = [ mp3 ? '♫' : null, composerName || null, arrangerName ? `(arr. ${arrangerName})` : null, ].filter(Boolean) return {title, subtitle: subtitleParts.join(' ')} }, }, }), ], }), defineField({ name: 'officialUrl', title: 'Official Link', type: 'url', group: 'links', fieldset: 'links', }), defineField({ name: 'spotifyUrl', title: 'Spotify', type: 'url', group: 'links', fieldset: 'links', }), defineField({ name: 'appleMusicUrl', title: 'Apple Music', type: 'url', group: 'links', fieldset: 'links', }), defineField({ name: 'deezerUrl', title: 'Deezer', type: 'url', group: 'links', fieldset: 'links', }), defineField({ name: 'amazonMusicUrl', title: 'Amazon Music', type: 'url', group: 'links', fieldset: 'links', }), defineField({name: 'tidalUrl', title: 'Tidal', type: 'url', group: 'links', fieldset: 'links'}), defineField({name: 'qobuzUrl', title: 'Qobuz', type: 'url', group: 'links', fieldset: 'links'}), defineField({ name: 'nativeDsdUrl', title: 'NativeDSD', type: 'url', group: 'links', fieldset: 'links', }), defineField({ name: 'credits', title: 'Credits', group: 'creditsSpecs', type: 'array', of: [ defineArrayMember({ name: 'credit', title: 'Credit', type: 'object', fields: [ defineField({ name: 'role', title: 'Role', type: 'string', description: 'e.g. “Recording engineer”', }), defineField({ name: 'name', title: 'Name', type: 'text', rows: 2, description: 'You may use “ | ” to indicate multiple names.', }), ], preview: { select: {role: 'role', name: 'name'}, prepare({role, name}) { return {title: name || '(No role)', subtitle: role} }, }, }), ], }), defineField({ name: 'recordingDate', title: 'Recording date(s)', group: 'creditsSpecs', type: 'string', description: 'You may use “ | ” to indicate multiple dates.', }), defineField({ name: 'recordingLocation', title: 'Recording location(s)', group: 'creditsSpecs', type: 'string', description: 'You may use “ | ” to indicate multiple locations.', }), defineField({ name: 'recordingFormat', title: 'Recording format', group: 'creditsSpecs', type: 'string', options: { list: [ {title: 'PCM 352.8 kHz 24 bit', value: 'PCM 352.8 kHz 24 bit'}, {title: 'PCM 352.8 kHz 32 bit', value: 'PCM 352.8 kHz 32 bit'}, {title: 'DSD 11.2 MHz 1 bit', value: 'DSD 11.2 MHz 1 bit'}, ], }, }), defineField({ name: 'masteringFormat', title: 'Mastering format', group: 'creditsSpecs', type: 'string', options: { list: [ {title: 'PCM 352.8 kHz 32 bit', value: 'PCM 352.8 kHz 32 bit'}, {title: 'PCM 352.8 kHz 64 bit', value: 'PCM 352.8 kHz 64 bit'}, {title: 'DSD 11.2 MHz 1 bit', value: 'DSD 11.2 MHz 1 bit'}, ], }, }), defineField({ name: 'equipment', title: 'Equipment', group: 'creditsSpecs', type: 'array', of: [ defineArrayMember({ name: 'equipmentItem', title: 'Equipment item', type: 'object', fields: [ defineField({ name: 'type', title: 'Type', type: 'string', description: 'e.g. “Microphones”', }), defineField({ name: 'name', title: 'Name', type: 'text', rows: 2, description: 'You may use “ | ” to indicate multiple items.', }), ], preview: { select: {type: 'type', name: 'name'}, prepare({type, name}) { return {title: type || '(No type)', subtitle: name} }, }, }), ], }), defineField({ name: 'genre', title: 'Genre(s)', type: 'array', of: [ defineArrayMember({ type: 'string', options: { layout: 'tags' as const, list: [ {title: 'Early Music', value: 'earlyMusic'}, {title: 'Baroque', value: 'baroque'}, {title: 'Classical', value: 'classical'}, {title: 'Romantic', value: 'romantic'}, {title: 'Contemporary', value: 'contemporary'}, {title: 'World Music', value: 'worldMusic'}, {title: 'Jazz', value: 'jazz'}, {title: 'Crossover', value: 'crossover'}, {title: 'Electronic', value: 'electronic'}, {title: 'Minimal', value: 'minimal'}, {title: 'Pop / Rock', value: 'popRock'}, ], }, } as any), ], group: 'references', }), defineField({ name: 'instrumentation', title: 'Instrumentation(s)', type: 'array', of: [ defineArrayMember({ type: 'string', options: { layout: 'tags' as const, list: [ {title: 'Solo', value: 'solo'}, {title: 'Chamber', value: 'chamber'}, {title: 'Ensemble', value: 'ensemble'}, {title: 'Orchestral', value: 'orchestra'}, {title: 'Vocal / Choral', value: 'vocalChoral'}, ], }, } as any), ], group: 'references', }), defineField({ name: 'artists', title: 'Artist(s)', group: 'references', type: 'array', of: [ defineArrayMember({ type: 'reference', to: [{type: 'artist'}], }), ], }), defineField({ name: 'reviews', title: 'Reviews', group: 'reviews', type: 'array', of: [ defineArrayMember({ type: 'object', name: 'review', fields: [ defineField({ name: 'quote', title: 'Quote', type: 'text', rows: 4, }), defineField({ name: 'author', title: 'Author', type: 'string', }), ], preview: { select: { quote: 'quote', author: 'author', }, prepare({quote, author}) { return { title: quote ? quote.length > 60 ? quote.substring(0, 60) + '...' : quote : '(Empty review)', subtitle: author || '', } }, }, }), ], }), defineField({ name: 'availableVariants', title: 'Available Variants', type: 'array', group: 'medusa', of: [defineArrayMember({type: 'string'})], options: { list: [ {title: 'CD', value: 'cd'}, {title: 'SACD', value: 'sacd'}, {title: 'LP', value: 'lp'}, {title: 'Stereo FLAC 88/24', value: '88k24b2ch'}, {title: 'Stereo FLAC 176/24', value: '176k24b2ch'}, {title: 'Stereo FLAC 352/24', value: '352k24b2ch'}, {title: 'Stereo FLAC 352/32', value: '352k32b2ch'}, {title: 'Surround FLAC 88/24', value: '88k24b5ch'}, {title: 'Surround FLAC 176/24', value: '176k24b5ch'}, {title: 'Surround FLAC 352/24', value: '352k24b5ch'}, {title: 'Surround FLAC 352/32', value: '352k32b5ch'}, {title: 'Dolby Atmos, DTS:X & Auro-3D in MKV', value: 'mkv'}, {title: 'Auro-3D FLAC', value: 'a3d'}, {title: 'Dolby Atmos ADM 48kHz', value: 'adm48'}, {title: 'Dolby Atmos ADM 96kHz', value: 'adm96'}, {title: 'HD Video', value: 'hd'}, {title: '4K Video', value: '4k'}, ], }, }), ], orderings: [ { title: 'Release Date (latest first)', name: 'releaseDateDesc', by: [{field: 'releaseDate', direction: 'desc'}], }, { title: 'Release Date (oldest first)', name: 'releaseDateAsc', by: [{field: 'releaseDate', direction: 'asc'}], }, { title: 'Catalog # (Asc.)', name: 'catalogNoAsc', by: [{field: 'catalogNo', direction: 'asc'}], }, { title: 'Catalog # (Desc.)', name: 'catalogNoDesc', by: [{field: 'catalogNo', direction: 'desc'}], }, ], preview: { select: { title: 'name', artist: 'albumArtist', catNo: 'catalogNo', media: 'albumCover', }, prepare({title, artist, catNo, media}) { return { title: title || '(Untitled release)', subtitle: artist ? artist : '', media, } }, }, })