marqetive-lib 0.1.10__py3-none-any.whl → 0.1.12__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.
@@ -52,7 +52,7 @@ MAX_VIDEO_SIZE = 512 * 1024 * 1024 # 512MB for videos
52
52
  DEFAULT_REQUEST_TIMEOUT = 120.0 # 2 minutes
53
53
 
54
54
  # Twitter API v2 media upload endpoints
55
- MEDIA_UPLOAD_BASE_URL = "https://upload.x.com/1.1/media"
55
+ MEDIA_UPLOAD_BASE_URL = "https://api.x.com/2/media/upload"
56
56
 
57
57
 
58
58
  class MediaCategory(str, Enum):
@@ -342,9 +342,9 @@ class TwitterMediaManager:
342
342
  async with aiofiles.open(file_path, "rb") as f:
343
343
  file_data = await f.read()
344
344
 
345
- # Prepare form data
345
+ # Prepare multipart form data
346
346
  files = {"media": (os.path.basename(file_path), file_data)}
347
- data = {}
347
+ data: dict[str, str] = {}
348
348
 
349
349
  if media_category:
350
350
  data["media_category"] = media_category.value
@@ -362,22 +362,24 @@ class TwitterMediaManager:
362
362
  total_bytes=file_size,
363
363
  )
364
364
 
365
- # Upload
365
+ # Upload - v2 API uses base URL directly
366
366
  response = await self.client.post(
367
- f"{self.base_url}/upload.json",
367
+ self.base_url,
368
368
  files=files,
369
369
  data=data,
370
370
  )
371
371
  response.raise_for_status()
372
372
  result_data = response.json()
373
+ logger.debug(f"Simple upload response: {result_data}")
373
374
 
374
- # Parse result
375
- media_id = str(result_data["media_id"])
375
+ # Parse result - v2 API returns {"data": {"id": "..."}}
376
+ media_data = result_data["data"]
377
+ media_id = str(media_data["id"])
376
378
  result = MediaUploadResult(
377
379
  media_id=media_id,
378
- media_key=result_data.get("media_key"),
379
- size=result_data.get("size"),
380
- expires_after_secs=result_data.get("expires_after_secs"),
380
+ media_key=media_data.get("media_key"),
381
+ size=media_data.get("size"),
382
+ expires_after_secs=media_data.get("expires_after_secs"),
381
383
  )
382
384
 
383
385
  # Notify completion
@@ -544,23 +546,27 @@ class TwitterMediaManager:
544
546
 
545
547
  @retry_async(config=STANDARD_BACKOFF)
546
548
  async def _do_init() -> str:
547
- params = {
548
- "command": "INIT",
549
+ # v2 API uses JSON body for INIT
550
+ json_data: dict[str, Any] = {
549
551
  "total_bytes": total_bytes,
550
552
  "media_type": media_type,
551
553
  "media_category": media_category.value,
554
+ "shared": False,
552
555
  }
553
556
 
554
557
  if additional_owners:
555
- params["additional_owners"] = ",".join(additional_owners)
558
+ json_data["additional_owners"] = ",".join(additional_owners)
556
559
 
560
+ logger.debug(f"INIT request: {json_data}")
557
561
  response = await self.client.post(
558
- f"{self.base_url}/upload.json",
559
- params=params,
562
+ f"{self.base_url}/initialize",
563
+ json=json_data,
560
564
  )
561
565
  response.raise_for_status()
562
566
  result = response.json()
563
- return str(result["media_id"])
567
+ logger.debug(f"INIT response: {result}")
568
+ # v2 API returns {"data": {"id": "..."}}
569
+ return str(result["data"]["id"])
564
570
 
565
571
  return await _do_init()
566
572
 
@@ -582,19 +588,18 @@ class TwitterMediaManager:
582
588
 
583
589
  @retry_async(config=STANDARD_BACKOFF)
584
590
  async def _do_append() -> None:
591
+ # v2 API uses /{media_id}/append endpoint with form data
585
592
  files = {"media": (filename, chunk_data)}
586
- params = {
587
- "command": "APPEND",
588
- "media_id": media_id,
589
- "segment_index": segment_index,
590
- }
593
+ data = {"segment_index": str(segment_index)}
591
594
 
592
595
  response = await self.client.post(
593
- f"{self.base_url}/upload.json",
594
- params=params,
596
+ f"{self.base_url}/{media_id}/append",
595
597
  files=files,
598
+ data=data,
596
599
  )
600
+ logger.debug(f"APPEND response status: {response.status_code}")
597
601
  response.raise_for_status()
602
+ # APPEND returns empty body on success
598
603
 
599
604
  await _do_append()
600
605
 
@@ -610,24 +615,23 @@ class TwitterMediaManager:
610
615
 
611
616
  @retry_async(config=STANDARD_BACKOFF)
612
617
  async def _do_finalize() -> MediaUploadResult:
613
- params = {
614
- "command": "FINALIZE",
615
- "media_id": media_id,
616
- }
617
-
618
+ # v2 API uses /{media_id}/finalize endpoint
619
+ logger.debug(f"Finalizing chunked upload for media_id: {media_id}")
618
620
  response = await self.client.post(
619
- f"{self.base_url}/upload.json",
620
- params=params,
621
+ f"{self.base_url}/{media_id}/finalize",
621
622
  )
622
623
  response.raise_for_status()
623
624
  result = response.json()
625
+ logger.debug(f"FINALIZE response: {result}")
624
626
 
627
+ # v2 API returns {"data": {...}}
628
+ media_data = result["data"]
625
629
  return MediaUploadResult(
626
- media_id=str(result["media_id"]),
627
- media_key=result.get("media_key"),
628
- size=result.get("size"),
629
- expires_after_secs=result.get("expires_after_secs"),
630
- processing_info=result.get("processing_info"),
630
+ media_id=str(media_data["id"]),
631
+ media_key=media_data.get("media_key"),
632
+ size=media_data.get("size"),
633
+ expires_after_secs=media_data.get("expires_after_secs"),
634
+ processing_info=media_data.get("processing_info"),
631
635
  )
632
636
 
633
637
  return await _do_finalize()
@@ -644,24 +648,28 @@ class TwitterMediaManager:
644
648
 
645
649
  @retry_async(config=STANDARD_BACKOFF)
646
650
  async def _do_status() -> MediaUploadResult:
651
+ # v2 API uses GET with media_id and command params
647
652
  params = {
648
- "command": "STATUS",
649
653
  "media_id": media_id,
654
+ "command": "STATUS",
650
655
  }
651
656
 
652
657
  response = await self.client.get(
653
- f"{self.base_url}/upload.json",
658
+ self.base_url,
654
659
  params=params,
655
660
  )
656
661
  response.raise_for_status()
657
662
  result = response.json()
663
+ logger.debug(f"STATUS response: {result}")
658
664
 
665
+ # v2 API returns {"data": {...}}
666
+ media_data = result["data"]
659
667
  return MediaUploadResult(
660
- media_id=str(result["media_id"]),
661
- media_key=result.get("media_key"),
662
- size=result.get("size"),
663
- expires_after_secs=result.get("expires_after_secs"),
664
- processing_info=result.get("processing_info"),
668
+ media_id=str(media_data["id"]),
669
+ media_key=media_data.get("media_key"),
670
+ size=media_data.get("size"),
671
+ expires_after_secs=media_data.get("expires_after_secs"),
672
+ processing_info=media_data.get("processing_info"),
665
673
  )
666
674
 
667
675
  return await _do_status()
@@ -684,7 +692,7 @@ class TwitterMediaManager:
684
692
 
685
693
  try:
686
694
  response = await self.client.post(
687
- f"{self.base_url}/metadata/create.json",
695
+ f"{self.base_url}/metadata/create",
688
696
  json={
689
697
  "media_id": media_id,
690
698
  "alt_text": {"text": alt_text},
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: marqetive-lib
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: Modern Python utilities for web APIs
5
5
  Keywords: api,utilities,web,http,marqetive
6
6
  Requires-Python: >=3.12
@@ -24,7 +24,7 @@ marqetive/platforms/tiktok/models.py,sha256=WWdjuFqhTIR8SnHkz-8UaNc5Mm2PrGomwQ3W
24
24
  marqetive/platforms/twitter/__init__.py,sha256=dvcgVT-v-JOtjSz-OUvxGrn_43OI6w_ep42Wx_nHTSM,217
25
25
  marqetive/platforms/twitter/client.py,sha256=08jV2hQVmGOpnG3C05u7bCqL7KapWn7bSsG0wbN_t5M,23270
26
26
  marqetive/platforms/twitter/exceptions.py,sha256=eZ-dJKOXH_-bAMg29zWKbEqMFud29piEJ5IWfC9wFts,8926
27
- marqetive/platforms/twitter/media.py,sha256=9j7JQpdlOhkMfQkDH0dLpp6HmlYkeB6SvNosRx5Oab8,27152
27
+ marqetive/platforms/twitter/media.py,sha256=KpPxnLCas8NhnsEvaXSJZ7To4wW4FY0YqQvUkJIIr7g,28010
28
28
  marqetive/platforms/twitter/models.py,sha256=yPQlx40SlNmz7YGasXUqdx7rEDEgrQ64aYovlPKo6oc,2126
29
29
  marqetive/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  marqetive/utils/__init__.py,sha256=bSrNajbxYBSKQayrPviLz8JeGjplnyK8y_NGDtgb7yQ,977
@@ -34,6 +34,6 @@ marqetive/utils/media.py,sha256=O1rISYdaP3CuuPxso7kqvxWXNfe2jjioNkaBc4cpwkY,1466
34
34
  marqetive/utils/oauth.py,sha256=1SkYCE6dcyPvcDqbjRFSSBcKTwLJy8u3jAANPdftVmo,13108
35
35
  marqetive/utils/retry.py,sha256=lAniJLMNWp9XsHrvU0XBNifpNEjfde4MGfd5hlFTPfA,7636
36
36
  marqetive/utils/token_validator.py,sha256=dNvDeHs2Du5UyMMH2ZOW6ydR7OwOEKA4c9e-rG0f9-0,6698
37
- marqetive_lib-0.1.10.dist-info/METADATA,sha256=oFcLMxRxsYYnE1CQDOh2LZ0OCY3GY_n5FNgDFJ9h9_8,7876
38
- marqetive_lib-0.1.10.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
39
- marqetive_lib-0.1.10.dist-info/RECORD,,
37
+ marqetive_lib-0.1.12.dist-info/METADATA,sha256=UfSKVNuQnGu17NTqgGUKv8ttCClMOCQMen3shK_GgxY,7876
38
+ marqetive_lib-0.1.12.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
39
+ marqetive_lib-0.1.12.dist-info/RECORD,,