upload-post 2.12.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.
- {upload_post-2.12.0 → upload_post-2.14.0}/PKG-INFO +134 -13
- {upload_post-2.12.0 → upload_post-2.14.0}/README.md +133 -12
- {upload_post-2.12.0 → upload_post-2.14.0}/setup.py +1 -1
- upload_post-2.14.0/tests/test_edit_scheduled.py +68 -0
- upload_post-2.14.0/tests/test_parity_fields.py +291 -0
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post/__init__.py +1 -1
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post/api_client.py +540 -77
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post.egg-info/PKG-INFO +134 -13
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post.egg-info/SOURCES.txt +2 -0
- {upload_post-2.12.0 → upload_post-2.14.0}/setup.cfg +0 -0
- {upload_post-2.12.0 → upload_post-2.14.0}/tests/test_comments_audience.py +0 -0
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post/cli.py +0 -0
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post.egg-info/dependency_links.txt +0 -0
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post.egg-info/requires.txt +0 -0
- {upload_post-2.12.0 → upload_post-2.14.0}/upload_post.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: upload-post
|
|
3
|
-
Version: 2.
|
|
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
|
```
|
|
@@ -481,7 +496,7 @@ TikTok account; check it before offering a feature.
|
|
|
481
496
|
| `location` | `tiktok_location_id` / `tiktok_location_name`, plus `get_tiktok_locations()` |
|
|
482
497
|
| `cover_image` | `tiktok_cover_image_url` |
|
|
483
498
|
| `cover_timestamp` | `cover_timestamp` |
|
|
484
|
-
| `draft` | `tiktok_upload_to_draft` |
|
|
499
|
+
| `draft` | `tiktok_upload_to_draft` or `post_mode="MEDIA_UPLOAD"` (same draft) |
|
|
485
500
|
| `video_privacy` | `privacy_level` on video |
|
|
486
501
|
| `photo_privacy` | `privacy_level` on photo posts |
|
|
487
502
|
| `profile_analytics` | `get_audience()` and `get_suggestions(type="hashtags")` with `platform="tiktok"` |
|
|
@@ -528,7 +543,7 @@ client.upload_video(
|
|
|
528
543
|
|
|
529
544
|
tiktok_cover_image_url="https://example.com/cover.jpg",
|
|
530
545
|
tiktok_is_ai_generated=False,
|
|
531
|
-
tiktok_upload_to_draft=False,
|
|
546
|
+
tiktok_upload_to_draft=False, # same draft as post_mode="MEDIA_UPLOAD"
|
|
532
547
|
)
|
|
533
548
|
```
|
|
534
549
|
|
|
@@ -544,10 +559,13 @@ client.upload_video(
|
|
|
544
559
|
| `tiktok_location_id` | str | `location` | Video + photos. `location_id` from `get_tiktok_locations()` |
|
|
545
560
|
| `tiktok_location_name` | str | `location` | Required whenever `tiktok_location_id` is set |
|
|
546
561
|
| `tiktok_cover_image_url` | str | `cover_image` | Video only. Custom cover image URL |
|
|
547
|
-
| `tiktok_is_ai_generated` | bool | — | Video + photos. AI-generated content disclosure |
|
|
548
|
-
| `tiktok_upload_to_draft` | bool | `draft` | Video
|
|
562
|
+
| `tiktok_is_ai_generated` | bool | — | Video + photos. AI-generated content disclosure (aliases: `is_ai_generated`) |
|
|
563
|
+
| `tiktok_upload_to_draft` | bool | `draft` | Video + photos. Same draft as `post_mode="MEDIA_UPLOAD"` (aliases: `upload_to_draft`, `is_draft`). TikTok ignores the rest of the post settings |
|
|
564
|
+
| `tiktok_is_ads_only` | bool | — | Video. Only show in ads (alias: `is_ads_only`) |
|
|
565
|
+
| `tiktok_tto_invite_link` | str | — | Video. TikTok One invite link; needs branded content (alias: `tto_invite_link`) |
|
|
549
566
|
| `photo_cover_index` | int | — | Cover photo index for photo posts (0-based) |
|
|
550
567
|
| `privacy_level` | str | `photo_privacy` / `video_privacy` | Accepted on video and photo posts alike; which values the account may use is decided by TikTok, see the note below |
|
|
568
|
+
| `post_mode` | str | `draft` | `DIRECT_POST` or `MEDIA_UPLOAD`. `MEDIA_UPLOAD` is the same draft as `tiktok_upload_to_draft=True` |
|
|
551
569
|
|
|
552
570
|
## Platform-Specific Options
|
|
553
571
|
|
|
@@ -557,9 +575,11 @@ client.upload_video(
|
|
|
557
575
|
- `disable_stitch` - Disable stitch
|
|
558
576
|
- `cover_timestamp` - Timestamp in ms for cover
|
|
559
577
|
- `is_aigc` - AI-generated content flag
|
|
560
|
-
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD
|
|
578
|
+
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD. `MEDIA_UPLOAD` is the same draft as `tiktok_upload_to_draft=True`
|
|
561
579
|
- `brand_content_toggle` - Branded content toggle
|
|
562
580
|
- `brand_organic_toggle` - Brand organic toggle
|
|
581
|
+
- `tiktok_is_ads_only` - Only show the video in ads (alias: `is_ads_only`)
|
|
582
|
+
- `tiktok_tto_invite_link` - TikTok One invite link; needs branded content (alias: `tto_invite_link`)
|
|
563
583
|
|
|
564
584
|
> **TikTok particularity:** `privacy_level` works on video and photo posts
|
|
565
585
|
> alike, but **TikTok decides per account which values are available**. A
|
|
@@ -578,17 +598,19 @@ for the music, location, cover and draft options.
|
|
|
578
598
|
|
|
579
599
|
### TikTok (Photos)
|
|
580
600
|
- `privacy_level` - PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY
|
|
581
|
-
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD
|
|
601
|
+
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD. `MEDIA_UPLOAD` is the same draft as `tiktok_upload_to_draft=True`
|
|
582
602
|
- `auto_add_music` - Auto add music
|
|
583
603
|
- `photo_cover_index` - Index of photo for cover (0-based)
|
|
584
604
|
- `disable_comment` - Disable comments
|
|
585
605
|
- `tiktok_music_id` - Commercial Music Library track id
|
|
586
606
|
- `tiktok_location_id` / `tiktok_location_name` - Location tag, both required together
|
|
587
607
|
- `tiktok_is_ai_generated` - AI-generated content disclosure
|
|
608
|
+
- `tiktok_upload_to_draft` - Same draft as `post_mode="MEDIA_UPLOAD"` (aliases: `upload_to_draft`, `is_draft`)
|
|
588
609
|
|
|
589
610
|
> TikTok's photo contract takes the music track id alone: `tiktok_music_volume`,
|
|
590
|
-
> `tiktok_music_start`, `tiktok_music_end`, `tiktok_original_sound_volume
|
|
591
|
-
> `tiktok_cover_image_url`
|
|
611
|
+
> `tiktok_music_start`, `tiktok_music_end`, `tiktok_original_sound_volume` and
|
|
612
|
+
> `tiktok_cover_image_url` are video-only. `post_mode="MEDIA_UPLOAD"` and
|
|
613
|
+
> `tiktok_upload_to_draft=True` are aliases of the same draft.
|
|
592
614
|
|
|
593
615
|
### Instagram
|
|
594
616
|
- `media_type` - REELS, STORIES, IMAGE
|
|
@@ -599,6 +621,7 @@ for the music, location, cover and draft options.
|
|
|
599
621
|
- `user_tags` - Comma-separated user tags
|
|
600
622
|
- `location_id` - Location ID
|
|
601
623
|
- `thumb_offset` - Thumbnail offset
|
|
624
|
+
- `instagram_alt_text` - Alt text (string, or JSON list / one per carousel item)
|
|
602
625
|
|
|
603
626
|
### YouTube
|
|
604
627
|
- `tags` - List or comma-separated tags
|
|
@@ -616,10 +639,19 @@ for the music, location, cover and draft options.
|
|
|
616
639
|
- `hasPaidProductPlacement` - Paid placement flag
|
|
617
640
|
- `recordingDate` - Recording date (ISO 8601)
|
|
618
641
|
- `youtube_playlist_id` - Playlist ID (single string, list, or comma-separated) to add the uploaded video to after publishing
|
|
642
|
+
- `youtube_notify_subscribers` - Notify subscribers (default `true`; send `false` to skip)
|
|
643
|
+
- `youtube_publish_at` - RFC3339 time; uploads as private and schedules on YouTube
|
|
619
644
|
|
|
620
645
|
### LinkedIn
|
|
621
|
-
- `visibility` - PUBLIC, CONNECTIONS, LOGGED_IN, CONTAINER
|
|
646
|
+
- `visibility` / `linkedin_visibility` - PUBLIC, CONNECTIONS, LOGGED_IN, CONTAINER
|
|
622
647
|
- `target_linkedin_page_id` - Page ID for organization posts
|
|
648
|
+
- `linkedin_alt_text` - Alt text per image (string, JSON list, or repeated)
|
|
649
|
+
- `linkedin_disable_reshare` - Disable reshare by others
|
|
650
|
+
- `linkedin_link_title` / `linkedin_link_description` / `linkedin_thumbnail_alt_text` - Article/link-share overrides
|
|
651
|
+
- `linkedin_target_geo_locations` / `linkedin_target_industries` / `linkedin_target_seniorities` / `linkedin_target_job_functions` / `linkedin_target_staff_count_ranges` / `linkedin_target_interface_locales` / `linkedin_target_degrees` / `linkedin_target_fields_of_study` / `linkedin_target_organizations` - Page targeting (needs `target_linkedin_page_id`)
|
|
652
|
+
- `linkedin_target_entities` - Raw targeting JSON instead of the fields above
|
|
653
|
+
- `linkedin_target_check_audience` - Reject if LinkedIn reports fewer than 300 followers in the segment
|
|
654
|
+
- `linkedin_subtitles` / `linkedin_subtitles_url` / `linkedin_subtitles_text` - English SRT captions on video
|
|
623
655
|
|
|
624
656
|
### Facebook
|
|
625
657
|
- `facebook_page_id` - Facebook Page ID (required)
|
|
@@ -627,13 +659,25 @@ for the music, location, cover and draft options.
|
|
|
627
659
|
- `facebook_media_type` - REELS, STORIES, or VIDEO (normal page video)
|
|
628
660
|
- `thumbnail_url` - Thumbnail URL for normal page videos (only when `facebook_media_type` is VIDEO)
|
|
629
661
|
- `facebook_link_url` - URL for text posts
|
|
662
|
+
- `facebook_alt_text` - Alt text per photo (string, `facebook_alt_text[]`, or JSON array)
|
|
663
|
+
- `facebook_place_id` - Place id on feed/photos/videos/reels
|
|
664
|
+
- `facebook_targeting` / `facebook_feed_targeting` - JSON audience objects
|
|
665
|
+
- `facebook_call_to_action` / `facebook_child_attachments` / `facebook_multi_share_end_card` - Link post CTA and carousel
|
|
666
|
+
- `facebook_is_ai_generated` - AI-generated flag on Reels
|
|
667
|
+
- `facebook_collaborators` - Page ids to invite on Reels
|
|
668
|
+
- `facebook_unpublished_content_type` - DRAFT / INLINE_CREATED / ADS_POST on page video (`SCHEDULED` is rejected; use `scheduled_date`)
|
|
669
|
+
- `facebook_no_story` / `facebook_secret` - Page video flags
|
|
630
670
|
|
|
631
671
|
### Pinterest
|
|
632
672
|
- `pinterest_board_id` - Board ID
|
|
633
673
|
- `pinterest_link` - Destination link
|
|
634
674
|
- `pinterest_alt_text` - Alt text for photos
|
|
635
675
|
- `pinterest_cover_image_url` - Cover image URL (video)
|
|
636
|
-
- `pinterest_cover_image_key_frame_time` - Key frame time in
|
|
676
|
+
- `pinterest_cover_image_key_frame_time` - Key frame time in seconds (values larger than the video duration are treated as milliseconds)
|
|
677
|
+
- `pinterest_board_section_id` - Board section id
|
|
678
|
+
- `pinterest_ai_disclosures` - `AI_MODIFIED` and/or `SYNTHETIC_PERFORMER`
|
|
679
|
+
- `pinterest_carousel_titles` / `pinterest_carousel_descriptions` / `pinterest_carousel_links` - Per-slide carousel fields (2–5 photos)
|
|
680
|
+
- `pinterest_carousel_index` - 0-based cover slide
|
|
637
681
|
|
|
638
682
|
### X (Twitter)
|
|
639
683
|
- `reply_settings` - everyone, following, mentionedUsers, subscribers, verified
|
|
@@ -649,15 +693,30 @@ for the music, location, cover and draft options.
|
|
|
649
693
|
- `card_uri` - Card URI for Twitter Cards
|
|
650
694
|
- `x_long_text_as_post` - Post long text as single post
|
|
651
695
|
- `x_thread_image_layout` - Comma-separated image layout for thread (e.g. "4,4" or "2,3,1"). Each value 1-4, total must equal image count. Auto-chunks into groups of 4 when >4 images.
|
|
696
|
+
- `x_alt_text` - Alt text (string, `x_alt_text[]`, or JSON array; ≤1000 chars)
|
|
697
|
+
- `x_subtitles` / `x_subtitles_url` / `x_subtitles_language` / `x_subtitles_name` - Video SRT captions
|
|
698
|
+
- `x_paid_partnership` - Paid partnership flag on the first post
|
|
699
|
+
- `x_article_title` / `x_article_body` / `x_article_content_state` / `x_article_draft` / `x_article_cover_media` - X Articles (text; Premium)
|
|
652
700
|
|
|
653
701
|
### Threads
|
|
654
|
-
- `threads_long_text_as_post` - Post long text as single post (vs thread)
|
|
655
|
-
- `threads_thread_media_layout` - Comma-separated list of how many media items to include in each Threads post (e.g. "5,5" or "3,4,3"). Each value
|
|
702
|
+
- `threads_long_text_as_post` - Post long text as single post (vs thread). Over 500 UTF-8 bytes this sends an official long post instead of truncating.
|
|
703
|
+
- `threads_thread_media_layout` - Comma-separated list of how many media items to include in each Threads post (e.g. "5,5" or "3,4,3"). Each value 0-20, total must equal media count. Auto-chunks into groups of 20 when >20 items.
|
|
656
704
|
- `threads_topic_tag` - Topic tag for the Threads post (1-50 characters, no periods or ampersands). One tag per post. Helps increase reach.
|
|
705
|
+
- `threads_reply_control` - Who can reply: `everyone`, `accounts_you_follow`, `mentioned_only`, `parent_post_author_only`, `followers_only`
|
|
706
|
+
- `threads_alt_text` - Alt text per image/video (`threads_alt_text[]`)
|
|
707
|
+
- `threads_reply_to_id` / `threads_quote_post_id` - Reply or quote
|
|
708
|
+
- `threads_link_attachment` - Force link preview (text)
|
|
709
|
+
- `threads_poll_options` - 2–4 poll options (text)
|
|
710
|
+
- `threads_auto_publish_text` - Skip the second publish call on text posts
|
|
657
711
|
|
|
658
712
|
### Reddit
|
|
713
|
+
Reddit uploads currently return `error_code=reddit_unavailable` until the Reddit app is restored.
|
|
714
|
+
|
|
659
715
|
- `subreddit` - Subreddit name (without r/)
|
|
660
716
|
- `flair_id` - Flair template ID
|
|
717
|
+
- `reddit_nsfw` / `reddit_spoiler` / `reddit_resubmit` / `reddit_send_replies` - Booleans
|
|
718
|
+
- `reddit_flair_text` - Custom text for editable flairs
|
|
719
|
+
- `reddit_gallery_captions` / `reddit_gallery_urls` - Per-image gallery caption and outbound URL
|
|
661
720
|
|
|
662
721
|
### Google Business
|
|
663
722
|
- `gbp_location_id` - Location, e.g. `"accounts/123/locations/456"` (list them with `get_google_business_locations`). Required when the account has more than one location; the API only auto-selects when exactly one exists.
|
|
@@ -668,6 +727,8 @@ for the music, location, cover and draft options.
|
|
|
668
727
|
- `gbp_cta_type` / `gbp_cta_url` - Call-to-action button
|
|
669
728
|
- `gbp_event_title` / `gbp_event_start_date` / `gbp_event_start_time` / `gbp_event_end_date` / `gbp_event_end_time` - Used with `gbp_topic_type="EVENT"`
|
|
670
729
|
- `gbp_offer_coupon` / `gbp_offer_redeem_url` / `gbp_offer_terms` - Used with `gbp_topic_type="OFFER"`
|
|
730
|
+
- `gbp_coupon_code` / `gbp_redeem_url` / `gbp_terms` - Aliases of the offer fields above (legacy names still win if both are sent)
|
|
731
|
+
- `gbp_language_code` - BCP-47 language (default `en`)
|
|
671
732
|
|
|
672
733
|
```python
|
|
673
734
|
locations = client.get_google_business_locations("my-profile")
|
|
@@ -683,6 +744,66 @@ client.upload_photos(
|
|
|
683
744
|
)
|
|
684
745
|
```
|
|
685
746
|
|
|
747
|
+
### Bluesky
|
|
748
|
+
- `bluesky_link_url` - External embed link preview (text)
|
|
749
|
+
- `bluesky_alt_text` - Alt text per image/video (JSON array, `||`-separated, or string)
|
|
750
|
+
- `bluesky_langs` - Comma list, max 3 BCP-47 codes
|
|
751
|
+
- `bluesky_labels` - `porn`, `sexual`, `nudity`, `graphic-media`
|
|
752
|
+
- `bluesky_gallery` - Publish up to 20 images as a gallery (`false` keeps the first 4)
|
|
753
|
+
- `bluesky_threadgate` / `bluesky_reply_settings` - Who can reply
|
|
754
|
+
- `bluesky_postgate` / `bluesky_quote_settings` - `disable_quotes` to block quotes
|
|
755
|
+
- `bluesky_quote_uri` / `bluesky_quote_id` / `bluesky_quote_url` - Quote another post
|
|
756
|
+
|
|
757
|
+
### Discord
|
|
758
|
+
- `discord_thread_id` / `discord_thread_name` / `discord_applied_tags` - Thread or forum post
|
|
759
|
+
- `discord_embeds` / `discord_poll` / `discord_allowed_mentions` - JSON
|
|
760
|
+
- `discord_username` / `discord_avatar_url` - Per-post webhook identity
|
|
761
|
+
- `discord_alt_text` / `discord_flags` / `discord_tts` / `discord_max_file_mb`
|
|
762
|
+
|
|
763
|
+
### Telegram
|
|
764
|
+
- `telegram_parse_mode` - `MarkdownV2` or `HTML`
|
|
765
|
+
- `telegram_message_thread_id` - Forum topic
|
|
766
|
+
- `telegram_disable_notification` / `telegram_protect_content` / `telegram_has_spoiler` / `telegram_pin`
|
|
767
|
+
- `telegram_link_preview` / `telegram_reply_markup` / `telegram_as_document`
|
|
768
|
+
- `telegram_caption_overflow` - `truncate` (default) or `split`
|
|
769
|
+
- `telegram_media_urls` - Comma-separated public URLs for a mixed album
|
|
770
|
+
|
|
771
|
+
### Mastodon
|
|
772
|
+
- `mastodon_visibility` / `mastodon_sensitive` / `mastodon_spoiler_text` / `mastodon_language`
|
|
773
|
+
- `mastodon_alt_text` - `|`-separated for albums
|
|
774
|
+
- `mastodon_poll_options` / `mastodon_poll_expires_in` / `mastodon_poll_multiple`
|
|
775
|
+
- `mastodon_scheduled_at` - ISO 8601, at least 5 minutes ahead
|
|
776
|
+
|
|
777
|
+
### WordPress
|
|
778
|
+
- `wordpress_status` / `wordpress_date` - `publish`, `draft`, `pending`, `future` (`date` required for `future`)
|
|
779
|
+
- `wordpress_categories` / `wordpress_tags` / `wordpress_excerpt` / `wordpress_slug`
|
|
780
|
+
- `wordpress_alt_text` / `wordpress_media_caption` / `wordpress_block_format`
|
|
781
|
+
|
|
782
|
+
### Lemmy
|
|
783
|
+
- `lemmy_url` / `lemmy_community` / `lemmy_nsfw` / `lemmy_language_id` / `lemmy_alt_text`
|
|
784
|
+
|
|
785
|
+
### Slack
|
|
786
|
+
- `slack_markdown` / `slack_mrkdwn` / `slack_blocks` / `slack_alt_text`
|
|
787
|
+
- `slack_first_comment_mode` - `separate` (default) or `inline`
|
|
788
|
+
|
|
789
|
+
### Nostr
|
|
790
|
+
- `nostr_kind` - `1` default, `20` picture, `22` short video, `34235` video, `30023` long-form
|
|
791
|
+
- `nostr_long_form` - Shorthand for kind 30023
|
|
792
|
+
|
|
793
|
+
### Dev.to
|
|
794
|
+
- `devto_tags` / `devto_canonical_url` / `devto_description` / `devto_main_image` / `devto_series` / `devto_published`
|
|
795
|
+
|
|
796
|
+
### Hashnode
|
|
797
|
+
- `hashnode_body` / `content` - Article body
|
|
798
|
+
- `hashnode_tags` / `hashnode_original_article_url` / `hashnode_subtitle` / `hashnode_cover_image_url` / `hashnode_draft`
|
|
799
|
+
|
|
800
|
+
### Whop
|
|
801
|
+
- `whop_body` / `whop_pinned` / `whop_is_mention` / `whop_paywall_amount` / `whop_paywall_currency` / `whop_attachment_ids`
|
|
802
|
+
|
|
803
|
+
### Listmonk
|
|
804
|
+
- `listmonk_content_type` - `richtext` (default), `markdown`, `html`, `plain`
|
|
805
|
+
- `listmonk_send_at` / `listmonk_lists` / `listmonk_template_id` / `listmonk_media_ids`
|
|
806
|
+
|
|
686
807
|
## Common Options
|
|
687
808
|
|
|
688
809
|
These options work across all upload methods:
|
|
@@ -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
|
```
|
|
@@ -443,7 +458,7 @@ TikTok account; check it before offering a feature.
|
|
|
443
458
|
| `location` | `tiktok_location_id` / `tiktok_location_name`, plus `get_tiktok_locations()` |
|
|
444
459
|
| `cover_image` | `tiktok_cover_image_url` |
|
|
445
460
|
| `cover_timestamp` | `cover_timestamp` |
|
|
446
|
-
| `draft` | `tiktok_upload_to_draft` |
|
|
461
|
+
| `draft` | `tiktok_upload_to_draft` or `post_mode="MEDIA_UPLOAD"` (same draft) |
|
|
447
462
|
| `video_privacy` | `privacy_level` on video |
|
|
448
463
|
| `photo_privacy` | `privacy_level` on photo posts |
|
|
449
464
|
| `profile_analytics` | `get_audience()` and `get_suggestions(type="hashtags")` with `platform="tiktok"` |
|
|
@@ -490,7 +505,7 @@ client.upload_video(
|
|
|
490
505
|
|
|
491
506
|
tiktok_cover_image_url="https://example.com/cover.jpg",
|
|
492
507
|
tiktok_is_ai_generated=False,
|
|
493
|
-
tiktok_upload_to_draft=False,
|
|
508
|
+
tiktok_upload_to_draft=False, # same draft as post_mode="MEDIA_UPLOAD"
|
|
494
509
|
)
|
|
495
510
|
```
|
|
496
511
|
|
|
@@ -506,10 +521,13 @@ client.upload_video(
|
|
|
506
521
|
| `tiktok_location_id` | str | `location` | Video + photos. `location_id` from `get_tiktok_locations()` |
|
|
507
522
|
| `tiktok_location_name` | str | `location` | Required whenever `tiktok_location_id` is set |
|
|
508
523
|
| `tiktok_cover_image_url` | str | `cover_image` | Video only. Custom cover image URL |
|
|
509
|
-
| `tiktok_is_ai_generated` | bool | — | Video + photos. AI-generated content disclosure |
|
|
510
|
-
| `tiktok_upload_to_draft` | bool | `draft` | Video
|
|
524
|
+
| `tiktok_is_ai_generated` | bool | — | Video + photos. AI-generated content disclosure (aliases: `is_ai_generated`) |
|
|
525
|
+
| `tiktok_upload_to_draft` | bool | `draft` | Video + photos. Same draft as `post_mode="MEDIA_UPLOAD"` (aliases: `upload_to_draft`, `is_draft`). TikTok ignores the rest of the post settings |
|
|
526
|
+
| `tiktok_is_ads_only` | bool | — | Video. Only show in ads (alias: `is_ads_only`) |
|
|
527
|
+
| `tiktok_tto_invite_link` | str | — | Video. TikTok One invite link; needs branded content (alias: `tto_invite_link`) |
|
|
511
528
|
| `photo_cover_index` | int | — | Cover photo index for photo posts (0-based) |
|
|
512
529
|
| `privacy_level` | str | `photo_privacy` / `video_privacy` | Accepted on video and photo posts alike; which values the account may use is decided by TikTok, see the note below |
|
|
530
|
+
| `post_mode` | str | `draft` | `DIRECT_POST` or `MEDIA_UPLOAD`. `MEDIA_UPLOAD` is the same draft as `tiktok_upload_to_draft=True` |
|
|
513
531
|
|
|
514
532
|
## Platform-Specific Options
|
|
515
533
|
|
|
@@ -519,9 +537,11 @@ client.upload_video(
|
|
|
519
537
|
- `disable_stitch` - Disable stitch
|
|
520
538
|
- `cover_timestamp` - Timestamp in ms for cover
|
|
521
539
|
- `is_aigc` - AI-generated content flag
|
|
522
|
-
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD
|
|
540
|
+
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD. `MEDIA_UPLOAD` is the same draft as `tiktok_upload_to_draft=True`
|
|
523
541
|
- `brand_content_toggle` - Branded content toggle
|
|
524
542
|
- `brand_organic_toggle` - Brand organic toggle
|
|
543
|
+
- `tiktok_is_ads_only` - Only show the video in ads (alias: `is_ads_only`)
|
|
544
|
+
- `tiktok_tto_invite_link` - TikTok One invite link; needs branded content (alias: `tto_invite_link`)
|
|
525
545
|
|
|
526
546
|
> **TikTok particularity:** `privacy_level` works on video and photo posts
|
|
527
547
|
> alike, but **TikTok decides per account which values are available**. A
|
|
@@ -540,17 +560,19 @@ for the music, location, cover and draft options.
|
|
|
540
560
|
|
|
541
561
|
### TikTok (Photos)
|
|
542
562
|
- `privacy_level` - PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY
|
|
543
|
-
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD
|
|
563
|
+
- `post_mode` - DIRECT_POST or MEDIA_UPLOAD. `MEDIA_UPLOAD` is the same draft as `tiktok_upload_to_draft=True`
|
|
544
564
|
- `auto_add_music` - Auto add music
|
|
545
565
|
- `photo_cover_index` - Index of photo for cover (0-based)
|
|
546
566
|
- `disable_comment` - Disable comments
|
|
547
567
|
- `tiktok_music_id` - Commercial Music Library track id
|
|
548
568
|
- `tiktok_location_id` / `tiktok_location_name` - Location tag, both required together
|
|
549
569
|
- `tiktok_is_ai_generated` - AI-generated content disclosure
|
|
570
|
+
- `tiktok_upload_to_draft` - Same draft as `post_mode="MEDIA_UPLOAD"` (aliases: `upload_to_draft`, `is_draft`)
|
|
550
571
|
|
|
551
572
|
> TikTok's photo contract takes the music track id alone: `tiktok_music_volume`,
|
|
552
|
-
> `tiktok_music_start`, `tiktok_music_end`, `tiktok_original_sound_volume
|
|
553
|
-
> `tiktok_cover_image_url`
|
|
573
|
+
> `tiktok_music_start`, `tiktok_music_end`, `tiktok_original_sound_volume` and
|
|
574
|
+
> `tiktok_cover_image_url` are video-only. `post_mode="MEDIA_UPLOAD"` and
|
|
575
|
+
> `tiktok_upload_to_draft=True` are aliases of the same draft.
|
|
554
576
|
|
|
555
577
|
### Instagram
|
|
556
578
|
- `media_type` - REELS, STORIES, IMAGE
|
|
@@ -561,6 +583,7 @@ for the music, location, cover and draft options.
|
|
|
561
583
|
- `user_tags` - Comma-separated user tags
|
|
562
584
|
- `location_id` - Location ID
|
|
563
585
|
- `thumb_offset` - Thumbnail offset
|
|
586
|
+
- `instagram_alt_text` - Alt text (string, or JSON list / one per carousel item)
|
|
564
587
|
|
|
565
588
|
### YouTube
|
|
566
589
|
- `tags` - List or comma-separated tags
|
|
@@ -578,10 +601,19 @@ for the music, location, cover and draft options.
|
|
|
578
601
|
- `hasPaidProductPlacement` - Paid placement flag
|
|
579
602
|
- `recordingDate` - Recording date (ISO 8601)
|
|
580
603
|
- `youtube_playlist_id` - Playlist ID (single string, list, or comma-separated) to add the uploaded video to after publishing
|
|
604
|
+
- `youtube_notify_subscribers` - Notify subscribers (default `true`; send `false` to skip)
|
|
605
|
+
- `youtube_publish_at` - RFC3339 time; uploads as private and schedules on YouTube
|
|
581
606
|
|
|
582
607
|
### LinkedIn
|
|
583
|
-
- `visibility` - PUBLIC, CONNECTIONS, LOGGED_IN, CONTAINER
|
|
608
|
+
- `visibility` / `linkedin_visibility` - PUBLIC, CONNECTIONS, LOGGED_IN, CONTAINER
|
|
584
609
|
- `target_linkedin_page_id` - Page ID for organization posts
|
|
610
|
+
- `linkedin_alt_text` - Alt text per image (string, JSON list, or repeated)
|
|
611
|
+
- `linkedin_disable_reshare` - Disable reshare by others
|
|
612
|
+
- `linkedin_link_title` / `linkedin_link_description` / `linkedin_thumbnail_alt_text` - Article/link-share overrides
|
|
613
|
+
- `linkedin_target_geo_locations` / `linkedin_target_industries` / `linkedin_target_seniorities` / `linkedin_target_job_functions` / `linkedin_target_staff_count_ranges` / `linkedin_target_interface_locales` / `linkedin_target_degrees` / `linkedin_target_fields_of_study` / `linkedin_target_organizations` - Page targeting (needs `target_linkedin_page_id`)
|
|
614
|
+
- `linkedin_target_entities` - Raw targeting JSON instead of the fields above
|
|
615
|
+
- `linkedin_target_check_audience` - Reject if LinkedIn reports fewer than 300 followers in the segment
|
|
616
|
+
- `linkedin_subtitles` / `linkedin_subtitles_url` / `linkedin_subtitles_text` - English SRT captions on video
|
|
585
617
|
|
|
586
618
|
### Facebook
|
|
587
619
|
- `facebook_page_id` - Facebook Page ID (required)
|
|
@@ -589,13 +621,25 @@ for the music, location, cover and draft options.
|
|
|
589
621
|
- `facebook_media_type` - REELS, STORIES, or VIDEO (normal page video)
|
|
590
622
|
- `thumbnail_url` - Thumbnail URL for normal page videos (only when `facebook_media_type` is VIDEO)
|
|
591
623
|
- `facebook_link_url` - URL for text posts
|
|
624
|
+
- `facebook_alt_text` - Alt text per photo (string, `facebook_alt_text[]`, or JSON array)
|
|
625
|
+
- `facebook_place_id` - Place id on feed/photos/videos/reels
|
|
626
|
+
- `facebook_targeting` / `facebook_feed_targeting` - JSON audience objects
|
|
627
|
+
- `facebook_call_to_action` / `facebook_child_attachments` / `facebook_multi_share_end_card` - Link post CTA and carousel
|
|
628
|
+
- `facebook_is_ai_generated` - AI-generated flag on Reels
|
|
629
|
+
- `facebook_collaborators` - Page ids to invite on Reels
|
|
630
|
+
- `facebook_unpublished_content_type` - DRAFT / INLINE_CREATED / ADS_POST on page video (`SCHEDULED` is rejected; use `scheduled_date`)
|
|
631
|
+
- `facebook_no_story` / `facebook_secret` - Page video flags
|
|
592
632
|
|
|
593
633
|
### Pinterest
|
|
594
634
|
- `pinterest_board_id` - Board ID
|
|
595
635
|
- `pinterest_link` - Destination link
|
|
596
636
|
- `pinterest_alt_text` - Alt text for photos
|
|
597
637
|
- `pinterest_cover_image_url` - Cover image URL (video)
|
|
598
|
-
- `pinterest_cover_image_key_frame_time` - Key frame time in
|
|
638
|
+
- `pinterest_cover_image_key_frame_time` - Key frame time in seconds (values larger than the video duration are treated as milliseconds)
|
|
639
|
+
- `pinterest_board_section_id` - Board section id
|
|
640
|
+
- `pinterest_ai_disclosures` - `AI_MODIFIED` and/or `SYNTHETIC_PERFORMER`
|
|
641
|
+
- `pinterest_carousel_titles` / `pinterest_carousel_descriptions` / `pinterest_carousel_links` - Per-slide carousel fields (2–5 photos)
|
|
642
|
+
- `pinterest_carousel_index` - 0-based cover slide
|
|
599
643
|
|
|
600
644
|
### X (Twitter)
|
|
601
645
|
- `reply_settings` - everyone, following, mentionedUsers, subscribers, verified
|
|
@@ -611,15 +655,30 @@ for the music, location, cover and draft options.
|
|
|
611
655
|
- `card_uri` - Card URI for Twitter Cards
|
|
612
656
|
- `x_long_text_as_post` - Post long text as single post
|
|
613
657
|
- `x_thread_image_layout` - Comma-separated image layout for thread (e.g. "4,4" or "2,3,1"). Each value 1-4, total must equal image count. Auto-chunks into groups of 4 when >4 images.
|
|
658
|
+
- `x_alt_text` - Alt text (string, `x_alt_text[]`, or JSON array; ≤1000 chars)
|
|
659
|
+
- `x_subtitles` / `x_subtitles_url` / `x_subtitles_language` / `x_subtitles_name` - Video SRT captions
|
|
660
|
+
- `x_paid_partnership` - Paid partnership flag on the first post
|
|
661
|
+
- `x_article_title` / `x_article_body` / `x_article_content_state` / `x_article_draft` / `x_article_cover_media` - X Articles (text; Premium)
|
|
614
662
|
|
|
615
663
|
### Threads
|
|
616
|
-
- `threads_long_text_as_post` - Post long text as single post (vs thread)
|
|
617
|
-
- `threads_thread_media_layout` - Comma-separated list of how many media items to include in each Threads post (e.g. "5,5" or "3,4,3"). Each value
|
|
664
|
+
- `threads_long_text_as_post` - Post long text as single post (vs thread). Over 500 UTF-8 bytes this sends an official long post instead of truncating.
|
|
665
|
+
- `threads_thread_media_layout` - Comma-separated list of how many media items to include in each Threads post (e.g. "5,5" or "3,4,3"). Each value 0-20, total must equal media count. Auto-chunks into groups of 20 when >20 items.
|
|
618
666
|
- `threads_topic_tag` - Topic tag for the Threads post (1-50 characters, no periods or ampersands). One tag per post. Helps increase reach.
|
|
667
|
+
- `threads_reply_control` - Who can reply: `everyone`, `accounts_you_follow`, `mentioned_only`, `parent_post_author_only`, `followers_only`
|
|
668
|
+
- `threads_alt_text` - Alt text per image/video (`threads_alt_text[]`)
|
|
669
|
+
- `threads_reply_to_id` / `threads_quote_post_id` - Reply or quote
|
|
670
|
+
- `threads_link_attachment` - Force link preview (text)
|
|
671
|
+
- `threads_poll_options` - 2–4 poll options (text)
|
|
672
|
+
- `threads_auto_publish_text` - Skip the second publish call on text posts
|
|
619
673
|
|
|
620
674
|
### Reddit
|
|
675
|
+
Reddit uploads currently return `error_code=reddit_unavailable` until the Reddit app is restored.
|
|
676
|
+
|
|
621
677
|
- `subreddit` - Subreddit name (without r/)
|
|
622
678
|
- `flair_id` - Flair template ID
|
|
679
|
+
- `reddit_nsfw` / `reddit_spoiler` / `reddit_resubmit` / `reddit_send_replies` - Booleans
|
|
680
|
+
- `reddit_flair_text` - Custom text for editable flairs
|
|
681
|
+
- `reddit_gallery_captions` / `reddit_gallery_urls` - Per-image gallery caption and outbound URL
|
|
623
682
|
|
|
624
683
|
### Google Business
|
|
625
684
|
- `gbp_location_id` - Location, e.g. `"accounts/123/locations/456"` (list them with `get_google_business_locations`). Required when the account has more than one location; the API only auto-selects when exactly one exists.
|
|
@@ -630,6 +689,8 @@ for the music, location, cover and draft options.
|
|
|
630
689
|
- `gbp_cta_type` / `gbp_cta_url` - Call-to-action button
|
|
631
690
|
- `gbp_event_title` / `gbp_event_start_date` / `gbp_event_start_time` / `gbp_event_end_date` / `gbp_event_end_time` - Used with `gbp_topic_type="EVENT"`
|
|
632
691
|
- `gbp_offer_coupon` / `gbp_offer_redeem_url` / `gbp_offer_terms` - Used with `gbp_topic_type="OFFER"`
|
|
692
|
+
- `gbp_coupon_code` / `gbp_redeem_url` / `gbp_terms` - Aliases of the offer fields above (legacy names still win if both are sent)
|
|
693
|
+
- `gbp_language_code` - BCP-47 language (default `en`)
|
|
633
694
|
|
|
634
695
|
```python
|
|
635
696
|
locations = client.get_google_business_locations("my-profile")
|
|
@@ -645,6 +706,66 @@ client.upload_photos(
|
|
|
645
706
|
)
|
|
646
707
|
```
|
|
647
708
|
|
|
709
|
+
### Bluesky
|
|
710
|
+
- `bluesky_link_url` - External embed link preview (text)
|
|
711
|
+
- `bluesky_alt_text` - Alt text per image/video (JSON array, `||`-separated, or string)
|
|
712
|
+
- `bluesky_langs` - Comma list, max 3 BCP-47 codes
|
|
713
|
+
- `bluesky_labels` - `porn`, `sexual`, `nudity`, `graphic-media`
|
|
714
|
+
- `bluesky_gallery` - Publish up to 20 images as a gallery (`false` keeps the first 4)
|
|
715
|
+
- `bluesky_threadgate` / `bluesky_reply_settings` - Who can reply
|
|
716
|
+
- `bluesky_postgate` / `bluesky_quote_settings` - `disable_quotes` to block quotes
|
|
717
|
+
- `bluesky_quote_uri` / `bluesky_quote_id` / `bluesky_quote_url` - Quote another post
|
|
718
|
+
|
|
719
|
+
### Discord
|
|
720
|
+
- `discord_thread_id` / `discord_thread_name` / `discord_applied_tags` - Thread or forum post
|
|
721
|
+
- `discord_embeds` / `discord_poll` / `discord_allowed_mentions` - JSON
|
|
722
|
+
- `discord_username` / `discord_avatar_url` - Per-post webhook identity
|
|
723
|
+
- `discord_alt_text` / `discord_flags` / `discord_tts` / `discord_max_file_mb`
|
|
724
|
+
|
|
725
|
+
### Telegram
|
|
726
|
+
- `telegram_parse_mode` - `MarkdownV2` or `HTML`
|
|
727
|
+
- `telegram_message_thread_id` - Forum topic
|
|
728
|
+
- `telegram_disable_notification` / `telegram_protect_content` / `telegram_has_spoiler` / `telegram_pin`
|
|
729
|
+
- `telegram_link_preview` / `telegram_reply_markup` / `telegram_as_document`
|
|
730
|
+
- `telegram_caption_overflow` - `truncate` (default) or `split`
|
|
731
|
+
- `telegram_media_urls` - Comma-separated public URLs for a mixed album
|
|
732
|
+
|
|
733
|
+
### Mastodon
|
|
734
|
+
- `mastodon_visibility` / `mastodon_sensitive` / `mastodon_spoiler_text` / `mastodon_language`
|
|
735
|
+
- `mastodon_alt_text` - `|`-separated for albums
|
|
736
|
+
- `mastodon_poll_options` / `mastodon_poll_expires_in` / `mastodon_poll_multiple`
|
|
737
|
+
- `mastodon_scheduled_at` - ISO 8601, at least 5 minutes ahead
|
|
738
|
+
|
|
739
|
+
### WordPress
|
|
740
|
+
- `wordpress_status` / `wordpress_date` - `publish`, `draft`, `pending`, `future` (`date` required for `future`)
|
|
741
|
+
- `wordpress_categories` / `wordpress_tags` / `wordpress_excerpt` / `wordpress_slug`
|
|
742
|
+
- `wordpress_alt_text` / `wordpress_media_caption` / `wordpress_block_format`
|
|
743
|
+
|
|
744
|
+
### Lemmy
|
|
745
|
+
- `lemmy_url` / `lemmy_community` / `lemmy_nsfw` / `lemmy_language_id` / `lemmy_alt_text`
|
|
746
|
+
|
|
747
|
+
### Slack
|
|
748
|
+
- `slack_markdown` / `slack_mrkdwn` / `slack_blocks` / `slack_alt_text`
|
|
749
|
+
- `slack_first_comment_mode` - `separate` (default) or `inline`
|
|
750
|
+
|
|
751
|
+
### Nostr
|
|
752
|
+
- `nostr_kind` - `1` default, `20` picture, `22` short video, `34235` video, `30023` long-form
|
|
753
|
+
- `nostr_long_form` - Shorthand for kind 30023
|
|
754
|
+
|
|
755
|
+
### Dev.to
|
|
756
|
+
- `devto_tags` / `devto_canonical_url` / `devto_description` / `devto_main_image` / `devto_series` / `devto_published`
|
|
757
|
+
|
|
758
|
+
### Hashnode
|
|
759
|
+
- `hashnode_body` / `content` - Article body
|
|
760
|
+
- `hashnode_tags` / `hashnode_original_article_url` / `hashnode_subtitle` / `hashnode_cover_image_url` / `hashnode_draft`
|
|
761
|
+
|
|
762
|
+
### Whop
|
|
763
|
+
- `whop_body` / `whop_pinned` / `whop_is_mention` / `whop_paywall_amount` / `whop_paywall_currency` / `whop_attachment_ids`
|
|
764
|
+
|
|
765
|
+
### Listmonk
|
|
766
|
+
- `listmonk_content_type` - `richtext` (default), `markdown`, `html`, `plain`
|
|
767
|
+
- `listmonk_send_at` / `listmonk_lists` / `listmonk_template_id` / `listmonk_media_ids`
|
|
768
|
+
|
|
648
769
|
## Common Options
|
|
649
770
|
|
|
650
771
|
These options work across all upload methods:
|
|
@@ -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.
|
|
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()
|