Tag Manager
Match and sync local tags with stash-box endpoints. Features multi-endpoint support, tag caching, layered search (exact, fuzzy, synonym), and field-by-field merge dialog.
No reviewer has approved this plugin yet. Read the source before installing, or review it for the community.
sha256 35db86ca87adf027b153e48520cbf46318e8527c38ba465a4502fb02617eb7dc
- Last updated
Sep 29, 2026- First created
- Version
0.7.0-2b306a8- Package id
tagManager- Commits
- 135
- Repo stars
- 31
Tasks
- Sync Scene Tags from StashDBAdd missing tags to scenes from each linked stash-box. Tags you removed from a scene are not added back.
- Reset Scene Tag Sync HistoryForget which stash-box tags each scene was synced with. The next sync considers every tag again, adding back tags you removed from scenes.
Settings
- Enable Fuzzy Search
booleanUse fuzzy matching for close variations (typos, etc). Enabled by default. - Enable Synonym Search
booleanUse custom synonym mappings. Enabled by default. - Fuzzy Match Threshold
numberMinimum score (0-100) for fuzzy matches. Default is 80. - Tags Per Page
numberNumber of tags to show per page. Default is 25. - Scene Tag Sync - Dry Run
booleanPreview what tags would be added without making changes (caps at 200 scenes). Default is enabled for safety. - Default to Stash-Box Name
booleanIn the merge dialog, default the Name selection to the stash-box value instead of keeping the local name. - Default to Stash-Box Description
booleanIn the merge dialog, default the Description selection to the stash-box value instead of keeping the local description. - Leave Parent Tags Alone
booleanDon't create or assign category parent tags when importing/matching. Keeps your own tag hierarchy untouched (imports tags flat). Default is off. - Category Mappings (Internal)
stringJSON mapping of stash-box categories to local parent tag IDs, kept per stash-box endpoint. Managed automatically. - Tag Blacklist
stringTags to exclude from matching and sync. Separate patterns with , or ; (or use the Blacklist button on the Match tab for one per line). Plain text matches the whole tag name. Use /regex/ for a regular expression (e.g., /^\d+p$/).
README
Tag Manager
Match and sync local tags with stash-box endpoints. Bulk cleanup your tag library with smart matching, browse and import tags from StashDB, and manage tag hierarchy.
Features
- Tag Matching - Smart layered search (exact, alias, fuzzy, synonym) to match local tags with StashDB
- Browse & Import - Browse StashDB tags by category and bulk import new tags
- Tag Hierarchy - Visual tree view with drag-and-drop editing for parent/child relationships
- Scene Tag Sync - Batch task to add stash-box tags to matched scenes, without re-adding tags you removed
- Tag Blacklist - Filter unwanted tags using literal strings or regex patterns, with an on-page editor
- Multi-endpoint Support - Works with StashDB, FansDB, and other stash-box instances
Note: While multi-endpoint support exists, the plugin is primarily tested with a single stash-box (StashDB). Using multiple endpoints simultaneously may produce unexpected behavior.
For detailed usage instructions, see the User Guide.
Requirements
- Stash v0.28+ (v0.30+ recommended for full
stash_idssupport) - At least one stash-box endpoint configured in Stash (Settings → Metadata Providers → Stash-Box Endpoints)
- Python 3.9+ (no packages required;
thefuzzandpython-Levenshteinare optional, see Installation)
Installation
Step 1: Install the Plugin
Option A: Via Stash Plugin Source (Recommended)
- In Stash, go to Settings → Plugins → Available Plugins
- Click Add Source
- Enter URL:
https://carrotwaxr.github.io/stash-plugins/stable/index.yml - Click Reload
- Find "Tag Manager" under "Carrot Waxxer" and click Install
Option B: Manual Installation
- Download or clone this repository
- Copy the
tagManagerfolder to your Stash plugins directory:- Windows:
C:\Users\<username>\.stash\plugins\ - macOS:
~/.stash/plugins/ - Linux:
~/.stash/plugins/
- Windows:
Step 2: Install Optional Python Packages
Tag Manager needs no Python packages to run, and Scene Tag Sync needs nothing extra. thefuzz and python-Levenshtein are optional: they only improve fuzzy tag matching. Without them Tag Manager falls back to basic matching. To install them, open a terminal/command prompt and run:
Windows (Command Prompt or PowerShell):
pip install thefuzz python-Levenshtein
macOS / Linux:
pip install thefuzz python-Levenshtein
Troubleshooting pip:
- If you have multiple Python versions, use
pip3instead ofpip- If pip isn't in your PATH, try
python -m pip install ...orpython3 -m pip install ...- On Windows, you can also try
py -m pip install ...
Step 3: Configure Stash-Box
- Go to Stash → Settings → Metadata Providers → Stash-Box Endpoints
- Add your stash-box (e.g., StashDB at
https://stashdb.org/graphql) - Enter your API key (get one from your stash-box account settings)
Step 4: Reload and Verify
- Go to Settings → Plugins and click "Reload Plugins"
- Navigate to the Tags page - you should see new icon buttons for Tag Manager
Quick Start
- Match Tags: Go to Tags page → Click the tag icon → Select your stash-box → Click "Find Matches for Page"
- Browse StashDB: Switch to "Browse StashDB" tab → Select a category → Check tags to import → Click "Import Selected"
- View Hierarchy: Click the sitemap icon → Browse your tag tree → Right-click to edit relationships
Plugin Settings
Go to Settings → Plugins → Tag Manager:
| Setting | Description | Default |
|---|---|---|
| Enable Fuzzy Search | Use fuzzy matching for typos | Enabled |
| Enable Synonym Search | Use custom synonym mappings | Enabled |
| Fuzzy Match Threshold | Minimum score (0-100) for fuzzy matches. 0 is allowed | 80 |
| Tags Per Page | Number of tags shown per page (at least 1) | 25 |
| Scene Tag Sync - Dry Run | Preview sync without making changes. Checks at most 200 scenes | Enabled |
| Default to Stash-Box Name | In the merge dialog, pick the stash-box name instead of keeping the local name | Off |
| Default to Stash-Box Description | In the merge dialog, pick the stash-box description instead of keeping the local one | Off |
| Leave Parent Tags Alone | Import/match tags flat: don't create or assign category parent tags, keeping your own hierarchy untouched | Off |
| Category Mappings (Internal) | Saved category to parent tag choices, kept per stash-box. Managed automatically, don't edit by hand | {} |
| Tag Blacklist | Patterns to exclude from matching and sync. Separate with , or ;, or use the Blacklist button on the Match tab. See the User Guide |
Empty |
The backend reads these settings from Stash's saved plugin config, so a change applies after you save it in Stash.
Default Settings File
Stash's plugin settings have no defaults of their own. assets/default_settings.json holds the default for each setting. Both the UI and the backend read it. Don't edit it to change your own settings: use the Settings page. If you add a setting to tagManager.yml, add it to this file too. A test checks that every setting is listed.
Troubleshooting
Fuzzy Matching Is Basic
thefuzz is optional. Without it Tag Manager uses basic matching. To get better fuzzy matching, install it for the Python that Stash uses:
# Check which Python Stash is using
python --version
python3 --version
# Install for the correct Python version
python3 -m pip install thefuzz python-Levenshtein
# On Windows, you may need to run as administrator
# Or try: py -m pip install thefuzz python-Levenshtein
"No Stash-Box Configured"
- Go to Settings → Metadata Providers → Stash-Box Endpoints
- Add your stash-box endpoint URL and API key
- Reload plugins and try again
Plugin Not Appearing
- Check that the
tagManagerfolder is in the correct plugins directory - Verify folder structure:
plugins/tagManager/tagManager.ymlshould exist - Check Stash logs (Settings → Logs) for error messages
- Reload plugins in Settings → Plugins
Cache Takes Too Long / Fetch Errors
The first fetch from StashDB downloads 20,000+ tags in pages of 1,000 and takes about 5 seconds. Later loads use the local cache. If a stash-box rejects the page size, Tag Manager falls back to pages of 100, which is slower. A failed or partial fetch is never cached. If you get errors:
- Read the error shown in the UI. It comes from the stash-box.
- If it says the stash-box rejected the API key (HTTP 401 or 403), check the API key in Settings → Metadata Providers.
- Try "Refresh Cache" again. The stash-box may be busy.
Scene Tag Sync Errors
- Ensure scenes have stash-box IDs (use Stash's Tagger first)
- Start with "Dry Run" enabled to preview changes
- An error naming a stash-box and HTTP 401 or 403 means that stash-box rejected the API key. Check it in Settings → Metadata Providers.
- If tags you removed from scenes keep coming back, or the history file can't be read, run the "Reset Scene Tag Sync History" task
- Check Stash logs for detailed error messages
SSL/Certificate Errors (Windows)
If you see SSL errors:
- Ensure Python is up to date
- Try:
pip install --upgrade certifi
Development
Running Tests
cd plugins/tagManager
# Python unit tests (offline)
python -m pytest
# JavaScript tests
for f in tests/test_*.js; do node "$f"; done
# Integration tests against StashDB (requires an API key)
STASH_PLUGINS_INTEGRATION=1 STASHDB_API_KEY=your-key python -m pytest tests/test_integration.py
Tests that talk to a real Stash, StashDB or Whisparr skip unless STASH_PLUGINS_INTEGRATION=1 is set. Point them at a test instance, never production.
File Structure
tagManager/
├── tagManager.yml # Plugin manifest
├── tag_manager.py # Python backend (search, cache, sync)
├── stashdb_api.py # Stash-box GraphQL client
├── stash_client.py # Client for the local Stash server
├── sync_history.py # Scene sync history (SQLite)
├── plugin_data.py # Location of runtime state
├── log.py # Plugin logging
├── stashdb_scene_sync.py # Scene tag sync logic
├── matcher.py # Tag matching algorithms
├── blacklist.py # Blacklist pattern matching
├── tag_cache.py # Local tag lookup cache
├── tag-manager.js # JavaScript UI
├── tag-manager.css # UI styles
├── synonyms.json # Custom synonym mappings
├── assets/ # Files served to the UI (default_settings.json)
├── requirements.txt # Optional Python packages
└── tests/ # Test suite
Where Runtime State Is Stored
Caches and sync history live in Stash's config folder, not the plugin folder, so plugin updates don't wipe them:
<Stash config dir>/plugin_data/tagManager/
├── tag_cache/ # Stash-box tag caches
└── sync_history.sqlite # Scene Tag Sync history
The cache/ folder inside the plugin folder from older versions is no longer used. You can delete it.
Changelog
v0.7.0
- Requirements: Python 3.9+ with no required packages.
thefuzzandpython-Levenshteinare optional.stashapp-toolsis no longer used (fixes #129). - Faster, clearer tag fetches: 1,000 tags per page, so a full StashDB fetch takes about 5 seconds instead of 30-40+. Falls back to 100 per page if a stash-box rejects it. Failed or partial fetches are never cached. Stash-box errors show in the UI, and HTTP 401/403 tells you to check the API key. Requests send a
User-Agent, which fixes HTTP 403 errors from ThePornDB and JAVStash. ThePornDB fetches now get every tag instead of the first 100. - State moved to Stash's config folder (
plugin_data/tagManager/). Plugin updates no longer wipe caches. The oldcache/folder can be deleted. - Settings are read by the backend from Stash's saved plugin config. The saved blacklist now applies to searches.
- Scene Tag Sync: syncs each scene against every linked stash-box that has an API key, adds tags in a way that keeps tags added during a long sync, and remembers matched tags so tags you removed are not added back. New "Reset Scene Tag Sync History" task. The first live sync after upgrading can re-add tags you removed before 0.7.0 one last time. Dry run checks at most 200 scenes and previews the history-aware result. A rejected API key stops the sync with an error naming the stash-box. Respects each stash-box's max requests per minute.
- Blacklist:
/regex/flagssyntax, patterns separated by newlines,,or;, and a Blacklist editor button on the Match tab. It now applies to the best match, Accept, "More matches" (Select now applies the tag you clicked), manual and backend searches, Import All and Scene Tag Sync. - Accept/Apply dialog: saved category mappings show as "(saved mapping)" and are pre-selected. Parent controls are hidden with "Leave Parent Tags Alone". The pre-selected
Create "<category>"option now really creates the parent tag. Names and aliases are checked for conflicts first, and Apply is disabled while it runs. - Category mappings are stored per stash-box endpoint. Existing mappings move under StashDB automatically. A failed settings load can no longer overwrite them.
- Merging tags asks for confirmation and shows what moves. On Stash 0.31+ the merge and the update happen in one transaction. Parents and children of the merged tag now carry over, and the parent you pick in the dialog is added.
- Import conflicts dialog: failed reverse merges clean up, other rows are re-checked after each action, "strip" lists dropped aliases, replacing a stash ID asks first, and the dialog stays open with a "Done" button.
- Import: shows progress and has a Cancel button. Import All skips blacklisted and already-linked tags and is much faster on large libraries.
- Tag Hierarchy: edit mode resets when you leave the page, saving re-reads current parents and keeps failed changes pending, alias search works, large trees render lazily, and keyboard shortcuts only act when the tree has focus.
- Navigation works when Stash is served under a sub-path and no longer reloads the page.
- Fuzzy Match Threshold and Tags Per Page are parsed safely. 0 is a valid threshold.
v0.6.1
- The backend now looks up the stash-box URL and API key in Stash's own configuration instead of accepting them from the browser. An endpoint that isn't configured in Stash is rejected.
- Only the
assets/folder is served to the browser, instead of the whole plugin directory.
v0.6.0
- Resolve import conflicts in the UI (#125): when an imported tag's name or alias is already used by a local tag, the import no longer fails silently to the console. A "Resolve Tag Conflicts" dialog now lets you merge into the existing tag, strip the clashing alias and import anyway, open the conflicting tag to fix it by hand, do a confirm-gated reverse merge, or skip. The import summary reports conflicts resolved and skipped.
v0.5.0
- Category mappings now persist reliably (#122): config writes are serialized and verified after saving, so a selected parent tag no longer reverts to "create new." A failed save now surfaces a toast instead of silently dropping.
- Tag list refreshes without a full page reload (#124): returning to the tab (e.g. after fixing a conflicting tag elsewhere) and finishing a merge/import now re-fetch from Stash automatically.
- New "Leave Parent Tags Alone" setting (#126): import/match tags flat without creating or assigning category parent tags, for users who maintain their own nested hierarchy.
- Hardened plugin config reads (empty/cleared numeric settings and malformed config no longer error).
License
MIT License - See repository root for details.
Contributors
More from carrotwaxr/stash-plugins
- mcMetadataGenerates NFO metadata files for Jellyfin/Emby/Plex, organizes/renames video files using customizable templates, and exports performer images to media server folders.
- Missing ScenesDiscover scenes from StashDB, other stash-boxes and ThePornDB that you don't have locally. View missing scenes for performers, studios and tags, with optional Whisparr v3 integration (StashDB scenes) for automated downloading and cleanup.
- Performer Image SearchSearch multiple sources for performer images. Supports mainstream, JAV, male, and trans performers with configurable sources.
- Scene MatcherFind matches for untagged scenes on the stash-box selected in the Tagger. Adds a "Match" button that searches by title, then by linked performers and studio.
- Studio ManagerManage studio hierarchy with visual tree editing. View and edit parent-child studio relationships.