Scene Operations¶
Operations for managing scenes (videos).
Bases: StashClientProtocol
Mixin for scene-related client methods.
Functions¶
find_scene
async
¶
find_scene(id: str) -> Scene | None
Find a scene by its ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The ID of the scene to find |
required |
Returns:
| Type | Description |
|---|---|
Scene | None
|
Scene object if found, None otherwise |
Raises:
| Type | Description |
|---|---|
ValueError
|
If scene ID is None or empty |
Examples:
Find a scene and check its title:
Access scene relationships:
scene = await client.find_scene("123")
if scene:
# Get performer names
performers = [p.name for p in scene.performers]
# Get studio name
studio_name = scene.studio.name if scene.studio else None
# Get tag names
tags = [t.name for t in scene.tags]
Check scene paths:
find_scenes
async
¶
find_scenes(
filter_: dict[str, Any] | None = None,
scene_filter: dict[str, Any] | None = None,
q: str | None = None,
) -> FindScenesResultType
Find scenes matching the given filters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filter_
|
dict[str, Any] | None
|
Optional general filter parameters: - q: str (search query) - direction: SortDirectionEnum (ASC/DESC) - page: int - per_page: int - sort: str (field to sort by) |
None
|
q
|
str | None
|
Optional search query (alternative to filter_["q"]) |
None
|
scene_filter
|
dict[str, Any] | None
|
Optional scene-specific filter: - file_count: IntCriterionInput - is_missing: str (what data is missing) - organized: bool - path: StringCriterionInput - performer_count: IntCriterionInput - performer_tags: HierarchicalMultiCriterionInput - performers: MultiCriterionInput - rating100: IntCriterionInput - resolution: ResolutionEnum - studios: HierarchicalMultiCriterionInput - tag_count: IntCriterionInput - tags: HierarchicalMultiCriterionInput - title: StringCriterionInput |
None
|
Returns:
| Type | Description |
|---|---|
FindScenesResultType
|
FindScenesResultType containing: - count: Total number of matching scenes - duration: Total duration in seconds - filesize: Total size in bytes - scenes: List of Scene objects |
Examples:
Find all organized scenes:
result = await client.find_scenes(
scene_filter={"organized": True}
)
print(f"Found {result.count} organized scenes")
for scene in result.scenes:
print(f"- {scene.title}")
Find scenes with specific performers:
result = await client.find_scenes(
scene_filter={
"performers": {
"value": ["performer1", "performer2"],
"modifier": "INCLUDES_ALL"
}
}
)
Find scenes with high rating and sort by date:
result = await client.find_scenes(
filter_={
"direction": "DESC",
"sort": "date",
},
scene_filter={
"rating100": {
"value": 80,
"modifier": "GREATER_THAN"
}
}
)
Paginate results:
create_scene
async
¶
Create a new scene in Stash.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scene
|
Scene
|
Scene object with the data to create. Required fields: - title: Scene title - urls: List of URLs associated with the scene - organized: Whether the scene is organized Note: created_at and updated_at are handled by Stash |
required |
Returns:
| Type | Description |
|---|---|
Scene
|
Created Scene object with ID and any server-generated fields |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the scene data is invalid |
TransportError
|
If the request fails |
Examples:
Create a basic scene:
scene = Scene(
title="My Scene",
urls=["https://example.com/scene"],
organized=True, # created_at and updated_at handled by Stash
)
created = await client.create_scene(scene)
print(f"Created scene with ID: {created.id}")
Create scene with relationships:
scene = Scene(
title="My Scene",
urls=["https://example.com/scene"],
organized=True, # created_at and updated_at handled by Stash
# Add relationships
performers=[performer1, performer2],
studio=studio,
tags=[tag1, tag2],
)
created = await client.create_scene(scene)
Create scene with metadata:
update_scene
async
¶
Update an existing scene in Stash.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scene
|
Scene
|
Scene object with updated data. Required fields: - id: Scene ID to update Any other fields that are set will be updated. Fields that are None will be ignored. |
required |
Returns:
| Type | Description |
|---|---|
Scene
|
Updated Scene object with any server-generated fields |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the scene data is invalid |
TransportError
|
If the request fails |
Examples:
Update scene title and rating:
scene = await client.find_scene("123")
if scene:
scene.title = "New Title"
scene.rating100 = 90
updated = await client.update_scene(scene)
print(f"Updated scene: {updated.title}")
Update scene relationships:
scene = await client.find_scene("123")
if scene:
# Add new performers
scene.performers.extend([new_performer1, new_performer2])
# Set new studio
scene.studio = new_studio
# Add new tags
scene.tags.extend([new_tag1, new_tag2])
updated = await client.update_scene(scene)
Update scene metadata:
scene = await client.find_scene("123")
if scene:
# Update metadata
scene.details = "New description"
scene.date = "2024-01-31"
scene.code = "NEWCODE123"
scene.organized = True
updated = await client.update_scene(scene)
Update scene URLs:
scene = await client.find_scene("123")
if scene:
# Replace URLs
scene.urls = [
"https://example.com/new-url",
]
updated = await client.update_scene(scene)
Remove scene relationships:
find_duplicate_scenes
async
¶
find_duplicate_scenes(
distance: int | None = None,
duration_diff: float | None = None,
) -> list[list[Scene]]
Find groups of scenes that are perceptual duplicates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
distance
|
int | None
|
Maximum phash distance between scenes to be considered duplicates |
None
|
duration_diff
|
float | None
|
Maximum difference in seconds between scene durations |
None
|
Returns:
| Type | Description |
|---|---|
list[list[Scene]]
|
List of scene groups, where each group is a list of duplicate scenes |
parse_scene_filenames
async
¶
parse_scene_filenames(
filter_: dict[str, Any] | None = None,
config: dict[str, Any] | None = None,
) -> dict[str, Any]
Parse scene filenames using the given configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filter_
|
dict[str, Any] | None
|
Optional filter to select scenes |
None
|
config
|
dict[str, Any] | None
|
Parser configuration: - whitespace_separator: bool - field_separator: str - fields: list[str] |
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Dictionary containing parse results |
scene_wall
async
¶
scene_wall(q: str | None = None) -> list[Scene]
Get random scenes for the wall.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
q
|
str | None
|
Optional search query |
None
|
Returns:
| Type | Description |
|---|---|
list[Scene]
|
List of random Scene objects |
bulk_scene_update
async
¶
bulk_scene_update(
input_data: dict[str, Any],
) -> list[Scene]
Update multiple scenes at once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_data
|
dict[str, Any]
|
Dictionary containing: - ids: List of scene IDs to update - Any other fields to update on all scenes |
required |
Returns:
| Type | Description |
|---|---|
list[Scene]
|
List of updated Scene objects |
scenes_update
async
¶
scene_generate_screenshot
async
¶
Generate a screenshot for a scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Scene ID |
required |
at
|
float | None
|
Optional time in seconds to take screenshot at |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Path to the generated screenshot |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the scene is not found |
TransportError
|
If the request fails |
find_scene_by_hash
async
¶
find_scene_by_hash(
input_data: SceneHashInput | dict[str, Any],
) -> Scene | None
Find a scene by its hash (checksum or oshash).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_data
|
SceneHashInput | dict[str, Any]
|
SceneHashInput object or dictionary containing: - checksum: MD5 checksum of the file (optional) - oshash: OSHash of the file (optional) Note: At least one hash must be provided |
required |
Returns:
| Type | Description |
|---|---|
Scene | None
|
Scene object if found, None otherwise |
Examples:
Find scene by MD5 checksum:
scene = await client.find_scene_by_hash({
"checksum": "abc123def456..."
})
if scene:
print(f"Found scene: {scene.title}")
Find scene by OSHash:
Using the input type:
scene_destroy
async
¶
Delete a scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_data
|
SceneDestroyInput | dict[str, Any]
|
SceneDestroyInput object or dictionary containing: - id: Scene ID to delete (required) - delete_file: Whether to delete the scene's file (optional, default: False) - delete_generated: Whether to delete generated files (optional, default: True) |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the scene was successfully deleted |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the scene ID is invalid |
TransportError
|
If the request fails |
Examples:
Delete a scene without deleting the file:
result = await client.scene_destroy({
"id": "123",
"delete_file": False,
"delete_generated": True
})
print(f"Scene deleted: {result}")
Delete a scene and its file:
Using the input type:
scenes_destroy
async
¶
Delete multiple scenes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_data
|
ScenesDestroyInput | dict[str, Any]
|
ScenesDestroyInput object or dictionary containing: - ids: List of scene IDs to delete (required) - delete_file: Whether to delete the scenes' files (optional, default: False) - delete_generated: Whether to delete generated files (optional, default: True) |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the scenes were successfully deleted |
Raises:
| Type | Description |
|---|---|
ValueError
|
If any scene ID is invalid |
TransportError
|
If the request fails |
Examples:
Delete multiple scenes without deleting files:
result = await client.scenes_destroy({
"ids": ["123", "456", "789"],
"delete_file": False,
"delete_generated": True
})
print(f"Scenes deleted: {result}")
Delete multiple scenes and their files:
result = await client.scenes_destroy({
"ids": ["123", "456"],
"delete_file": True,
"delete_generated": True
})
Using the input type:
scene_merge
async
¶
scene_merge(
input_data: SceneMergeInput | dict[str, Any],
) -> Scene
Merge multiple scenes into one destination scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_data
|
SceneMergeInput | dict[str, Any]
|
SceneMergeInput object or dictionary containing: - source: List of source scene IDs to merge (required) - destination: Destination scene ID (required) - values: Optional SceneUpdateInput with values to apply to merged scene - play_history: Whether to merge play history (optional, default: False) - o_history: Whether to merge o-count history (optional, default: False) |
required |
Returns:
| Type | Description |
|---|---|
Scene
|
Updated destination Scene object |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the input data is invalid |
TransportError
|
If the request fails |
Examples:
Merge two scenes into one:
merged = await client.scene_merge({
"source": ["123", "456"],
"destination": "789"
})
print(f"Merged into scene: {merged.title}")
Merge scenes and update metadata:
scene_add_o
async
¶
Add O-count entry for a scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Scene ID |
required |
times
|
list[Timestamp] | None
|
Optional list of timestamps. If not provided, uses current time. |
None
|
Returns:
| Type | Description |
|---|---|
HistoryMutationResult
|
HistoryMutationResult containing: - count: New O-count value - history: List of all O timestamps |
Examples:
Add O-count with current time:
Add O-count with specific times:
scene_delete_o
async
¶
Delete O-count entry from a scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Scene ID |
required |
times
|
list[Timestamp] | None
|
Optional list of timestamps to remove. If not provided, removes last entry. |
None
|
Returns:
| Type | Description |
|---|---|
HistoryMutationResult
|
HistoryMutationResult containing: - count: New O-count value - history: List of remaining O timestamps |
Examples:
Remove last O-count entry:
Remove specific timestamp:
scene_reset_o
async
¶
scene_save_activity
async
¶
scene_save_activity(
id: str,
resume_time: float | None = None,
play_duration: float | None = None,
) -> bool
Save scene playback activity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Scene ID |
required |
resume_time
|
float | None
|
Resume time point in seconds |
None
|
play_duration
|
float | None
|
Duration played in seconds |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if activity was saved successfully |
Examples:
Save resume point:
Save play duration:
Save both:
scene_reset_activity
async
¶
Reset scene activity tracking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Scene ID |
required |
reset_resume
|
bool
|
Whether to reset resume time point |
False
|
reset_duration
|
bool
|
Whether to reset play duration |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
True if activity was reset successfully |
Examples:
Reset resume point:
Reset play duration:
Reset both:
scene_add_play
async
¶
Add play count entry for a scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Scene ID |
required |
times
|
list[Timestamp] | None
|
Optional list of timestamps. If not provided, uses current time. |
None
|
Returns:
| Type | Description |
|---|---|
HistoryMutationResult
|
HistoryMutationResult containing: - count: New play count value - history: List of all play timestamps |
Examples:
Add play with current time:
Add play with specific times:
scene_delete_play
async
¶
Delete play count entry from a scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Scene ID |
required |
times
|
list[Timestamp] | None
|
Optional list of timestamps to remove. If not provided, removes last entry. |
None
|
Returns:
| Type | Description |
|---|---|
HistoryMutationResult
|
HistoryMutationResult containing: - count: New play count value - history: List of remaining play timestamps |
Examples:
Remove last play entry:
Remove specific timestamp:
scene_reset_play_count
async
¶
find_scenes_by_path_regex
async
¶
find_scenes_by_path_regex(
filter_: dict[str, Any] | None = None,
) -> FindScenesResultType
Find scenes by path regex pattern.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filter_
|
dict[str, Any] | None
|
Filter parameters |
None
|
Returns:
| Type | Description |
|---|---|
FindScenesResultType
|
FindScenesResultType containing: - count: Total number of matches - duration: Total duration in seconds - filesize: Total file size in bytes - scenes: List of Scene objects matching the path pattern |
scene_streams
async
¶
Get streaming endpoints for a scene.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scene_id
|
str
|
The ID of the scene |
required |
Returns:
| Type | Description |
|---|---|
list[SceneStreamEndpoint]
|
List of SceneStreamEndpoint objects containing: - url: Stream URL - mime_type: MIME type of the stream - label: Label for the stream quality/format |
Examples:
Get streaming endpoints:
streams = await client.scene_streams("123")
for stream in streams:
print(f"{stream.label}: {stream.url} ({stream.mime_type})")
Access specific stream properties:
merge_scene_markers
async
¶
Merge scene markers from source scenes to target scene.
This utility method copies all markers from one or more source scenes to a target scene. Useful when consolidating duplicate scenes or merging content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_scene_id
|
str
|
The ID of the target scene to copy markers to |
required |
source_scene_ids
|
list[str]
|
List of source scene IDs to copy markers from |
required |
Returns:
| Type | Description |
|---|---|
list[Any]
|
List of SceneMarker objects that were created on the target scene |
Examples:
Merge markers from a single source:
markers = await client.merge_scene_markers(
target_scene_id="123",
source_scene_ids=["456"]
)
print(f"Copied {len(markers)} markers to target scene")
Merge markers from multiple sources:
markers = await client.merge_scene_markers(
target_scene_id="123",
source_scene_ids=["456", "789", "101"]
)
for marker in markers:
print(f"Marker: {marker.title} at {marker.seconds}s")
Use with scene merge workflow:
find_duplicate_scenes_wrapper
async
¶
find_duplicate_scenes_wrapper(
distance: int = 0, duration_diff: float | None = None
) -> list[list[Scene]]
Find duplicate scenes with sensible default parameters.
This is a convenience wrapper around find_duplicate_scenes() that provides better defaults for common use cases. A distance of 0 finds exact phash matches (true duplicates), while higher values find similar scenes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
distance
|
int
|
Maximum phash distance (default: 0 for exact duplicates) - 0: Exact phash matches (identical frames) - 1-5: Very similar scenes (same content, different encode) - 6-10: Similar scenes (same source, different quality) - 11+: Potentially different scenes |
0
|
duration_diff
|
float | None
|
Maximum duration difference in seconds (default: None) - None: No duration filtering - 0.0: Exact duration match - 1.0-10.0: Similar duration (accounts for encoding differences) - 10.0+: Loose duration matching |
None
|
Returns:
| Type | Description |
|---|---|
list[list[Scene]]
|
List of scene groups, where each group is a list of duplicate scenes |
Examples:
Find exact duplicates (phash distance = 0):
duplicates = await client.find_duplicate_scenes_wrapper()
for group in duplicates:
print(f"Found {len(group)} exact duplicates:")
for scene in group:
print(f" - {scene.title}")
Find similar scenes (allow small phash differences):
similar = await client.find_duplicate_scenes_wrapper(distance=5)
for group in similar:
print(f"Found {len(group)} similar scenes")
Find duplicates with similar duration:
duplicates = await client.find_duplicate_scenes_wrapper(
distance=0,
duration_diff=2.0 # Within 2 seconds
)
Use in cleanup workflow: