@kitsunekode/yt-ddp
v0.2.0
Published
CLI for safely finding and removing duplicate videos from a YouTube playlist.
Maintainers
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-ddpRun once without installing:
bunx @kitsunekode/yt-ddp --helpLocal development:
bun installQuick 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 setupyt-ddp loginyt-ddp <playlist-id-or-url>yt-ddp scan --playlist <playlist-id-or-url>yt-ddp completion zsh
Useful flags:
--playlistfor explicit input--executeto delete duplicates--yesto skip the final prompt--jsonfor machine-readable output--helpto print usage
OAuth Setup
- Create or select a Google Cloud project.
- Enable the YouTube Data API v3.
- Configure the OAuth consent screen.
- Create
Desktop appOAuth credentials. - Download the JSON file.
- Run
yt-ddp setupand paste the JSON path when prompted, or just press Enter if the downloaded file is already in the current directory. yt-ddpstores 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-secretOther supported env options:
YT_DDP_OAUTH_CLIENT_JSON_BASE64YT_DDP_OAUTH_CLIENT_FILEYT_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_IDPlaylist 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 --executeNon-interactive execute:
yt-ddp PLAYLIST_ID --execute --yesJSON output:
yt-ddp PLAYLIST_ID --jsonZsh completion:
mkdir -p ~/.zfunc
yt-ddp completion zsh > ~/.zfunc/_yt-ddp
fpath=(~/.zfunc $fpath)
autoload -Uz compinit && compinitPrivacy
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 --helpRemove the global link:
bun run unlink:globalVersioning With Changesets
This repo uses Changesets for release versioning.
Create a changeset after a user-facing change:
bun run changesetPreview pending release state:
bun run changeset:statusApply version bumps and changelog updates from pending changesets:
bun run version-packagesRecommended release flow:
bun run changeset
bun run version-packages
bun run publish:check
bun publish --access publicIf you want Changesets to publish directly:
bun run releaseProduction Scripts
bun run fixauto-formats and applies safe Biome fixesbun run lintruns Biome lint rulesbun run checkruns Biome checks, typecheck, and testsbun run buildcreates the publishabledist/outputbun run smoke:builtverifies the built CLI runsbun run pack:dry-runshows exactly what npm would publishbun run publish:checkruns 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 tomain, and manual dispatch.github/workflows/release.yml: runs onmainand 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:
- Add a changeset in your feature PR.
- Merge the PR to
main. - The release workflow opens or updates a
Version PackagesPR. - Merge that PR when the version/changelog looks right.
- 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/dirNotes
- Use a playlist ID such as one starting with
PL, or a full YouTube URL that includeslist=... - 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:checkUse .env.example only for advanced local development overrides if you want Bun to auto-load credentials.
License
MIT. See LICENSE.
