| Summary | Provides advanced sorting capabilities including multi-field sorting, random sorting, interleaved sorting, and more. | |
| Repository | https://github.com/efirlus/stashsorter |
Stash Sorter
Advanced scene group sorting and management tool for StashApp
Overview
While StashApp natively supports only single-field sorting, Stash Sorter provides advanced sorting capabilities including multi-field sorting, random sorting, interleaved sorting, and more.
Why Stash Sorter?
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.
Key Features
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
Quick Start
1. Requirements
- Python 3.8 or higher
- StashApp server (v0.20.0 or higher recommended)
- StashApp API Key
2. Installation
# Clone the repository
git clone <repository-url>
cd stashsorter
# Install dependencies
pip install -r requirements.txt
3. Configuration
Create 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=3
How to get your API Key:
- Open StashApp web interface
- Go to Settings → Security → Generate API Key
4. Run
python run.py
Usage
Main Menu
1. Create Sorted Group - Create new group with filters and sorting rules
2. Manage Group - Remove scenes and re-sort existing groups
3. Exit
Creating a Sorted Group
Phase 1: Filter Setup
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/"
Phase 2: Sorting Method Selection
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
Phase 3: Preview & Confirmation
- 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
Phase 4: Group Creation
- Enter group name
- Review final summary
- Create group and add scenes
Group Management
Remove Played 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-sort Group
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
Use Cases
Example 1: High-Rated Unwatched Scenes
Filters:
- rating100 >= 80
- play_count = 0
Sort:
1. rating100 (desc)
2. date (asc)
Result: Unwatched scenes rated 80+ sorted by rating, then by date
Example 2: Interleaved Folder Playlist
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
Example 3: Random Studio Rotation
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
Example 4: Date + Performer Sorting
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
Project Structure
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
Sortable Fields
| 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
Configuration Options
Environment Variables
| 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 |
Troubleshooting
Stash Server Connection Failed
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
Invalid API Key Error
Error: Invalid API Key
Solutions:
- Generate new API Key in StashApp Settings → Security
- Update
.envfile with new key - Restart the program
No Scenes Found
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
Template Not Found
Error: Template not found
Solutions:
- Check
templates/directory exists - Verify template files are valid JSON
- Re-create templates if corrupted