transcodely 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. buf/__init__.py +0 -0
  2. buf/validate/__init__.py +0 -0
  3. buf/validate/validate_pb2.py +469 -0
  4. buf/validate/validate_pb2.pyi +651 -0
  5. transcodely/__init__.py +175 -0
  6. transcodely/_codec/__init__.py +0 -0
  7. transcodely/_codec/json_codec.py +193 -0
  8. transcodely/_transport/__init__.py +0 -0
  9. transcodely/_transport/error_mapping.py +102 -0
  10. transcodely/_transport/headers.py +31 -0
  11. transcodely/_transport/transport.py +312 -0
  12. transcodely/client.py +160 -0
  13. transcodely/errors.py +115 -0
  14. transcodely/pagination.py +53 -0
  15. transcodely/py.typed +0 -0
  16. transcodely/resources/__init__.py +0 -0
  17. transcodely/resources/_helpers.py +29 -0
  18. transcodely/resources/api_keys.py +48 -0
  19. transcodely/resources/apps.py +59 -0
  20. transcodely/resources/events.py +118 -0
  21. transcodely/resources/health.py +19 -0
  22. transcodely/resources/jobs.py +120 -0
  23. transcodely/resources/memberships.py +54 -0
  24. transcodely/resources/organizations.py +73 -0
  25. transcodely/resources/origins.py +55 -0
  26. transcodely/resources/presets.py +63 -0
  27. transcodely/resources/users.py +46 -0
  28. transcodely/resources/videos.py +114 -0
  29. transcodely/resources/webhook_endpoints.py +242 -0
  30. transcodely/streaming.py +47 -0
  31. transcodely/types.py +570 -0
  32. transcodely/v1/api_key_pb2.py +69 -0
  33. transcodely/v1/api_key_pb2.pyi +97 -0
  34. transcodely/v1/app_pb2.py +107 -0
  35. transcodely/v1/app_pb2.pyi +219 -0
  36. transcodely/v1/codec_av1_pb2.py +55 -0
  37. transcodely/v1/codec_av1_pb2.pyi +34 -0
  38. transcodely/v1/codec_h264_pb2.py +71 -0
  39. transcodely/v1/codec_h264_pb2.pyi +44 -0
  40. transcodely/v1/codec_h265_pb2.py +69 -0
  41. transcodely/v1/codec_h265_pb2.pyi +42 -0
  42. transcodely/v1/codec_vp9_pb2.py +57 -0
  43. transcodely/v1/codec_vp9_pb2.pyi +30 -0
  44. transcodely/v1/common_pb2.py +67 -0
  45. transcodely/v1/common_pb2.pyi +171 -0
  46. transcodely/v1/content_aware_pb2.py +56 -0
  47. transcodely/v1/content_aware_pb2.pyi +51 -0
  48. transcodely/v1/drm_pb2.py +65 -0
  49. transcodely/v1/drm_pb2.pyi +68 -0
  50. transcodely/v1/hdr_pb2.py +57 -0
  51. transcodely/v1/hdr_pb2.pyi +64 -0
  52. transcodely/v1/health_pb2.py +44 -0
  53. transcodely/v1/health_pb2.pyi +44 -0
  54. transcodely/v1/job_pb2.py +201 -0
  55. transcodely/v1/job_pb2.pyi +546 -0
  56. transcodely/v1/media_pb2.py +43 -0
  57. transcodely/v1/media_pb2.pyi +100 -0
  58. transcodely/v1/membership_pb2.py +71 -0
  59. transcodely/v1/membership_pb2.pyi +122 -0
  60. transcodely/v1/organization_pb2.py +81 -0
  61. transcodely/v1/organization_pb2.pyi +121 -0
  62. transcodely/v1/origin_pb2.py +157 -0
  63. transcodely/v1/origin_pb2.pyi +317 -0
  64. transcodely/v1/preset_pb2.py +192 -0
  65. transcodely/v1/preset_pb2.pyi +290 -0
  66. transcodely/v1/streaming_pb2.py +59 -0
  67. transcodely/v1/streaming_pb2.pyi +56 -0
  68. transcodely/v1/subtitles_pb2.py +69 -0
  69. transcodely/v1/subtitles_pb2.pyi +75 -0
  70. transcodely/v1/thumbnails_pb2.py +63 -0
  71. transcodely/v1/thumbnails_pb2.pyi +74 -0
  72. transcodely/v1/user_pb2.py +72 -0
  73. transcodely/v1/user_pb2.pyi +137 -0
  74. transcodely/v1/video_pb2.py +166 -0
  75. transcodely/v1/video_pb2.pyi +367 -0
  76. transcodely/v1/webhook_pb2.py +151 -0
  77. transcodely/v1/webhook_pb2.pyi +336 -0
  78. transcodely/version.py +11 -0
  79. transcodely/webhooks/__init__.py +44 -0
  80. transcodely/webhooks/construct_event.py +111 -0
  81. transcodely/webhooks/signature.py +110 -0
  82. transcodely/webhooks/types.py +115 -0
  83. transcodely-0.2.0.dist-info/METADATA +349 -0
  84. transcodely-0.2.0.dist-info/RECORD +86 -0
  85. transcodely-0.2.0.dist-info/WHEEL +4 -0
  86. transcodely-0.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,114 @@
1
+ """Video resource — uploads, get/list/update/delete, watch, usage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Generator, Optional
6
+
7
+ from .._transport.transport import CallOptions, Transport
8
+ from ..pagination import Page, PageContents
9
+ from ..streaming import watch
10
+ from ..v1 import video_pb2
11
+ from ._helpers import fill_from_dict
12
+
13
+ _SERVICE = "transcodely.v1.VideoService"
14
+
15
+
16
+ class Videos:
17
+ def __init__(self, transport: Transport) -> None:
18
+ self._t = transport
19
+
20
+ def create_upload(self, **kwargs: Any) -> video_pb2.CreateUploadResponse:
21
+ req = fill_from_dict(video_pb2.CreateUploadRequest(), kwargs)
22
+ return self._t.unary(_SERVICE, "CreateUpload", req, video_pb2.CreateUploadResponse())
23
+
24
+ def complete_upload(self, **kwargs: Any) -> video_pb2.CompleteUploadResponse:
25
+ req = fill_from_dict(video_pb2.CompleteUploadRequest(), kwargs)
26
+ return self._t.unary(_SERVICE, "CompleteUpload", req, video_pb2.CompleteUploadResponse())
27
+
28
+ def create_multipart_upload(self, **kwargs: Any) -> video_pb2.CreateMultipartUploadResponse:
29
+ req = fill_from_dict(video_pb2.CreateMultipartUploadRequest(), kwargs)
30
+ return self._t.unary(
31
+ _SERVICE,
32
+ "CreateMultipartUpload",
33
+ req,
34
+ video_pb2.CreateMultipartUploadResponse(),
35
+ )
36
+
37
+ def get_upload_part_urls(self, **kwargs: Any) -> video_pb2.GetUploadPartUrlsResponse:
38
+ req = fill_from_dict(video_pb2.GetUploadPartUrlsRequest(), kwargs)
39
+ return self._t.unary(
40
+ _SERVICE, "GetUploadPartUrls", req, video_pb2.GetUploadPartUrlsResponse()
41
+ )
42
+
43
+ def complete_multipart_upload(self, **kwargs: Any) -> video_pb2.CompleteMultipartUploadResponse:
44
+ req = fill_from_dict(video_pb2.CompleteMultipartUploadRequest(), kwargs)
45
+ return self._t.unary(
46
+ _SERVICE,
47
+ "CompleteMultipartUpload",
48
+ req,
49
+ video_pb2.CompleteMultipartUploadResponse(),
50
+ )
51
+
52
+ def abort_multipart_upload(self, **kwargs: Any) -> video_pb2.AbortMultipartUploadResponse:
53
+ req = fill_from_dict(video_pb2.AbortMultipartUploadRequest(), kwargs)
54
+ return self._t.unary(
55
+ _SERVICE,
56
+ "AbortMultipartUpload",
57
+ req,
58
+ video_pb2.AbortMultipartUploadResponse(),
59
+ )
60
+
61
+ def get(self, id: str, opts: Optional[CallOptions] = None) -> video_pb2.Video:
62
+ req = video_pb2.GetVideoRequest(id=id)
63
+ return self._t.unary(_SERVICE, "Get", req, video_pb2.GetVideoResponse(), opts).video
64
+
65
+ def list(
66
+ self,
67
+ *,
68
+ page_size: Optional[int] = None,
69
+ status: Optional[str] = None,
70
+ opts: Optional[CallOptions] = None,
71
+ ) -> Page[video_pb2.Video]:
72
+ def fetch(cursor: Optional[str]) -> PageContents[video_pb2.Video]:
73
+ req = video_pb2.ListVideosRequest()
74
+ if page_size is not None:
75
+ req.page_size = page_size
76
+ if status is not None:
77
+ req.status = status
78
+ if cursor is not None:
79
+ req.page_token = cursor
80
+ res = self._t.unary(_SERVICE, "List", req, video_pb2.ListVideosResponse(), opts)
81
+ return PageContents(items=list(res.videos), next_cursor=res.next_page_token or None)
82
+
83
+ return Page(fetch)
84
+
85
+ def update(self, **kwargs: Any) -> video_pb2.Video:
86
+ req = fill_from_dict(video_pb2.UpdateVideoRequest(), kwargs)
87
+ return self._t.unary(_SERVICE, "Update", req, video_pb2.UpdateVideoResponse()).video
88
+
89
+ def delete(self, id: str, opts: Optional[CallOptions] = None) -> None:
90
+ req = video_pb2.DeleteVideoRequest(id=id)
91
+ self._t.unary(_SERVICE, "Delete", req, video_pb2.DeleteVideoResponse(), opts)
92
+
93
+ def watch(
94
+ self,
95
+ id: str,
96
+ *,
97
+ include_heartbeats: bool = False,
98
+ max_reconnects: int = 5,
99
+ opts: Optional[CallOptions] = None,
100
+ ) -> Generator[video_pb2.WatchVideoResponse, None, None]:
101
+ def factory() -> Generator[video_pb2.WatchVideoResponse, None, None]:
102
+ req = video_pb2.WatchVideoRequest(id=id)
103
+ yield from self._t.stream(_SERVICE, "Watch", req, video_pb2.WatchVideoResponse, opts)
104
+
105
+ yield from watch(
106
+ factory,
107
+ is_heartbeat=lambda e: getattr(e, "event", "") == "heartbeat",
108
+ include_heartbeats=include_heartbeats,
109
+ max_reconnects=max_reconnects,
110
+ )
111
+
112
+ def get_usage(self, **kwargs: Any) -> video_pb2.GetUsageResponse:
113
+ req = fill_from_dict(video_pb2.GetUsageRequest(), kwargs)
114
+ return self._t.unary(_SERVICE, "GetUsage", req, video_pb2.GetUsageResponse())
@@ -0,0 +1,242 @@
1
+ """Webhook endpoint resource — manage signed endpoints, test deliveries, and health.
2
+
3
+ Stripe parity: method names match the resource verbs (``create``, ``retrieve``,
4
+ ``update``, ``delete``, ``list``) without re-stating the "Webhook" prefix already
5
+ implied by ``client.webhook_endpoints``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Literal, Mapping, Optional
11
+
12
+ from .._transport.transport import CallOptions, Transport
13
+ from ..pagination import Page, PageContents
14
+ from ..v1 import common_pb2, webhook_pb2
15
+
16
+ _SERVICE = "transcodely.v1.WebhookService"
17
+
18
+
19
+ class WebhookEndpoints:
20
+ def __init__(self, transport: Transport) -> None:
21
+ self._t = transport
22
+
23
+ def create(
24
+ self,
25
+ *,
26
+ app_id: Optional[str] = None,
27
+ url: Optional[str] = None,
28
+ enabled_events: Optional[list[str]] = None,
29
+ description: Optional[str] = None,
30
+ api_version: Optional[str] = None,
31
+ metadata: Optional[Mapping[str, str]] = None,
32
+ request: Optional[webhook_pb2.CreateWebhookEndpointRequest] = None,
33
+ opts: Optional[CallOptions] = None,
34
+ ) -> webhook_pb2.WebhookEndpoint:
35
+ """Create a webhook endpoint.
36
+
37
+ The signing secret is populated on the returned endpoint **only here**
38
+ (and on :meth:`rotate_secret`) — store it securely; it cannot be
39
+ retrieved again.
40
+ """
41
+ if request is None:
42
+ req = webhook_pb2.CreateWebhookEndpointRequest()
43
+ if app_id is not None:
44
+ req.app_id = app_id
45
+ if url is not None:
46
+ req.url = url
47
+ if enabled_events:
48
+ req.enabled_events.extend(enabled_events)
49
+ if description is not None:
50
+ req.description = description
51
+ if api_version is not None:
52
+ req.api_version = api_version
53
+ if metadata:
54
+ for k, v in metadata.items():
55
+ req.metadata[k] = v
56
+ else:
57
+ req = request
58
+ res = self._t.unary(
59
+ _SERVICE,
60
+ "CreateWebhookEndpoint",
61
+ req,
62
+ webhook_pb2.CreateWebhookEndpointResponse(),
63
+ opts,
64
+ )
65
+ return res.endpoint
66
+
67
+ def retrieve(self, id: str, opts: Optional[CallOptions] = None) -> webhook_pb2.WebhookEndpoint:
68
+ req = webhook_pb2.RetrieveWebhookEndpointRequest(id=id)
69
+ res = self._t.unary(
70
+ _SERVICE,
71
+ "RetrieveWebhookEndpoint",
72
+ req,
73
+ webhook_pb2.RetrieveWebhookEndpointResponse(),
74
+ opts,
75
+ )
76
+ return res.endpoint
77
+
78
+ def update(
79
+ self,
80
+ id: str,
81
+ *,
82
+ url: Optional[str] = None,
83
+ description: Optional[str] = None,
84
+ enabled_events: Optional[list[str]] = None,
85
+ status: Optional[str] = None,
86
+ metadata: Optional[Mapping[str, str]] = None,
87
+ request: Optional[webhook_pb2.UpdateWebhookEndpointRequest] = None,
88
+ opts: Optional[CallOptions] = None,
89
+ ) -> webhook_pb2.WebhookEndpoint:
90
+ """Update an endpoint. Only provided fields are changed; omit the rest."""
91
+ if request is None:
92
+ req = webhook_pb2.UpdateWebhookEndpointRequest(id=id)
93
+ if url is not None:
94
+ req.url = url
95
+ if description is not None:
96
+ req.description = description
97
+ if enabled_events is not None:
98
+ req.enabled_events.extend(enabled_events)
99
+ if status is not None:
100
+ req.status = status
101
+ if metadata is not None:
102
+ for k, v in metadata.items():
103
+ req.metadata[k] = v
104
+ else:
105
+ req = request
106
+ res = self._t.unary(
107
+ _SERVICE,
108
+ "UpdateWebhookEndpoint",
109
+ req,
110
+ webhook_pb2.UpdateWebhookEndpointResponse(),
111
+ opts,
112
+ )
113
+ return res.endpoint
114
+
115
+ def delete(self, id: str, opts: Optional[CallOptions] = None) -> None:
116
+ req = webhook_pb2.DeleteWebhookEndpointRequest(id=id)
117
+ self._t.unary(
118
+ _SERVICE,
119
+ "DeleteWebhookEndpoint",
120
+ req,
121
+ webhook_pb2.DeleteWebhookEndpointResponse(),
122
+ opts,
123
+ )
124
+
125
+ def list(
126
+ self,
127
+ *,
128
+ app_id: str,
129
+ limit: Optional[int] = None,
130
+ opts: Optional[CallOptions] = None,
131
+ ) -> Page[webhook_pb2.WebhookEndpoint]:
132
+ def fetch(cursor: Optional[str]) -> PageContents[webhook_pb2.WebhookEndpoint]:
133
+ req = webhook_pb2.ListWebhookEndpointsRequest(app_id=app_id)
134
+ pagination = common_pb2.PaginationRequest()
135
+ if limit is not None:
136
+ pagination.limit = limit
137
+ if cursor is not None:
138
+ pagination.cursor = cursor
139
+ req.pagination.CopyFrom(pagination)
140
+ res = self._t.unary(
141
+ _SERVICE,
142
+ "ListWebhookEndpoints",
143
+ req,
144
+ webhook_pb2.ListWebhookEndpointsResponse(),
145
+ opts,
146
+ )
147
+ return PageContents(
148
+ items=list(res.endpoints),
149
+ next_cursor=res.pagination.next_cursor or None,
150
+ )
151
+
152
+ return Page(fetch)
153
+
154
+ def rotate_secret(
155
+ self, id: str, opts: Optional[CallOptions] = None
156
+ ) -> webhook_pb2.WebhookEndpoint:
157
+ """Rotate the signing secret.
158
+
159
+ The previous secret stays valid for 24 h to drain in-flight deliveries.
160
+ The returned endpoint includes the new plain-text ``secret`` — the only
161
+ response besides :meth:`create` that exposes it.
162
+ """
163
+ req = webhook_pb2.RotateWebhookSecretRequest(id=id)
164
+ res = self._t.unary(
165
+ _SERVICE,
166
+ "RotateWebhookSecret",
167
+ req,
168
+ webhook_pb2.RotateWebhookSecretResponse(),
169
+ opts,
170
+ )
171
+ return res.endpoint
172
+
173
+ def send_test(
174
+ self, endpoint_id: str, event_type: str, opts: Optional[CallOptions] = None
175
+ ) -> webhook_pb2.WebhookDelivery:
176
+ """Send a synthetic test event to a single endpoint.
177
+
178
+ The synthetic event is invisible to ``client.events.list`` and never
179
+ bumps ``pending_webhooks`` on any other event. ``event_type`` must be a
180
+ concrete type (the ``"*"`` wildcard is rejected server-side).
181
+ Rate-limited to 10/min per endpoint.
182
+ """
183
+ req = webhook_pb2.SendTestWebhookRequest(endpoint_id=endpoint_id, event_type=event_type)
184
+ res = self._t.unary(
185
+ _SERVICE, "SendTestWebhook", req, webhook_pb2.SendTestWebhookResponse(), opts
186
+ )
187
+ return res.delivery
188
+
189
+ def list_deliveries(
190
+ self,
191
+ *,
192
+ endpoint_id: Optional[str] = None,
193
+ event_id: Optional[str] = None,
194
+ status: Optional[str] = None,
195
+ limit: Optional[int] = None,
196
+ opts: Optional[CallOptions] = None,
197
+ ) -> Page[webhook_pb2.WebhookDelivery]:
198
+ """List delivery attempts. At least one of ``endpoint_id`` / ``event_id``
199
+ is required server-side."""
200
+
201
+ def fetch(cursor: Optional[str]) -> PageContents[webhook_pb2.WebhookDelivery]:
202
+ req = webhook_pb2.ListWebhookDeliveriesRequest()
203
+ if endpoint_id is not None:
204
+ req.endpoint_id = endpoint_id
205
+ if event_id is not None:
206
+ req.event_id = event_id
207
+ if status is not None:
208
+ req.status = status
209
+ pagination = common_pb2.PaginationRequest()
210
+ if limit is not None:
211
+ pagination.limit = limit
212
+ if cursor is not None:
213
+ pagination.cursor = cursor
214
+ req.pagination.CopyFrom(pagination)
215
+ res = self._t.unary(
216
+ _SERVICE,
217
+ "ListWebhookDeliveries",
218
+ req,
219
+ webhook_pb2.ListWebhookDeliveriesResponse(),
220
+ opts,
221
+ )
222
+ return PageContents(
223
+ items=list(res.deliveries),
224
+ next_cursor=res.pagination.next_cursor or None,
225
+ )
226
+
227
+ return Page(fetch)
228
+
229
+ def get_health(
230
+ self,
231
+ endpoint_id: str,
232
+ window: Optional[Literal["24h", "7d", "30d"]] = None,
233
+ opts: Optional[CallOptions] = None,
234
+ ) -> webhook_pb2.GetEndpointHealthResponse:
235
+ """Aggregate delivery health for one endpoint over a rolling window.
236
+ Response is cached server-side for ~30 s."""
237
+ req = webhook_pb2.GetEndpointHealthRequest(endpoint_id=endpoint_id)
238
+ if window is not None:
239
+ req.window = window
240
+ return self._t.unary(
241
+ _SERVICE, "GetEndpointHealth", req, webhook_pb2.GetEndpointHealthResponse(), opts
242
+ )
@@ -0,0 +1,47 @@
1
+ """Watch-stream wrapper with auto-reconnect and HEARTBEAT filtering."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import random
6
+ import time
7
+ from typing import Callable, Generator, Optional, TypeVar
8
+
9
+ from .errors import APIConnectionError
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ def watch(
15
+ factory: Callable[[], Generator[T, None, None]],
16
+ *,
17
+ is_heartbeat: Optional[Callable[[T], bool]] = None,
18
+ include_heartbeats: bool = False,
19
+ max_reconnects: int = 5,
20
+ ) -> Generator[T, None, None]:
21
+ """Wrap a stream factory with reconnect + heartbeat-filter semantics.
22
+
23
+ ``factory`` is called once per connection; if it raises an
24
+ :class:`APIConnectionError` we re-invoke it with backoff up to
25
+ ``max_reconnects`` times. Watch RPCs are idempotent — every reconnect
26
+ starts with a SNAPSHOT event so resumption is safe.
27
+ """
28
+ attempt = 0
29
+ while True:
30
+ try:
31
+ for event in factory():
32
+ if not include_heartbeats and is_heartbeat is not None and is_heartbeat(event):
33
+ continue
34
+ yield event
35
+ return
36
+ except APIConnectionError:
37
+ attempt += 1
38
+ if attempt > max_reconnects:
39
+ raise
40
+ _backoff(attempt)
41
+
42
+
43
+ def _backoff(attempt: int) -> None:
44
+ base = 0.5
45
+ cap = 5.0
46
+ expo = min(cap, base * 2 ** (attempt - 1))
47
+ time.sleep(expo * (0.5 + random.random() * 0.5))