Nushell Example #3 -- Woodstock Film Festival
Oct 12, 2025The Woodstock Film Festival is next week but the site’s view of the schedule leaves more than a bit to be desired when you’re trying to plan what to see across multiple days and venues.
Aside, I’m not sure who it’s optimized for but it’s not a good
experience using Firefox on desktop. I’m annoyed enough by it to
include this bit of the Google Pagespeed report 
After a little fiddling with the Firefox Developer Tools, I saw that they’re using a service Eventive which exposes the data via API. Perfect. We love JSON and API’s. So I turned to Nushell again. I’m going to leave out the details of exploring the API and the response payloads as it would be an article in itself.
Note: Because it’s not polite to abuse other people’s API’s I have stashed gzipped copies of the config (38kb) and a full response (286kb) (1.4MB uncompressed).
I start by defining a little function to convert a timestamp (assumed UTC) into a datetime in this timezone (NY). I have a bunch of these conveniences it my toolkit.
def ts->ny []: string -> datetime {
$in | into datetime | date to-timezone America/New_York
}
Get the configuration via HTTP. This has a lot of data but we’re only interested in the event “bucket” and the api key:
let resp = (http get https://woodstock2025.eventive.org/config.json -f -e)
if $resp.status != 200 {
error make {msg: $"Failed to get config!\n($resp.body|to text)"}
}
let cfg = $resp.body
let auth = ($cfg.api_key | encode base64)
let bucket = $cfg.event_bucket
Now we can assemble another HTTP request to and fetch the events:
let req = {
scheme: "https",
host: "api.eventive.org",
path: $"event_buckets/($bucket)/events",
query: ({upcoming_only: true} | url build-query)
} | url join
let resp = (http get -H ["Authorization" $"Basic ($auth)"] -f -e $req)
if $resp.status != 200 {
error make {msg: $"Failed to get event data:\n\t($resp.body | to text)"}
}
let $events = $resp.body.events
Filter the event data into just the columns we want as table of records. There’s some regularity to the address so we can trim it to make it fit nicely. We use the function we defined earlier to coerce the timestamps.
$events |
each {|e|
{
name: $e.name,
venue: ($e.venue.short_name),
address: ($e.venue.address |
str replace --regex ", NY 124.*$" "" |
str replace --regex "\n" " "),
day: ($e.start_time | ts->ny | format date "%a"),
time: ($e.start_time | ts->ny | format date "%F %R"),
duration: (($e.end_time | into datetime) - ($e.start_time | into datetime))
}
} | sort-by time
That gives us a long table (135 records) that looks like this:
────────────────────────────────────────────────────────────────────────┬──────────────────┬────────────────────────────────────┬─────┬──────────────────┬───────────
name │ venue │ address │ day │ time │ duration
────────────────────────────────────────────────────────────────────────┼──────────────────┼────────────────────────────────────┼─────┼──────────────────┼───────────
Fatal Watch │ Broken Wing Barn │ 1389 State Route 212, Saugerties │ Tue │ 2025-10-14 19:15 │ 2hr 5min
Barry Feinstein Photography Exhibition │ │ │ Wed │ 2025-10-15 11:00 │ 4day 6hr
A Life Illuminated │ Bearsville │ 291 Tinker Street Woodstock │ Wed │ 2025-10-15 17:00 │ 1hr 50min
Blue Moon │ Playhouse │ 103 Mill Hill Rd Woodstock │ Wed │ 2025-10-15 17:15 │ 1hr 45min
Arco │ Tinker St │ 132 Tinker Street Woodstock │ Wed │ 2025-10-15 17:30 │ 1hr 35min
A BREAK IN THE RAIN │ Colony │ 22 Rock City Rd, Woodstock │ Wed │ 2025-10-15 19:00 │ 2hr 55min
River of Grass │ Broken Wing Barn │ 1389 State Route 212, Saugerties │ Wed │ 2025-10-15 19:15 │ 1hr 45min
... elided ...
Now we can easily search for showtimes! (I’ve used a temporary variable to hold the table)
$output | where name =~ "Cover|Honky|Keeper"
───────────────────────────┬─────────────────┬────────────────────────────────────┬─────┬──────────────────┬───────────
name │ venue │ address │ day │ time │ duration
───────────────────────────┼─────────────────┼────────────────────────────────────┼─────┼──────────────────┼───────────
Honky Tonkin' in Kingston │ Assembly │ 236 Wall Street 3rd Floor Kingston │ Wed │ 2025-10-15 19:30 │ 3hr 25min
Cover-Up │ Orpheum T2 │ 156 Main St Saugerties │ Sat │ 2025-10-18 12:45 │ 2hr 20min
The Keeper │ Bearsville │ 291 Tinker Street Woodstock │ Sat │ 2025-10-18 16:00 │ 2hr 5min
Cover-Up │ Bearsville │ 291 Tinker Street Woodstock │ Sun │ 2025-10-19 15:30 │ 2hr 20min
The Keeper │ Upstate Midtown │ 591 Broadway Kingston │ Sun │ 2025-10-19 19:15 │ 2hr 5min
───────────────────────────┴─────────────────┴────────────────────────────────────┴─────┴──────────────────┴───────────