Advanced scene group sorting and management tool for StashApp
While StashApp natively supports only single-field sorting, Stash Sorter provides advanced sorting capabilities including multi-field sorting, random sorting, interleaved sorting, and more.
This project was developed with a focus on portability and independence:
- Pure GraphQL API: Instead of relying on external libraries like
stashapp-tools, this tool communicates directly with Stash via the GraphQL API. This ensures lightweight execution and avoids issues caused by library version mismatches or incomplete documentation. - AI-Optimized Architecture: The entire logic was orchestrated by Claude Code, utilizing raw API calls to ensure precise control over data manipulation without the overhead of third-party wrappers.
- π― Multi-Field Sorting: Complex sorting with multiple fields (e.g., rating β date β studio)
- π² Random Sorting: Randomly shuffle scenes with the same field values
- π Interleaved Sorting: Randomly interleave groups while maintaining order within each group
- π Advanced Filters: Precise scene search with 8 filter types
- π Template System: Save and reuse custom sorting configurations
- β»οΈ Group Management: Auto-remove played scenes, re-sort existing groups
- π» User-Friendly: Interactive menu-based interface
- π‘οΈ DRY_RUN Mode: Safe testing before making actual changes
- Python 3.8 or higher
- StashApp server (v0.20.0 or higher recommended)
- StashApp API Key
# Clone the repository
git clone <repository-url>
cd stashsorter
# Install dependencies
pip install -r requirements.txtCreate a .env file and configure the following:
STASH_URL=http://localhost:9999/graphql
STASH_API_KEY=your_api_key_here
# Optional settings
LOG_LEVEL=INFO
DRY_RUN=false
TIMEOUT=30
MAX_RETRIES=3How to get your API Key:
- Open StashApp web interface
- Go to Settings β Security β Generate API Key
python run.py1. Create Sorted Group - Create new group with filters and sorting rules
2. Manage Group - Remove scenes and re-sort existing groups
3. Exit
Define criteria to search for scenes.
Available Filters:
- Rating (rating100)
- Path (path)
- Tags (tags)
- Play Count (play_count)
- Performers (performers)
- Studio (studio)
- Date (date)
- Organized (organized)
Example:
Rating >= 80 + Play Count = 0 + Path includes "/favorites/"
Option A: Use Template
- Select from built-in templates:
- Interleaved by Folder, Sorted by Filename
- Interleaved by Folder, Sorted by Path
- Random Folder + Rating Descending
- Random Studio + Rating Descending
- Or use your saved custom templates
Option B: Custom Sort
- Manually configure sort keys
- Optionally save as a template for future use
Sort Directions:
asc: Ascending order (smallest first)desc: Descending order (largest first)random: Random shuffleinterleaved: Randomly interleave groups, maintain order within groups
Example:
1. rating100 (desc) - Highest rated first
2. date (asc) - Oldest first
3. studio.parent_studio.name (random) - Random within each studio
- Preview first 50 sorted scenes
- View directory distribution analysis
- View directory transition patterns
- Options:
- Confirm and proceed to group creation
- Re-sort with different settings
- Cancel
- Enter group name
- Review final summary
- Create group and add scenes
Automatically removes scenes with play_count > 0 from a group.
- Enter group ID
- Review list of scenes to be removed
- Confirm and bulk remove
Re-sorts scenes in a group using sorting rules stored in the group description.
- Enter group ID
- Auto-parse sorting rules (or manually input)
- Re-sort scenes and update group
Filters:
- rating100 >= 80
- play_count = 0
Sort:
1. rating100 (desc)
2. date (asc)
Result: Unwatched scenes rated 80+ sorted by rating, then by date
Filters:
- path INCLUDES "/favorites/"
- organized = true
Sort:
1. files.0.parent_folder.path (interleaved)
2. files.0.basename (asc)
Result: Folders randomly interleaved, files within each folder sorted alphabetically
Filters:
- date >= 2024-01-01
Sort:
1. studio.name (random)
2. rating100 (desc)
Result: Studios in random order, scenes within each studio sorted by rating
Filters:
- date >= 2024-01-01
- performers INCLUDES_ALL [1, 5, 10]
Sort:
1. date (desc)
2. performers.0.name (asc)
3. rating100 (desc)
Result: Latest first, then by performer name, then by rating
stashsorter/
βββ run.py # Main launcher
βββ lib/ # Core library modules
β βββ config.py # Configuration
β βββ logger.py # Logging
β βββ stash_client.py # GraphQL client
β βββ validators.py # Validation functions
β βββ ui_helpers.py # UI helper functions
β βββ sorters.py # Sorting logic
β βββ template_manager.py # Template management
β βββ cache_manager.py # Cache management
βββ scripts/ # Executable scripts
β βββ create_sorted_group.py # Create sorted groups
β βββ create_sorted_group_interactive.py # Interactive workflow
β βββ manage_group.py # Group management
βββ templates/ # Sorting templates
β βββ builtin_templates.json # Built-in templates
β βββ user_templates.json # User-created templates
βββ cache/ # Cache files (auto-generated)
βββ logs/ # Log files (auto-generated)
βββ .env # Environment variables
βββ .env.example # Environment template
βββ requirements.txt # Python dependencies
βββ README.md # This file
Each module can be tested independently:
# Config test
python lib/config.py
# Logger test
python lib/logger.py
# Validators test
python lib/validators.py
# Sorters test (sorting algorithms)
python lib/sorters.py
# UI Helpers test
python lib/ui_helpers.py
# StashClient test (requires server connection)
python lib/stash_client.pySet DRY_RUN=true in .env to simulate operations without making actual changes:
DRY_RUN=trueSet log level to DEBUG for detailed execution logs:
LOG_LEVEL=DEBUGLogs are saved to logs/stashsorter.log.
- Scene data is cached in
cache/directory during operations - Cache files are automatically cleaned up on program exit
- Manual cleanup: Delete files in
cache/directory
| Field Name | Description | Type |
|---|---|---|
title |
Scene title | String |
date |
Scene date | Date |
rating100 |
Rating (0-100) | Number |
play_count |
Play count | Number |
play_duration |
Total play duration | Number |
last_played_at |
Last played timestamp | Date |
o_counter |
O counter | Number |
created_at |
Created timestamp | Date |
updated_at |
Updated timestamp | Date |
duration |
Video duration | Number |
filesize |
File size | Number |
framerate |
Frame rate | Number |
bitrate |
Bit rate | Number |
path |
File path | String |
organized |
Organized flag | Boolean |
studio.name |
Studio name | String |
Nested Field Access:
studio.name
studio.parent_studio.name
files.0.path
files.0.basename
files.0.parent_folder.path
performers.0.name
tags.0.name
| Variable | Required | Default | Description |
|---|---|---|---|
STASH_URL |
β | - | StashApp GraphQL endpoint |
STASH_API_KEY |
β | - | StashApp API key |
DRY_RUN |
β | false |
Test mode (no actual changes) |
LOG_LEVEL |
β | INFO |
Log level (DEBUG, INFO, WARNING, ERROR) |
TIMEOUT |
β | 30 |
API request timeout (seconds) |
MAX_RETRIES |
β | 3 |
Maximum API request retries |
Error: Failed to connect to Stash server
Solutions:
- Verify Stash server is running
- Check
STASH_URLin.envis correct - Verify API Key is valid
- Check firewall settings
Error: Invalid API Key
Solutions:
- Generate new API Key in StashApp Settings β Security
- Update
.envfile with new key - Restart the program
Error: No scenes found matching criteria
Solutions:
- Check if filter conditions are too strict
- Try the same filters in StashApp web UI
- Set
LOG_LEVEL=DEBUGand check query output
Error: Template not found
Solutions:
- Check
templates/directory exists - Verify template files are valid JSON
- Re-create templates if corrupted
This project is licensed under the MIT License.
Notice: The entire codebase was generated and orchestrated by Claude Code. As the publisher, I have limited technical knowledge of the internal logic.
Bug reports and feature requests are welcome, but I strongly encourage you to solve them via Fork and Pull Request:
- Fork the repository
- Create a feature branch
- Implement your changes or fixes
- Submit a Pull Request
Please note that direct technical support may be limited due to the nature of this project's development.
- GitHub Issues: For reporting critical bugs (resolution depends on community contribution)
- GraphQL Reference: See Stash GraphQL references
- Claude Code for generating the core logic
- StashApp Team for the excellent media management platform
- StashApp Community for their invaluable feedback and suggestions
Made with β€οΈ for the StashApp community