trptk-sanity/schemaTypes/release-type.ts
2026-02-24 12:22:46 +01:00

563 lines
15 KiB
TypeScript

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<string, unknown>) =>
[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,
}
},
},
})