marqetive-lib 0.1.10__tar.gz → 0.1.12__tar.gz

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 (39) hide show
  1. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/PKG-INFO +1 -1
  2. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/pyproject.toml +1 -1
  3. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/twitter/media.py +51 -43
  4. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/README.md +0 -0
  5. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/__init__.py +0 -0
  6. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/core/__init__.py +0 -0
  7. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/core/base.py +0 -0
  8. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/core/client.py +0 -0
  9. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/core/exceptions.py +0 -0
  10. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/core/models.py +0 -0
  11. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/factory.py +0 -0
  12. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/__init__.py +0 -0
  13. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/instagram/__init__.py +0 -0
  14. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/instagram/client.py +0 -0
  15. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/instagram/exceptions.py +0 -0
  16. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/instagram/media.py +0 -0
  17. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/instagram/models.py +0 -0
  18. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/linkedin/__init__.py +0 -0
  19. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/linkedin/client.py +0 -0
  20. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/linkedin/exceptions.py +0 -0
  21. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/linkedin/media.py +0 -0
  22. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/linkedin/models.py +0 -0
  23. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/tiktok/__init__.py +0 -0
  24. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/tiktok/client.py +0 -0
  25. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/tiktok/exceptions.py +0 -0
  26. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/tiktok/media.py +0 -0
  27. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/tiktok/models.py +0 -0
  28. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/twitter/__init__.py +0 -0
  29. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/twitter/client.py +0 -0
  30. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/twitter/exceptions.py +0 -0
  31. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/platforms/twitter/models.py +0 -0
  32. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/py.typed +0 -0
  33. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/utils/__init__.py +0 -0
  34. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/utils/file_handlers.py +0 -0
  35. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/utils/helpers.py +0 -0
  36. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/utils/media.py +0 -0
  37. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/utils/oauth.py +0 -0
  38. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/utils/retry.py +0 -0
  39. {marqetive_lib-0.1.10 → marqetive_lib-0.1.12}/src/marqetive/utils/token_validator.py +0 -0
@@ -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
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [project]
6
6
  name = "marqetive-lib"
7
- version = "0.1.10"
7
+ version = "0.1.12"
8
8
  description = "Modern Python utilities for web APIs"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -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},
File without changes