From 055f587dd8028c6f4edfe2c45e27d4525f294624 Mon Sep 17 00:00:00 2001 From: Arvid Fahlstrom Myrman <885076+arvidfm@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:47:52 +0000 Subject: [PATCH 1/5] update linter and dev dependencies --- flixpy/examples/pyproject.toml | 2 +- flixpy/flix/cli/main.py | 5 +- flixpy/flix/extension/types.py | 36 +++---- flixpy/flix/lib/client.py | 2 +- flixpy/flix/lib/forms.py | 12 +-- flixpy/flix/lib/types.py | 49 +++++----- flixpy/flix/lib/webhooks.py | 22 ++--- flixpy/flix/lib/websocket.py | 30 +++--- flixpy/poetry.lock | 169 ++++++++++++++++++--------------- flixpy/pyproject.toml | 16 ++-- 10 files changed, 177 insertions(+), 166 deletions(-) diff --git a/flixpy/examples/pyproject.toml b/flixpy/examples/pyproject.toml index afe7ec7..5447902 100644 --- a/flixpy/examples/pyproject.toml +++ b/flixpy/examples/pyproject.toml @@ -7,5 +7,5 @@ ignore = [ "T201", # allow print() "ERA001", # allow commented-out code "PLR2004", # allow magic values in comparisons - "ASYNC101", # allow blocking calls in async functions for demonstration purposes + "ASYNC230", # allow blocking calls in async functions for demonstration purposes ] diff --git a/flixpy/flix/cli/main.py b/flixpy/flix/cli/main.py index e810962..9550e46 100644 --- a/flixpy/flix/cli/main.py +++ b/flixpy/flix/cli/main.py @@ -310,8 +310,7 @@ async def _handler(event: webhooks.WebhookEvent) -> None: await site.start() click.echo(f"Listening for events on {address}:{port} (press CTRL+C to abort)...", err=True) - while True: - await asyncio.sleep(3600) + await asyncio.Event().wait() @flix_cli.group(help="Manage contact sheet templates.") @@ -344,7 +343,7 @@ async def contactsheet_edit_loop( pdf_response = await flix_client.request( "GET", "/contactsheet/preview", params={"data": b64, "format": "pdf"} ) - with pathlib.Path(preview_file).open("wb") as f: # noqa: ASYNC101 + with pathlib.Path(preview_file).open("wb") as f: # noqa: ASYNC230 f.write(await pdf_response.read()) elif action == "save": return data diff --git a/flixpy/flix/extension/types.py b/flixpy/flix/extension/types.py index f270d43..c86dca7 100644 --- a/flixpy/flix/extension/types.py +++ b/flixpy/flix/extension/types.py @@ -20,32 +20,32 @@ ) __all__ = [ - "SourceFile", - "SourceFileType", - "SourceFilePreviewMode", - "ProjectDetails", - "Event", - "ClientEvent", - "OpenEvent", - "ClientPingEvent", "ActionEvent", - "ConnectionEvent", - "OpenPanelData", - "ProjectEvent", - "ProjectIds", - "ClientEventType", "ActionState", "ActionType", + "ActionsInProgress", "AssetType", - "VersionEvent", - "OpenSourceFileEvent", + "ClientEvent", + "ClientEventType", + "ClientPingEvent", + "ConnectionEvent", "DownloadResponse", - "ActionsInProgress", + "Event", + "OpenEvent", + "OpenPanelData", + "OpenSourceFileEvent", "PanelBrowserStatus", - "StatusEvent", + "PanelRequestResponse", + "ProjectDetails", + "ProjectEvent", + "ProjectIds", "RevisionStatus", + "SourceFile", + "SourceFilePreviewMode", + "SourceFileType", "Status", - "PanelRequestResponse", + "StatusEvent", + "VersionEvent", ] diff --git a/flixpy/flix/lib/client.py b/flixpy/flix/lib/client.py index a17477c..0553525 100644 --- a/flixpy/flix/lib/client.py +++ b/flixpy/flix/lib/client.py @@ -23,7 +23,7 @@ if TYPE_CHECKING: from types import TracebackType -__all__ = ["Client", "AccessKey"] +__all__ = ["AccessKey", "Client"] logger = logging.getLogger(__name__) diff --git a/flixpy/flix/lib/forms.py b/flixpy/flix/lib/forms.py index 15fbf13..868fd21 100644 --- a/flixpy/flix/lib/forms.py +++ b/flixpy/flix/lib/forms.py @@ -13,15 +13,15 @@ import asyncclick as click __all__ = [ - "Form", - "Field", - "StringField", - "IntField", - "FloatField", "BoolField", + "Choice", "EnumField", + "Field", + "FloatField", + "Form", + "IntField", "MultichoiceField", - "Choice", + "StringField", "prompt_enum", ] diff --git a/flixpy/flix/lib/types.py b/flixpy/flix/lib/types.py index f1b025c..3f42d3b 100644 --- a/flixpy/flix/lib/types.py +++ b/flixpy/flix/lib/types.py @@ -18,39 +18,39 @@ from . import client, errors, models, transfers, websocket __all__ = [ - "MetadataField", - "Metadata", - "Group", - "Permission", - "Role", - "GroupRolePair", - "User", - "MediaObjectStatus", - "MediaObjectHash", - "MediaObject", - "Episode", - "Sequence", "Asset", + "ColorTag", + "ContactSheet", + "ContactSheetCoverOptions", "ContactSheetOrientation", - "ContactSheetStyle", "ContactSheetPanelOptions", - "ContactSheetCoverOptions", - "ContactSheet", - "Show", + "ContactSheetStyle", + "Dialogue", + "DialogueFormat", + "DuplicateRef", + "Episode", + "Group", + "GroupRolePair", "Keyframe", - "PanelComment", - "OriginSBP", + "MediaObject", + "MediaObjectHash", + "MediaObjectStatus", + "Metadata", + "MetadataField", "OriginAvid", "OriginFCPXML", - "DuplicateRef", + "OriginSBP", "Panel", + "PanelComment", "PanelRevision", + "Permission", + "Role", + "Sequence", "SequencePanel", "SequenceRevision", - "DialogueFormat", "Server", - "Dialogue", - "ColorTag", + "Show", + "User", ] @@ -70,8 +70,7 @@ def client(self) -> client.Client: class AddressableFlixType(Protocol): - def path_prefix(self) -> str: - ... + def path_prefix(self) -> str: ... class MetadataField: @@ -605,7 +604,7 @@ async def upload( transfers.chunk_file(f), self.asset_id, self.media_object_id, - name=name, + name=name or "unknown", size=size, ) diff --git a/flixpy/flix/lib/webhooks.py b/flixpy/flix/lib/webhooks.py index a598572..e4a29f1 100644 --- a/flixpy/flix/lib/webhooks.py +++ b/flixpy/flix/lib/webhooks.py @@ -28,21 +28,21 @@ import ssl __all__ = [ - "EventType", - "WebhookEvent", "ErrorEvent", - "PublishEditorialEvent", - "PublishFlixEvent", + "EventFactory", + "EventType", "ExportSBPEvent", - "NewSequenceRevisionEvent", - "NewPanelRevisionEvent", "NewContactSheetEvent", + "NewPanelRevisionEvent", + "NewSequenceRevisionEvent", "PingEvent", + "PublishEditorialEvent", + "PublishFlixEvent", + "WebhookEvent", "WebhookHandler", - "webhook", "WebhookHandlerType", - "EventFactory", "run_webhook_server", + "webhook", ] logger = logging.getLogger(__name__) @@ -422,15 +422,13 @@ async def run_webhook_server( @flix.webhook(path="/events", secret="...") def on_event( event: flix.WebhookEvent, - ) -> None: - ... + ) -> None: ... @on_event.handle(flix.PublishEditorialEvent) def on_publish( event: flix.PublishEditorialEvent, - ) -> None: - ... + ) -> None: ... # start webhook server diff --git a/flixpy/flix/lib/websocket.py b/flixpy/flix/lib/websocket.py index 95f93c0..a126af0 100644 --- a/flixpy/flix/lib/websocket.py +++ b/flixpy/flix/lib/websocket.py @@ -24,29 +24,29 @@ from types import TracebackType __all__ = [ - "MessageType", - "WebsocketMessage", + "ChainWaiter", "KnownWebsocketMessage", - "UnknownWebsocketMessage", - "MessagePing", + "MessageAAFCreated", "MessageAssetStatus", "MessageAssetUpdated", - "MessagePublishCompleted", - "MessageQuicktimeCreated", - "MessageJobError", - "MessageLicenseValid", - "MessageAAFCreated", - "MessageJobChainStatus", - "MessageEditorialImportStatus", - "MessageEditorialImportComplete", - "MessageFCPXMLCreated", "MessageContactSheetCreated", "MessageDialogueComplete", + "MessageEditorialImportComplete", + "MessageEditorialImportStatus", + "MessageFCPXMLCreated", + "MessageJobChainStatus", + "MessageJobError", + "MessageLicenseValid", + "MessagePing", + "MessagePublishCompleted", + "MessageQuicktimeCreated", + "MessageStateYAMLCreated", "MessageStoryboardProImportComplete", "MessageThumbnailCreationError", - "MessageStateYAMLCreated", + "MessageType", + "UnknownWebsocketMessage", "Websocket", - "ChainWaiter", + "WebsocketMessage", ] diff --git a/flixpy/poetry.lock b/flixpy/poetry.lock index 84ba5fd..ecb9541 100644 --- a/flixpy/poetry.lock +++ b/flixpy/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -1003,47 +1003,59 @@ files = [ [[package]] name = "mypy" -version = "1.8.0" +version = "1.14.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, - {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, - {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, - {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, - {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, - {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, - {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, - {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, - {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, - {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, - {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, - {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, - {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, - {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, - {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, - {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, - {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, - {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, - {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, - {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, - {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, - {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, - {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, + {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, + {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, + {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, + {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, + {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, + {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, + {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, + {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, + {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, + {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, + {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, + {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, + {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, + {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, ] [package.dependencies] -mypy-extensions = ">=1.0.0" +mypy_extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing_extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] @@ -1061,18 +1073,18 @@ files = [ [[package]] name = "mypy-protobuf" -version = "3.5.0" +version = "3.6.0" description = "Generate mypy stub files from protobuf specs" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-protobuf-3.5.0.tar.gz", hash = "sha256:21f270da0a9792a9dac76b0df463c027e561664ab6973c59be4e4d064dfe67dc"}, - {file = "mypy_protobuf-3.5.0-py3-none-any.whl", hash = "sha256:0d0548c6b9a6faf14ce1a9ce2831c403a5c1f2a9363e85b1e2c51d5d57aa8393"}, + {file = "mypy-protobuf-3.6.0.tar.gz", hash = "sha256:02f242eb3409f66889f2b1a3aa58356ec4d909cdd0f93115622e9e70366eca3c"}, + {file = "mypy_protobuf-3.6.0-py3-none-any.whl", hash = "sha256:56176e4d569070e7350ea620262478b49b7efceba4103d468448f1d21492fd6c"}, ] [package.dependencies] -protobuf = ">=4.23.4" -types-protobuf = ">=4.23.0.2" +protobuf = ">=4.25.3" +types-protobuf = ">=4.24" [[package]] name = "nodeenv" @@ -1261,24 +1273,22 @@ files = [ [[package]] name = "protobuf" -version = "4.24.3" +version = "4.25.5" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "protobuf-4.24.3-cp310-abi3-win32.whl", hash = "sha256:20651f11b6adc70c0f29efbe8f4a94a74caf61b6200472a9aea6e19898f9fcf4"}, - {file = "protobuf-4.24.3-cp310-abi3-win_amd64.whl", hash = "sha256:3d42e9e4796a811478c783ef63dc85b5a104b44aaaca85d4864d5b886e4b05e3"}, - {file = "protobuf-4.24.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:6e514e8af0045be2b56e56ae1bb14f43ce7ffa0f68b1c793670ccbe2c4fc7d2b"}, - {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:ba53c2f04798a326774f0e53b9c759eaef4f6a568ea7072ec6629851c8435959"}, - {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f6ccbcf027761a2978c1406070c3788f6de4a4b2cc20800cc03d52df716ad675"}, - {file = "protobuf-4.24.3-cp37-cp37m-win32.whl", hash = "sha256:1b182c7181a2891e8f7f3a1b5242e4ec54d1f42582485a896e4de81aa17540c2"}, - {file = "protobuf-4.24.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b0271a701e6782880d65a308ba42bc43874dabd1a0a0f41f72d2dac3b57f8e76"}, - {file = "protobuf-4.24.3-cp38-cp38-win32.whl", hash = "sha256:e29d79c913f17a60cf17c626f1041e5288e9885c8579832580209de8b75f2a52"}, - {file = "protobuf-4.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:067f750169bc644da2e1ef18c785e85071b7c296f14ac53e0900e605da588719"}, - {file = "protobuf-4.24.3-cp39-cp39-win32.whl", hash = "sha256:2da777d34b4f4f7613cdf85c70eb9a90b1fbef9d36ae4a0ccfe014b0b07906f1"}, - {file = "protobuf-4.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:f631bb982c5478e0c1c70eab383af74a84be66945ebf5dd6b06fc90079668d0b"}, - {file = "protobuf-4.24.3-py3-none-any.whl", hash = "sha256:f6f8dc65625dadaad0c8545319c2e2f0424fede988368893ca3844261342c11a"}, - {file = "protobuf-4.24.3.tar.gz", hash = "sha256:12e9ad2ec079b833176d2921be2cb24281fa591f0b119b208b788adc48c2561d"}, + {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, + {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, + {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, + {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, + {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, + {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, + {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, + {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, + {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] [[package]] @@ -1442,21 +1452,23 @@ files = [ [[package]] name = "pyright" -version = "1.1.350" +version = "1.1.391" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.350-py3-none-any.whl", hash = "sha256:f1dde6bcefd3c90aedbe9dd1c573e4c1ddbca8c74bf4fa664dd3b1a599ac9a66"}, - {file = "pyright-1.1.350.tar.gz", hash = "sha256:a8ba676de3a3737ea4d8590604da548d4498cc5ee9ee00b1a403c6db987916c6"}, + {file = "pyright-1.1.391-py3-none-any.whl", hash = "sha256:54fa186f8b3e8a55a44ebfa842636635688670c6896dcf6cf4a7fc75062f4d15"}, + {file = "pyright-1.1.391.tar.gz", hash = "sha256:66b2d42cdf5c3cbab05f2f4b76e8bec8aa78e679bfa0b6ad7b923d9e027cadb2"}, ] [package.dependencies] nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" [package.extras] -all = ["twine (>=3.4.1)"] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "python-dateutil" @@ -1573,28 +1585,29 @@ files = [ [[package]] name = "ruff" -version = "0.2.1" +version = "0.9.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dd81b911d28925e7e8b323e8d06951554655021df8dd4ac3045d7212ac4ba080"}, - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dc586724a95b7d980aa17f671e173df00f0a2eef23f8babbeee663229a938fec"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c92db7101ef5bfc18e96777ed7bc7c822d545fa5977e90a585accac43d22f18a"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13471684694d41ae0f1e8e3a7497e14cd57ccb7dd72ae08d56a159d6c9c3e30e"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a11567e20ea39d1f51aebd778685582d4c56ccb082c1161ffc10f79bebe6df35"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:00a818e2db63659570403e44383ab03c529c2b9678ba4ba6c105af7854008105"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be60592f9d218b52f03384d1325efa9d3b41e4c4d55ea022cd548547cc42cd2b"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd2288890b88e8aab4499e55148805b58ec711053588cc2f0196a44f6e3d855"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ef052283da7dec1987bba8d8733051c2325654641dfe5877a4022108098683"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7022d66366d6fded4ba3889f73cd791c2d5621b2ccf34befc752cb0df70f5fad"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0a725823cb2a3f08ee743a534cb6935727d9e47409e4ad72c10a3faf042ad5ba"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0034d5b6323e6e8fe91b2a1e55b02d92d0b582d2953a2b37a67a2d7dedbb7acc"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e5cb5526d69bb9143c2e4d2a115d08ffca3d8e0fddc84925a7b54931c96f5c02"}, - {file = "ruff-0.2.1-py3-none-win32.whl", hash = "sha256:6b95ac9ce49b4fb390634d46d6ece32ace3acdd52814671ccaf20b7f60adb232"}, - {file = "ruff-0.2.1-py3-none-win_amd64.whl", hash = "sha256:e3affdcbc2afb6f5bd0eb3130139ceedc5e3f28d206fe49f63073cb9e65988e0"}, - {file = "ruff-0.2.1-py3-none-win_arm64.whl", hash = "sha256:efababa8e12330aa94a53e90a81eb6e2d55f348bc2e71adbf17d9cad23c03ee6"}, - {file = "ruff-0.2.1.tar.gz", hash = "sha256:3b42b5d8677cd0c72b99fcaf068ffc62abb5a19e71b4a3b9cfa50658a0af02f1"}, + {file = "ruff-0.9.0-py3-none-linux_armv6l.whl", hash = "sha256:949b3513f931741e006cf267bf89611edff04e1f012013424022add3ce78f319"}, + {file = "ruff-0.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:99fbcb8c7fe94ae1e462ab2a1ef17cb20b25fb6438b9f198b1bcf5207a0a7916"}, + {file = "ruff-0.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b022afd8eb0fcfce1e0adec84322abf4d6ce3cd285b3b99c4f17aae7decf749"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:336567ce92c9ca8ec62780d07b5fa11fbc881dc7bb40958f93a7d621e7ab4589"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d338336c44bda602dc8e8766836ac0441e5b0dfeac3af1bd311a97ebaf087a75"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9b3ececf523d733e90b540e7afcc0494189e8999847f8855747acd5a9a8c45f"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a11c0872a31232e473e2e0e2107f3d294dbadd2f83fb281c3eb1c22a24866924"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5fd06220c17a9cc0dc7fc6552f2ac4db74e8e8bff9c401d160ac59d00566f54"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0457e775c74bf3976243f910805242b7dcd389e1d440deccbd1194ca17a5728c"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05415599bbcb318f730ea1b46a39e4fbf71f6a63fdbfa1dda92efb55f19d7ecf"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fbf9864b009e43cfc1c8bed1a6a4c529156913105780af4141ca4342148517f5"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:37b3da222b12e2bb2ce628e02586ab4846b1ed7f31f42a5a0683b213453b2d49"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:733c0fcf2eb0c90055100b4ed1af9c9d87305b901a8feb6a0451fa53ed88199d"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8221a454bfe5ccdf8017512fd6bb60e6ec30f9ea252b8a80e5b73619f6c3cefd"}, + {file = "ruff-0.9.0-py3-none-win32.whl", hash = "sha256:d345f2178afd192c7991ddee59155c58145e12ad81310b509bd2e25c5b0247b3"}, + {file = "ruff-0.9.0-py3-none-win_amd64.whl", hash = "sha256:0cbc0905d94d21305872f7f8224e30f4bbcd532bc21b2225b2446d8fc7220d19"}, + {file = "ruff-0.9.0-py3-none-win_arm64.whl", hash = "sha256:7b1148771c6ca88f820d761350a053a5794bc58e0867739ea93eb5e41ad978cd"}, + {file = "ruff-0.9.0.tar.gz", hash = "sha256:143f68fa5560ecf10fc49878b73cee3eab98b777fcf43b0e62d43d42f5ef9d8b"}, ] [[package]] @@ -1720,13 +1733,13 @@ files = [ [[package]] name = "types-protobuf" -version = "4.24.0.1" +version = "5.29.1.20241207" description = "Typing stubs for protobuf" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-protobuf-4.24.0.1.tar.gz", hash = "sha256:90adea3b693d6a40d8ef075c58fe6b5cc6e01fe1496301a7e6fc70398dcff92e"}, - {file = "types_protobuf-4.24.0.1-py3-none-any.whl", hash = "sha256:df203a204e4ae97d4cca4c9cf725262579dd7857a19f9e7fc74871ccfa073c01"}, + {file = "types_protobuf-5.29.1.20241207-py3-none-any.whl", hash = "sha256:92893c42083e9b718c678badc0af7a9a1307b92afe1599e5cba5f3d35b668b2f"}, + {file = "types_protobuf-5.29.1.20241207.tar.gz", hash = "sha256:2ebcadb8ab3ef2e3e2f067e0882906d64ba0dc65fc5b0fd7a8b692315b4a0be9"}, ] [[package]] @@ -1884,4 +1897,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "9e3ec46daf889d6f8386147e6b91db950055b59c481d665cb928598306e6c4ad" +content-hash = "08d95be3b544b4aaef5e5872def21216b14d4fde92f889daff79dc07cf78359d" diff --git a/flixpy/pyproject.toml b/flixpy/pyproject.toml index adeeec5..ba6563e 100644 --- a/flixpy/pyproject.toml +++ b/flixpy/pyproject.toml @@ -32,12 +32,12 @@ types-python-dateutil = "^2.8.19.14" types-appdirs = "^1.4.3.5" grpcio-tools = "^1.57.0" grpc-stubs = {git = "https://github.com/shabbyrobe/grpc-stubs.git"} -types-protobuf = "^4.24.0.1" -mypy-protobuf = "^3.5.0" -mypy = "^1.8.0" +types-protobuf = "^5.29.1.20241207" +mypy-protobuf = "^3.6.0" +mypy = "^1.14.1" openapi-python-client = "^0.15.1" -pyright = "^1.1.350" -ruff = "^0.2.1" +pyright = "^1.1.391" +ruff = "^0.9.0" pre-commit = "^3.6.1" @@ -77,8 +77,6 @@ select = ["ALL"] ignore = [ # conflict with ruff format "ISC001", "COM812", - # type hinting self/cls is superfluous - "ANN101", "ANN102", # lots of arguments is ok as long as they're keyword arguments "PLR0913", # very silly rule, why would you want less informative exceptions @@ -89,6 +87,10 @@ ignore = [ "TID252", # removing else would change the semantics if the return was removed "RET505", "RET506", "RET507", "RET508", + # str.format is more readable than f-strings sometimes + "UP032", + # breaking change + "A005", # TODO re-enable these ASAP "FBT001", "FBT002", "ANN401", "D101", "D102", "D103", "D105", "D106", "D107", From 1fdbb57ed372fac35ecdbb15ccfea8ea5e2d097d Mon Sep 17 00:00:00 2001 From: Arvid Fahlstrom Myrman <885076+arvidfm@users.noreply.github.com> Date: Wed, 15 Jan 2025 19:02:45 +0000 Subject: [PATCH 2/5] update models to handle shots --- flixpy/flix/lib/models.py | 26 +++- flixpy/flix/lib/types.py | 261 +++++++++++++++++++++++++++++--------- 2 files changed, 220 insertions(+), 67 deletions(-) diff --git a/flixpy/flix/lib/models.py b/flixpy/flix/lib/models.py index 0a1c0b5..9d9032a 100644 --- a/flixpy/flix/lib/models.py +++ b/flixpy/flix/lib/models.py @@ -4,7 +4,7 @@ from typing import Any, TypedDict -from typing_extensions import NotRequired +from typing_extensions import NotRequired, Required class MetadataField(TypedDict, total=False): @@ -117,7 +117,7 @@ class SequenceRevision(TypedDict, total=False): owner: User created_date: str metadata: list[MetadataField] - revisioned_panels: list[SequencePanel] + related_shots: list[SequenceRevisionShot] comment: str published: bool imported: bool @@ -126,7 +126,6 @@ class SequenceRevision(TypedDict, total=False): autosave: bool sequence_id: int show_id: int - episode_id: int task_id: str source_files: list[Asset] @@ -247,8 +246,8 @@ class PanelRevision(TypedDict, total=False): panel: Panel -class SequencePanel(PanelRevision): - sequence_revision: NotRequired[int] +class ShotPanelRevision(TypedDict, total=False): + panel_revision: PanelRevision duration: int trim_in_frame: int trim_out_frame: int @@ -256,6 +255,23 @@ class SequencePanel(PanelRevision): dialogue: NotRequired[Dialogue] +class Shot(TypedDict, total=False): + id: int + show_id: int + sequence_id: int + owner: User + created_date: str + transitive: bool + related_panel_revisions: list[ShotPanelRevision] + metadata: list[MetadataField] + + +class SequenceRevisionShot(TypedDict, total=False): + sequence_revision: int + shot: Required[Shot] + name: str + + class PageSize(TypedDict): width: int height: int diff --git a/flixpy/flix/lib/types.py b/flixpy/flix/lib/types.py index 3f42d3b..3283847 100644 --- a/flixpy/flix/lib/types.py +++ b/flixpy/flix/lib/types.py @@ -1,5 +1,7 @@ """Pythonic abstractions of the models used by the Flix Server REST API.""" +# pyright: reportTypedDictNotRequiredAccess=warning + from __future__ import annotations import base64 @@ -46,9 +48,9 @@ "Permission", "Role", "Sequence", - "SequencePanel", "SequenceRevision", "Server", + "ShotPanelRevision", "Show", "User", ] @@ -910,12 +912,12 @@ def color_tag_name(self, value: str) -> None: def new_sequence_revision( self, comment: str = "", - panels: list[SequencePanel] | None = None, + shots: list[SequenceRevisionShot] | None = None, source_files: list[Asset] | None = None, ) -> SequenceRevision: return SequenceRevision( comment=comment, - panels=panels, + shots=shots, source_files=source_files, _sequence=self, _client=self.client, @@ -957,13 +959,13 @@ class _Panels(TypedDict): ) async def get_sequence_revision( - self, revision_number: int, *, fetch_panels: bool = False + self, revision_number: int, *, fetch_shots: bool = False ) -> SequenceRevision: """Fetch an existing revision of this sequence. Args: revision_number: The revision number of the sequence revision to fetch. - fetch_panels: Automatically populate the sequence revision with its panels. + fetch_shots: Automatically populate the sequence revision with its shots. Returns: The sequence revision with the requested revision number. @@ -972,8 +974,8 @@ async def get_sequence_revision( revision = cast(models.SequenceRevision, await self.client.get(path)) seqrev = SequenceRevision.from_dict(revision, _sequence=self, _client=self.client) - if fetch_panels: - await seqrev.get_all_panel_revisions() + if fetch_shots: + await seqrev.get_all_shots() return seqrev async def get_all_sequence_revisions(self) -> list[SequenceRevision]: @@ -2097,16 +2099,14 @@ def new_sequence_panel( trim_out_frame: int | None = None, dialogue: Dialogue | None = None, hidden: bool = False, - sequence_revision: int | None = None, - ) -> SequencePanel: - return SequencePanel( + ) -> ShotPanelRevision: + return ShotPanelRevision( panel=self, duration=duration, trim_in_frame=trim_in_frame or 0, trim_out_frame=trim_out_frame or 0, dialogue=dialogue, hidden=hidden, - sequence_revision=sequence_revision, ) def new_keyframe( @@ -2226,14 +2226,13 @@ async def save(self) -> None: @dataclasses.dataclass -class SequencePanel: +class ShotPanelRevision: panel: PanelRevision duration: int trim_in_frame: int trim_out_frame: int dialogue: Dialogue | None = None hidden: bool = False - sequence_revision: int | None = None @property def dialogue_text(self) -> str: @@ -2257,14 +2256,15 @@ def dialogue_text(self, value: str) -> None: @classmethod def from_dict( cls, - data: models.SequencePanel, + data: models.ShotPanelRevision, *, _sequence: Sequence, _client: client.Client | None, - ) -> SequencePanel: + ) -> ShotPanelRevision: return cls( - panel=PanelRevision.from_dict(data, _sequence=_sequence, _client=_client), - sequence_revision=data["sequence_revision"], + panel=PanelRevision.from_dict( + data["panel_revision"], _sequence=_sequence, _client=_client + ), duration=data["duration"], trim_in_frame=data.get("trim_in_frame") or 0, trim_out_frame=data.get("trim_out_frame") or 0, @@ -2276,20 +2276,160 @@ def from_dict( hidden=data["hidden"], ) - def to_dict(self) -> models.SequencePanel: - pr = models.SequencePanel( + def to_dict(self) -> models.ShotPanelRevision: + spr = models.ShotPanelRevision( duration=self.duration, trim_in_frame=self.trim_in_frame, trim_out_frame=self.trim_out_frame, hidden=self.hidden, + panel_revision=models.PanelRevision(), ) + if self.panel.show_id is not None: + spr["panel_revision"]["show_id"] = self.panel.show_id + if self.panel.sequence_id is not None: + spr["panel_revision"]["sequence_id"] = self.panel.sequence_id if self.panel.panel_id is not None: - pr["panel_id"] = self.panel.panel_id + spr["panel_revision"]["panel_id"] = self.panel.panel_id if self.panel.revision_number is not None: - pr["revision_number"] = self.panel.revision_number + spr["panel_revision"]["revision_number"] = self.panel.revision_number if self.dialogue: - pr["dialogue"] = self.dialogue.to_dict() - return pr + spr["dialogue"] = self.dialogue.to_dict() + return spr + + +class Shot(FlixType): + def __init__( + self, + panel_revisions: list[ShotPanelRevision] | None = None, + transitive: bool = True, + *, + owner: User | None = None, + created_date: datetime.datetime | None = None, + metadata: Mapping[str, Any] | None = None, + shot_id: int | None = None, + show_id: int | None = None, + sequence_id: int | None = None, + _client: client.Client | None, + ) -> None: + super().__init__(_client) + self.panel_revisions: list[ShotPanelRevision] = panel_revisions or [] + self.transitive: bool = transitive + self.owner: User | None = owner + self.created_date: datetime.datetime = created_date or datetime.datetime.now( + datetime.timezone.utc + ) + self.metadata = Metadata(metadata, parent=self, _client=_client) + self.shot_id: int | None = shot_id + self.show_id: int | None = show_id + self.sequence_id: int | None = sequence_id + + @classmethod + def from_dict( + cls, + data: models.Shot, + *, + into: Shot | None = None, + _sequence: Sequence, + _client: client.Client | None, + ) -> Shot: + if into is None: + into = cls(_client=_client) + into.transitive = t if (t := data.get("transitive")) is not None else True + into.panel_revisions = [ + ShotPanelRevision.from_dict(p, _sequence=_sequence, _client=_client) + for p in (data.get("related_panel_revisions") or []) + ] + into.owner = User.from_dict(o, _client=_client) if (o := data.get("owner")) else None + into.created_date = ( + dateutil.parser.parse(d) + if (d := data.get("created_date")) + else datetime.datetime.now(datetime.timezone.utc) + ) + into.shot_id = data.get("id") + into.show_id = data.get("show_id") + into.sequence_id = data.get("sequence_id") + into.metadata = Metadata.from_dict(data.get("metadata"), parent=into, _client=_client) + return into + + def to_dict(self) -> models.Shot: + shot = models.Shot( + transitive=self.transitive, + related_panel_revisions=[p.to_dict() for p in self.panel_revisions], + metadata=self.metadata.to_dict(), + ) + if self.shot_id: + shot["id"] = self.shot_id + if self.show_id: + shot["show_id"] = self.show_id + if self.sequence_id: + shot["sequence_id"] = self.sequence_id + return shot + + def path_prefix(self) -> str: + return f"/shot/{self.shot_id}" + + def add_panel( + self, + panel: PanelRevision, + duration: int = 12, + trim_in_frame: int | None = None, + trim_out_frame: int | None = None, + ) -> None: + """Add a panel revision to this shot. + + Args: + panel: The panel revision to add to the end of this shot. + duration: The duration of the panel within the shot. + trim_in_frame: How many frames to skip at the start of the panel. + Only relevant to animated panels. + trim_out_frame: How many frames to skip at the end of the panel. + Only relevant to animated panels. + """ + self.add_shot_panel_revision( + panel.new_sequence_panel( + duration=duration, + trim_in_frame=trim_in_frame, + trim_out_frame=trim_out_frame, + ) + ) + + def add_shot_panel_revision(self, spr: ShotPanelRevision) -> None: + """Add an already timed panel to this shot. + + Args: + spr: The panel to add to the end of this shot. + """ + self.panel_revisions.append(spr) + + +@dataclasses.dataclass +class SequenceRevisionShot: + name: str + shot: Shot + sequence_revision: int | None = None + + @classmethod + def from_dict( + cls, + data: models.SequenceRevisionShot, + *, + _sequence: Sequence, + _client: client.Client | None, + ) -> SequenceRevisionShot: + return cls( + name=data.get("name") or "", + shot=Shot.from_dict(data["shot"], _sequence=_sequence, _client=_client), + sequence_revision=data.get("sequence_revision"), + ) + + def to_dict(self) -> models.SequenceRevisionShot: + srs = models.SequenceRevisionShot( + name=self.name, + shot=self.shot.to_dict(), + ) + if self.sequence_revision: + srs["sequence_revision"] = self.sequence_revision + return srs class DialogueFormat(enum.Enum): @@ -2300,14 +2440,13 @@ class DialogueFormat(enum.Enum): class SequenceRevision(FlixType): def __init__( self, - panels: list[SequencePanel] | None = None, + shots: list[SequenceRevisionShot] | None = None, comment: str = "", hidden: bool = False, color_tag: ColorTag | None = None, autosave: bool = False, *, sequence_id: int | None = None, - episode_id: int | None = None, show_id: int | None = None, revision_number: int | None = None, owner: User | None = None, @@ -2323,10 +2462,9 @@ def __init__( super().__init__(_client) self._sequence = _sequence self.sequence_id = sequence_id - self.episode_id = episode_id self.show_id = show_id self.revision_number = revision_number - self.panels = panels or [] + self.shots = shots or [] self.comment = comment self.hidden = hidden self.color_tag = color_tag @@ -2354,7 +2492,6 @@ def from_dict( into = cls(_sequence=_sequence, _client=_client) into.revision_number = data["revision"] into.sequence_id = data["sequence_id"] - into.episode_id = data.get("episode_id", 0) into.show_id = data["show_id"] into.comment = data.get("comment", "") into.hidden = data["hidden"] @@ -2374,7 +2511,7 @@ def from_dict( def to_dict(self) -> models.SequenceRevision: revision = models.SequenceRevision( comment=self.comment, - revisioned_panels=[panel.to_dict() for panel in self.panels], + related_shots=[shot.to_dict() for shot in self.shots], source_files=[asset.to_dict() for asset in self.source_files], hidden=self.hidden, autosave=self.autosave, @@ -2383,8 +2520,6 @@ def to_dict(self) -> models.SequenceRevision: ) if self.show_id is not None: revision["show_id"] = self.show_id - if self.episode_id is not None: - revision["episode_id"] = self.episode_id if self.sequence_id is not None: revision["sequence_id"] = self.sequence_id if self.revision_number is not None: @@ -2402,6 +2537,12 @@ def sequence(self) -> Sequence: def show(self) -> Show: return self.sequence.show + @property + def panels(self) -> Iterator[ShotPanelRevision]: + """An iterator over all panels of the shots added to this sequence revision.""" + for shot in self.shots: + yield from shot.shot.panel_revisions + @property def color_tag_name(self) -> str: """The name of the current color tag for this sequence. @@ -2422,42 +2563,37 @@ def color_tag_name(self, value: str) -> None: else: self.color_tag = None - def add_panel( - self, - panel: PanelRevision, - duration: int = 12, - trim_in_frame: int | None = None, - trim_out_frame: int | None = None, - ) -> None: - self.add_sequence_panel( - panel.new_sequence_panel( - duration=duration, - trim_in_frame=trim_in_frame, - trim_out_frame=trim_out_frame, - sequence_revision=self.revision_number, + def add_shot(self, shot: Shot, name: str = "") -> None: + """Append a single shot to this sequence revision with the given name. + + Args: + shot: The shot to add to the end of this sequence revision. + name: The name of the shot within the sequence revision. + """ + self.shots.append( + SequenceRevisionShot( + shot=shot, + name=name, ) ) - def add_sequence_panel(self, sequence_panel: SequencePanel) -> None: - self.panels.append(sequence_panel) + async def get_all_shots(self) -> list[SequenceRevisionShot]: + """Fetch all shots belonging to this sequence revision. - async def get_all_panel_revisions(self) -> list[SequencePanel]: - """Fetch all panels belonging to this sequence revision. - - This method also populates [panels][flix.SequenceRevision.panels] - with the returned panels. + This method also populates [shots][flix.SequenceRevision.shots] + with the returned shots. """ - class _AllPanels(TypedDict): - panels: list[models.SequencePanel] + class _AllShots(TypedDict): + sequence_revision_shots: list[models.SequenceRevisionShot] - path = f"{self.path_prefix()}/panels" - all_panels = cast(_AllPanels, await self.client.get(path)) - self.panels = [ - SequencePanel.from_dict(panel, _sequence=self._sequence, _client=self.client) - for panel in all_panels["panels"] + path = f"{self.path_prefix()}/shots" + all_shots = cast(_AllShots, await self.client.get(path)) + self.shots = [ + SequenceRevisionShot.from_dict(shot, _sequence=self._sequence, _client=self.client) + for shot in all_shots["sequence_revision_shots"] ] - return self.panels + return self.shots async def _export( self, @@ -2528,9 +2664,10 @@ async def save(self, force_create_new: bool = False) -> None: if self.revision_number is None or force_create_new: # auto-save any unsaved dialogue - for panel in self.panels: - if panel.dialogue and not panel.dialogue.dialogue_id: - await panel.dialogue.save() + for shot in self.shots: + for panel in shot.shot.panel_revisions: + if panel.dialogue and not panel.dialogue.dialogue_id: + await panel.dialogue.save() path = f"{self._sequence.path_prefix()}/revision" result = cast( From d4042603e69dedefbbec1f330f89865a173b6b5b Mon Sep 17 00:00:00 2001 From: Arvid Fahlstrom Myrman <885076+arvidfm@users.noreply.github.com> Date: Wed, 15 Jan 2025 19:03:05 +0000 Subject: [PATCH 3/5] add shot helper methods --- flixpy/flix/lib/types.py | 148 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/flixpy/flix/lib/types.py b/flixpy/flix/lib/types.py index 3283847..bfd223e 100644 --- a/flixpy/flix/lib/types.py +++ b/flixpy/flix/lib/types.py @@ -2227,6 +2227,8 @@ async def save(self) -> None: @dataclasses.dataclass class ShotPanelRevision: + """A timed view of a panel revision within a shot.""" + panel: PanelRevision duration: int trim_in_frame: int @@ -2298,6 +2300,12 @@ def to_dict(self) -> models.ShotPanelRevision: class Shot(FlixType): + """A list of panels representing a single shot. + + A shot may be either transitive (implicit) or non-transitive (explicit). + Only non-transitive shots are rendered as shots by the Flix client. + """ + def __init__( self, panel_revisions: list[ShotPanelRevision] | None = None, @@ -2401,9 +2409,37 @@ def add_shot_panel_revision(self, spr: ShotPanelRevision) -> None: """ self.panel_revisions.append(spr) + def add_panels_from(self, other: Shot) -> None: + """Append the panels belonging to the given shot to this shot. + + Args: + other: The shot whose panels to append to the end of this shot. + """ + self.panel_revisions.extend(other.panel_revisions) + + def split_at(self, panel: int) -> Shot: + """Split this shot at the given panel index. + + This will shrink the current shot and create a new shot containing the panels + after the cut point. This method will *not* automatically add the sequence revision + associated with this shot. + + Args: + panel: The panel index within the shot to split the shot at. + The panel at the given index will be added to the right-hand shot. + + Returns: + The new right-hand shot. + """ + right_shot = Shot(self.panel_revisions[panel:], self.transitive, _client=self._client) + self.panel_revisions = self.panel_revisions[:panel] + return right_shot + @dataclasses.dataclass class SequenceRevisionShot: + """A named view of a shot within a sequence revision.""" + name: str shot: Shot sequence_revision: int | None = None @@ -2431,6 +2467,32 @@ def to_dict(self) -> models.SequenceRevisionShot: srs["sequence_revision"] = self.sequence_revision return srs + def split_at(self, panel: int) -> SequenceRevisionShot: + """Split this shot at the given panel index. + + This will shrink the current shot and create a new shot containing the panels + after the cut point. This method will *not* automatically add the sequence revision + associated with this shot. + + Args: + panel: The panel index within the shot to split the shot at. + The panel at the given index will be added to the right-hand shot. + + Returns: + The new right-hand shot. + """ + right_shot = self.shot.split_at(panel) + return SequenceRevisionShot(self.name, right_shot) + + def make_explicit(self, name: str) -> None: + """Make this an explicit, non-transitive shot. + + Args: + name: The name to use for the shot within its current sequence revision. + """ + self.shot.transitive = False + self.name = name + class DialogueFormat(enum.Enum): SRT = "srt" @@ -2595,6 +2657,92 @@ class _AllShots(TypedDict): ] return self.shots + def split_shot_at(self, panel: int) -> tuple[SequenceRevisionShot, SequenceRevisionShot]: + """Split a shot at the given panel index. + + Args: + panel: The index within the sequence revision of the panel at which to split the shot. + The panel at the index will be the first panel of the right-hand shot. + + Returns: + The two new shots formed by splitting the existing one. + """ + i = 0 + for shot_i, shot in enumerate(self.shots): + for j, _ in enumerate(shot.shot.panel_revisions): + if i == panel: + return self._split_shot(shot, shot_i, j) + i += 1 + raise IndexError + + def _split_shot( + self, shot: SequenceRevisionShot, shot_index: int, panel_index_in_shot: int + ) -> tuple[SequenceRevisionShot, SequenceRevisionShot]: + new_shot = shot.split_at(panel_index_in_shot) + self.shots.insert(shot_index + 1, new_shot) + return shot, new_shot + + def make_shot(self, start_panel: int, end_panel: int, name: str = "") -> SequenceRevisionShot: + """Add the panels in the given inclusive range to a new shot with the given name. + + Args: + start_panel: The index of the first panel within the sequence revision + to add to the shot. + end_panel: The index of the last panel within the sequence revision to add to the shot. + name: The name of the new shot. + + Returns: + The new shot. + """ + start_shot, start_shot_panel = self.find_panel_index(start_panel) + end_shot, end_shot_panel = self.find_panel_index(end_panel) + # split shots for panels that don't fall on existing shot boundaries; + # split end shot first since it doesn't affect shot/panel indices, + # to make calculations simpler + if end_shot_panel + 1 < len(self.shots[start_shot].shot.panel_revisions): + self.split_shot_at(end_panel + 1) + if start_shot_panel > 0: + self.split_shot_at(start_panel) + start_shot += 1 + end_shot += 1 + + new_shot = self.merge_shots(start_shot, end_shot) + new_shot.make_explicit(name) + return new_shot + + def merge_shots(self, start_shot: int, end_shot: int) -> SequenceRevisionShot: + """Combine the shots in the given (inclusive) range into a single shot. + + Args: + start_shot: The index of the first shot to include. + end_shot: The index of the last shot to include. + + Returns: + The merged shot. + """ + for i in range(start_shot + 1, end_shot + 1): + self.shots[start_shot].shot.add_panels_from(self.shots[i].shot) + if end_shot > start_shot: + self.shots = self.shots[: start_shot + 1] + self.shots[end_shot + 1 :] + return self.shots[start_shot] + + def find_panel_index(self, panel: int) -> tuple[int, int]: + """Get the index of a panel within the shot it belongs to. + + Args: + panel: The index of the panel. + + Returns: + A tuple of the index containing the panel and the index of the panel within its shot. + """ + i = 0 + for shot_i, shot in enumerate(self.shots): + panels_in_shot = len(shot.shot.panel_revisions) + if i + panels_in_shot > panel: + return shot_i, panel - i + i += panels_in_shot + raise IndexError(f"sequence revision does not contain a panel with index: {panel}") + async def _export( self, msg_type: type[websocket.AssetCreatedMessageType], From 0584ccd4a5f7d753420a5cfcc68ecc406e1949c5 Mon Sep 17 00:00:00 2001 From: Arvid Fahlstrom Myrman <885076+arvidfm@users.noreply.github.com> Date: Wed, 15 Jan 2025 19:54:20 +0000 Subject: [PATCH 4/5] [main][592391](UserStory) bump sdk version --- flixpy/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixpy/pyproject.toml b/flixpy/pyproject.toml index ba6563e..1fe9e5d 100644 --- a/flixpy/pyproject.toml +++ b/flixpy/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "flix-sdk" -version = "2.0.3" +version = "3.0.0" description = "Python SDK and command-line utilities for Flix" authors = [] homepage = "https://www.foundry.com/products/flix" From 9577a3ffd876fbd50df311c603e0135ed5166f85 Mon Sep 17 00:00:00 2001 From: Arvid Fahlstrom Myrman <885076+arvidfm@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:22:52 +0000 Subject: [PATCH 5/5] [main][592391](UserStory) add constructor methods for shots --- flixpy/flix/lib/types.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/flixpy/flix/lib/types.py b/flixpy/flix/lib/types.py index bfd223e..6f7dafb 100644 --- a/flixpy/flix/lib/types.py +++ b/flixpy/flix/lib/types.py @@ -939,6 +939,24 @@ def new_panel( _client=self.client, ) + def new_shot( + self, panels: list[ShotPanelRevision] | None = None, *, transitive: bool = True + ) -> Shot: + """Construct a new shot. + + Args: + panels: The panel revisions to include in the shot, if any. + transitive: Whether the shot should be transitive (implicit). + + Returns: + The new shot. + """ + return Shot( + panel_revisions=panels or [], + transitive=transitive, + _client=self.client, + ) + async def save_panels(self, panels: list[PanelRevision]) -> None: """Save a batch of panel revisions. @@ -2092,7 +2110,7 @@ def path_prefix(self, include_episode: bool = False) -> str: prefix = self._sequence.path_prefix(include_episode=include_episode) return f"{prefix}/panel/{self.panel_id}/revision/{self.revision_number}" - def new_sequence_panel( + def new_shot_panel_revision( self, duration: int = 12, trim_in_frame: int | None = None, @@ -2376,6 +2394,20 @@ def to_dict(self) -> models.Shot: def path_prefix(self) -> str: return f"/shot/{self.shot_id}" + def new_sequence_revision_shot(self, name: str = "") -> SequenceRevisionShot: + """Construct a named view of this shot that can be added to a sequence revision. + + Args: + name: The name of the shot. + + Returns: + The new shot view. + """ + return SequenceRevisionShot( + name=name, + shot=self, + ) + def add_panel( self, panel: PanelRevision, @@ -2394,7 +2426,7 @@ def add_panel( Only relevant to animated panels. """ self.add_shot_panel_revision( - panel.new_sequence_panel( + panel.new_shot_panel_revision( duration=duration, trim_in_frame=trim_in_frame, trim_out_frame=trim_out_frame,