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

Breaks for Portable Text

How to add different types of “breaks” for Portable Text that can be translated to horizontal rules, “read more”, sections, chapters, and so on.

Knut Melvær
Go to Breaks for Portable Text