Skip to content

Commit 9668e5f

Browse files
MeorgeMarshalX
andauthored
Add Client.send_video high-level method (#395)
Co-authored-by: Ilya (Marshal) <[email protected]>
1 parent ec10135 commit 9668e5f

File tree

4 files changed

+111
-1
lines changed

4 files changed

+111
-1
lines changed

examples/send_video.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from atproto import Client
2+
3+
4+
def main() -> None:
5+
client = Client()
6+
client.login('my-handle', 'my-password')
7+
8+
# replace the path to your video file
9+
with open('video.mp4', 'rb') as f:
10+
vid_data = f.read()
11+
12+
client.send_video(text='Post with video from Python', video=vid_data, video_alt='Text version of the video (ALT)')
13+
14+
15+
if __name__ == '__main__':
16+
main()

packages/atproto_client/client/async_client.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ async def send_post(
129129
'models.AppBskyEmbedExternal.Main',
130130
'models.AppBskyEmbedRecord.Main',
131131
'models.AppBskyEmbedRecordWithMedia.Main',
132+
'models.AppBskyEmbedVideo.Main',
132133
]
133134
] = None,
134135
langs: t.Optional[t.List[str]] = None,
@@ -287,6 +288,50 @@ async def send_image(
287288
facets=facets,
288289
)
289290

291+
async def send_video(
292+
self,
293+
text: t.Union[str, TextBuilder],
294+
video: bytes,
295+
video_alt: t.Optional[str] = None,
296+
profile_identify: t.Optional[str] = None,
297+
reply_to: t.Optional['models.AppBskyFeedPost.ReplyRef'] = None,
298+
langs: t.Optional[t.List[str]] = None,
299+
facets: t.Optional[t.List['models.AppBskyRichtextFacet.Main']] = None,
300+
) -> 'models.AppBskyFeedPost.CreateRecordResponse':
301+
"""Send post with attached video.
302+
303+
Note:
304+
If `profile_identify` is not provided will be sent to the current profile.
305+
306+
Args:
307+
text: Text of the post.
308+
video: Binary video to attach.
309+
video_alt: Text version of the video.
310+
profile_identify: Handle or DID. Where to send post.
311+
reply_to: Root and parent of the post to reply to.
312+
langs: List of used languages in the post.
313+
facets: List of facets (rich text items).
314+
315+
Returns:
316+
:obj:`models.AppBskyFeedPost.CreateRecordResponse`: Reference to the created record.
317+
318+
Raises:
319+
:class:`atproto.exceptions.AtProtocolError`: Base exception.
320+
"""
321+
if video_alt is None:
322+
video_alt = ''
323+
324+
upload = await self.upload_blob(video)
325+
326+
return await self.send_post(
327+
text,
328+
profile_identify=profile_identify,
329+
reply_to=reply_to,
330+
embed=models.AppBskyEmbedVideo.Main(video=upload.blob, alt=video_alt),
331+
langs=langs,
332+
facets=facets,
333+
)
334+
290335
async def get_post(
291336
self, post_rkey: str, profile_identify: t.Optional[str] = None, cid: t.Optional[str] = None
292337
) -> 'models.AppBskyFeedPost.GetRecordResponse':

packages/atproto_client/client/client.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def send_post(
120120
'models.AppBskyEmbedExternal.Main',
121121
'models.AppBskyEmbedRecord.Main',
122122
'models.AppBskyEmbedRecordWithMedia.Main',
123+
'models.AppBskyEmbedVideo.Main',
123124
]
124125
] = None,
125126
langs: t.Optional[t.List[str]] = None,
@@ -278,6 +279,50 @@ def send_image(
278279
facets=facets,
279280
)
280281

282+
def send_video(
283+
self,
284+
text: t.Union[str, TextBuilder],
285+
video: bytes,
286+
video_alt: t.Optional[str] = None,
287+
profile_identify: t.Optional[str] = None,
288+
reply_to: t.Optional['models.AppBskyFeedPost.ReplyRef'] = None,
289+
langs: t.Optional[t.List[str]] = None,
290+
facets: t.Optional[t.List['models.AppBskyRichtextFacet.Main']] = None,
291+
) -> 'models.AppBskyFeedPost.CreateRecordResponse':
292+
"""Send post with attached video.
293+
294+
Note:
295+
If `profile_identify` is not provided will be sent to the current profile.
296+
297+
Args:
298+
text: Text of the post.
299+
video: Binary video to attach.
300+
video_alt: Text version of the video.
301+
profile_identify: Handle or DID. Where to send post.
302+
reply_to: Root and parent of the post to reply to.
303+
langs: List of used languages in the post.
304+
facets: List of facets (rich text items).
305+
306+
Returns:
307+
:obj:`models.AppBskyFeedPost.CreateRecordResponse`: Reference to the created record.
308+
309+
Raises:
310+
:class:`atproto.exceptions.AtProtocolError`: Base exception.
311+
"""
312+
if video_alt is None:
313+
video_alt = ''
314+
315+
upload = self.upload_blob(video)
316+
317+
return self.send_post(
318+
text,
319+
profile_identify=profile_identify,
320+
reply_to=reply_to,
321+
embed=models.AppBskyEmbedVideo.Main(video=upload.blob, alt=video_alt),
322+
langs=langs,
323+
facets=facets,
324+
)
325+
281326
def get_post(
282327
self, post_rkey: str, profile_identify: t.Optional[str] = None, cid: t.Optional[str] = None
283328
) -> 'models.AppBskyFeedPost.GetRecordResponse':

packages/atproto_codegen/clients/generate_async_client.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def gen_client(input_filename: str, output_filename: str) -> None:
1616
'send_post',
1717
'send_image',
1818
'send_images',
19+
'upload_blob',
1920
'_set_session',
2021
'_get_and_set_session',
2122
'_refresh_and_set_session',
@@ -39,10 +40,13 @@ def gen_client(input_filename: str, output_filename: str) -> None:
3940
code = code.replace('self.app', 'await self.app')
4041

4142
for method in methods:
43+
# TODO(MarshalX): abnormally hacky; rework
44+
code = re.sub(rf'(\[self\.{method}.*\])', r'await asyncio.gather(*\1)', code)
45+
4246
code = code.replace(f'self.{method}(', f'await self.{method}(')
4347
code = code.replace(f'super().{method}(', f'await super().{method}(')
4448

45-
code = re.sub(r'(\[self\.upload_blob.*\])', r'await asyncio.gather(*\1)', code)
49+
code = code.replace('gather(*[await', 'gather(*[') # rollback specific case
4650

4751
code = DISCLAIMER + code
4852

0 commit comments

Comments
 (0)