Skip to main content

Async Python client for Stash GraphQL API

Reason this release was yanked:

Removing all pre v0.10.x builds

Project description

stash-graphql-client

PyPI version Python 3.12+ License: AGPL-3.0 codecov

Async Python client for Stash GraphQL API.

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

Available Operations

Scenes

  • find_scenes(filter) - Search scenes with filters
  • find_scene(id) - Find scene by ID
  • create_scene(scene) - Create new scene
  • update_scene(scene) - Update scene details
  • destroy_scene(id) - Delete scene

Galleries

  • find_galleries(filter) - Search galleries with filters
  • find_gallery(id) - Find gallery by ID
  • create_gallery(gallery) - Create new gallery
  • update_gallery(gallery) - Update gallery details
  • destroy_gallery(id) - Delete gallery

Images

  • find_images(filter) - Search images with filters
  • find_image(id) - Find image by ID
  • update_image(image) - Update image details
  • destroy_image(id) - Delete image

Performers

  • find_performers(filter) - Search performers with filters
  • find_performer(id) - Find performer by ID
  • map_performer_ids(performers, create) - Convert performer names/objects to IDs
  • all_performers() - Get all performers (deprecated, use find_performers)
  • create_performer(performer) - Create new performer
  • update_performer(performer) - Update performer details
  • bulk_performer_update(input) - Update multiple performers
  • destroy_performer(id) - Delete performer

Studios

  • find_studios(filter) - Search studios with filters
  • find_studio(id) - Find studio by ID
  • find_studio_hierarchy(id) - Get full parent chain from root to studio
  • find_studio_root(id) - Find the top-level parent studio
  • map_studio_ids(studios, create) - Convert studio names/objects to IDs
  • create_studio(studio) - Create new studio
  • update_studio(studio) - Update studio details
  • bulk_studio_update(input) - Update multiple studios
  • studio_destroy(id) - Delete studio

Tags

  • find_tags(filter) - Search tags with filters
  • find_tag(id) - Find tag by ID
  • map_tag_ids(tags, create) - Convert tag names/objects to IDs
  • create_tag(tag) - Create new tag
  • update_tag(tag) - Update tag details
  • tags_merge(source, destination) - Merge tags
  • bulk_tag_update(input) - Update multiple tags
  • destroy_tag(id) - Delete tag

Groups (formerly Movies)

  • find_groups(filter) - Search groups with filters
  • find_group(id) - Find group by ID
  • create_group(group) - Create new group
  • update_group(group) - Update group details
  • destroy_group(id) - Delete group

Markers

  • find_scene_markers(filter) - Search scene markers
  • find_scene_marker(id) - Find marker by ID
  • create_scene_marker(marker) - Create new marker
  • update_scene_marker(marker) - Update marker
  • destroy_scene_marker(id) - Delete marker

System Operations

  • get_system_status() - Get current system status
  • check_system_ready() - Verify system is ready for processing
  • stats() - Get database statistics
  • version() - Get Stash version information
  • latestversion() - Get latest available Stash version
  • logs() - Get system logs
  • directory(path, locale) - Browse filesystem
  • dlna_status() - Get DLNA server status
  • sql_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 settings
  • configure_interface(input) - Configure Stash interface settings
  • configure_dlna(input) - Configure DLNA server settings
  • configure_defaults(input) - Configure default metadata operation settings
  • configure_ui(input, partial) - Configure UI settings
  • configure_ui_setting(key, value) - Configure a single UI setting
  • configure_scraping(input) - Configure scraping settings
  • generate_api_key(input) - Generate a new API key
  • find_saved_filter(id) - Find a saved filter by ID
  • find_saved_filters(mode) - Find all saved filters
  • validate_stashbox_credentials(input) - Validate StashBox credentials
  • enable_dlna(input) - Enable DLNA server
  • disable_dlna(input) - Disable DLNA server
  • add_temp_dlna_ip(input) - Add temporary DLNA IP whitelist
  • remove_temp_dlna_ip(input) - Remove temporary DLNA IP from whitelist

Metadata Operations

  • metadata_scan(options, input) - Scan for new/updated files
  • metadata_generate(options, input) - Generate metadata (covers, sprites, previews, etc.)
  • metadata_clean(options, input) - Clean metadata
  • metadata_clean_generated(input) - Clean generated files
  • metadata_auto_tag(input) - Auto-tag entities
  • metadata_identify(input) - Identify entities via scrapers
  • metadata_import(input) - Import metadata from JSON
  • metadata_export(input) - Export metadata to JSON
  • get_configuration_defaults() - Get default configuration settings

Migration & Database Operations

  • migrate(input) - Run database migration
  • migrate_hash_naming() - Migrate to new hash naming scheme
  • migrate_scene_screenshots(input) - Migrate scene screenshots
  • migrate_blobs(input) - Migrate binary data to new storage
  • anonymise_database(input) - Anonymize database for sharing
  • optimise_database() - Optimize database performance
  • backup_database(input) - Create database backup

Plugin Operations

  • get_plugins() - Get all loaded plugins
  • get_plugin_tasks() - Get available plugin tasks
  • set_plugins_enabled(plugins, enabled) - Enable/disable plugins
  • reload_plugins() - Reload all plugins
  • run_plugin_task(plugin_id, task_name, args) - Run plugin task
  • run_plugin_operation(plugin_id, args) - Run plugin operation
  • configure_plugin(plugin_id, input) - Configure plugin settings

Package Operations

  • installed_packages(type) - Get installed packages
  • available_packages(type, source) - Get available packages
  • install_packages(type, packages) - Install packages
  • update_packages(type, packages) - Update packages
  • uninstall_packages(type, packages) - Uninstall packages

Job Operations

  • find_job(id) - Find job by ID
  • stop_job(id) - Stop running job
  • wait_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

stash_graphql_client-0.5.0.tar.gz (183.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stash_graphql_client-0.5.0-py3-none-any.whl (225.1 kB view details)

Uploaded Python 3

File details

Details for the file stash_graphql_client-0.5.0.tar.gz.

File metadata

  • Download URL: stash_graphql_client-0.5.0.tar.gz
  • Upload date:
  • Size: 183.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for stash_graphql_client-0.5.0.tar.gz
Algorithm Hash digest
SHA256 990330efd96a5da2ae998d2bf3e2218c92d2948654487c38a064385d771aaead
MD5 f811bf0712f4a9769d813f4281e826db
BLAKE2b-256 f05dba6ff550cee0713d8c03dbfec47ffb932b22e4cd39d2642164824d046b25

See more details on using hashes here.

Provenance

The following attestation bundles were made for stash_graphql_client-0.5.0.tar.gz:

Publisher: publish.yml on Jakan-Kink/stash-graphql-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stash_graphql_client-0.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for stash_graphql_client-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 77b717c4eb5ed6ec3e8914f5f2f012cdf6a77650a31f795ef8e9457348c8bf71
MD5 bfe2d6b505b967c02ec7ac3a214d910b
BLAKE2b-256 f5749f1044a13238de288551da711fd4f6612df9c2f8baa8154c59a639b69ad1

See more details on using hashes here.

Provenance

The following attestation bundles were made for stash_graphql_client-0.5.0-py3-none-any.whl:

Publisher: publish.yml on Jakan-Kink/stash-graphql-client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page