podcast/item.go

147 lines
4.0 KiB
Go

package podcast
import (
"encoding/xml"
"fmt"
"time"
"unicode/utf8"
)
// Item represents a single entry in a podcast.
//
// Article minimal requirements are:
// - Title
// - Description
// - Link
//
// Audio minimal requirements are:
// - Title
// - Description
// - Enclosure (HREF, Type and Length all required)
// - IEpisode (iTunes episode number)
//
// Recommendations:
// - Setting the minimal fields sets most of other fields, including iTunes.
// - Use the Published time.Time setting instead of PubDate.
// - Always set an Enclosure.Length, to be nice to your downloaders.
// - Use Enclosure.Type instead of setting TypeFormatted for valid extensions.
type Item struct {
XMLName xml.Name `xml:"item"`
GUID string `xml:"guid"`
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Author *Author `xml:"-"`
AuthorFormatted string `xml:"author,omitempty"`
Category string `xml:"category,omitempty"`
Comments string `xml:"comments,omitempty"`
Source string `xml:"source,omitempty"`
PubDate *time.Time `xml:"-"`
PubDateFormatted string `xml:"pubDate,omitempty"`
Enclosure *Enclosure
// https://help.apple.com/itc/podcasts_connect/#/itcb54353390
IAuthor string `xml:"itunes:author,omitempty"`
IEpisode int `xml:"itunes:episode"`
ISubtitle string `xml:"itunes:subtitle,omitempty"`
ISummary *ISummary
IImage *IImage
IDuration string `xml:"itunes:duration,omitempty"`
IExplicit string `xml:"itunes:explicit,omitempty"`
IIsClosedCaptioned string `xml:"itunes:isClosedCaptioned,omitempty"`
IOrder string `xml:"itunes:order,omitempty"`
}
// AddEnclosure adds the downloadable asset to the podcast Item.
func (i *Item) AddEnclosure(
url string, enclosureType EnclosureType, lengthInBytes int64) {
i.Enclosure = &Enclosure{
URL: url,
Type: enclosureType,
Length: lengthInBytes,
}
}
// AddImage adds the image as an iTunes-only IImage. RSS 2.0 does not have
// the specification of Images at the Item level.
//
// Podcast feeds contain artwork that is a minimum size of
// 1400 x 1400 pixels and a maximum size of 3000 x 3000 pixels,
// 72 dpi, in JPEG or PNG format with appropriate file
// extensions (.jpg, .png), and in the RGB colorspace. To optimize
// images for mobile devices, Apple recommends compressing your
// image files.
func (i *Item) AddImage(url string) {
if len(url) > 0 {
i.IImage = &IImage{HREF: url}
}
}
// AddPubDate adds the datetime as a parsed PubDate.
//
// UTC time is used by default.
func (i *Item) AddPubDate(datetime *time.Time) {
i.PubDate = datetime
i.PubDateFormatted = parseDateRFC1123Z(i.PubDate)
}
// AddSummary adds the iTunes summary.
//
// Limit: 4000 characters
//
// Note that this field is a CDATA encoded field which allows for rich text
// such as html links: `<a href="http://www.apple.com">Apple</a>`.
func (i *Item) AddSummary(summary string) {
count := utf8.RuneCountInString(summary)
if count > 4000 {
s := []rune(summary)
summary = string(s[0:4000])
}
i.ISummary = &ISummary{
Text: summary,
}
}
// AddDuration adds the duration to the iTunes duration field.
func (i *Item) AddDuration(durationInSeconds int64) {
if durationInSeconds <= 0 {
return
}
i.IDuration = parseDuration(durationInSeconds)
}
var parseDuration = func(duration int64) string {
h := duration / 3600
duration = duration % 3600
m := duration / 60
duration = duration % 60
s := duration
// HH:MM:SS
if h > 9 {
return fmt.Sprintf("%02d:%02d:%02d", h, m, s)
}
// H:MM:SS
if h > 0 {
return fmt.Sprintf("%d:%02d:%02d", h, m, s)
}
// MM:SS
if m > 9 {
return fmt.Sprintf("%02d:%02d", m, s)
}
// M:SS
return fmt.Sprintf("%d:%02d", m, s)
}
var parseDateRFC1123Z = func(t *time.Time) string {
if t != nil && !t.IsZero() {
return t.Format(time.RFC1123Z)
}
return time.Now().UTC().Format(time.RFC1123Z)
}