Skip to main content

Bulk-train YouTube's recommendation algorithm via 'Don't recommend channel' — works on Fire TV, mobile, and smart TVs

Project description

YouTube "Don't Recommend Channel" Bulk Trainer

Automates YouTube's "Don't recommend channel" action in bulk, using any channel blocklist you provide. Because the signal is tied to your Google account (not the device), it trains the algorithm everywhere you're signed in — including Fire TV, mobile apps, smart TVs, and game consoles.

No browser extension can do this. Extensions filter content client-side on a single browser. This tool affects the server-side recommendation engine.

Install

With uv:

uv tool install yt-dont-recommend
uvx playwright install chromium    # one-time: installs the Chromium browser
yt-dont-recommend --login

With pipx:

pipx install yt-dont-recommend
playwright install chromium
yt-dont-recommend --login

After --login your session is saved to ~/.yt-dont-recommend/browser-profile/ and reused automatically.


Upgrading

With uv:

uv tool upgrade yt-dont-recommend

With pipx:

pipx upgrade yt-dont-recommend

Your session and state (~/.yt-dont-recommend/) are preserved. If Playwright warns that the browser binary is outdated after an upgrade, re-run the install command for your package manager.

With uv:

uvx playwright install chromium

With pipx:

playwright install chromium

Development Setup

Requires Python 3.10+ and Git. Developed and tested on Linux (Fedora); macOS and Windows are untested.

With uv (recommended):

git clone https://github.com/cmeans/yt-dont-recommend.git
cd yt-dont-recommend
uv sync
uv run playwright install chromium
uv run python yt_dont_recommend.py --login

With pip/venv:

git clone https://github.com/cmeans/yt-dont-recommend.git
cd yt-dont-recommend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
playwright install chromium
python yt_dont_recommend.py --login

A browser window opens — sign into your Google account, then close it. Your session is saved to ~/.yt-dont-recommend/browser-profile/ and reused on every subsequent run.

Debian/Ubuntu: after installing Chromium, you may also need system dependencies:

  • uv tool: uvx playwright install-deps chromium
  • uv (dev): uv run playwright install-deps chromium
  • pipx / pip/venv: playwright install-deps chromium

Usage

For a full list of options:

yt-dont-recommend --help

Examples below use python — either activate the venv first (source .venv/bin/activate) or prefix each command with uv run if using uv.

# Dry run — see what channels would be processed
python yt_dont_recommend.py --dry-run

# Process all built-in sources consecutively (default)
python yt_dont_recommend.py

# Use a specific built-in source
python yt_dont_recommend.py --source deslop
python yt_dont_recommend.py --source aislist

# Use multiple sources explicitly (comma-separated)
python yt_dont_recommend.py --source deslop,aislist

# Use a local blocklist file
python yt_dont_recommend.py --source /path/to/my-list.txt

# Use a remote blocklist URL
python yt_dont_recommend.py --source https://example.com/blocklist.txt

# Process only 10 channels (good for first test)
python yt_dont_recommend.py --limit 10

# Protect specific channels from ever being blocked (overrides the default exclude file)
python yt_dont_recommend.py --exclude ~/.yt-dont-recommend/exclude.txt

# Run in headless mode (no visible browser)
python yt_dont_recommend.py --headless

# Check progress (includes subscription-protected channels)
python yt_dont_recommend.py --stats

# Control when a channel is auto-unblocked after being removed from a list
python yt_dont_recommend.py --unblock-policy all   # default: unblock only when gone from all sources
python yt_dont_recommend.py --unblock-policy any   # unblock as soon as gone from any source

# Start over
python yt_dont_recommend.py --reset-state

# List built-in sources
python yt_dont_recommend.py --list-sources

Exclusion List

If a community blocklist includes a channel you want to keep, add it to your personal exclusion file:

~/.yt-dont-recommend/exclude.txt

This file is loaded automatically on every run — no flag required. The format is the same plain-text format as blocklists, and supports inline # comments:

# Channels I want to keep despite being on community lists
@SomeChannel
@AnotherChannel  # keeping this one — it's a friend's channel

To use a different file instead (or a remote URL), pass --exclude:

python yt_dont_recommend.py --exclude /path/to/other-list.txt
python yt_dont_recommend.py --exclude https://example.com/my-exclusions.txt

--exclude does not accept built-in source names.

Subscription Protection

The tool automatically skips any channel you are subscribed to — even if it appears on a blocklist. Blocking a channel you subscribe to would signal YouTube to stop recommending it, which is usually not what you want.

When a subscribed channel appears on the blocklist, a WARNING is logged and the event is recorded in state under would_have_blocked. This warning fires only once per channel (not on every run). Use --stats to see the full list.

If a channel you subscribe to genuinely should be blocked, add it to your exclusion file (~/.yt-dont-recommend/exclude.txt) to suppress the warning, or unsubscribe and let the tool handle it on the next run.

Auto-Unblock (False Positive Correction)

When a channel is removed from a blocklist, the tool can automatically reverse the "Don't recommend channel" action.

--unblock-policy all (default): Unblock only when the channel has been dropped from every source that originally blocked it. Useful when running multiple lists — a channel removed from one aggressive list but still present in another stays blocked.

--unblock-policy any: Unblock as soon as the channel disappears from any source that blocked it. More aggressive about reversing false positives.

Auto-unblock events are logged prominently so they are easy to spot.

Blocklist Format

Plain text, one channel per line. Full-line comments start with #. Inline # comments are also supported.

# My custom blocklist
@SomeHandle
@AnotherChannel           # optional note about why this is here
UCxxxxxxxxxxxxxxxxxxxxxxxx

This format is shared with the DeSlop project. You can point --source at any file or URL using this format, or at JSON files using common channel object schemas.

Built-in Sources

Source Description
deslop DeSlop project (~130+ channels, plain text, actively maintained)
aislist AiSList community text list (~8400+ channels, broader)

Running without --source processes all built-in sources consecutively. The state tracker prevents re-processing the same channel twice across sources or runs.

How It Works

  1. Fetches the blocklist (local file, URL, or built-in source)
  2. Checks whether any previously blocked channels have since been removed from the list and auto-unblocks them per --unblock-policy
  3. Opens Chromium using your saved YouTube login session
  4. Scrapes your subscriptions so subscribed channels are never blocked
  5. Scans the YouTube home feed for cards matching blocklist channels
  6. For each match:
    • Clicks the "More actions" menu on the video card
    • Clicks "Don't recommend channel"
    • Saves progress immediately (crash-safe, always resumable)
  7. Scrolls for more cards and repeats until the list is exhausted or --limit is reached
  8. Rate-limits itself: 3–7s between actions, 30s break every 25 channels

Why the home feed? Live testing confirmed that "Don't recommend channel" only appears in home feed recommendation contexts. It does not appear on a channel's own /videos page, in search results, or on the video watch page.

State & Logs

All data lives in ~/.yt-dont-recommend/:

Path Purpose
browser-profile/ Chromium profile with your login session
processed.json Channels already handled, blocked-by source tracking, subscription warnings
run.log Timestamped log of all actions (rotates at 1 MB, 5 backups kept)

Caveats

  • YouTube ToS: Automating UI interactions may violate YouTube's Terms of Service. Personal use, your own account, your own risk.
  • Selector fragility: YouTube's HTML structure changes frequently. The script detects broken selectors automatically — if several consecutive scroll passes yield no parseable channel links, it logs a POSSIBLE SELECTOR FAILURE warning and exits early. Run --check-selectors to diagnose and get a timestamped report with screenshots.
  • Home feed matching: The tool can only block channels that appear in your home feed during a run. Channels on the blocklist that never surface in the feed during that session will not be processed. Resume runs until the list is exhausted.
  • Handle vs. channel ID: YouTube feed cards expose @handle links only — UCxxx IDs in a blocklist are automatically resolved to @handles before scanning. Results are cached in state so re-resolution is skipped on subsequent runs. Both built-in sources already use @handle format; this only applies to custom blocklists.
  • Start small: Use --limit 10 for your first real run to confirm everything is working before processing a full list.

Running Periodically

YouTube's home feed refreshes throughout the day, so twice-daily runs are recommended. After the initial processing pass, runs that find nothing new are fast.

Automatic setup (recommended)

yt-dont-recommend --schedule install

That's it. No crontab editing, no path hunting. Schedules runs at 3:00 AM and 3:00 PM daily using launchd (macOS) or cron (Linux), with the correct binary path filled in automatically.

yt-dont-recommend --schedule status   # check what's installed
yt-dont-recommend --schedule remove   # remove the schedule

Each run picks up where the last left off. New channels added to the blocklist since the last run will be processed when they appear in the home feed.

Manual cron setup (advanced)

If you prefer to manage cron yourself, use crontab -e and add one of the following.

Cron runs without your shell environment — use absolute paths throughout.

Installed via uv tool or pipx:

# Twice daily — 3am and 3pm
0 3,15 * * * /path/to/yt-dont-recommend --headless

Find the full path with which yt-dont-recommend.

Cloned repo (uv):

0 3,15 * * * cd /path/to/yt-dont-recommend && uv run python yt_dont_recommend.py --headless

Cloned repo (pip/venv):

0 3,15 * * * cd /path/to/yt-dont-recommend && .venv/bin/python yt_dont_recommend.py --headless

Checking and Updating Selectors

YouTube changes its DOM structure frequently. When the script starts silently skipping everything (SKIP entries in the log), the selectors are probably broken.

Run the selector checker to diagnose:

python yt_dont_recommend.py --check-selectors

This opens a visible browser, tests the current selectors against four contexts (home feed, search results, channel header, video watch page), prints every menu item found, and saves a timestamped report with screenshots to ~/.yt-dont-recommend/.

Confirmed behavior (as of 2026-03-05): "Don't recommend channel" appears only in the home feed. It does not appear in search results, on channel pages, or on the video watch page. The tool's home feed scanner reflects this.

Exit code is 0 if the target option was found, 1 if not — suitable for scripting:

Installed (uv tool / pipx):

0 0 1 * * /path/to/yt-dont-recommend --check-selectors || echo "Selectors broken — check ~/.yt-dont-recommend/" | mail -s "yt-dont-recommend alert" you@example.com

Cloned repo (uv):

0 0 1 * * cd /path/to/yt-dont-recommend && uv run python yt_dont_recommend.py --check-selectors || echo "Selectors broken — check ~/.yt-dont-recommend/" | mail -s "yt-dont-recommend alert" you@example.com

Cloned repo (pip/venv):

0 0 1 * * cd /path/to/yt-dont-recommend && .venv/bin/python yt_dont_recommend.py --check-selectors || echo "Selectors broken — check ~/.yt-dont-recommend/" | mail -s "yt-dont-recommend alert" you@example.com

To test against a specific channel instead of the default (@YouTube):

python yt_dont_recommend.py --check-selectors --test-channel @SomeChannel

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yt_dont_recommend-0.1.2.tar.gz (32.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yt_dont_recommend-0.1.2-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file yt_dont_recommend-0.1.2.tar.gz.

File metadata

  • Download URL: yt_dont_recommend-0.1.2.tar.gz
  • Upload date:
  • Size: 32.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for yt_dont_recommend-0.1.2.tar.gz
Algorithm Hash digest
SHA256 4384510b2837dc0bddf093106f01cf7faca095490a74697b2bf2b33a9253a915
MD5 09c20c9abe6f0aa081e8e5b7eb185fcf
BLAKE2b-256 1fdbddda76296cc4615dc5439bdb2c50e036d95d651f740abf1fc72b82217544

See more details on using hashes here.

Provenance

The following attestation bundles were made for yt_dont_recommend-0.1.2.tar.gz:

Publisher: publish.yml on cmeans/yt-dont-recommend

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file yt_dont_recommend-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for yt_dont_recommend-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d1ac7e5ad9686530cae0042a86790692896d62277f93a397fd350a946cd43891
MD5 b8d24b7a5eb769436d7358b10cea4042
BLAKE2b-256 5c5dce5456c2ea055607f5a4a5c19d967648481e1a2e98955638ecec14fc4ad9

See more details on using hashes here.

Provenance

The following attestation bundles were made for yt_dont_recommend-0.1.2-py3-none-any.whl:

Publisher: publish.yml on cmeans/yt-dont-recommend

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page