đź‘‹ Next.js Conf 2024: Come build, party, run, and connect with us! See all events

Podcast

By Knut Melvær

Schemas for running a podcast backend on Sanity.io

Podcast

// podcast.js
import languages from './languages';

export default {
  name: 'podcast',
  title: 'Podcast',
  description: 'Create and configure a podcast',
  type: 'document',
  fields: [
    {
      name: 'title',
      type: 'string',
      required: true,
      description:
        'Remember that if your title is too long, it may be truncated in various podcatchers-'
    },
    {
      name: 'subtitle',
      type: 'string',
      description: 'That catchy tagline.'
    },
    {
      name: 'slug',
      title: 'Podcast slug',
      type: 'slug',
      description: 'For when you need to refer to your podcast in a url.',
      options: {
        source: 'title',
        slugify: input =>
          input
            .toLowerCase()
            .replace(/\s+/g, '-')
            .slice(0, 200)
      }
    },
    {
      name: 'description',
      type: 'text',
      required: true,
      description:
        'What is this podcast about and why should people subscribe to it?'
    },
    {
      name: 'coverArt',
      title: 'Cover art',
      type: 'image',
      required: true,
      description:
        'The image should be either jpg or png. Preferably 3000 x 3000, minimum 1400 x 1400 pixels.'
    },
    {
      name: 'copyright',
      type: 'string',
      description: 'Who owns the rights to this podcast?'
    },
    {
      name: 'language',
      type: 'string',
      description: 'What language is this podcast in?',
      options: {
        list: languages
      }
    },
    {
      name: 'explicit',
      type: 'boolean',
      description:
        'Do you need to warn parents about the content in this podcast? (You can set this for individual episodes to)'
    },
    {
      name: 'itunes',
      title: 'iTunes settings',
      type: 'itunes',
      description: 'iTunes has extra stuff',
    }
  ],
  preview: {
    select: {
      title: 'title',
      subtitle: 'subtitle',
      description: 'description',
      media: 'coverArt'
    }
  }
};

Categories

// iTunes Categories
const categories = [
{ value: "Arts", title: "Arts" },
{ value: "Arts > Design", title: "Arts > Design" },
{ value: "Arts > Fashion & Beauty", title: "Arts > Fashion & Beauty" },
{ value: "Arts > Food", title: "Arts > Food" },
{ value: "Arts > Literature", title: "Arts > Literature" },
{ value: "Arts > Performing Arts", title: "Arts > Performing Arts" },
{ value: "Arts > Visual Arts", title: "Arts > Visual Arts" },
{ value: "Business", title: "Business" },
{ value: "Business > Business News", title: "Business > Business News" },
{ value: "Business > Careers", title: "Business > Careers" },
{ value: "Business > Investing", title: "Business > Investing" },
{ value: "Business > Management & Marketing", title: "Business > Management & Marketing" },
{ value: "Business > Shopping", title: "Business > Shopping" },
{ value: "Comedy", title: "Comedy" },
{ value: "Education", title: "Education" },
{ value: "Education > Education", title: "Education > Education" },
{ value: "Education > Education Technology", title: "Education > Education Technology" },
{ value: "Education > Higher Education", title: "Education > Higher Education" },
{ value: "Education > K-12", title: "Education > K-12" },
{ value: "Education > Language Courses", title: "Education > Language Courses" },
{ value: "Education > Training", title: "Education > Training" },
{ value: "Games & Hobbies", title: "Games & Hobbies" },
{ value: "Games & Hobbies > Automotive", title: "Games & Hobbies > Automotive" },
{ value: "Games & Hobbies > Aviation", title: "Games & Hobbies > Aviation" },
{ value: "Games & Hobbies > Hobbies", title: "Games & Hobbies > Hobbies" },
{ value: "Games & Hobbies > Other Games", title: "Games & Hobbies > Other Games" },
{ value: "Games & Hobbies > Video Games", title: "Games & Hobbies > Video Games" },
{ value: "Government & Organizations", title: "Government & Organizations" },
{ value: "Government & Organizations > Local", title: "Government & Organizations > Local" },
{ value: "Government & Organizations > National", title: "Government & Organizations > National" },
{ value: "Government & Organizations > Non-Profit", title: "Government & Organizations > Non-Profit" },
{ value: "Government & Organizations > Regional", title: "Government & Organizations > Regional" },
{ value: "Health", title: "Health" },
{ value: "Health > Alternative Health", title: "Health > Alternative Health" },
{ value: "Health > Fitness & Nutrition", title: "Health > Fitness & Nutrition" },
{ value: "Health > Self-Help", title: "Health > Self-Help" },
{ value: "Health > Sexuality", title: "Health > Sexuality" },
{ value: "Kids & Family", title: "Kids & Family" },
{ value: "Music", title: "Music" },
{ value: "News & Politics", title: "News & Politics" },
{ value: "Religion & Spirituality", title: "Religion & Spirituality" },
{ value: "Religion & Spirituality > Buddhism", title: "Religion & Spirituality > Buddhism" },
{ value: "Religion & Spirituality > Christianity", title: "Religion & Spirituality > Christianity" },
{ value: "Religion & Spirituality > Hinduism", title: "Religion & Spirituality > Hinduism" },
{ value: "Religion & Spirituality > Islam", title: "Religion & Spirituality > Islam" },
{ value: "Religion & Spirituality > Judaism", title: "Religion & Spirituality > Judaism" },
{ value: "Religion & Spirituality > Other", title: "Religion & Spirituality > Other" },
{ value: "Religion & Spirituality > Spirituality", title: "Religion & Spirituality > Spirituality" },
{ value: "Science & Medicine", title: "Science & Medicine" },
{ value: "Science & Medicine > Medicine", title: "Science & Medicine > Medicine" },
{ value: "Science & Medicine > Natural Sciences", title: "Science & Medicine > Natural Sciences" },
{ value: "Science & Medicine > Social Sciences", title: "Science & Medicine > Social Sciences" },
{ value: "Society & Culture", title: "Society & Culture" },
{ value: "Society & Culture > History", title: "Society & Culture > History" },
{ value: "Society & Culture > Personal Journals", title: "Society & Culture > Personal Journals" },
{ value: "Society & Culture > Philosophy", title: "Society & Culture > Philosophy" },
{ value: "Society & Culture > Places & Travel", title: "Society & Culture > Places & Travel" },
{ value: "Sports & Recreation", title: "Sports & Recreation" },
{ value: "Sports & Recreation > Amateur", title: "Sports & Recreation > Amateur" },
{ value: "Sports & Recreation > College & High School", title: "Sports & Recreation > College & High School" },
{ value: "Sports & Recreation > Outdoor", title: "Sports & Recreation > Outdoor" },
{ value: "Sports & Recreation > Professional", title: "Sports & Recreation > Professional" },
{ value: "Technology", title: "Technology" },
{ value: "Technology > Gadgets", title: "Technology > Gadgets" },
{ value: "Technology > Tech News", title: "Technology > Tech News" },
{ value: "Technology > Podcasting", title: "Technology > Podcasting" },
{ value: "Technology > Software How-To", title: "Technology > Software How-To" },
{ value: "TV & Film", title: "TV & Film" },
];

export default {
  name: 'categories',
  title: 'Categories',
  type: 'object',
  description:
    'Each podcast can belong to 3 separate categories, and will be ranked separately in each.',
  fieldsets: [
    {
      title: 'Optional categories',
      name: 'categories',
      options: {
        collapsable: true
      }
    }
  ],
  fields: ['first', 'secondary', 'tertiary'].map((category, index) => ({
    name: `${category}Category`,
    type: 'string',
    description: 'Where should this podcast be organized in iTunes?',
    options: {
      list: categories
    },
    fieldset: index ? 'categories' : null
  }))
}

Episode

export default {
  name: 'episode',
  title: 'Episode',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: 'Title',
      required: true,
      description: 'Remember that long titles can be truncated in podcast apps',
      type: 'string'
    },
    {
      name: 'podcast',
      description: 'Choose podcast(s) to publish this episode in',
      type: 'array',
      of: [{ type: 'reference', weak: true, to: [{ type: 'podcast' }] }]
    },
    {
      name: 'schedule',
      type: 'schedule',
      title: 'Publish schedule',
    },
    {
      name: 'file',
      title: 'Podcast media file',
      description:
        'Most podcatchers support .mp3, but other audio-formats may work as well',
      type: 'file'
    },
    {
      name: 'fileUrl',
      title: 'External location for podcast media file',
      description: 'For when you host your podcast media file elsewhere',
      type: 'url'
    },
    {
      name: 'duration',
      title: 'Duration',
      description: 'HH:MM:SS',
      type: 'string'
    },
    {
      name: 'subtitle',
      type: 'string',
      title: 'Subtitle',

    },
    {
      name: 'explicit',
      title: 'Explicit content',
      type: 'boolean'
    },
    {
      name: 'summary',
      title: 'Summary',
      description: 'An episode summary is a string containing one or more descriptive sentences summarizing your episode for potential listeners. You can specify up to 4000 characters.',
      type: 'text'
    },
    {
      name: 'description',
      title: 'Description',
      description: `An episode description is a string containing one or more sentences describing your episode to potential listeners. You can specify up to 4000 characters.`,
      type: 'text',
      validation: Rule => Rule.max(4000)
    },
    {
      name: 'content',
      title: 'Content',
      description: 'An episode note. Where encoded is a string containing information about your episode.',
      type: 'array',
      of: [{
        type: 'block'
      }]
    },
    {
      name: 'linkList',
      title: 'Link list',
      description: 'A more structured way to add links for show notes. Will be compiled at the end of the episode content field in a podcast RSS feed',
      type: 'array',
      of: [
        {
          type: 'linkListItem'
        }
      ]
    },
    {
      name: 'slug',
      title: 'Episode slug',
      type: 'slug',
      description: 'When you need to refer to your podcast episode in a url',
      options: {
        source: 'title',
        slugify: input =>
          input
            .toLowerCase()
            .replace(/\s+/g, '-')
            .slice(0, 200)
      }
    },
    {
      name: 'tags',
      title: 'Tags',
      type: 'array',
      options: {
        layout: 'tags'
      },
      of: [
        {
          type: 'string'
        }
      ]
    },
    {
      name: 'itunes',
      title: 'iTunes Settings',
      type: 'itunesEpisodeSettings',
    },
    {
      name: 'coverArt',
      title: 'Cover art',
      type: 'image'
    },
    {
      name: 'sponsors',
      type: 'array',
      title: 'Sponsors',
      of: [
        { type: 'sponsorRead' }
      ]
    },
  ],
  orderings: [
    {
      title: 'Publish Date, New',
      name: 'publishDateDesc',
      by: [
        {field: 'schedule.publish', direction: 'desc'}
      ]
    },
    {
      title: 'Publish Date, Old',
      name: 'publishDateAsc',
      by: [
        {field: 'schedule.publish', direction: 'asc'}
      ]
    }
  ],
  preview: {
    select: {
      title: 'title',
      subtitle: 'podcast.0.title',
      description: 'summary',
      media: 'coverArt',
      schedule: 'schedule'
    },
    prepare({title, subtitle, description, media, schedule}) {
      return {
        title,
        esubtitle: `${new Date(schedule.publish).toDateString()} – ${subtitle}`,
        description,
        media,
      }
    }
  }
};

Host

export default {
    name: 'host',
    title: 'Host',
    type: 'document',
    fields: [
        {
            name: 'name',
            title: 'Name',
            type: 'string',
        },
        {
            name: 'email',
            type: 'email',
        },
        {
            name: 'description',
            title: 'Description',
            description: 'Describe your host in the most interesting way',
            type: 'array',
            of: [
                {
                    type: 'block'
                }
            ]
        },
        {
            name: 'image',
            type: 'image'
        }

    ]
}

Itunes

export default {
  name: 'itunes',
  title: 'iTunes settings',
  type: 'object',
  description: 'iTunes has extra stuff',
  fields: [
    {
      name: 'author',
      type: 'string',
      title: 'Author',
      description: 'Podcast author, typically the host(s)'
    },
    {
      name: 'owner',
      type: 'owner',
      title: 'Owner',
    },
    {
      name: 'url',
      type: 'url',
      description:
        'Helpful for when you want to show this podcast in other contexts than iTunes'
    },
    {
      name: 'type',
      type: 'string',
      description:
        'Is this a ongoing (episodic) podcast, or is it structured in seasons?',
      options: {
        list: [
          {
            value: 'episodic',
            title: 'Episodic'
          },
          {
            value: 'serial',
            title: 'Serial'
          }
        ]
      }
    },
    {
      name: 'categories',
      title: 'Categories',
      type: 'categories'
    }
  ]
}

Itunes Episode Settings

export default {
  name: 'itunesEpisodeSettings',
  title: 'iTunes Settings',
  type: 'object',
  fields: [
    {
      name: 'type',
      title: 'Episode type',
      type: 'string',
      options: {
        list: [
          { value: 'full', title: 'Full' },
          { value: 'trailer', title: 'Trailer' },
          { value: 'bonus', title: 'Bonus' }
        ]
      }
    },
    {
      name: 'season',
      title: 'Season',
      type: 'number'
    }
  ]
}

Languages

export default [
    { value: "af", title: "af" },
    { value: "ar", title: "ar" },
    { value: "az", title: "az" },
    { value: "bg", title: "bg" },
    { value: "bn", title: "bn" },
    { value: "bs", title: "bs" },
    { value: "ca", title: "ca" },
    { value: "cs", title: "cs" },
    { value: "cy", title: "cy" },
    { value: "da", title: "da" },
    { value: "de", title: "de" },
    { value: "de-at", title: "de-at" },
    { value: "de-ch", title: "de-ch" },
    { value: "el", title: "el" },
    { value: "en", title: "en" },
    { value: "en-au", title: "en-au" },
    { value: "en-ca", title: "en-ca" },
    { value: "en-gb", title: "en-gb" },
    { value: "en-ie", title: "en-ie" },
    { value: "en-in", title: "en-in" },
    { value: "en-nz", title: "en-nz" },
    { value: "en-us", title: "en-us" },
    { value: "en-za", title: "en-za" },
    { value: "eo", title: "eo" },
    { value: "es", title: "es" },
    { value: "es-419", title: "es-419" },
    { value: "es-ar", title: "es-ar" },
    { value: "es-cl", title: "es-cl" },
    { value: "es-co", title: "es-co" },
    { value: "es-cr", title: "es-cr" },
    { value: "es-ec", title: "es-ec" },
    { value: "es-mx", title: "es-mx" },
    { value: "es-pa", title: "es-pa" },
    { value: "es-pe", title: "es-pe" },
    { value: "es-us", title: "es-us" },
    { value: "es-ve", title: "es-ve" },
    { value: "et", title: "et" },
    { value: "eu", title: "eu" },
    { value: "fa", title: "fa" },
    { value: "fi", title: "fi" },
    { value: "fr", title: "fr" },
    { value: "fr-ca", title: "fr-ca" },
    { value: "fr-ch", title: "fr-ch" },
    { value: "gl", title: "gl" },
    { value: "he", title: "he" },
    { value: "hi", title: "hi" },
    { value: "hi-in", title: "hi-in" },
    { value: "hr", title: "hr" },
    { value: "hu", title: "hu" },
    { value: "id", title: "id" },
    { value: "is", title: "is" },
    { value: "it", title: "it" },
    { value: "it-ch", title: "it-ch" },
    { value: "ja", title: "ja" },
    { value: "km", title: "km" },
    { value: "kn", title: "kn" },
    { value: "ko", title: "ko" },
    { value: "lo", title: "lo" },
    { value: "lt", title: "lt" },
    { value: "lv", title: "lv" },
    { value: "mk", title: "mk" },
    { value: "mn", title: "mn" },
    { value: "mr-in", title: "mr-in" },
    { value: "ms", title: "ms" },
    { value: "nb", title: "nb" },
    { value: "ne", title: "ne" },
    { value: "nl", title: "nl" },
    { value: "nn", title: "nn" },
    { value: "or", title: "or" },
    { value: "pl", title: "pl" },
    { value: "pt", title: "pt" },
    { value: "pt-br", title: "pt-br" },
    { value: "rm", title: "rm" },
    { value: "ro", title: "ro" },
    { value: "ru", title: "ru" },
    { value: "sk", title: "sk" },
    { value: "sl", title: "sl" },
    { value: "sr", title: "sr" },
    { value: "sv", title: "sv" },
    { value: "sw", title: "sw" },
    { value: "ta", title: "ta" },
    { value: "th", title: "th" },
    { value: "tl", title: "tl" },
    { value: "tr", title: "tr" },
    { value: "tt", title: "tt" },
    { value: "uk", title: "uk" },
    { value: "ug", title: "ug" },
    { value: "ur", title: "ur" },
    { value: "uz", title: "uz" },
    { value: "vi", title: "vi" },
    { value: "wo", title: "wo" },
    { value: "zh-cn", title: "zh-cn" },
    { value: "zh-hk", title: "zh-hk" },
    { value: "zh-tw", title: "zh-tw" },
    { value: "zh-yue", title: "zh-yue" },
]

Link list item

export default {
  name: "linkListItem",
  type: 'object',
  fields: [
    {
      name: 'title',
      type: 'string'
    },
    {
      name: 'URL',
      type: 'url'
    },
    {
      name: 'excerpt',
      type: 'text'
    },
    {
      name: 'timestamp',
      type: 'string',
    }
  ]
}

Owner

export default {
  name: 'owner',
  type: 'object',
  description:
    'The name and email of the person or organization that iTunes should list in their different views',
  fields: [
    {
      name: 'email',
      type: 'email'
    },
    {
      name: 'name',
      type: 'string'
    }
  ]
}

Schedule

export default {
  name: 'schedule',
  type: 'object',
  title: 'Publish schedule',
  fields: [
    {
      name: 'publish',
      type: 'datetime',
      title: 'Date of publication',
      description: 'When should this episode be available?',
      options: {
        inputUtc: false,
        dateFormat: 'YYYY-MM-DD',
        timeFormat: 'HH:mm',
        inputDate: true,
        inputTime: true,
        timeStep: 15,
        calendarTodayLabel: 'Today',
        placeholderDate: '2017-04-18',
        placeholderTime: '11:29'
      }
    },
    {
      name: 'unpublish',
      type: 'datetime',
      title: 'When should this episode be unaccessible?',
      description:
        'In case you want to unpublish an episode on a said date.',
      options: {
        inputUtc: false,
        dateFormat: 'YYYY-MM-DD',
        timeFormat: 'HH:mm',
        inputDate: true,
        inputTime: true,
        timeStep: 15,
        calendarTodayLabel: 'Today',
        placeholderDate: '2017-04-18',
        placeholderTime: '11:29'
      }
    }
  ]
}

Social

export default {
  name: 'social',
  title: 'Social network presence',
  type: 'object',
  fields: [
      {
          name: 'twitter',
          type: 'string',
      },
      {
          name: 'facebook',
          type: 'string',
      },
      {
          name: 'googleplus',
          type: 'string',
      },
      {
          name: 'instagram',
          type: 'string',
      },
      {
          name: 'linkedin',
          type: 'string',
      },
      {
          name: 'youtube',
          type: 'string',
      },
  ]
}

Sponsor

export default {
  name: 'sponsor',
  type: 'document',
  title: 'Sponsor',
  fields: [
      {
        name: 'name',
        type: 'string',
        title: 'Name of sponsor'
      },
      {
        name: 'url',
        type: 'url',
        title: 'URL',
      },
      {
        name: 'description',
        type: 'array',
        title: 'Description',
        of: [{ type: 'block' }]
      },
      {
        name: 'logo',
        type: 'image',
        title: 'Logo',
      },
  ]
}

Sponsor read

export default {
  name: 'sponsorRead',
  type: 'object',
  title: 'Sponsor read',
  fields: [
    {
      name: 'sponsor',
      type: 'reference',
      to: [
        {
          type: 'sponsor'
        }
      ]
    },
    {
      name: 'copy',
      type: 'array',
      title: 'copy',
      of: [{ type: 'block'}]
    },
  ]
}

These schemas add the following document types to your Sanity dataset: Podcast, Episode, Host, Sponsor. You can create as many podcasts as you want, and attach episodes to them. This plugin supports cross-posting episodes to multiple podcasts, as well as scheduling and connecting specific sponsor reads to episodes.

Contributor

Other schemas by author