package handler import ( "encoding/xml" "net/http" "time" "podstalk/store" ) type RSSHandler struct { Store *store.Store BaseTitle string Link string Author string } func (h *RSSHandler) resolveTitle() string { title, err := h.Store.GetSetting("podcast_title") if err == nil && title != "" { return title } if h.BaseTitle != "" { return h.BaseTitle } return "Podstalk" } func (h *RSSHandler) resolveImage() string { img, err := h.Store.GetSetting("podcast_image") if err == nil && img != "" { return img } return "" } type rssFeed struct { XMLName xml.Name `xml:"rss"` Version string `xml:"version,attr"` ItunesNS string `xml:"xmlns:itunes,attr"` Channel rssChannel `xml:"channel"` } type rssChannel struct { Title string `xml:"title"` Link string `xml:"link"` Description string `xml:"description"` Language string `xml:"language"` Image *rssImage `xml:"image,omitempty"` ItunesImage *rssItunesImage `xml:"itunes:image,omitempty"` Items []rssItem `xml:"item"` } type rssImage struct { URL string `xml:"url"` Title string `xml:"title"` Link string `xml:"link"` } type rssItunesImage struct { Href string `xml:"href,attr"` } type rssItem struct { Title string `xml:"title"` Description string `xml:"description"` Enclosure rssEnclosure `xml:"enclosure"` PubDate string `xml:"pubDate"` GUID string `xml:"guid"` ItunesImage *rssItunesImage `xml:"itunes:image,omitempty"` } type rssEnclosure struct { URL string `xml:"url,attr"` Length string `xml:"length,attr,omitempty"` Type string `xml:"type,attr"` } func (h *RSSHandler) ServeRSS(w http.ResponseWriter, r *http.Request) { episodes, err := h.Store.ListEpisodes() if err != nil { http.Error(w, "failed to load episodes", http.StatusInternalServerError) return } podcastTitle := h.resolveTitle() podcastImage := h.resolveImage() items := make([]rssItem, 0, len(episodes)) for _, ep := range episodes { var itemImage *rssItunesImage epImage := ep.ImagePath if epImage == "" { epImage = podcastImage } if epImage != "" { itemImage = &rssItunesImage{Href: h.fullURL(epImage)} } items = append(items, rssItem{ Title: ep.Title, Description: ep.Description, Enclosure: rssEnclosure{ URL: h.fullURL(ep.AudioPath), Type: "audio/mpeg", }, PubDate: ep.PublishedAt.Format(time.RFC1123Z), GUID: h.fullURL(ep.AudioPath), ItunesImage: itemImage, }) } var chanImage *rssImage var chanItunesImage *rssItunesImage if podcastImage != "" { chanImage = &rssImage{ URL: h.fullURL(podcastImage), Title: podcastTitle, Link: h.Link, } chanItunesImage = &rssItunesImage{Href: h.fullURL(podcastImage)} } feed := rssFeed{ Version: "2.0", ItunesNS: "http://www.itunes.com/dtds/podcast-1.0.dtd", Channel: rssChannel{ Title: podcastTitle, Link: h.Link, Description: podcastTitle, Language: "en-us", Image: chanImage, ItunesImage: chanItunesImage, Items: items, }, } w.Header().Set("Content-Type", "application/rss+xml; charset=utf-8") w.Write([]byte(xml.Header)) enc := xml.NewEncoder(w) enc.Indent("", " ") enc.Encode(feed) } func (h *RSSHandler) fullURL(path string) string { if path == "" { return "" } if h.Link == "" { return path } return h.Link + "/uploads/" + path }