From 6b9a4e1d82f4176dbaf30c51c85327d439ab56fb Mon Sep 17 00:00:00 2001 From: Vlad M Date: Sat, 6 Sep 2025 15:45:52 +0100 Subject: [PATCH 1/3] Add type checking with mypy --- packages/lambda-python/.gitignore | 1 + packages/lambda-python/build.mjs | 3 ++- packages/lambda-python/dev-requirements.txt | 4 +++- packages/lambda-python/remotion_lambda/models.py | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/lambda-python/.gitignore b/packages/lambda-python/.gitignore index 4a035f5c846..354e61281e4 100644 --- a/packages/lambda-python/.gitignore +++ b/packages/lambda-python/.gitignore @@ -14,4 +14,5 @@ # Python virtual environments test-env/ remotion-env-lint/ +.idea diff --git a/packages/lambda-python/build.mjs b/packages/lambda-python/build.mjs index ac89f196e13..2fa258f19b7 100644 --- a/packages/lambda-python/build.mjs +++ b/packages/lambda-python/build.mjs @@ -19,8 +19,9 @@ if (!hasPython()) { const commands = [ "python -m venv remotion-env-lint", ". ./remotion-env-lint/bin/activate", - "pip install boto3 pylint", + "pip install boto3 pylint mypy 'boto3-stubs[essential]'", "pylint ./remotion_lambda", + "mypy ./remotion_lambda", "deactivate", "rm -rf remotion-env-lint", ]; diff --git a/packages/lambda-python/dev-requirements.txt b/packages/lambda-python/dev-requirements.txt index 08f7b02b1fc..cf88bb972a7 100644 --- a/packages/lambda-python/dev-requirements.txt +++ b/packages/lambda-python/dev-requirements.txt @@ -1,2 +1,4 @@ pytest -dotenv \ No newline at end of file +dotenv +boto3-stubs[essential] +mypy \ No newline at end of file diff --git a/packages/lambda-python/remotion_lambda/models.py b/packages/lambda-python/remotion_lambda/models.py index 4e6723f4520..3cc50a6db30 100644 --- a/packages/lambda-python/remotion_lambda/models.py +++ b/packages/lambda-python/remotion_lambda/models.py @@ -415,7 +415,7 @@ class RenderStillParams: timeout_in_milliseconds: Optional[int] = 30000 chromium_options: Optional[ChromiumOptions] = None scale: Optional[float] = 1 - download_behavior: Dict = field(default_factory=lambda: PlayInBrowser(type='play-in-browser')) + download_behavior: Union[PlayInBrowser, ShouldDownload] = field(default_factory=lambda: PlayInBrowser(type='play-in-browser')) force_width: Optional[int] = None api_key: Optional[int] = None storage_class: Optional[str] = None From b5e650ff80394ef241d7e9c4fc3f681ef02a93c6 Mon Sep 17 00:00:00 2001 From: Vlad M Date: Sat, 6 Sep 2025 19:33:58 +0100 Subject: [PATCH 2/3] Formatting --- .../lambda-python/remotion_lambda/__init__.py | 28 ++-- .../lambda-python/remotion_lambda/models.py | 80 +++++++--- .../remotion_lambda/remotionclient.py | 149 +++++++++++------- .../lambda-python/remotion_lambda/version.py | 2 +- 4 files changed, 166 insertions(+), 93 deletions(-) diff --git a/packages/lambda-python/remotion_lambda/__init__.py b/packages/lambda-python/remotion_lambda/__init__.py index 1602fbc2f59..c4123d092b4 100644 --- a/packages/lambda-python/remotion_lambda/__init__.py +++ b/packages/lambda-python/remotion_lambda/__init__.py @@ -1,14 +1,24 @@ # pylint: disable=missing-module-docstring from .models import ( - RenderMediaParams, RenderMediaProgress, - RenderMediaResponse, RenderProgressParams, - RenderStillParams, RenderStillResponse, CostsInfo, - Privacy, ValidStillImageFormats, LogLevel, OpenGlRenderer, - ChromiumOptions, CustomCredentialsWithoutSensitiveData, CustomCredentials, - OutNameInputObject, PlayInBrowser, ShouldDownload, DeleteAfter, - Webhook - - + RenderMediaParams, + RenderMediaProgress, + RenderMediaResponse, + RenderProgressParams, + RenderStillParams, + RenderStillResponse, + CostsInfo, + Privacy, + ValidStillImageFormats, + LogLevel, + OpenGlRenderer, + ChromiumOptions, + CustomCredentialsWithoutSensitiveData, + CustomCredentials, + OutNameInputObject, + PlayInBrowser, + ShouldDownload, + DeleteAfter, + Webhook, ) from .remotionclient import RemotionClient from .version import VERSION diff --git a/packages/lambda-python/remotion_lambda/models.py b/packages/lambda-python/remotion_lambda/models.py index 3cc50a6db30..bb046f6a5b5 100644 --- a/packages/lambda-python/remotion_lambda/models.py +++ b/packages/lambda-python/remotion_lambda/models.py @@ -10,6 +10,7 @@ RenderType = Union[Literal["video-or-audio"], Literal["still"]] + class ValidStillImageFormats(str, Enum): """ Enumeration of valid image formats for still images. @@ -20,6 +21,7 @@ class ValidStillImageFormats(str, Enum): PDF: Represents the PDF format for images. WEBP: Represents the WEBP image format. """ + PNG = 'png' JPEG = 'jpeg' PDF = 'pdf' @@ -34,6 +36,7 @@ class Privacy(str, Enum): PUBLIC: Indicates a public setting. PRIVATE: Indicates a private setting. """ + PUBLIC = 'public' PRIVATE = 'private' NO_ACL = 'no-acl' @@ -49,6 +52,7 @@ class LogLevel(str, Enum): WARN: Warning logging level. ERROR: Error logging level. """ + VERBOSE = 'verbose' INFO = 'info' WARN = 'warn' @@ -66,6 +70,7 @@ class OpenGlRenderer(str, Enum): SWIFTSHADER: Represents the SWIFTSHADER OpenGL renderer. VULKAN: Represents the VULKAN OpenGL renderer. """ + SWANGLE = 'swangle' ANGLE = 'angle' EGL = 'egl' @@ -84,9 +89,10 @@ class ChromiumOptions: gl (Optional[OpenGlRenderer]): Specifies the OpenGL renderer to use. headless (Optional[bool]): If True, runs Chromium in headless mode. user_agent (Optional[str]): Specifies a custom user agent. - enable_multi_process_on_linux (Optional[bool]): + enable_multi_process_on_linux (Optional[bool]): If True, enables multi-process mode on Linux. """ + ignore_certificate_errors: Optional[bool] = None disable_web_security: Optional[bool] = None gl: Optional[OpenGlRenderer] = None @@ -103,18 +109,20 @@ class CustomCredentialsWithoutSensitiveData: Attributes: endpoint (str): The endpoint associated with the credentials. """ + endpoint: str @dataclass class CustomCredentials(CustomCredentialsWithoutSensitiveData): """ - Represents custom credentials, extending credentials without sensitive data. + Represents custom credentials, extending credentials without sensitive data. + + Attributes: + access_key_id (Optional[str]): The access key ID. + secret_access_key (Optional[str]): The secret access key. + """ - Attributes: - access_key_id (Optional[str]): The access key ID. - secret_access_key (Optional[str]): The secret access key. - """ access_key_id: Optional[str] = None secret_access_key: Optional[str] = None region: Optional[str] = None @@ -143,6 +151,7 @@ class OutNameInputObject: s3_output_provider (Optional[CustomCredentials]): Optional custom credentials for the S3 output provider. """ + bucketName: str key: str s3_output_provider: Optional[CustomCredentials] = None @@ -158,6 +167,7 @@ class DeleteAfter(Enum): SEVEN_DAYS: Represents deletion after seven days. THIRTY_DAYS: Represents deletion after thirty days. """ + ONE_DAY = '1-day' THREE_DAYS = '3-days' SEVEN_DAYS = '7-days' @@ -169,6 +179,7 @@ class RenderMediaResponse: """ Response data after rendering. """ + bucket_name: str render_id: str @@ -222,6 +233,7 @@ class CostsInfo: currency (str): The type of currency used for the costs, e.g., 'USD', 'EUR'. disclaimer (str): Any disclaimer or additional information related to the costs. """ + accrued_so_far: float display_cost: str currency: str @@ -233,6 +245,7 @@ class PlayInBrowser: """ The video should play in the browser when the link is clicked. """ + type: Literal['play-in-browser'] # You can define additional fields as needed @@ -242,6 +255,7 @@ class ShouldDownload: """ The video should download when the link is clicked. """ + type: Literal['download'] fileName: str # Additional fields for this type @@ -251,6 +265,7 @@ class Webhook: """ Represents a webhook. """ + secret: str url: str customData: Optional[Dict] = None @@ -261,6 +276,7 @@ class RenderMediaParams: """ Parameters for video rendering. """ + input_props: Optional[Dict[str, Any]] = None bucket_name: Optional[str] = None region: Optional[str] = None @@ -290,7 +306,8 @@ class RenderMediaParams: concurrency_per_lambda: Optional[int] = 1 concurrency: Optional[int] = None download_behavior: Optional[Union[PlayInBrowser, ShouldDownload]] = field( - default_factory=lambda: PlayInBrowser(type='play-in-browser')) + default_factory=lambda: PlayInBrowser(type='play-in-browser') + ) muted: bool = False overwrite: bool = False force_path_style: Optional[bool] = None @@ -337,7 +354,9 @@ def serialize_params(self) -> Dict: 'outName': self.out_name, 'preferLossless': self.prefer_lossless, 'timeoutInMilliseconds': self.timeout_in_milliseconds, - 'chromiumOptions': self.chromium_options if self.chromium_options is not None else {}, + 'chromiumOptions': ( + self.chromium_options if self.chromium_options is not None else {} + ), 'scale': self.scale, 'everyNthFrame': self.every_nth_frame, 'numberOfGifLoops': self.number_of_gif_loops, @@ -360,7 +379,7 @@ def serialize_params(self) -> Dict: 'deleteAfter': self.delete_after, 'encodingBufferSize': self.encoding_buffer_size, 'encodingMaxRate': self.encoding_max_rate, - 'type': 'start' + 'type': 'start', } if self.crf is not None: @@ -399,6 +418,7 @@ class RenderStillParams: """ Parameters for video rendering. """ + composition: str serve_url: str = "" input_props: Optional[Dict[str, Any]] = None @@ -415,7 +435,9 @@ class RenderStillParams: timeout_in_milliseconds: Optional[int] = 30000 chromium_options: Optional[ChromiumOptions] = None scale: Optional[float] = 1 - download_behavior: Union[PlayInBrowser, ShouldDownload] = field(default_factory=lambda: PlayInBrowser(type='play-in-browser')) + download_behavior: Union[PlayInBrowser, ShouldDownload] = field( + default_factory=lambda: PlayInBrowser(type='play-in-browser') + ) force_width: Optional[int] = None api_key: Optional[int] = None storage_class: Optional[str] = None @@ -430,21 +452,21 @@ class RenderStillParams: def serialize_params(self) -> Dict: """ - Serializes the parameters of the current object into a dictionary. + Serializes the parameters of the current object into a dictionary. - This method consolidates both mandatory and optional attributes of the object - into a single dictionary. Mandatory attributes include region, functionName, - serveUrl, composition, inputProps, imageFormat, and privacy. Optional attributes - are added to the dictionary only if they are not None. + This method consolidates both mandatory and optional attributes of the object + into a single dictionary. Mandatory attributes include region, functionName, + serveUrl, composition, inputProps, imageFormat, and privacy. Optional attributes + are added to the dictionary only if they are not None. - The optional attributes include maxRetries, envVariables, jpegQuality, frame, - logLevel, outName, timeoutInMilliseconds, chromiumOptions, scale, downloadBehavior, - forceWidth, forceHeight, forceBucketName, and deleteAfter. - Default values are provided for 'inputProps' (empty dictionary) and 'downloadBehavior' - ('type': 'play-in-browser') if they are not explicitly set. + The optional attributes include maxRetries, envVariables, jpegQuality, frame, + logLevel, outName, timeoutInMilliseconds, chromiumOptions, scale, downloadBehavior, + forceWidth, forceHeight, forceBucketName, and deleteAfter. + Default values are provided for 'inputProps' (empty dictionary) and 'downloadBehavior' + ('type': 'play-in-browser') if they are not explicitly set. - Returns: - Dict: A dictionary containing all the serialized parameters of the object. + Returns: + Dict: A dictionary containing all the serialized parameters of the object. """ parameters = { 'type': 'still', @@ -456,15 +478,20 @@ def serialize_params(self) -> Dict: 'version': VERSION, 'timeoutInMilliseconds': self.timeout_in_milliseconds, 'maxRetries': self.max_retries, - 'envVariables': self.env_variables if self.env_variables is not None else {}, + 'envVariables': ( + self.env_variables if self.env_variables is not None else {} + ), 'jpegQuality': self.jpeg_quality, 'storageClass': self.storage_class, 'frame': self.frame, 'logLevel': self.log_level, 'outName': self.out_name, - 'chromiumOptions': self.chromium_options if self.chromium_options is not None else {}, + 'chromiumOptions': ( + self.chromium_options if self.chromium_options is not None else {} + ), 'scale': self.scale, - 'downloadBehavior': self.download_behavior or PlayInBrowser(type='play-in-browser'), + 'downloadBehavior': self.download_behavior + or PlayInBrowser(type='play-in-browser'), 'forceWidth': self.force_width, 'apiKey': self.api_key, 'forceHeight': self.force_height, @@ -490,7 +517,7 @@ class RenderStillResponse: Represents the output information of a rendering operation performed on AWS Lambda. Attributes: - estimated_price (CostsInfo): + estimated_price (CostsInfo): An object containing detailed cost information related to the rendering. url (str): The URL where the rendered image is stored or can be accessed. size_in_bytes (int): The size of the rendered image file in bytes. @@ -498,6 +525,7 @@ class RenderStillResponse: render_id (str): A unique identifier for the rendering operation. cloud_watch_logs (str): The CloudWatch logs associated with the rendering operation. """ + estimated_price: CostsInfo url: str size_in_bytes: int diff --git a/packages/lambda-python/remotion_lambda/remotionclient.py b/packages/lambda-python/remotion_lambda/remotionclient.py index c154487dde6..c2c8676f32d 100644 --- a/packages/lambda-python/remotion_lambda/remotionclient.py +++ b/packages/lambda-python/remotion_lambda/remotionclient.py @@ -9,17 +9,32 @@ import boto3 from botocore.exceptions import ClientError from botocore.config import Config -from .models import (CostsInfo, CustomCredentials, RenderMediaParams, RenderMediaProgress, - RenderMediaResponse, RenderProgressParams, RenderStillResponse, - RenderStillParams, RenderType) +from .models import ( + CostsInfo, + CustomCredentials, + RenderMediaParams, + RenderMediaProgress, + RenderMediaResponse, + RenderProgressParams, + RenderStillResponse, + RenderStillParams, + RenderType, +) class RemotionClient: """A client for interacting with the Remotion service.""" # pylint: disable=too-many-arguments - def __init__(self, region, serve_url, function_name, access_key=None, secret_key=None, - force_path_style=False): + def __init__( + self, + region, + serve_url, + function_name, + access_key=None, + secret_key=None, + force_path_style=False, + ): """ Initialize the RemotionClient. @@ -65,11 +80,13 @@ def _create_s3_client(self): config = Config(s3={'addressing_style': 'path'}) if self.access_key and self.secret_key: - return boto3.client('s3', - aws_access_key_id=self.access_key, - aws_secret_access_key=self.secret_key, - region_name=self.region, - config=config) + return boto3.client( + 's3', + aws_access_key_id=self.access_key, + aws_secret_access_key=self.secret_key, + region_name=self.region, + config=config, + ) return boto3.client('s3', region_name=self.region, config=config) def _get_remotion_buckets(self): @@ -83,10 +100,14 @@ def _get_remotion_buckets(self): if bucket_name.startswith('remotionlambda-'): # Check if bucket is in the correct region try: - bucket_region = s3_client.get_bucket_location(Bucket=bucket_name) + bucket_region = s3_client.get_bucket_location( + Bucket=bucket_name + ) location = bucket_region.get('LocationConstraint') # us-east-1 returns None for LocationConstraint - if location == self.region or (location is None and self.region == 'us-east-1'): + if location == self.region or ( + location is None and self.region == 'us-east-1' + ): buckets.append(bucket_name) except ClientError: # Ignore buckets we can't access @@ -120,7 +141,7 @@ def _get_or_create_bucket(self): else: s3_client.create_bucket( Bucket=bucket_name, - CreateBucketConfiguration={'LocationConstraint': self.region} + CreateBucketConfiguration={'LocationConstraint': self.region}, ) return bucket_name except ClientError as e: @@ -134,7 +155,7 @@ def _upload_to_s3(self, bucket_name, key, payload): Bucket=bucket_name, Key=key, Body=payload, - ContentType='application/json' + ContentType='application/json', ) except ClientError as e: raise ValueError(f"Failed to upload to S3: {str(e)}") from e @@ -146,7 +167,9 @@ def _needs_upload(self, payload_size, render_type): max_still_inline_size = 5_000_000 - margin max_video_inline_size = 200_000 - margin - max_size = max_still_inline_size if render_type == 'still' else max_video_inline_size + max_size = ( + max_still_inline_size if render_type == 'still' else max_video_inline_size + ) if payload_size > max_size: # Log warning similar to JavaScript implementation @@ -184,27 +207,29 @@ def _serialize_input_props(self, input_props, render_type): return { 'type': 'bucket-url', 'hash': hash_value, - 'bucketName': bucket_name + 'bucketName': bucket_name, } # Return payload format for smaller payloads return { 'type': 'payload', - 'payload': payload if payload not in ('', 'null') else json.dumps({}) + 'payload': payload if payload not in ('', 'null') else json.dumps({}), } except (ValueError, TypeError) as error: raise ValueError( - 'Error serializing InputProps. Check for circular ' + - 'references or reduce the object size.' + 'Error serializing InputProps. Check for circular ' + + 'references or reduce the object size.' ) from error def _create_lambda_client(self): if self.access_key and self.secret_key and self.region: - return boto3.client('lambda', - aws_access_key_id=self.access_key, - aws_secret_access_key=self.secret_key, - region_name=self.region) + return boto3.client( + 'lambda', + aws_access_key_id=self.access_key, + aws_secret_access_key=self.secret_key, + region_name=self.region, + ) - return boto3.client('lambda', region_name=self.region) + return boto3.client('lambda', region_name=self.region) def _find_json_objects(self, input_string): """Finds and returns a list of complete JSON object strings.""" @@ -220,7 +245,7 @@ def _find_json_objects(self, input_string): elif char == '}': depth -= 1 if depth == 0: - objects.append(input_string[start_index:i + 1]) + objects.append(input_string[start_index : i + 1]) return objects @@ -234,20 +259,17 @@ def _invoke_lambda(self, function_name, payload): client = self._create_lambda_client() try: - response = client.invoke( - FunctionName=function_name, Payload=payload) + response = client.invoke(FunctionName=function_name, Payload=payload) result = response['Payload'].read().decode('utf-8') decoded_result = self._parse_stream(result)[-1] except client.exceptions.ResourceNotFoundException as e: - raise ValueError( - f"The function {function_name} does not exist.") from e + raise ValueError(f"The function {function_name} does not exist.") from e except client.exceptions.InvalidRequestContentException as e: raise ValueError("The request content is invalid.") from e except client.exceptions.RequestTooLargeException as e: raise ValueError("The request payload is too large.") from e except client.exceptions.ServiceException as e: - raise ValueError( - f"An internal service error occurred: {str(e)}") from e + raise ValueError(f"An internal service error occurred: {str(e)}") from e except Exception as e: raise ValueError(f"An unexpected error occurred: {str(e)}") from e @@ -272,8 +294,11 @@ def _custom_serializer(self, obj): return list(obj) return asdict(obj) - def construct_render_request(self, render_params: Union[RenderMediaParams, RenderStillParams], - render_type: RenderType) -> str: + def construct_render_request( + self, + render_params: Union[RenderMediaParams, RenderStillParams], + render_type: RenderType, + ) -> str: """ Construct a render request in JSON format. @@ -286,19 +311,19 @@ def construct_render_request(self, render_params: Union[RenderMediaParams, Rende render_params.serve_url = self.serve_url render_params.private_serialized_input_props = self._serialize_input_props( - input_props=render_params.input_props, - render_type=render_type + input_props=render_params.input_props, render_type=render_type ) payload = render_params.serialize_params() return json.dumps(payload, default=self._custom_serializer) - def construct_render_progress_request(self, - render_id: str, - bucket_name: str, - log_level="info", - s3_output_provider: Optional[CustomCredentials] = None - ) -> str: + def construct_render_progress_request( + self, + render_id: str, + bucket_name: str, + log_level="info", + s3_output_provider: Optional[CustomCredentials] = None, + ) -> str: """ Construct a render progress request in JSON format. @@ -315,7 +340,7 @@ def construct_render_progress_request(self, function_name=self.function_name, region=self.region, log_level=log_level, - s3_output_provider=s3_output_provider + s3_output_provider=s3_output_provider, ) return json.dumps(progress_params.serialize_params()) @@ -332,11 +357,15 @@ def render_media_on_lambda( RenderResponse: Response from the render operation. """ params = self.construct_render_request( - render_params, render_type="video-or-audio") + render_params, render_type="video-or-audio" + ) body_object = self._invoke_lambda( - function_name=self.function_name, payload=params) + function_name=self.function_name, payload=params + ) if body_object: - return RenderMediaResponse(body_object['bucketName'], body_object['renderId']) + return RenderMediaResponse( + body_object['bucketName'], body_object['renderId'] + ) return None @@ -352,11 +381,11 @@ def render_still_on_lambda( Returns: RenderResponse: Response from the render operation. """ - params = self.construct_render_request( - render_params, render_type='still') + params = self.construct_render_request(render_params, render_type='still') body_object = self._invoke_lambda( - function_name=self.function_name, payload=params) + function_name=self.function_name, payload=params + ) if body_object: return RenderStillResponse( @@ -364,7 +393,7 @@ def render_still_on_lambda( accrued_so_far=body_object['estimatedPrice']['accruedSoFar'], display_cost=body_object['estimatedPrice']['displayCost'], currency=body_object['estimatedPrice']['currency'], - disclaimer=body_object['estimatedPrice']['disclaimer'] + disclaimer=body_object['estimatedPrice']['disclaimer'], ), url=body_object['output'], size_in_bytes=body_object['sizeInBytes'], @@ -375,12 +404,13 @@ def render_still_on_lambda( return None - def get_render_progress(self, - render_id: str, - bucket_name: str, - log_level="info", - s3_output_provider: Optional[CustomCredentials] = None - ) -> Optional[RenderMediaProgress]: + def get_render_progress( + self, + render_id: str, + bucket_name: str, + log_level="info", + s3_output_provider: Optional[CustomCredentials] = None, + ) -> Optional[RenderMediaProgress]: """ Get the progress of a render. @@ -393,9 +423,14 @@ def get_render_progress(self, RenderProgress: Progress of the render. """ params = self.construct_render_progress_request( - render_id, bucket_name, log_level=log_level, s3_output_provider=s3_output_provider) + render_id, + bucket_name, + log_level=log_level, + s3_output_provider=s3_output_provider, + ) progress_response = self._invoke_lambda( - function_name=self.function_name, payload=params) + function_name=self.function_name, payload=params + ) if progress_response: render_progress = RenderMediaProgress() render_progress.__dict__.update(progress_response) diff --git a/packages/lambda-python/remotion_lambda/version.py b/packages/lambda-python/remotion_lambda/version.py index 949bb91cdc8..9467903834f 100644 --- a/packages/lambda-python/remotion_lambda/version.py +++ b/packages/lambda-python/remotion_lambda/version.py @@ -1,2 +1,2 @@ # pylint: disable=missing-module-docstring, missing-final-newline -VERSION = "4.0.341" \ No newline at end of file +VERSION = "4.0.341" From 962fb1dc4a1e2e8e18fd22cdf68c9c651cc36ed6 Mon Sep 17 00:00:00 2001 From: Vlad M Date: Sat, 6 Sep 2025 19:49:54 +0100 Subject: [PATCH 3/3] Convert RenderMediaProgress to dataclass --- .../lambda-python/remotion_lambda/models.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/lambda-python/remotion_lambda/models.py b/packages/lambda-python/remotion_lambda/models.py index bb046f6a5b5..e09ce55c2ba 100644 --- a/packages/lambda-python/remotion_lambda/models.py +++ b/packages/lambda-python/remotion_lambda/models.py @@ -1,7 +1,7 @@ # pylint: disable=too-few-public-methods, missing-module-docstring, broad-exception-caught,invalid-name from enum import Enum -from typing import Optional, Dict, Any, Union, Literal +from typing import Optional, Dict, Any, Union, Literal, List from dataclasses import dataclass, field from .version import VERSION @@ -534,26 +534,26 @@ class RenderStillResponse: outKey: str +@dataclass class RenderMediaProgress: """ Progress of video rendering. """ - def __init__(self): - self.overallProgress = float() - self.chunks = int() - self.done = bool() - self.encodingStatus = None - self.costs = None - self.renderId = str() - self.renderMetadata = None - self.outputFile = None - self.outKey = None - self.timeToFinish = None - self.errors = [] - self.fatalErrorEncountered = bool() - self.currentTime = int() - self.renderSize = int() - self.outputSizeInBytes = None - self.lambdasInvoked = int() - self.framesRendered = None + overallProgress: float = 0.0 + chunks: int = 0 + done: bool = False + encodingStatus: Optional[Any] = None + costs: Optional[Any] = None + renderId: str = "" + renderMetadata: Optional[Any] = None + outputFile: Optional[Any] = None + outKey: Optional[str] = None + timeToFinish: Optional[float] = None + errors: List[Any] = field(default_factory=list) + fatalErrorEncountered: bool = False + currentTime: int = 0 + renderSize: int = 0 + outputSizeInBytes: Optional[int] = None + lambdasInvoked: int = 0 + framesRendered: Optional[int] = None