YouTube Without the Algorithm

YouTube Without the Algorithm Top Image

I grew up on Nova, Scientific American Frontiers and Modern Marvels. Those shows got me into STEM and eventually into computers. I wanted something like that for my boys…content that’s not only interesting but somewhat educational as well. So I built something.

I had three goals:

  1. I need an easy way to safely curate videos for my kids.
  2. It has to be simple enough for my wife and the grandparents to use.
  3. Must be easy to view content.

With those in mind, here’s what I do now: I find a video I’m ok with my son watching, I’ll send the YouTube URL (or several) to a dedicated email address, and minutes later it’s available.

To get this working, I’m using a combination of: Docker, n8n, and Plex. Proxmox powers my little home lab on a BeeLink PC.

Disclosure: AI helped me refine this post and saved some time troubleshooting building this one Saturday morning.

The Workflow

At a high level, this works in the simplest way possible with existing building blocks:

  1. Find videos you want, copy the URLs.
  2. Send them to a dedicated email address.
  3. Videos are available on Plex within 10–15 minutes.

Workflow 1 — Store Incoming URLs

Store Incoming Urls Workflow

n8n is setup with a Gmail trigger which watches the inbox, parses any YouTube URLs out of the email body and stores them in a Data Table for processing.

Here’s the URL parsing code for reference:

const items = $input.all();
const results = [];
const DOMAINS = ['youtube.com', 'youtu.be'];
const ALLOWED_SENDERS = ['email1@example.com', 'email2@example.com'];

for (const item of items) {
  const raw = item.json.headers?.subject || '';
  const subject = raw.indexOf(': ') >= 0 ? raw.slice(raw.indexOf(': ') + 2).trim() : raw.trim();
  const from = item.json.from?.text || '';

  const fromEmail = (from.match(/<(.+?)>/) || [null, from])[1].trim().toLowerCase();
  if (!ALLOWED_SENDERS.includes(fromEmail)) continue;

  const lines = (item.json.text || '').split('\n');
  for (const line of lines) {
    const url = line.trim();
    if (!url.startsWith('http')) continue;
    const noProto = url.replace('https://', '').replace('http://', '');
    const rawHost = noProto.split('/')[0];
    const host = rawHost.startsWith('www.') ? rawHost.slice(4) : rawHost;
    if (DOMAINS.indexOf(host) > -1) {
      results.push({ json: { url, from, subject, messageId: item.json.id } });
    }
  }
}
return results;

The Data Table schema is fairly basic as well:

url,status,from_email,subject,queued_at,title,channel,site_name,skipped,downloaded_at,error_message
https://www.youtube.com/watch?v=rjFxthTkw7A,done,"""Dad"" <foobar@example.com>",,2026-04-05T17:21:21.752Z,How To Draw A Scary Witch Folding Surprise,Art for Kids Hub,YouTube,1,2026-04-05T17:40:43.261Z,

Workflow 2 - Download Videos

Download Workflow

A second workflow runs separately and picks up queued URLs one at a time. I split this out because a single long-running workflow kept timing out or kept running into other wierd issues that were cumbersome to debug. This one marks each URL as “downloading,” calls yt-dlp via a small Python wrapper, then sends a Pushover notification when it’s done. Videos land in a NAS folder that Plex scans periodically.

One bug I haven’t fixed is that downloads that fail silently stay stuck at “downloading” forever. I have a safeguard in place to ensure videos aren’t too long or too large so they silently fail…for now.

Some Learnings

The allowlist isn’t optional. Early on I got spam with YouTube links that made it into the queue. Nothing harmful got through, but it was a reminder that anything internet-facing needs filtering at the door.

Set up failure notifications. OAuth with Google has been flaky a couple times. Without Pushover alerts I wouldn’t have known videos weren’t downloading until my son asked why something wasn’t there.


This has been running for about a month now, with much fanfare. My son now gets inspired with building STEM-related project and plays a handful of safe music videos, this has been a favorite recently.