Last active
December 2, 2015 23:39
-
-
Save dewey/cb70e13f3618981f059f to your computer and use it in GitHub Desktop.
Parse multiple .ics feeds into one and create the file and serve it via http.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"crypto/md5" | |
"encoding/hex" | |
"fmt" | |
"log" | |
"net/http" | |
"os" | |
"time" | |
"github.com/PuloV/ics-golang" | |
) | |
// EventParsed ... i just want this warning gone | |
type EventParsed struct { | |
start time.Time | |
end time.Time | |
created time.Time | |
importedId string | |
description string | |
summary string | |
id string | |
wholeDayEvent bool | |
} | |
func GetMD5Hash(text string) string { | |
hasher := md5.New() | |
hasher.Write([]byte(text)) | |
return hex.EncodeToString(hasher.Sum(nil)) | |
} | |
func main() { | |
parser := ics.New() | |
// We are just using this for the timestamp on the served static site | |
var lastModified time.Time | |
m := make(map[string]EventParsed) | |
f, err := os.Create("tmp/output-calendar.ics") | |
lastModified = time.Now() | |
w := bufio.NewWriter(f) | |
// Write the header of the calendar file | |
_, err = w.WriteString("BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:icalendar-ruby\r\nCALSCALE:GREGORIAN\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Vienna\r\nEND:VTIMEZONE\r\n") | |
if err != nil { | |
log.Print("error while writing file") | |
} | |
// Parse all our RSS feeds we want to unify | |
parserChan := parser.GetInputChan() | |
parserChan <- "https://trakt.tv/calendars/premieres.ics?slurm=xxx&countries=au%2Cat%2Cbr%2Cde%2Cnz%2Cgb%2Cus&ratings=10-100" | |
parserChan <- "https://trakt.tv/calendars/new-shows.ics?slurm=xxx&countries=au%2Cat%2Cbr%2Cde%2Cnz%2Cgb%2Cus&ratings=10-100" | |
parserChan <- "https://trakt.tv/calendars/premieres.ics?slurm=xxx&networks=Netflix" | |
outputChan := parser.GetOutputChan() | |
// Add all our events to our map. Key is on summary because that way we can take advantage of the maps unique key constraint | |
go func() { | |
for event := range outputChan { | |
m[fmt.Sprintf("%x", md5.Sum([]byte(event.GetSummary())))] = EventParsed{event.GetStart(), event.GetEnd(), event.GetCreated(), fmt.Sprintf("%x", md5.Sum([]byte(event.GetSummary()))), event.GetDescription(), event.GetSummary(), event.GetID(), event.GetWholeDayEvent()} | |
} | |
}() | |
parser.Wait() | |
t := time.Now() | |
// iterate through all events in our map and add them to the file | |
for _, value := range m { | |
var datestringStart string | |
var dateStringEnd string | |
datestring := fmt.Sprintf("%d%02d%02dT%02d%02d%02d", value.start.Year(), value.start.Month(), value.start.Day(), t.Hour(), t.Minute(), t.Second()) | |
// If there's something close to the end of the month we just move it a few days earlier. This is needed because some calender clients need to have the | |
// timestamp in a certain format for events to show up as all day events. Because I don't want to deal with timestamps overlapping month / years etc. it's done | |
// this way. | |
if value.start.Day() == 30 || value.start.Day() == 31 || value.start.Day() == 29 { | |
datestringStart = fmt.Sprintf("%d%02d%02d", value.start.Year(), value.start.Month(), value.start.Day()-1) | |
dateStringEnd = fmt.Sprintf("%d%02d%02d", value.start.Year(), value.start.Month(), value.start.Day()) | |
} else { | |
datestringStart = fmt.Sprintf("%d%02d%02d", value.start.Year(), value.start.Month(), value.start.Day()) | |
dateStringEnd = fmt.Sprintf("%d%02d%02d", value.start.Year(), value.start.Month(), value.start.Day()+1) | |
} | |
stringEvent := fmt.Sprintf("BEGIN:VEVENT\r\nDTSTAMP:%sZ\r\nUID:%s\r\nDTSTART;TZID=Europe/Vienna:%s\r\nDTEND;TZID=Europe/Vienna:%s\r\nDESCRIPTION:%s\r\nSUMMARY:%s\r\nURL:%s\r\nEND:VEVENT\r\n", datestring, value.importedId, datestringStart, dateStringEnd, value.description, value.summary, "https://example.com") | |
_, err = w.WriteString(stringEvent) | |
if err != nil { | |
log.Print("error") | |
} | |
} | |
// Write the last line of the calender | |
_, err = w.WriteString("END:VCALENDAR") | |
if err != nil { | |
log.Print("error while writing end calendar") | |
} | |
// Make sure all write operations are cleared | |
w.Flush() | |
// Serve file via http on localhost | |
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |
handler(w, r, lastModified) | |
}) | |
// Serve static file | |
http.HandleFunc("/tmp/", func(w http.ResponseWriter, r *http.Request) { | |
http.ServeFile(w, r, r.URL.Path[1:]) | |
}) | |
// Listen on localhost | |
err = http.ListenAndServe(":9001", nil) | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
// Render the site | |
func handler(w http.ResponseWriter, r *http.Request, t time.Time) { | |
fmt.Fprintf(w, fmt.Sprintf("<body><h1>Unified Trakt.TV calendar</h1><p>Download .ics file <a href=/tmp/output-calendar.ics>here</a><br>Last updated: %s</body>", t.Format("Mon Jan _2 15:04:05 2006"))) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment