Async Python client for Stash GraphQL API
Reason this release was yanked:
Removing all pre v0.10.x builds
Project description
stash-graphql-client
Async Python client for Stash GraphQL API.
🔗 Quick Links
- 📦 PyPI Package - Install via pip
- 📚 Documentation - Full API reference and guides
- 🎬 Stash Project - Upstream media server this client connects to
- 📝 Changelog - Version history and release notes
- 🤝 Contributing - Development guide and contribution guidelines
- 🔒 Security Policy - Vulnerability reporting and security best practices
Features
- Async-first: Built with
gql+HTTPXAsyncTransport+WebsocketsTransport - Pydantic types: All Stash GraphQL schema objects as Pydantic v2 models
- Identity Map: Smart caching with read-through and field-aware population
- UNSET Pattern: Three-level field system (value, null, UNSET) for precise partial updates
- UUID4 Auto-generation: Automatic temporary IDs for new objects
- Full CRUD: Operations for all entity types (Scene, Gallery, Performer, Studio, Tag, etc.)
- Job management: Metadata scanning, generation, and job status tracking
- Subscriptions: GraphQL subscription support for real-time updates
- Fuzzy Dates: Support for year-only and year-month date formats (Stash v0.30.0+)
Installation
From PyPI (Recommended)
pip install stash-graphql-client
With Poetry
poetry add stash-graphql-client
From Source
git clone https://github.com/Jakan-Kink/stash-graphql-client.git
cd stash-graphql-client
poetry install
Requirements
- Python 3.12+
- Poetry for development
Quick Start
from stash_graphql_client import StashClient, StashContext
from stash_graphql_client.types import Tag
# Using context manager (recommended)
async with StashContext(conn={
"Host": "localhost",
"Port": 9999,
"ApiKey": "your-api-key", # Optional
}) as client:
# Find all studios
result = await client.find_studios()
print(f"Found {result.count} studios")
# Create a new tag
tag_obj = Tag(name="My Tag")
tag = await client.create_tag(tag_obj)
print(f"Created tag: {tag.name}")
# Or manual lifecycle management
context = StashContext(conn={"Host": "localhost", "Port": 9999})
client = await context.get_client()
try:
scenes = await client.find_scenes()
finally:
await context.close()
Connection Options
conn = {
"Scheme": "http", # or "https"
"Host": "localhost", # Stash server host
"Port": 9999, # Stash server port
"ApiKey": "...", # Optional API key
}
Architecture
This library follows a three-layer architecture:
1. StashClient - GraphQL Transport Layer
Located in stash_graphql_client/client/, the client provides direct access to Stash's GraphQL API through typed mixin methods:
# Direct GraphQL operations
scenes = await client.find_scenes()
performer = await client.create_performer(Performer(name="Alice"))
job_id = await client.metadata_scan()
2. Pydantic Types - Schema/ORM Layer
Located in stash_graphql_client/types/, all Stash entities are Pydantic v2 models with:
- UNSET Pattern: Three-level field system distinguishes between "set to value", "set to null", and "never touched"
- UUID4 Auto-generation: New objects get temporary IDs replaced with server IDs on save
- Bidirectional Relationships: Automatic sync between related entities
from stash_graphql_client.types import Scene, UNSET
# Create with partial data - UUID4 auto-generated
scene = Scene(title="My Scene", rating100=85)
print(f"New object ID: {scene.id}") # UUID4 hex string (32 chars)
print(f"Is new: {scene.is_new()}") # True
scene.details = UNSET # Don't touch this field on save
# Save sends only non-UNSET fields
await scene.save(client)
print(f"Server ID: {scene.id}") # Server-assigned ID
print(f"Is new: {scene.is_new()}") # False
3. StashEntityStore - Identity Map & Caching Layer
Located in stash_graphql_client/store.py, provides SQLAlchemy/ActiveRecord-style data access:
from stash_graphql_client import StashContext
async with StashContext(conn=...) as client:
store = StashEntityStore(client)
# Read-through caching
performer = await store.get(Performer, "123")
# Field-aware population (load only what you need)
performer = await store.populate(performer, fields=["scenes", "images"])
# Get-or-create pattern
tag = await store.get_or_create(Tag, name="Action")
await store.save(tag)
Documentation
- Quick Reference - UNSET & UUID4 patterns cheat sheet
- UNSET & UUID4 Guide - Comprehensive guide with examples
- Usage Examples - ID mapping, hierarchy navigation, convenience methods
- Fuzzy Dates Guide - Working with partial date formats (v0.30.0+)
- Bidirectional Relationships - Entity relationship architecture
- API Reference - Complete API documentation
Available Operations
Scenes
find_scenes(filter)- Search scenes with filtersfind_scene(id)- Find scene by IDcreate_scene(scene)- Create new sceneupdate_scene(scene)- Update scene detailsdestroy_scene(id)- Delete scene
Galleries
find_galleries(filter)- Search galleries with filtersfind_gallery(id)- Find gallery by IDcreate_gallery(gallery)- Create new galleryupdate_gallery(gallery)- Update gallery detailsdestroy_gallery(id)- Delete gallery
Images
find_images(filter)- Search images with filtersfind_image(id)- Find image by IDupdate_image(image)- Update image detailsdestroy_image(id)- Delete image
Performers
find_performers(filter)- Search performers with filtersfind_performer(id)- Find performer by IDmap_performer_ids(performers, create)- Convert performer names/objects to IDsall_performers()- Get all performers (deprecated, use find_performers)create_performer(performer)- Create new performerupdate_performer(performer)- Update performer detailsbulk_performer_update(input)- Update multiple performersdestroy_performer(id)- Delete performer
Studios
find_studios(filter)- Search studios with filtersfind_studio(id)- Find studio by IDfind_studio_hierarchy(id)- Get full parent chain from root to studiofind_studio_root(id)- Find the top-level parent studiomap_studio_ids(studios, create)- Convert studio names/objects to IDscreate_studio(studio)- Create new studioupdate_studio(studio)- Update studio detailsbulk_studio_update(input)- Update multiple studiosstudio_destroy(id)- Delete studio
Tags
find_tags(filter)- Search tags with filtersfind_tag(id)- Find tag by IDmap_tag_ids(tags, create)- Convert tag names/objects to IDscreate_tag(tag)- Create new tagupdate_tag(tag)- Update tag detailstags_merge(source, destination)- Merge tagsbulk_tag_update(input)- Update multiple tagsdestroy_tag(id)- Delete tag
Groups (formerly Movies)
find_groups(filter)- Search groups with filtersfind_group(id)- Find group by IDcreate_group(group)- Create new groupupdate_group(group)- Update group detailsdestroy_group(id)- Delete group
Markers
find_scene_markers(filter)- Search scene markersfind_scene_marker(id)- Find marker by IDcreate_scene_marker(marker)- Create new markerupdate_scene_marker(marker)- Update markerdestroy_scene_marker(id)- Delete marker
System Operations
get_system_status()- Get current system statuscheck_system_ready()- Verify system is ready for processingstats()- Get database statisticsversion()- Get Stash version informationlatestversion()- Get latest available Stash versionlogs()- Get system logsdirectory(path, locale)- Browse filesystemdlna_status()- Get DLNA server statussql_query(sql, args)- Execute SQL query (⚠️ DANGEROUS - use with caution)sql_exec(sql, args)- Execute SQL statement (⚠️ DANGEROUS - use with caution)get_configuration()- Get complete configuration
Configuration Operations
configure_general(input)- Configure general Stash settingsconfigure_interface(input)- Configure Stash interface settingsconfigure_dlna(input)- Configure DLNA server settingsconfigure_defaults(input)- Configure default metadata operation settingsconfigure_ui(input, partial)- Configure UI settingsconfigure_ui_setting(key, value)- Configure a single UI settingconfigure_scraping(input)- Configure scraping settingsgenerate_api_key(input)- Generate a new API keyfind_saved_filter(id)- Find a saved filter by IDfind_saved_filters(mode)- Find all saved filtersvalidate_stashbox_credentials(input)- Validate StashBox credentialsenable_dlna(input)- Enable DLNA serverdisable_dlna(input)- Disable DLNA serveradd_temp_dlna_ip(input)- Add temporary DLNA IP whitelistremove_temp_dlna_ip(input)- Remove temporary DLNA IP from whitelist
Metadata Operations
metadata_scan(options, input)- Scan for new/updated filesmetadata_generate(options, input)- Generate metadata (covers, sprites, previews, etc.)metadata_clean(options, input)- Clean metadatametadata_clean_generated(input)- Clean generated filesmetadata_auto_tag(input)- Auto-tag entitiesmetadata_identify(input)- Identify entities via scrapersmetadata_import(input)- Import metadata from JSONmetadata_export(input)- Export metadata to JSONget_configuration_defaults()- Get default configuration settings
Migration & Database Operations
migrate(input)- Run database migrationmigrate_hash_naming()- Migrate to new hash naming schememigrate_scene_screenshots(input)- Migrate scene screenshotsmigrate_blobs(input)- Migrate binary data to new storageanonymise_database(input)- Anonymize database for sharingoptimise_database()- Optimize database performancebackup_database(input)- Create database backup
Plugin Operations
get_plugins()- Get all loaded pluginsget_plugin_tasks()- Get available plugin tasksset_plugins_enabled(plugins, enabled)- Enable/disable pluginsreload_plugins()- Reload all pluginsrun_plugin_task(plugin_id, task_name, args)- Run plugin taskrun_plugin_operation(plugin_id, args)- Run plugin operationconfigure_plugin(plugin_id, input)- Configure plugin settings
Package Operations
installed_packages(type)- Get installed packagesavailable_packages(type, source)- Get available packagesinstall_packages(type, packages)- Install packagesupdate_packages(type, packages)- Update packagesuninstall_packages(type, packages)- Uninstall packages
Job Operations
find_job(id)- Find job by IDstop_job(id)- Stop running jobwait_for_job(id)- Wait for job to complete (async)
Fuzzy Date Support (Stash v0.30.0+)
Stash v0.30.0 introduced support for partial dates (year-only or year-month formats) in addition to full dates. This client includes utilities to work with these "fuzzy" dates:
from stash_graphql_client.types import (
FuzzyDate,
validate_fuzzy_date,
normalize_date,
DatePrecision,
)
# Validate date formats
validate_fuzzy_date("2024") # True - year only
validate_fuzzy_date("2024-03") # True - year and month
validate_fuzzy_date("2024-03-15") # True - full date
validate_fuzzy_date("2024-3") # False - invalid format
# Create fuzzy dates
date = FuzzyDate("2024-03")
print(date.precision) # DatePrecision.MONTH
print(date.to_datetime()) # datetime(2024, 3, 1)
# Normalize dates to different precisions
normalize_date("2024-03-15", "year") # "2024"
normalize_date("2024-03-15", "month") # "2024-03"
normalize_date("2024", "day") # "2024-01-01"
# Use in performer/scene data
performer = Performer(name="John Doe", birthdate="1990") # Year-only birthdate
await client.create_performer(performer)
scene_data = {
"date": "2024-03", # Month precision
}
Supported date formats:
YYYY- Year precision (e.g., "2024")YYYY-MM- Month precision (e.g., "2024-03")YYYY-MM-DD- Day precision (e.g., "2024-03-15")
License
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later).
See LICENSE for the full license text.
This license ensures:
- ✅ Open source code sharing
- ✅ Network use requires source disclosure
- ✅ Compatible with Stash (also AGPL-3.0)
- ✅ Derivative works must also be AGPL-3.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stash_graphql_client-0.6.0.tar.gz.
File metadata
- Download URL: stash_graphql_client-0.6.0.tar.gz
- Upload date:
- Size: 181.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1812bf4f912934c1ab87535c8cd0bba9960b40ef2d1aa866356648b467dee706
|
|
| MD5 |
eea8286e36743c016a37dcba03698c08
|
|
| BLAKE2b-256 |
9f1b2852f0dbbd95351bd2823171479e113fed883cb7c82268deb1a0caeffb64
|
Provenance
The following attestation bundles were made for stash_graphql_client-0.6.0.tar.gz:
Publisher:
publish.yml on Jakan-Kink/stash-graphql-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stash_graphql_client-0.6.0.tar.gz -
Subject digest:
1812bf4f912934c1ab87535c8cd0bba9960b40ef2d1aa866356648b467dee706 - Sigstore transparency entry: 782608320
- Sigstore integration time:
-
Permalink:
Jakan-Kink/stash-graphql-client@ef9acab71edb5fcd1cfb46a60abe13980522a40a -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/Jakan-Kink
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ef9acab71edb5fcd1cfb46a60abe13980522a40a -
Trigger Event:
push
-
Statement type:
File details
Details for the file stash_graphql_client-0.6.0-py3-none-any.whl.
File metadata
- Download URL: stash_graphql_client-0.6.0-py3-none-any.whl
- Upload date:
- Size: 221.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09c978ec56125c8547eb767f7d882a37f47a45cad16dd8883477a21b8f42c2fa
|
|
| MD5 |
99fec1a2ff8d40b05dd7e0d67971690e
|
|
| BLAKE2b-256 |
03b035a5bcb342b6e477bde029744a23e148ea448acd2faf26446e324471c36a
|
Provenance
The following attestation bundles were made for stash_graphql_client-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on Jakan-Kink/stash-graphql-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stash_graphql_client-0.6.0-py3-none-any.whl -
Subject digest:
09c978ec56125c8547eb767f7d882a37f47a45cad16dd8883477a21b8f42c2fa - Sigstore transparency entry: 782608324
- Sigstore integration time:
-
Permalink:
Jakan-Kink/stash-graphql-client@ef9acab71edb5fcd1cfb46a60abe13980522a40a -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/Jakan-Kink
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ef9acab71edb5fcd1cfb46a60abe13980522a40a -
Trigger Event:
push
-
Statement type: