chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:23:54 +08:00
commit ecb5ae4e59
153 changed files with 21551 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
from .camera import Camera
from .character import CharacterInScene, CharacterInEvent, CharacterInNovel
from .event import Event
from .frame import Frame
from .image_output import ImageOutput
from .scene import Scene
from .shot_description import ShotDescription, ShotBriefDescription
from .video_output import VideoOutput
__all__ = [
"Camera",
"CharacterInScene",
"CharacterInEvent",
"CharacterInNovel",
"Event",
"Frame",
"ImageOutput",
"Scene",
"ShotBriefDescription",
"ShotDescription",
"VideoOutput",
]
+43
View File
@@ -0,0 +1,43 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Union, Dict, Tuple
class Camera(BaseModel):
idx: int = Field(
description="The index of the camera in the scene, starting from 0.",
)
active_shot_idxs: List[int] = Field(
description="The indices of the shots that the camera can film.",
)
parent_cam_idx: Optional[int] = Field(
default=None,
description="The index of the parent camera. If the camera has no parent, set this to None.",
)
parent_shot_idx: Optional[int] = Field(
default=None,
description="The index of the dependent shot. If the camera has no parent, set this to None.",
)
reason: Optional[str] = Field(
default=None,
description="The reason for the selection of the parent camera. If the camera has no parent, set this to None.",
)
parent_shot_idx: Optional[int] = Field(
default=None,
description="The index of the dependent shot. If the camera has no parent, set this to None.",
)
is_parent_fully_covers_child: Optional[bool] = Field(
default=None,
description="Whether the parent camera fully covers the child camera's content. If the camera has no parent, set this to None.",
)
missing_info: Optional[str] = Field(
default=None,
description="The missing information in the child shot that is not covered by the parent shot. If the parent shot fully covers the child shot, set this to None.",
)
+100
View File
@@ -0,0 +1,100 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Union, Dict
from PIL import Image
class CharacterInScene(BaseModel):
idx: int = Field(
description="The index of the character in the scene, starting from 0",
)
identifier_in_scene: str = Field(
description="The identifier for the character in this specific scene, which may differ from the base identifier",
examples=["Alice", "Bob the Builder"],
)
is_visible: bool = Field(
description="Indicates whether the character is visible in this scene",
examples=[True, False],
)
static_features: str = Field(
description="The static features of the character in this specific scene, such as facial features and body shape that remain constant or are rarely changed. If the character is not visible, this field can be left empty.",
examples=[
"Alice has long blonde hair and blue eyes, and is of slender build.",
"Bob the Builder is a middle-aged man with a sturdy build.",
]
)
dynamic_features: str = Field(
description="The dynamic features of the character in this specific scene, such as clothing and accessories that may change from scene to scene. If not mentioned, this field can be left empty. If the character is not visible, this field should be None.",
examples=[
"Wearing a red scarf and a black leather jacket",
]
)
def __str__(self):
# Alice[visible]
# static features: Alice has long blonde hair and blue eyes, and is of slender build.
# dynamic features: Wearing a red scarf and a black leather jacket
s = f"{self.identifier_in_scene}"
s += "[visible]" if self.is_visible else "[not visible]"
s += "\n"
s += f"static features: {self.static_features}\n"
s += f"dynamic features: {self.dynamic_features}\n"
return s
class CharacterInEvent(BaseModel):
index: int = Field(
description="The index of the character in the event, starting from 0",
)
identifier_in_event: str = Field(
description="The unique identifier for the character in the event",
examples=["Alice", "Bob the Builder"],
)
active_scenes: Dict[int, str] = Field(
description="A dictionary mapping scene indices to their identifiers in specific scenes.",
examples=[
{0: "Alice", 2: "Alice in Wonderland", 5: "Alice"},
{1: "Bob the Builder", 3: "Bob", 4: "Bob"},
]
)
static_features: str = Field(
description="The static features of the character in the event, such as facial features and body shape that remain constant or are rarely changed.",
examples=[
"Alice has long blonde hair and blue eyes, and is of slender build. She often wears casual, comfortable clothing.",
"Bob the Builder is a middle-aged man with a sturdy build. He typically wears a hard hat and work overalls.",
]
)
class CharacterInNovel(BaseModel):
index: int = Field(
description="The index of the character in the novel, starting from 0",
)
identifier_in_novel: str = Field(
description="The unique identifier for the character in the novel",
examples=["Alice", "Bob the Builder"],
)
active_events: Dict[int, str] = Field(
description="A dictionary mapping event indices to their identifiers in specific events.",
examples=[
{0: "Alice", 2: "Alice in Wonderland", 5: "Alice"},
{1: "Bob the Builder", 3: "Bob", 4: "Bob"},
]
)
static_features: str = Field(
description="The static features of the character in the novel, such as facial features and body shape that remain constant or are rarely changed.",
examples=[
"Alice has long blonde hair and blue eyes, and is of slender build. She often wears casual, comfortable clothing.",
"Bob the Builder is a middle-aged man with a sturdy build. He typically wears a hard hat and work overalls.",
]
)
+26
View File
@@ -0,0 +1,26 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Union, Dict
from PIL import Image
class EnvironmentInScene(BaseModel):
slugline: str = Field(
description="The slugline of the scene, indicating the location and time of day",
examples=[
"INT. COFFEE SHOP - NIGHT",
"EXT. PARK - DAY",
]
)
description: str = Field(
description="A detailed description of the environment in the specific scene. Don't describe any characters or actions here, just the setting.",
examples=[
"The warm yellow light glowed against the mottled brick wall, while raindrops streaked the glass window with blurred neon reflections. Among the empty booths sat a lone half-finished iced latte—its foam collapsed, a faint lipstick mark on the rim. beads of condensation gleamed on the stainless steel espresso machine, and the record player's turntable rotated slowly in the shadows. A patch of wet floor shimmered with hazy reflected light.",
]
)
def __str__(self):
s = f"{self.slugline} -- {self.description}"
return s
+43
View File
@@ -0,0 +1,43 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Union, Dict
class Event(BaseModel):
index: int = Field(
description="The index of the event, starting from 0",
)
is_last: bool = Field(
description="Indicates if this is the last event in the sequence"
)
description: str = Field(
description="A concise description of the event, capturing its essence in one sentence",
examples=[
"A thief who stole a gem from a museum was caught after a rooftop chase with guards, and the gem was recovered.",
]
)
process_chain: List[str] = Field(
description="A list of steps or actions that make up the event's process chain, which constitutes a complete causal chain.",
examples=[
[
"A thief steals a gem from a museum, triggering the alarm. Guards notice and begin the chase.",
"The thief rushes out the museum's back door and dashes through narrow alleys, with guards closely pursuing and calling for backup.",
"The thief climbs a fire escape to the rooftops; the guards follow using low platforms on adjacent buildings.",
"The thief leaps across a 1.5-meter gap between two buildings. The guards hesitate but take the risky jump, nearly losing their footing.",
"The thief knocks over stacked wooden planks to create an obstacle. The guards dodge but lose speed.",
"The thief attempts to slide down a rope to the opposite rooftop, but a guard lunges and grabs their ankle. Both tumble and grapple.",
"Backup arrives, subduing the thief and recovering the gem.",
],
]
)
def __str__(self):
s = f"<Event {self.index}>"
s += f"\nDescription: {self.description}"
s += f"\nProcess Chain:"
for process in self.process_chain:
s += f"\n- {process}"
return s
+20
View File
@@ -0,0 +1,20 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Union, Dict, Tuple, Literal
class Frame(BaseModel):
shot_idx: int = Field(
description="The index of the shot in the sequence, starting from 0."
)
frame_type: Literal["first", "last"] = Field(
description="The type of the frame, 'first' for the first frame of the shot, 'last' for the last frame of the shot."
)
cam_idx: int = Field(
description="The index of the camera used for this frame, starting from 0."
)
vis_char_idxs: List[int] = Field(
description="A list of indices of characters that are visible in this frame, corresponding to the character list provided in the input."
)
+61
View File
@@ -0,0 +1,61 @@
import base64
import cv2
from typing import List, Literal, Optional, Union
from PIL import Image
from utils.image import download_image
class ImageOutput:
fmt: Literal["b64", "url", "pil", "np"]
ext: str = "png"
data: Union[str, Image.Image]
def __init__(
self,
fmt: Literal["b64", "url", "pil", "np"],
ext: str,
data: Union[str, Image.Image],
):
self.fmt = fmt
self.ext = ext
self.data = data
def save_b64(self, path: str) -> None:
"""Save a base64 encoded image to the specified path.
Args:
path (str): Path where the image will be saved.
"""
with open(path, 'wb') as f:
f.write(base64.b64decode(self.data))
def save_url(self, path: str) -> None:
"""Download and save an image from a URL to the specified path.
Args:
path (str): Path where the image will be saved.
"""
download_image(self.data, path)
def save_pil(self, path: str) -> None:
"""Save a PIL Image to the specified path.
Args:
path (str): Path where the image will be saved.
"""
self.data.save(path)
def save_np(self, path: str) -> None:
"""Save a numpy array to the specified path.
Args:
path (str): Path where the image will be saved.
"""
cv2.imencode('.png', self.data)[1].tofile(path)
def save(self, path: str) -> None:
save_func = getattr(self, f"save_{self.fmt}")
save_func(path)
+57
View File
@@ -0,0 +1,57 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Literal, Tuple
from interfaces.environment import EnvironmentInScene
from interfaces.character import CharacterInScene
class Scene(BaseModel):
idx: int = Field(
description="The scene index, starting from 0",
examples=[0, 1, 2],
)
is_last: bool = Field(
description="Indicates if this is the last scene",
examples=[False, True],
)
environment: EnvironmentInScene = Field(
description="The detailed scene setting, including location and time",
)
characters: List[CharacterInScene] = Field(
description="A list of characters appearing in the scene, along with their dynamic features like clothing and accessories",
)
script: str = Field(
description="The screenplay script for the scene, including character actions and dialogues. Character names in the script should be enclosed in <>, except for character names within dialogues.",
examples=[
"<Jane> paces nervously, clutching a letter. She turns to <John>.\n<Jane>: John, we need to leave tonight.\n<John> shakes his head, stepping toward the window.\n<John>: It's too dangerous.",
"<Alice> sits quietly, observing the chaos around her. She whispers to <Bob>.\n<Alice>: Bob, do you think they'll find us here?\n<Bob> nods slowly, his expression grim."
],
)
def __str__(self):
s = f"Scene {self.idx}:"
s += f"\nEnvironment: {str(self.environment)}"
s += f"\nCharacters: {', '.join([str(c) for c in self.characters])}"
s += f"\nScript: \n{self.script}"
return s
# class Scene(BaseModel):
# index: int = Field(
# description="The index of the scene within the event, starting from 0"
# )
# character_indices: List[int] = Field(
# description="List of indices of characters appearing in this scene, including main characters, supporting characters, and extras.",
# )
# environment_index: int = Field(
# description="The index of the environment where the scene takes place."
# )
# key_items_indices: List[int] = Field(
# default=[],
# description="List of indices of key items involved in this scene, if any.",
# )
# script: str = Field(
# description="The script of the scene, including actions and dialogues"
# )
+185
View File
@@ -0,0 +1,185 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Literal, Tuple
class ShotBriefDescription(BaseModel):
idx: int = Field(
description="The index of the shot in the sequence, starting from 0.",
examples=[0, 1, 2],
)
is_last: bool = Field(
description="Whether this is the last shot. If True, the story of the script has ended and no more shots will be planned after this one.",
examples=[False, True],
)
# visual
cam_idx: int = Field(
description="The index of the camera in the scene.",
examples=[0, 1, 2],
)
visual_desc: str = Field(
description='''A vivid and detailed visual description of the shot that convey rich visual information through text. The character identifiers in the description must match those in the character list and be enclosed in angle brackets (e.g., <Alice>, <Bob>). All visible characters should be described.
If there is a conversation, please write down the content of the conversation), when you meet some dialogue, you should write into the visual content description with :" " symbols and the character's features (eg. <SLING> (male, late 20s, Texan accent softened by military precision, confident and energetic.) says: "Gear retracted. Flaps transitioning. Flight path stable. You are clear to climb.").
''',
examples=[
"An over-the-shoulder shot at eye level, positioned behind <Alice>. The foreground, including <Alice>'s shoulder and head, is softly blurred, directing focus onto <Bob>'s face. <Bob>'s subtle reactions—shifting from surprise to delight—are clearly visible. The supermarket background is gently blurred with cool fluorescent lighting.",
]
)
# audio
audio_desc: str = Field(
description="A detailed description of the audio in the shot.",
examples=[
"[Sound Effect] Ambient sound (supermarket background noise, shopping cart wheels rolling)",
"[Speaker] Alice (Happy): Hello, how are you?",
None,
],
)
# sound_effect: Optional[str] = Field(
# default=None,
# description="The sound effects used in the shot.",
# examples=[
# "Ambient sound (supermarket background noise, shopping cart wheels rolling)",
# None,
# ],
# )
# speaker: Optional[str] = Field(
# default=None,
# description="The speaker in the shot, if applicable. If there is no speaker, this field should be set to None.",
# examples=[
# "Alice",
# None,
# ],
# )
# is_speaker_lip_visible: Optional[bool] = Field(
# default=None,
# description="Indicates whether the speaker's lips are visible in the shot. If there is no speaker, this field should be set to None.",
# examples=[
# True,
# False,
# None,
# ],
# )
# line: Optional[str] = Field(
# default=None,
# description="The dialogue or monologue in the shot, if applicable. If there is a speaker, there must be a line. If there is no speaker, this field should be set to None.",
# examples=[
# "Hello, how are you?",
# None,
# ],
# )
# emotion: Optional[str] = Field(
# default=None,
# description="The emotion of the speaker when delivering the line, if applicable. If there is a speaker, there must be an emotion. If there is no speaker, this field should be set to None.",
# examples=[
# "Happy",
# None,
# ],
# )
def __str__(self):
s = f"Shot {self.idx}:\n"
s += f"Camera Index: {self.cam_idx}\n"
s += f"Visual: {self.visual_desc}\n"
if self.audio_desc:
s += f"Audio: {self.audio_desc}"
return s
class ShotDescription(BaseModel):
idx: int = Field(
description="The index of the shot in the sequence, starting from 0."
)
is_last: bool = Field(
description="Whether this is the last shot in the sequence. If True, no more shots will be planned after this one."
)
# visual
cam_idx: int = Field(
description="The index of the camera in the scene.",
examples=[0, 1, 2],
)
visual_desc: str = Field(
description='''A vivid and detailed visual description of the shot that convey rich visual information through text. The character identifiers in the description must match those in the character list and be enclosed in angle brackets (e.g., <Alice>, <Bob>).
If there is a conversation, please write down the content of the conversation), when you meet some dialogue, you should write into the visual content description with :" " symbols and the character's features (eg. <SLING> (male, late 20s, Texan accent softened by military precision, confident and energetic.) says: "Gear retracted. Flaps transitioning. Flight path stable. You are clear to climb."). ''',
examples=[
"An over-the-shoulder shot at eye level, positioned behind <Alice>. The foreground, including <Alice>'s shoulder and head, is softly blurred, directing focus onto <Bob>'s face. <Bob>'s subtle reactions—shifting from surprise to delight—are clearly visible. The supermarket background is gently blurred with cool fluorescent lighting.",
]
)
variation_type: Literal["large", "medium", "small"] = Field(
description="Indicates the degree of change in the shot's content.",
examples=["large", "medium", "small"],
)
variation_reason: str = Field(
description="The reason for the variation type of the shot.",
examples=[
"This is a transition shot where the content of the first frame and the last frame differs dramatically. So the variation type is large.",
"Compared to the first frame, a new character appears in the last frame, and there are no significant changes in the composition. So the variation type is medium.",
"Compared to the first frame, there are only minor changes in the composition. So the variation type is small.",
"This shot only shows Alice speaking and the changes in her facial expressions, thus the variation type is small.",
],
)
ff_desc: str = Field(
description="The first frame of the shot.",
examples=[
"Medium shot of a supermarket aisle at eye level. Bob(a tall man wearing a blue shirt and jeans) is positioned on the right side of the frame, captured in profile and facing right, while Alice(a young woman with short hair, wearing a green dress) is on the left, shown pushing a shopping cart with her gaze lowered toward the ground. They are arranged in a front-to-back spatial relationship. Shelves line both sides of the frame, and cool-toned fluorescent lighting from above washes over the scene. The vibrant colors of product packaging contrast with the metallic gray of the shopping cart, all contained within a stable, horizontally balanced composition.",
"Extreme long shot. Aerial view from hundreds of meters above the ground. The boundless golden desert resembles undulating frozen waves, occupying the vast majority of the frame. At the very center of the image, a tiny, solitary explorer appears only as a faint dark speck, dragging a long, lonely trail of footprints behind him, stretching all the way to the edge of the frame.",
"Medium shot at eye level angle. Designer A(with a beard, wearing a white suit) leans forward passionately, speaking emphatically. Product Manager B(with a beard, wearing a white T-shirt) sits with crossed arms, looking skeptical. Between them, Development Engineer C(brown hair, wearing a blue T-shirt) appears anxious, glancing between the two. Project Manager D(curly hair, wearing a red T-shirt) prepares to mediate, focusing on a whiteboard. Bright overhead lighting highlights their expressions, with a blurred whiteboard and glass wall in the background.",
"A low-angle close-up shot captures the figure from below, framing him from the chest up. His face appears resolute and commanding, his eyes piercing as he speaks passionately. Flecks of saliva are visible, emphasizing his intensity. The overcast sky breaks with occasional light, casting him as a heroic, almost monumental figure against the gloom.",
"An extremely close-up of an old, motionless pocket watch. Soft light highlights scratches on its brass case and the enamel dial with Roman numerals. The second hand remains fixed at 'VIII', casting a sharp shadow. A wrinkled finger gently touches the glass surface, evoking a tangible sense of stillness and time.",
"An over-the-shoulder shot at eye level, positioned behind Character A(red hair, wearing a white T-shirt). The foreground, including A's shoulder and head, is softly blurred, directing focus onto Character B(with a beard, wearing a white T-shirt)'s face. B's subtle reactions—shifting from surprise to confusion, then to a glimmer of understanding—are clearly visible. The café background is gently blurred with warm lighting.",
]
)
ff_vis_char_idxs: List[int] = Field(
default=[],
description="The indices of the characters in the first frame.",
examples=[
[0, 1],
[0],
[],
],
)
lf_desc: str = Field(
description="The last frame of the shot.",
)
lf_vis_char_idxs: List[int] = Field(
default=[],
description="The indices of the characters in the last frame.",
)
motion_desc: str = Field(
description='''The motion description of the shot.
If there is a conversation, please write down the content of the conversation), when you meet some dialogue, you should write into the visual content description with :" " symbols and the character's features (eg. SLING (male, late 20s, Texan accent softened by military precision, confident and energetic.) says: "Gear retracted. Flaps transitioning. Flight path stable. You are clear to climb."). If there is a narration, you should write into the visual content description with :" " symbols and the narration's features (eg. Narration: "Everything is looking good. "). ''',
)
# audio
audio_desc: str = Field(
description="A detailed description of the audio in the shot.",
examples=[
"[Sound Effect] Ambient sound (supermarket background noise, shopping cart wheels rolling)",
"[Speaker] Alice (Happy): Hello, how are you?",
None,
],
)
# sound_effect: Optional[str] = Field(
# default=None,
# description="The sound effects used in the shot. For example, a door creaking or footsteps approaching.",
# )
# speaker: Optional[str] = Field(
# default=None,
# description="The speaker in the shot, if applicable. If there is no speaker, this field should be set to None.",
# )
# is_speaker_lip_visible: Optional[bool] = Field(
# default=None,
# description="Indicates whether the speaker's lips are visible in the shot. If there is no speaker, this field should be set to None.",
# )
# line: Optional[str] = Field(
# default=None,
# description="The dialogue or monologue in the shot, if applicable. If there is a speaker, there must be a line. If there is no speaker, this field should be set to None.",
# )
# emotion: Optional[str] = Field(
# default=None,
# description="The emotion of the speaker when delivering the line, if applicable. If there is a speaker, there must be an emotion. If there is no speaker, this field should be set to None.",
# )
+43
View File
@@ -0,0 +1,43 @@
import asyncio
from typing import List, Literal, Optional, Union
from PIL import Image
from utils.video import download_video
class VideoOutput:
fmt: Literal["url", "bytes"]
ext: str = "mp4"
data: Union[str, bytes]
def __init__(
self,
fmt: Literal["url", "bytes"],
ext: str,
data: Union[str, bytes],
):
self.fmt = fmt
self.ext = ext
self.data = data
def save_url(self, path: str) -> None:
"""Download and save a video from a URL to the specified path.
Args:
path (str): Path where the video will be saved.
"""
download_video(self.data, path)
def save_bytes(self, path: str) -> None:
"""Save a bytes object to the specified path.
Args:
path (str): Path where the video will be saved.
"""
with open(path, 'wb') as f:
f.write(self.data)
def save(self, path: str) -> None:
save_func = getattr(self, f"save_{self.fmt}")
save_func(path)