professional hands typing on white keyboard

5 PRs, 5 Projects, 1 Day: My First Open-Source Contributions

   

Written by:

I’ve been meaning to contribute to open source for a while. Not just in a vague “I should do that someday” way — but actually doing it. Finding a bug, fixing it, opening a PR, and shipping something real. Today I finally did it. Five times.

Here’s what I worked on, what I fixed, and what I learned along the way.

professional hands typing on white keyboard
Photo by Vitaly Gariev on Pexels.com

🧰 The Setup

My home lab runs on Unraid, and my media stack is the usual suspects: Plex, Radarr, Sonarr, Tautulli, and a few syncing tools. These are projects I use every single day — which made them the perfect place to start. I already knew where the rough edges were.

I connected the GitHub CLI (gh) to my account, cloned each repo, and got to work.


1. PlexTraktSync — Watchlist Pagination Bug

The Project

PlexTraktSync is a Python tool that keeps your Plex watch history and watchlist in sync with Trakt.tv. If you’ve watched something in Plex, it marks it on Trakt. If something’s on your Trakt watchlist, it appears in Plex. It’s one of those tools that just lives quietly in the background and makes your media life better.

The Bug

Issue #2452: If you had more than 100 items on your Trakt watchlist, only the first 100 were ever fetched. Worse—on a sync, anything beyond item 100 would get removed from Plex, because as far as PlexTraktSync could see, those items weren’t in your watchlist at all.

blurred photo of a man sitting on a bridge
Photo by Ramon Karolan on Pexels.com

The Fix

The underlying pytrakt library makes a single API call to users/{username}/watchlist/movies with no pagination. Trakt’s API returns at most 100 items per page by default.

The fix: a new get_watchlist() function in pytrakt_extensions.py that loops through pages of 1000 items until it gets a partial page, signaling the end of the list. One while loop. No more phantom removals.

def get_watchlist(media_type: str) -> list:
page = 1
limit = 1000
results = []
while True:
data = trakt.core.api().request("get", f"users/me/watchlist/{media_type}", {"page": page, "limit": limit})
if not data:
break
# ... build results ...
if len(data) < limit:
break
page += 1
return results

🔗 PR #2469


2. Tautulli—Gotify Notifications Ate My Line Breaks

The Project

Tautulli is a monitoring and analytics tool for Plex. It tracks what you (and your friends) are watching, and sends notifications for just about anything—new media added, streams started, playback paused, you name it. I use it to push alerts to Gotify, a self-hosted notification server.

The Bug

Issue #2702: When you typed a notification message in Tautulli with multiple lines—pressing Enter between them — the Gotify notification arrived as a single squished line. Line one\nLine two became Line one Line two.

The cause: Tautulli sends notifications to Gotify using text/markdown content type (required to display poster images). In Markdown, a single newline \n isn’t a line break — it collapses to a space. You need two trailing spaces before the newline: \n.

GIF PLACEHOLDER — suggested: something getting squished or compressed. Search giphy.com for 'squish' or 'compressed'
Your carefully formatted notification message.

The Fix

One line of Python in notifiers.py:

# Before
'message': body,
# After
'message': body.replace('\n', ' \n'),

Poster images still work. Line breaks now render correctly. Zero breaking changes.

🔗 PR #2708


3. Sonarr — The {Season Year} Renaming Token

The Project

Sonarr is an automated TV show downloader and organizer. It watches RSS feeds, grabs new episodes, and files them away in whatever folder structure you define using naming tokens like {Series Title}, {Season}, and {Series Year}.

The Feature

Issue #7667: A user wanted season folders that reflected when that season aired, not just the year the whole series premiered. A show that started in 2010 with a new season in 2024 would have all its season folders labelled (2010) — because {Series Year} is the only year token available.

The ask: a {Season Year} token that resolves to the year of the first episode in that season.

pile of documents in close up photography
Photo by AI25.Studio Studio on Pexels.com

The Fix

Sonarr is a C# backend with a React/TypeScript frontend, so this was a different kind of lift. Changes across four files:

  • FileNameBuilder.cs — added {Season Year} handler in AddSeasonTokens, plumbed an optional int? seasonYear parameter through the naming pipeline, and added a GetSeasonYear() helper that parses the year from episode air dates
  • FileNameSampleService.cs — passes a sample year so the UI preview works correctly
  • NamingModal.tsx — added the token to the season token picker in settings
  • GetSeasonFolderFixture.cs — added test cases for the new token

Now you can use Season {season:00} ({Season Year}) and get folders like Season 01 (2013), Season 02 (2014). If no air date is available, the token gracefully returns Unknown.

🔗 PR #8622


4. WordPress Gutenberg — Cleaning Up CI

The Project

Gutenberg is the block editor that powers WordPress. It’s one of the largest and most active open-source projects in the world, with thousands of contributors and a famously competitive “good first issue” board — most tickets get claimed within hours.

The Fix

Issue #78203: The build-plugin-zip.yml GitHub Actions workflow was defining a custom job_status output to pass the build result to downstream jobs — essentially re-implementing something GitHub Actions already provides natively via needs.<job_id>.result.

This is the kind of thing that seems harmless but can introduce subtle bugs or security issues as the codebase evolves. The fix: remove the custom output, use the native context instead.

# Before
needs.build.outputs.job_status == 'success'
# After
needs.build.result == 'success'

Three lines changed. Identical behavior. No footguns left behind.

hands cleaning wall with spray bottle and sponge
Photo by Jonathan Borba on Pexels.com

🔗 PR #78319


5. Radarr — Freebox Download Client Crash

The Project

Radarr is the movie equivalent of Sonarr — it monitors RSS feeds for movies you want, downloads them, and organizes your library. It supports a wide range of download clients, including the Freebox — a French ISP’s router with a built-in download manager.

The Bug

Issue #11388: If your Freebox download queue was completely empty, Radarr would throw an unhandled ArgumentNullException and show a health error. The cause: when the queue is empty, the Freebox API returns { "success": true } with no result field. Radarr’s GetTasks() method passed that null straight to callers, which then called .Where() on it and exploded.

dramatic fireball explosion in outdoor setting
Photo by Edu Raw on Pexels.com

The Fix

One null-coalescing operator in FreeboxDownloadProxy.cs:

// Before
return ProcessRequest<List<FreeboxDownloadTask>>(request, settings).Result;
// After
return ProcessRequest<List<FreeboxDownloadTask>>(request, settings).Result ?? new List<FreeboxDownloadTask>();

Empty queue? Return an empty list. No crash. No health warning. No drama.

🔗 PR #11464


What I Learned

  • Start with what you already use. Every one of these bugs annoyed a real user — in some cases, me. That context makes it much easier to understand the bug and write a useful PR description.
  • Read the issue comments before diving in. Two of the issues I initially looked at already had open PRs from other contributors. Always check before cloning.
  • The gh CLI makes this dramatically easier. Cloning, forking, and opening PRs all from the terminal without touching a browser is a game changer.
  • Bug reporters often do half the work for you. Both the Tautulli and Radarr reporters pinpointed the exact file, line, and fix. The job was just translating their analysis into code.
  • Commit messages matter. A good commit message explains why, not just what. The diff already shows what changed.
text on a letter board
Photo by Leeloo The First on Pexels.com

All the PRs

  • 🐍 PlexTraktSyncPR #2469 — Fix watchlist pagination beyond 100 items
  • 🐍 TautulliPR #2708 — Fix Gotify notification line breaks in Markdown mode
  • ⚙️ SonarrPR #8622 — Add {Season Year} renaming token
  • 🟦 WordPress GutenbergPR #78319 — Replace custom CI job status output with native GitHub Actions context
  • ⚙️ RadarrPR #11464 — Fix Freebox download client crash on empty queue

Leave a Reply

Latest Projects

Discover more from EnRoute

Subscribe now to keep reading and get access to the full archive.

Continue reading