npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kitsunekode/yt-ddp

v0.2.0

Published

CLI for safely finding and removing duplicate videos from a YouTube playlist.

Readme

@kitsunekode/yt-ddp

yt-ddp is a production-ready CLI for safely finding and removing duplicate videos from a single YouTube playlist.

It is dry-run by default, accepts either a playlist ID or full YouTube URL, includes a guided OAuth setup flow, and is packaged for npm as @kitsunekode/yt-ddp.

See CHANGELOG.md for release notes.

Install

Global install:

npm install -g @kitsunekode/yt-ddp

Run once without installing:

bunx @kitsunekode/yt-ddp --help

Local development:

bun install

Quick Start

Published package:

yt-ddp setup
yt-ddp "https://www.youtube.com/playlist?list=PLAYLIST_ID"

If the downloaded Google Desktop app OAuth JSON is already in your current directory with a name like client_secret*.json or client*.json, yt-ddp setup will detect it and pressing Enter will use it.

Local repo:

bun run setup
bun run start -- "https://www.youtube.com/playlist?list=PLAYLIST_ID"

What It Does

  • Authenticates with Google using the installed-app OAuth flow
  • Scans exactly one playlist
  • Detects duplicates by video ID
  • Keeps the first occurrence of each video
  • Deletes duplicates only when you explicitly opt in
  • Stores refreshed OAuth tokens locally so re-auth is not needed every run
  • Retries transient delete failures
  • Deletes duplicate items sequentially with bounded retries to avoid unnecessary rate-limit pressure

Safety

  • Dry run is the default
  • Deletion requires --execute
  • Deletion also requires a confirmation prompt unless you pass --yes
  • Known protected/system playlists are blocked
  • Only the playlist you pass is scanned or modified

Commands

  • yt-ddp setup
  • yt-ddp login
  • yt-ddp <playlist-id-or-url>
  • yt-ddp scan --playlist <playlist-id-or-url>
  • yt-ddp completion zsh

Useful flags:

  • --playlist for explicit input
  • --execute to delete duplicates
  • --yes to skip the final prompt
  • --json for machine-readable output
  • --help to print usage

OAuth Setup

  1. Create or select a Google Cloud project.
  2. Enable the YouTube Data API v3.
  3. Configure the OAuth consent screen.
  4. Create Desktop app OAuth credentials.
  5. Download the JSON file.
  6. Run yt-ddp setup and paste the JSON path when prompted, or just press Enter if the downloaded file is already in the current directory.
  7. yt-ddp stores the normalized client config in its local app config directory for future runs.

By default the saved client config and OAuth token live here:

~/.config/yt-ddp/

Advanced overrides are still supported when you need them:

export YT_DDP_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
export YT_DDP_OAUTH_CLIENT_SECRET=your-client-secret

Other supported env options:

  • YT_DDP_OAUTH_CLIENT_JSON_BASE64
  • YT_DDP_OAUTH_CLIENT_FILE
  • YT_DDP_CONFIG_DIR

Legacy YOUTUBE_* and YT_PLAYLIST_DEDUPE_* env vars are still accepted.

Need a walkthrough for the Google Cloud side? See docs/google-oauth-setup.md.

Usage Examples

Dry run:

yt-ddp PLAYLIST_ID

Playlist URL:

yt-ddp "https://www.youtube.com/playlist?list=PLAYLIST_ID"

Watch URL with list=:

yt-ddp "https://www.youtube.com/watch?v=VIDEO_ID&list=PLAYLIST_ID"

Execute deletions:

yt-ddp PLAYLIST_ID --execute

Non-interactive execute:

yt-ddp PLAYLIST_ID --execute --yes

JSON output:

yt-ddp PLAYLIST_ID --json

Zsh completion:

mkdir -p ~/.zfunc
yt-ddp completion zsh > ~/.zfunc/_yt-ddp
fpath=(~/.zfunc $fpath)
autoload -Uz compinit && compinit

Privacy

yt-ddp is designed to run locally. In the normal CLI flow there is no hosted yt-ddp backend that receives your playlist data.

  • your OAuth token is stored locally on your machine
  • your playlist scan happens locally on your machine
  • your downloaded OAuth client JSON stays local unless you share it

Google still handles the sign-in and API authorization, but the tool itself is meant to be a local utility.

Local Global Linking

Make the local repo available globally as yt-ddp:

bun run link:global
yt-ddp --help

Remove the global link:

bun run unlink:global

Versioning With Changesets

This repo uses Changesets for release versioning.

Create a changeset after a user-facing change:

bun run changeset

Preview pending release state:

bun run changeset:status

Apply version bumps and changelog updates from pending changesets:

bun run version-packages

Recommended release flow:

bun run changeset
bun run version-packages
bun run publish:check
bun publish --access public

If you want Changesets to publish directly:

bun run release

Production Scripts

  • bun run fix auto-formats and applies safe Biome fixes
  • bun run lint runs Biome lint rules
  • bun run check runs Biome checks, typecheck, and tests
  • bun run build creates the publishable dist/ output
  • bun run smoke:built verifies the built CLI runs
  • bun run pack:dry-run shows exactly what npm would publish
  • bun run publish:check runs the full prepublish gate

CI And Release Automation

This repo now includes two GitHub Actions workflows:

  • .github/workflows/ci.yml: runs the full validation gate on pull requests, pushes to main, and manual dispatch
  • .github/workflows/release.yml: runs on main and uses Changesets to open or update the release PR, then publish to npm after that PR is merged

Required GitHub secret for automated publishing:

  • NPM_TOKEN: npm token with permission to publish @kitsunekode/yt-ddp

Expected release behavior:

  1. Add a changeset in your feature PR.
  2. Merge the PR to main.
  3. The release workflow opens or updates a Version Packages PR.
  4. Merge that PR when the version/changelog looks right.
  5. The release workflow publishes the new version to npm.

Config Storage

By default yt-ddp stores both the saved OAuth client config and OAuth token here:

~/.config/yt-ddp/

Override that location with:

export YT_DDP_CONFIG_DIR=/custom/config/dir

Notes

  • Use a playlist ID such as one starting with PL, or a full YouTube URL that includes list=...
  • Private/deleted items without a usable video ID are skipped
  • Large playlists are processed page by page

Development

Run the full release gate:

bun run publish:check

Use .env.example only for advanced local development overrides if you want Bun to auto-load credentials.

License

MIT. See LICENSE.