How to Mass Unsubscribe from Non-Japanese YouTube Channels Using a Browser Script

Full Disclosure: The script was generated with claude.ai

If you’ve spent years accumulating YouTube subscriptions and now want a cleaner, more focused feed, say, one dedicated entirely to Japanese content/immersion, doing it manually would take hours. This guide walks you through a browser script that automates the process, keeping only channels with Japanese text in their name or description and unsubscribing from everything else.

[This script worked best in the Edge browser, but it didn’t work in the Brave browser (although that might be because I have a bunch of extensions running in the background, basically, if it doesn’t work in one browser, try another). Given that YouTube keeps changing stuff, you can expect this script to break, in which case, just copy this script into an AI and pray that it can get it working again.]

The tldr: is that the script works when you copy, paste and run it in “Console” (press F12 ->console)  in your browser while on the YouTube Subscriptions page (https://www.youtube.com/feed/channels). It removes all channels which don’t have Japanese characters in the channel name or description.


Before You Start

A few things worth knowing:

  • This only works on channels that use Japanese script in their name or description. A Japanese channel called “Anime Clips” or “Tokyo Daily” with an entirely English description will be unsubscribed, because the script has no way to know it’s Japanese. You may want to manually re-subscribe to any such channels afterwards. Conversely, any English language channels which have Japanese characters in their description or channel name will be kept.
  • Go slowly. The script adds deliberate pauses between each action to avoid triggering YouTube’s rate limiting. Give it a few hours if you have thousands of Youtube Channels. At least I didn’t have any issues with the process being paused by youtube.
  • Don’t close the tab while the script is running.
  • You cannot undo this in bulk. Once unsubscribed, you’ll need to re-subscribe manually to anything removed by mistake.

Step-by-Step Instructions

Step 1 — Go to Your Subscriptions Page

Navigate to:

https://www.youtube.com/feed/channels

This is the page that lists all your subscriptions. Make sure you’re logged in.

Step 2 — Open the Browser Console

Press F12 on Windows/Linux, or Cmd + Option + J on Mac, to open the browser developer tools. Click the Console tab at the top of the panel that appears.

Step 3 — Paste and Run the Script

Copy the full script below and paste it into the console, then press Enter. If it doesn’t let you paste, you might need to type and enter ‘allow pasting’ before it let’s you.

(async function () {
  const delay = (ms) => new Promise(r => setTimeout(r, ms));

  const isJapanese = (text) => {
    return /[\u3040-\u30ff\u4e00-\u9faf\uff00-\uffef]/.test(text);
  };

  const waitAndClickConfirm = async (maxMs = 3000) => {
    const start = Date.now();
    while (Date.now() - start < maxMs) {
      const selectors = [
        "yt-confirm-dialog-renderer #confirm-button button",
        "yt-confirm-dialog-renderer #confirm-button",
        "ytd-confirm-dialog-renderer #confirm-button button",
        "ytd-confirm-dialog-renderer #confirm-button",
        "tp-yt-paper-dialog yt-confirm-dialog-renderer #confirm-button",
      ];
      for (let sel of selectors) {
        const btn = document.querySelector(sel);
        if (btn) {
          btn.click();
          return true;
        }
      }
      await delay(200);
    }
    return false;
  };

  const processAllVisible = async (alreadyRemoved) => {
    const channels = Array.from(document.querySelectorAll("ytd-channel-renderer"));
    let unsubThisPass = 0;

    for (let ch of channels) {
      const nameEl = ch.querySelector("#channel-title, #text");
      const name = nameEl ? nameEl.innerText.trim() : "";
      if (!name || alreadyRemoved.has(name)) continue;

      const descEl = ch.querySelector(
        "#description, yt-formatted-string#description, #channel-description, #description-container yt-formatted-string"
      );
      const desc = descEl ? descEl.innerText.trim() : "";

      if (isJapanese(`${name} ${desc}`)) {
        console.log(`🇯🇵 Kept: ${name}`);
        alreadyRemoved.add(name);
        continue;
      }

      const unsubBtn = ch.querySelector('[aria-label^="Unsubscribe from"]');
      if (!unsubBtn) {
        alreadyRemoved.add(name);
        continue;
      }

      unsubBtn.scrollIntoView({ behavior: "smooth", block: "center" });
      await delay(600);
      unsubBtn.click();

      const confirmed = await waitAndClickConfirm(3000);

      if (confirmed) {
        await delay(1500);
        console.log(`❌ Unsubscribed: ${name}`);
        unsubThisPass++;
        totalUnsub++;
      } else {
        console.warn(`⚠️ No dialog appeared for: ${name} — skipping`);
        document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", keyCode: 27, bubbles: true }));
        await delay(500);
      }

      alreadyRemoved.add(name);
    }

    return unsubThisPass;
  };

  const alreadyRemoved = new Set();
  let totalUnsub = 0;
  let stuckCount = 0;

  console.log("🚀 Starting...");

  while (true) {
    const beforeCount = alreadyRemoved.size;

    window.scrollTo(0, 0);
    await delay(1000);

    let lastScrollY = -1;
    while (true) {
      await processAllVisible(alreadyRemoved);
      window.scrollBy(0, 800);
      await delay(1200);
      if (window.scrollY === lastScrollY) break;
      lastScrollY = window.scrollY;
    }

    const newlyProcessed = alreadyRemoved.size - beforeCount;
    console.log(`\n✅ Pass complete — processed ${newlyProcessed} new channels. Total unsubscribed: ${totalUnsub}`);

    if (newlyProcessed === 0) {
      stuckCount++;
      if (stuckCount >= 2) {
        console.log(`\n🎉 All done! Unsubscribed from ${totalUnsub} channels total.`);
        break;
      }
      console.log("⏳ No new channels, trying one more pass...");
      await delay(3000);
    } else {
      stuckCount = 0;
    }
  }
})();

Step 4 — Watch the Console Output

The script logs everything it does in real time. You’ll see three types of lines:

  • 🇯🇵 Kept: [channel name] : a Japanese channel that was preserved
  • ❌ Unsubscribed: [channel name] : a non-Japanese channel that was removed
  • ⚠️ No dialog appeared for: [channel name] : the script couldn’t confirm the dialog and skipped that channel (rare)

At the end of each pass it prints a summary, and when it’s fully finished you’ll see:

🎉 All done! Unsubscribed from X channels total.

Optional: Dry Run (Check Before You Remove) [I have not tested this]

If you want to see what would be removed without actually unsubscribing anything, you can comment out the two click lines before running. Find these two lines in the script:

unsubBtn.click();
btn.click();

And add // in front of each one. The script will then log every channel it would remove without taking any action, giving you a chance to review the list first.


Troubleshooting

The script stops early and not all channels were processed. Just run it again. The alreadyRemoved set resets each time you paste it fresh, but the script will scroll through and pick up where YouTube left off. Channels already unsubscribed simply won’t appear any more.

Many channels show ⚠️ No dialog appeared. This usually means YouTube’s dialog is taking longer to appear than expected. Try increasing the polling timeout from 3000 to 5000 in the waitAndClickConfirm call.

A Japanese channel was accidentally removed. The script only detects Japanese script characters, it can’t detect Japanese-language channels that use English names. You’ll need to manually re-subscribe to those. Searching YouTube for the channel name is the quickest way to find them again.

I see a lot of red errors in the console. Mostly ignore them. The 403 (Forbidden) errors and preload warnings come from YouTube’s own internal processes and have nothing to do with the script. If in doubt just copy the errors into an AI and try to vibe code it. Or better yet get a friend who knows how to code to have a look at it (lol).


This script was developed iteratively through AI and is provided as-is. Use it at your own discretion, while it only performs actions you could do manually, running automated scripts always carries a small risk of unexpected behaviour if YouTube updates its page structure.