Skip to content

Commit bb17099

Browse files
committed
feat(media): ✅ add tests for media upload api v2
1 parent 4e06d5b commit bb17099

File tree

6 files changed

+169
-13
lines changed

6 files changed

+169
-13
lines changed

pytwitter/api.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ def upload_media_chunked_status(
768768

769769
def upload_media_simple_v2(
770770
self,
771-
media: Optional[IO] = None,
771+
media: Optional[bytes] = None,
772772
media_category: Optional[str] = None,
773773
additional_owners: Optional[List[str]] = None,
774774
return_json: bool = False,
@@ -779,16 +779,19 @@ def upload_media_simple_v2(
779779
Note: The simple upload endpoint can only be used to upload images.
780780
781781
:param media: The raw binary file content being uploaded.
782-
:param media_category: The category that represents how the media will be used.
783-
This field is required when using the media with the Ads API.
782+
:param media_category: A string enum value which identifies a media use-case.
783+
This identifier is used to enforce use-case specific constraints (e.g. file size, video duration) and enable advanced features.
784784
Possible values:
785785
- tweet_image
786786
- tweet_gif
787787
- tweet_video
788788
- amplify_video
789-
:param additional_owners: A comma-separated list of user IDs to set as additional owners
790-
allowed to use the returned media_id in Tweets or Cards.
791-
Up to 100 additional owners may be specified.
789+
- dm_video
790+
- subtitles
791+
:param additional_owners: A comma-separated list of user IDs to set as additional owners allowed to use the
792+
returned media_id in Tweets or Cards. Up to 100 additional owners may be specified.
793+
Unique identifier of this User. This is returned as a string in order to avoid complications with
794+
languages and tools that cannot handle large integers.
792795
:param return_json: Type for returned data. If you set True JSON data will be returned.
793796
:return: Media upload response.
794797
"""
@@ -834,18 +837,19 @@ def upload_media_chunked_init_v2(
834837
835838
:param total_bytes: The total size of the media being uploaded in bytes.
836839
:param media_type: The MIME type of the media being uploaded. example: image/jpeg, image/gif, and video/mp4.
837-
:param media_category: The category that represents how the media will be used.
838-
This field is required when using the media with the Ads API.
840+
:param media_category: A string enum value which identifies a media use-case.
841+
This identifier is used to enforce use-case specific constraints (e.g. file size, video duration) and enable advanced features.
839842
Possible values:
840843
- tweet_image
841844
- tweet_gif
842845
- tweet_video
843846
- amplify_video
844847
- dm_video
845848
- subtitles
846-
:param additional_owners: A comma-separated list of user IDs to set as additional owners
847-
allowed to use the returned media_id in Tweets or Cards.
848-
Up to 100 additional owners may be specified.
849+
:param additional_owners: A comma-separated list of user IDs to set as additional owners allowed to use the
850+
returned media_id in Tweets or Cards. Up to 100 additional owners may be specified.
851+
Unique identifier of this User. This is returned as a string in order to avoid complications with
852+
languages and tools that cannot handle large integers.
849853
:param return_json: Type for returned data. If you set True JSON data will be returned.
850854
:return: Media upload response.
851855
"""
@@ -877,15 +881,15 @@ def upload_media_chunked_append_v2(
877881
self,
878882
media_id: str,
879883
segment_index: int,
880-
media: Optional[IO],
884+
media: Optional[bytes],
881885
) -> bool:
882886
"""
883887
Used to upload a chunk (consecutive byte range) of the media file.
884888
885889
:param media_id: The `media_id` returned from the INIT step.
886890
:param segment_index: An ordered index of file chunk. It must be between 0-999 inclusive.
887891
The first segment has index 0, second segment has index 1, and so on.
888-
:param media: The raw binary file content being uploaded. Cannot be used with `media_data`.
892+
:param media: The raw binary file content being uploaded.
889893
:return: True if upload success.
890894
"""
891895
resp = self._request(
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"data":{"id":"1912090619981471744","media_key":"7_1912090619981471744","size":19048761,"expires_after_secs":86400,"processing_info":{"state":"succeeded"}}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"data":{"id":"1912103767639719936","expires_after_secs":86400,"media_key":"3_1912103767639719936"}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"data":{"expires_after_secs":83800,"id":"1912090619981471744","media_key":"7_1912090619981471744","processing_info":{"progress_percent":100,"state":"succeeded"},"size":19048761}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"id":"1726817595448610817","media_key":"3_1726817595448610817","size":1864939,"expires_after_secs":86400,"image":{"image_type":"image/jpeg","w":4928,"h":3280}}

tests/apis/test_media_upload_v2.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
"""
2+
Tests for media upload API v2
3+
"""
4+
5+
import pytest
6+
import responses
7+
8+
from pytwitter import PyTwitterError
9+
10+
11+
@responses.activate
12+
def test_media_upload_simple_v2(api_with_user, helpers):
13+
with pytest.raises(PyTwitterError):
14+
api_with_user.upload_media_simple_v2()
15+
16+
responses.add(
17+
responses.POST,
18+
url="https://api.twitter.com/2/media/upload",
19+
json=helpers.load_json_data(
20+
"testdata/apis/media_upload_v2/upload_simple_resp.json"
21+
),
22+
)
23+
24+
with open("testdata/apis/media_upload/x-logo.png", "rb") as media:
25+
resp = api_with_user.upload_media_simple_v2(
26+
media=media,
27+
media_category="tweet_image",
28+
additional_owners=["123456789"],
29+
)
30+
assert resp.id == "1726817595448610817"
31+
32+
with open("testdata/apis/media_upload/x-logo.png", "rb") as media:
33+
resp = api_with_user.upload_media_simple_v2(
34+
media=media, media_category="tweet_image", return_json=True
35+
)
36+
assert resp["id"] == "1726817595448610817"
37+
38+
39+
@responses.activate
40+
def test_upload_media_chunked_init_v2(api_with_user, helpers):
41+
responses.add(
42+
responses.POST,
43+
url="https://api.twitter.com/2/media/upload",
44+
json=helpers.load_json_data(
45+
"testdata/apis/media_upload_v2/upload_chunk_init_resp.json"
46+
),
47+
)
48+
49+
resp = api_with_user.upload_media_chunked_init_v2(
50+
total_bytes=1000000,
51+
media_type="video/mp4",
52+
media_category="tweet_video",
53+
additional_owners=["123456789"],
54+
)
55+
assert resp.data.id == "1912103767639719936"
56+
57+
resp_json = api_with_user.upload_media_chunked_init_v2(
58+
total_bytes=1000000,
59+
media_type="video/mp4",
60+
return_json=True,
61+
)
62+
assert resp_json["data"]["id"] == "1912103767639719936"
63+
64+
65+
@responses.activate
66+
def test_upload_media_chunked_append_v2(api_with_user, helpers):
67+
media_id = "1912090619981471744"
68+
69+
responses.add(
70+
responses.POST,
71+
url="https://api.twitter.com/2/media/upload",
72+
)
73+
74+
with open("testdata/apis/media_upload/x-logo.png", "rb") as media:
75+
segment_index = 0
76+
while True:
77+
chunk = media.read(1 * 1024 * 1024)
78+
if not chunk:
79+
break
80+
status = api_with_user.upload_media_chunked_append_v2(
81+
media_id=media_id,
82+
media=media,
83+
segment_index=segment_index,
84+
)
85+
assert status
86+
87+
segment_index += 1
88+
89+
responses.add(
90+
responses.POST,
91+
url="https://api.twitter.com/2/media/upload",
92+
status=401,
93+
json={"errors": [{"code": 32, "message": "Could not authenticate you."}]},
94+
)
95+
with pytest.raises(PyTwitterError):
96+
api_with_user.upload_media_chunked_append_v2(
97+
media_id=media_id,
98+
media=b"",
99+
segment_index=1,
100+
)
101+
102+
103+
@responses.activate
104+
def test_upload_media_chunked_finalize_v2(api_with_user, helpers):
105+
media_id = "1912090619981471744"
106+
107+
responses.add(
108+
responses.POST,
109+
url="https://api.twitter.com/2/media/upload",
110+
json=helpers.load_json_data(
111+
"testdata/apis/media_upload_v2/upload_chunk_finalize_resp.json"
112+
),
113+
)
114+
115+
resp = api_with_user.upload_media_chunked_finalize_v2(
116+
media_id=media_id,
117+
)
118+
assert resp.data.id == media_id
119+
120+
resp_json = api_with_user.upload_media_chunked_finalize_v2(
121+
media_id=media_id,
122+
return_json=True,
123+
)
124+
assert resp_json["data"]["id"] == media_id
125+
126+
127+
@responses.activate
128+
def test_upload_media_chunked_status_v2(api_with_user, helpers):
129+
media_id = "1912090619981471744"
130+
131+
responses.add(
132+
responses.GET,
133+
url="https://api.twitter.com/2/media/upload",
134+
json=helpers.load_json_data(
135+
"testdata/apis/media_upload_v2/upload_chunk_status_resp.json"
136+
),
137+
)
138+
139+
resp = api_with_user.upload_media_chunked_status_v2(
140+
media_id=media_id,
141+
)
142+
assert resp.data.processing_info.state == "succeeded"
143+
144+
resp_json = api_with_user.upload_media_chunked_status_v2(
145+
media_id=media_id,
146+
return_json=True,
147+
)
148+
assert resp_json["data"]["processing_info"]["state"] == "succeeded"

0 commit comments

Comments
 (0)