all repos

moviefeed @ 6d7ce55

rss feed server for tracking new tv show episodes
5 files changed, 101 insertions(+), 0 deletions(-)
init
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-01-01 00:53:44 +0200
Change ID: runlpqxpmopzxkzpnwwrrqoykrnqquyv
A .gitignore

@@ -0,0 +1,2 @@

+/moviefeed +/config.yaml
A config.example.yaml

@@ -0,0 +1,5 @@

+api_key: "<your_tmdb_api_key>" +port: 8080 +shows: + - "tt0944947" # Game of Thrones IMDB ID + - "1399" # The Office (US) TMDB ID
A go.mod

@@ -0,0 +1,8 @@

+module olexsmir.xyz/moviefeed + +go 1.25.3 + +require ( + github.com/gorilla/feeds v1.2.0 + gopkg.in/yaml.v2 v2.4.0 +)
A go.sum

@@ -0,0 +1,12 @@

+github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= +github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
A main.go

@@ -0,0 +1,74 @@

+package main + +import ( + "flag" + "fmt" + "log/slog" + "net/http" + "os" + "time" + + "github.com/gorilla/feeds" +) + +func main() { + configFile := flag.String("config", "config.yaml", "Path to config file") + flag.Parse() + + config, err := loadConfig(*configFile) + if err != nil { + slog.Error("failed to load config", "err", err) + os.Exit(1) + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + episodes, err := fetchNewEpisodes(config) + if err != nil { + slog.Error("failed to fetch episodes", "err", err) + os.Exit(1) + } + + rssFeed := generateRSS(episodes) + w.Header().Set("Content-Type", "application/rss+xml") + w.Write([]byte(rssFeed)) + }) + + slog.Info("server starting", "port", config.Port) + http.ListenAndServe(":"+config.Port, nil) +} + +func generateRSS(episodes []TMDBEpisode) string { + feed := &feeds.Feed{ + Title: "moviefeed", + Link: &feeds.Link{Href: "http://localhost"}, + Description: "Latest episodes from followed shows", + Created: time.Now(), + } + + for _, ep := range episodes { + airDate, _ := time.Parse("2006-01-02", ep.AirDate) + feed.Items = append(feed.Items, &feeds.Item{ + Id: fmt.Sprintf("%s-%d-%d", ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber), + Title: fmt.Sprintf( + "%s S%dE%d: %s", + ep.ShowName, + ep.SeasonNumber, + ep.EpisodeNumber, + ep.Name, + ), + Link: &feeds.Link{ + Href: fmt.Sprintf("https://www.themoviedb.org/tv/episode/%d", ep.ID), + }, + Description: ep.Overview, + Created: airDate, + }) + } + + rss, err := feed.ToRss() + if err != nil { + slog.Error("error generating RSS", "err", err) + return "" + } + + return rss +}