upload-post 2.13.0__tar.gz → 2.14.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: upload-post
3
- Version: 2.13.0
3
+ Version: 2.14.0
4
4
  Summary: Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, Discord, Telegram, and X (Twitter)
5
5
  Home-page: https://www.upload-post.com/
6
6
  Author: Upload-Post
@@ -191,6 +191,21 @@ client.edit_scheduled(
191
191
  timezone="America/New_York",
192
192
  )
193
193
 
194
+ # Fix the text in place (only the platforms listed change)
195
+ client.edit_scheduled(
196
+ "job-id",
197
+ platform_content={"instagram": {"caption": "New Instagram caption"}},
198
+ )
199
+
200
+ # Change any other option by its API form name (None removes it), schedule the
201
+ # video natively on YouTube, or replace the Reels cover
202
+ client.edit_scheduled(
203
+ "job-id",
204
+ fields={"privacyStatus": "unlisted", "disable_comment": True},
205
+ youtube_publish_at="2026-11-01T09:00:00Z",
206
+ cover_image_base64="data:image/jpeg;base64,...", # or remove_cover=True
207
+ )
208
+
194
209
  # Cancel a scheduled post
195
210
  client.cancel_scheduled("job-id")
196
211
  ```
@@ -153,6 +153,21 @@ client.edit_scheduled(
153
153
  timezone="America/New_York",
154
154
  )
155
155
 
156
+ # Fix the text in place (only the platforms listed change)
157
+ client.edit_scheduled(
158
+ "job-id",
159
+ platform_content={"instagram": {"caption": "New Instagram caption"}},
160
+ )
161
+
162
+ # Change any other option by its API form name (None removes it), schedule the
163
+ # video natively on YouTube, or replace the Reels cover
164
+ client.edit_scheduled(
165
+ "job-id",
166
+ fields={"privacyStatus": "unlisted", "disable_comment": True},
167
+ youtube_publish_at="2026-11-01T09:00:00Z",
168
+ cover_image_base64="data:image/jpeg;base64,...", # or remove_cover=True
169
+ )
170
+
156
171
  # Cancel a scheduled post
157
172
  client.cancel_scheduled("job-id")
158
173
  ```
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="upload-post",
8
- version="2.13.0",
8
+ version="2.14.0",
9
9
  author="Upload-Post",
10
10
  author_email="hi@img2html.com",
11
11
  description="Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, Discord, Telegram, and X (Twitter)",
@@ -0,0 +1,68 @@
1
+ """edit_scheduled must forward every editable part of the PATCH body.
2
+
3
+ The SDK builds the body from fixed arguments, so anything it does not map is
4
+ unreachable. No network: the session's PATCH is patched.
5
+ """
6
+
7
+ import unittest
8
+ from unittest.mock import MagicMock, patch
9
+
10
+ from upload_post import UploadPostClient
11
+
12
+
13
+ class TestEditScheduled(unittest.TestCase):
14
+ def setUp(self):
15
+ self.client = UploadPostClient("k")
16
+ response = MagicMock()
17
+ response.status_code = 200
18
+ response.json.return_value = {"success": True}
19
+ response.raise_for_status.return_value = None
20
+ self.patch = patch.object(self.client.session, "patch", return_value=response).start()
21
+ self.addCleanup(patch.stopall)
22
+
23
+ def body(self):
24
+ return self.patch.call_args.kwargs["json"]
25
+
26
+ def test_positional_date_and_timezone_still_work(self):
27
+ self.client.edit_scheduled("job1", "2026-11-01T09:00:00Z", "Europe/Madrid")
28
+ self.assertTrue(self.patch.call_args.args[0].endswith("/uploadposts/schedule/job1"))
29
+ self.assertEqual(self.body(), {"scheduled_date": "2026-11-01T09:00:00Z", "timezone": "Europe/Madrid"})
30
+
31
+ def test_text_and_platform_content(self):
32
+ pc = {"instagram": {"caption": "ig"}, "youtube": {"title": "yt"}}
33
+ self.client.edit_scheduled("job1", title="T", caption="C", platform_content=pc)
34
+ self.assertEqual(self.body(), {"title": "T", "caption": "C", "platform_content": pc})
35
+
36
+ def test_description_is_alias_and_caption_wins(self):
37
+ self.client.edit_scheduled("job1", description="D")
38
+ self.assertEqual(self.body(), {"description": "D"})
39
+ self.client.edit_scheduled("job1", caption="C", description="D")
40
+ self.assertEqual(self.body(), {"caption": "C"})
41
+
42
+ def test_fields_and_youtube_publish_at(self):
43
+ self.client.edit_scheduled(
44
+ "job1",
45
+ fields={"privacyStatus": "unlisted", "disable_comment": True, "cover_url": None,
46
+ "youtube_publish_at": "old"},
47
+ youtube_publish_at="2026-11-01T09:00:00Z",
48
+ )
49
+ self.assertEqual(self.body(), {"fields": {
50
+ "privacyStatus": "unlisted", "disable_comment": True, "cover_url": None,
51
+ "youtube_publish_at": "2026-11-01T09:00:00Z",
52
+ }})
53
+
54
+ def test_cover_and_its_removal(self):
55
+ self.client.edit_scheduled("job1", cover_image_base64="AAAA", cover_filename="c.png",
56
+ cover_content_type="image/png")
57
+ self.assertEqual(self.body(), {"cover_image_base64": "AAAA", "cover_filename": "c.png",
58
+ "cover_content_type": "image/png"})
59
+ self.client.edit_scheduled("job1", remove_cover=True, cover_image_base64="AAAA")
60
+ self.assertEqual(self.body(), {"remove_cover": True})
61
+
62
+ def test_empty_fields_are_not_sent(self):
63
+ self.client.edit_scheduled("job1", "2026-11-01T09:00:00Z", fields={})
64
+ self.assertEqual(self.body(), {"scheduled_date": "2026-11-01T09:00:00Z"})
65
+
66
+
67
+ if __name__ == "__main__":
68
+ unittest.main()
@@ -38,7 +38,6 @@ class TestVersions(unittest.TestCase):
38
38
  match = re.search(r'version="([^"]*)"', text)
39
39
  self.assertIsNotNone(match)
40
40
  self.assertEqual(match.group(1), __version__)
41
- self.assertEqual(__version__, "2.13.0")
42
41
 
43
42
 
44
43
  class TestTikTokDraftAliases(unittest.TestCase):
@@ -15,7 +15,7 @@ Example:
15
15
  ... )
16
16
  """
17
17
 
18
- __version__ = "2.13.0"
18
+ __version__ = "2.14.0"
19
19
 
20
20
  from .api_client import UploadPostClient, UploadPostError
21
21
 
@@ -1843,24 +1843,78 @@ class UploadPostClient:
1843
1843
  self,
1844
1844
  job_id: str,
1845
1845
  scheduled_date: Optional[str] = None,
1846
- timezone: Optional[str] = None
1846
+ timezone: Optional[str] = None,
1847
+ *,
1848
+ title: Optional[str] = None,
1849
+ caption: Optional[str] = None,
1850
+ description: Optional[str] = None,
1851
+ platform_content: Optional[Dict[str, Dict[str, str]]] = None,
1852
+ fields: Optional[Dict[str, Any]] = None,
1853
+ youtube_publish_at: Optional[str] = None,
1854
+ cover_image_base64: Optional[str] = None,
1855
+ cover_filename: Optional[str] = None,
1856
+ cover_content_type: Optional[str] = None,
1857
+ remove_cover: bool = False,
1847
1858
  ) -> Dict:
1848
1859
  """
1849
- Edit a scheduled post.
1860
+ Edit a scheduled post (``PATCH /api/uploadposts/schedule/<job_id>``).
1861
+
1862
+ Every argument is optional; only what you pass is changed. The media,
1863
+ profile and platforms of a scheduled post cannot be edited.
1850
1864
 
1851
1865
  Args:
1852
1866
  job_id: Scheduled job ID.
1853
- scheduled_date: New scheduled date (ISO 8601).
1854
- timezone: New timezone.
1867
+ scheduled_date: New scheduled date (ISO 8601), future and within 1 year.
1868
+ timezone: IANA timezone ``scheduled_date`` is read in (default UTC).
1869
+ title: New title for every platform of the post.
1870
+ caption: New caption/description for every platform of the post.
1871
+ description: Alias of ``caption`` (``caption`` wins if both are given).
1872
+ platform_content: Per-platform text, e.g.
1873
+ ``{"instagram": {"caption": "..."}, "youtube": {"title": "..."}}``.
1874
+ Only the platforms listed are touched.
1875
+ fields: Any other editable upload option, keyed by its API form name
1876
+ (``privacyStatus``, ``tags``, ``disable_comment``,
1877
+ ``instagram_first_comment``, ``gbp_cta_url``...). ``None`` as a
1878
+ value removes that option; unknown or internal keys are ignored.
1879
+ youtube_publish_at: Native YouTube scheduling (RFC3339), sent as
1880
+ ``fields["youtube_publish_at"]``; wins over the same key in
1881
+ ``fields``. To remove it pass ``fields={"youtube_publish_at": None}``.
1882
+ cover_image_base64: New cover image (Instagram / Facebook Reels) as
1883
+ base64 or a data URL.
1884
+ cover_filename: File name of the new cover (default ``cover.jpg``).
1885
+ cover_content_type: MIME type of the new cover (default ``image/jpeg``).
1886
+ remove_cover: Remove the custom cover (ignores ``cover_image_base64``).
1855
1887
 
1856
1888
  Returns:
1857
- Edit result.
1889
+ Edit result (``success``, ``job_id``, and the new ``scheduled_date``,
1890
+ ``title``, ``caption``, ``platform_content`` when they changed).
1858
1891
  """
1859
- body = {}
1892
+ body: Dict[str, Any] = {}
1860
1893
  if scheduled_date:
1861
1894
  body["scheduled_date"] = scheduled_date
1862
1895
  if timezone:
1863
1896
  body["timezone"] = timezone
1897
+ if title is not None:
1898
+ body["title"] = title
1899
+ if caption is not None:
1900
+ body["caption"] = caption
1901
+ elif description is not None:
1902
+ body["description"] = description
1903
+ if platform_content:
1904
+ body["platform_content"] = platform_content
1905
+ edited_fields = dict(fields or {})
1906
+ if youtube_publish_at is not None:
1907
+ edited_fields["youtube_publish_at"] = youtube_publish_at
1908
+ if edited_fields:
1909
+ body["fields"] = edited_fields
1910
+ if remove_cover:
1911
+ body["remove_cover"] = True
1912
+ elif cover_image_base64:
1913
+ body["cover_image_base64"] = cover_image_base64
1914
+ if cover_filename:
1915
+ body["cover_filename"] = cover_filename
1916
+ if cover_content_type:
1917
+ body["cover_content_type"] = cover_content_type
1864
1918
  return self._request(f"/uploadposts/schedule/{job_id}", "PATCH", json_data=body)
1865
1919
 
1866
1920
  # ==================== User Management ====================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: upload-post
3
- Version: 2.13.0
3
+ Version: 2.14.0
4
4
  Summary: Cross-platform social media upload for TikTok, Instagram, YouTube, LinkedIn, Facebook, Pinterest, Threads, Reddit, Bluesky, Discord, Telegram, and X (Twitter)
5
5
  Home-page: https://www.upload-post.com/
6
6
  Author: Upload-Post
@@ -191,6 +191,21 @@ client.edit_scheduled(
191
191
  timezone="America/New_York",
192
192
  )
193
193
 
194
+ # Fix the text in place (only the platforms listed change)
195
+ client.edit_scheduled(
196
+ "job-id",
197
+ platform_content={"instagram": {"caption": "New Instagram caption"}},
198
+ )
199
+
200
+ # Change any other option by its API form name (None removes it), schedule the
201
+ # video natively on YouTube, or replace the Reels cover
202
+ client.edit_scheduled(
203
+ "job-id",
204
+ fields={"privacyStatus": "unlisted", "disable_comment": True},
205
+ youtube_publish_at="2026-11-01T09:00:00Z",
206
+ cover_image_base64="data:image/jpeg;base64,...", # or remove_cover=True
207
+ )
208
+
194
209
  # Cancel a scheduled post
195
210
  client.cancel_scheduled("job-id")
196
211
  ```
@@ -1,6 +1,7 @@
1
1
  README.md
2
2
  setup.py
3
3
  tests/test_comments_audience.py
4
+ tests/test_edit_scheduled.py
4
5
  tests/test_parity_fields.py
5
6
  upload_post/__init__.py
6
7
  upload_post/api_client.py
File without changes