Fixing Twitch’s Broken iCal Feeds

Published

This all started with Peter announcing that there will be Twitch.tv streams semiweekly1 for Lobsters office hours and development. I attended the first one for a couple hours, mostly having it on in the background, and it was a great time.

So, as one does, I added iCal feed to my calendar to stay up to date because I wanted to attend more in the future. Fast forward a week and I went to attend, realized my calendar wasn’t reflecting the correctly scheduled time, and promptly dug in to figure out why.

Initially, I thought it was on my end e.g. when importing the calendar. I quickly realized that’s not the case, and some research led me to complaints that the timezone in the iCal file was mangled and not-quite-to-spec. Even more surprising, providing iCals of stream schedules is relatively new from the past couple years.

From this, I referenced the API docs which ultimately led me to seeing what the API is returning:

BEGIN:VCALENDAR
PRODID:-//twitch.tv//StreamSchedule//1.0
VERSION:2.0
CALSCALE:GREGORIAN
REFRESH-INTERVAL;VALUE=DURATION:PT6H
X-PUBLISHED-TTL:PT6H
NAME:pushcx
X-WR-CALNAME:pushcx
BEGIN:VEVENT
UID:24a52982-3ba5-47b7-a513-72ae97b572bc
DTSTAMP:20240812T172447Z
DTSTART;TZID=/America/Chicago:20240815T090000
DTEND;TZID=/America/Chicago:20240815T120000
SUMMARY:Lobsters office hours + development
DESCRIPTION:Software and Game Development.
CATEGORIES:Software and Game Development
RRULE:FREQ=WEEKLY;BYDAY=TH
END:VEVENT
BEGIN:VEVENT
UID:6df7b8b5-ce84-4d5a-b564-b74de0501f18
DTSTAMP:20240812T172436Z
DTSTART;TZID=/America/Chicago:20240819T140000
DTEND;TZID=/America/Chicago:20240819T170000
SUMMARY:Lobsters office hours + development
DESCRIPTION:Software and Game Development.
CATEGORIES:Software and Game Development
RRULE:FREQ=WEEKLY;BYDAY=MO
END:VEVENT
END:VCALENDAR

This is done rather simply via the browser’s2 builtin view-source: scheme:

view-source:https://api.twitch.tv/helix/schedule/icalendar?broadcaster_id=95536715

Fortunately, when viewing this the problem becomes rather obvious: TZID= should not3 start with a /. I verified the quickly by downloading the file, removing the prefix, and importing it into my calendar. Bless & bliss, it’s fixed.

Unfortunately, Twitch doesn’t seem to be prioritizing fixing this in the upstream API so it’s up to me to create a quick fix. As I keep doing, I reached for Val Town.

Behold, another val: https://www.val.town/v/pinjasaur/twitch

Here’s the source code in its entirety:

const getText = async url => (await fetch(url)).text();

export default async function(req: Request): Promise<Response> {
  if (req.headers.get("referer")?.includes("val.")) {
    return Response.json({ error: "Cannot access from val.town" }, {
      status: 400,
    });
  }
  const params = new URL(req.url).searchParams;
  if (!params.has("broadcaster_id")) {
    return Response.json({ error: "Missing 'broadcaster_id'" }, {
      status: 400,
    });
  }
  try {
    // https://api.twitch.tv/helix/schedule/icalendar?broadcaster_id=95536715
    const url = new URL("https://api.twitch.tv/helix/schedule/icalendar");
    url.searchParams
      .set("broadcaster_id", params.get("broadcaster_id"));
    const ical = await getText(url.toString());
    return new Response(
      ical.replaceAll(
        ";TZID=/",
        ";TZID=",
      ),
      {
        headers: {
          "content-type": params.get("raw") ? "text/plain" : "text/calendar",
        },
      },
    );
  } catch (err) {
    return Response.json({ error: err.message }, {
      status: 400,
    });
  }
}

I haven’t used the new URL() or searchParams APIs as much as I would have liked, so it’s always nice to have an excuse to use em in production. Speaking of, I’m totally using this in production and you can too.

You’ll just need the broadcaster_id from the Twitch schedule you’re looking to subscribe to. For Lobsters, just import this URL into your calendar:

https://pinjasaur-twitch.web.val.run/?broadcaster_id=95536715

And yes, I asked what I should title this post:

cat twitch.md | qq What should I title this article?
"Fixing Twitch's Broken iCal Feeds"

Hurray for end-user programming.


  1. Not biweekly because that is more typically used in the every-two-weeks context.↩︎

  2. You’re using Firefox, right?↩︎

  3. The original RFC 5545 breaks down the correct timezone format.↩︎


I love hearing from readers so please feel free to reach out.

Reply via emailSubscribe via RSS or email

Last modified  #programming   #js   #ux   #syndication 


← Newer post  •  Older post →