feat(backend): Enable Ayrshare YouTube support (#10505)

## Summary
- Enabled the YouTube posting block that was previously disabled
- The block provides comprehensive YouTube-specific posting options
including titles, visibility settings, thumbnails, playlists, tags, and
more

## Changes 🏗️
- Removed `disabled=True` from YouTube posting block to enable
functionality
- Added full YouTube API integration with all supported options:
  - Video title and description
  - Visibility settings (private/public/unlisted)
  - Thumbnail support
  - Playlist management
  - Video tags and categories
  - YouTube Shorts support
  - Subtitle/caption support
  - Country-based targeting
  - Synthetic media disclosure

## Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Verified YouTube block is now available in the block list



https://github.com/user-attachments/assets/d4459f15-fe57-47bf-8459-f06f1af45ad6

<img width="374" height="593" alt="Screenshot 2025-07-31 at 11 26 29"
src="https://github.com/user-attachments/assets/4dcf30dd-439c-4a44-b56a-640832d6c550"
/>
This commit is contained in:
Swifty
2025-07-31 11:29:15 +02:00
committed by GitHub
parent 903a3b80b4
commit 68974dc6da
2 changed files with 38 additions and 34 deletions

View File

@@ -1,3 +1,4 @@
from enum import Enum
from typing import Any
from backend.integrations.ayrshare import PostIds, PostResponse, SocialPlatform
@@ -14,6 +15,12 @@ from backend.sdk import (
from ._util import BaseAyrshareInput, create_ayrshare_client
class YouTubeVisibility(str, Enum):
PRIVATE = "private"
PUBLIC = "public"
UNLISTED = "unlisted"
class PostToYouTubeBlock(Block):
"""Block for posting to YouTube with YouTube-specific options."""
@@ -23,7 +30,6 @@ class PostToYouTubeBlock(Block):
# Override post field to include YouTube-specific information
post: str = SchemaField(
description="Video description (max 5,000 chars, empty string allowed). Cannot contain < or > characters.",
default="",
advanced=False,
)
@@ -37,55 +43,54 @@ class PostToYouTubeBlock(Block):
# YouTube-specific required options
title: str = SchemaField(
description="Video title (max 100 chars, required). Cannot contain < or > characters.",
default="",
advanced=False,
)
# YouTube-specific optional options
visibility: str = SchemaField(
description="Video visibility: 'private' (default), 'public', or 'unlisted'",
default="private",
advanced=True,
visibility: YouTubeVisibility = SchemaField(
description="Video visibility: 'private' (default), 'public' , or 'unlisted'",
default=YouTubeVisibility.PRIVATE,
advanced=False,
)
thumbnail: str = SchemaField(
thumbnail: str | None = SchemaField(
description="Thumbnail URL (JPEG/PNG under 2MB, must end in .png/.jpg/.jpeg). Requires phone verification.",
default="",
default=None,
advanced=True,
)
playlist_id: str = SchemaField(
playlist_id: str | None = SchemaField(
description="Playlist ID to add video (user must own playlist)",
default="",
default=None,
advanced=True,
)
tags: list[str] = SchemaField(
tags: list[str] | None = SchemaField(
description="Video tags (min 2 chars each, max 500 chars total)",
default_factory=list,
default=None,
advanced=True,
)
made_for_kids: bool = SchemaField(
description="Self-declared kids content", default=False, advanced=True
made_for_kids: bool | None = SchemaField(
description="Self-declared kids content", default=None, advanced=True
)
is_shorts: bool = SchemaField(
is_shorts: bool | None = SchemaField(
description="Post as YouTube Short (max 3 minutes, adds #shorts)",
default=False,
default=None,
advanced=True,
)
notify_subscribers: bool = SchemaField(
description="Send notification to subscribers", default=True, advanced=True
notify_subscribers: bool | None = SchemaField(
description="Send notification to subscribers", default=None, advanced=True
)
category_id: int = SchemaField(
category_id: int | None = SchemaField(
description="Video category ID (e.g., 24 = Entertainment)",
default=0,
default=None,
advanced=True,
)
contains_synthetic_media: bool = SchemaField(
contains_synthetic_media: bool | None = SchemaField(
description="Disclose realistic AI/synthetic content",
default=False,
default=None,
advanced=True,
)
publish_at: str = SchemaField(
publish_at: str | None = SchemaField(
description="UTC publish time (YouTube controlled, format: 2022-10-08T21:18:36Z)",
default="",
default=None,
advanced=True,
)
# YouTube targeting options (flattened from YouTubeTargeting object)
@@ -99,19 +104,19 @@ class PostToYouTubeBlock(Block):
default=None,
advanced=True,
)
subtitle_url: str = SchemaField(
subtitle_url: str | None = SchemaField(
description="URL to SRT or SBV subtitle file (must be HTTPS and end in .srt/.sbv, under 100MB)",
default="",
default=None,
advanced=True,
)
subtitle_language: str = SchemaField(
subtitle_language: str | None = SchemaField(
description="Language code for subtitles (default: 'en')",
default="en",
default=None,
advanced=True,
)
subtitle_name: str = SchemaField(
subtitle_name: str | None = SchemaField(
description="Name of caption track (max 150 chars, default: 'English')",
default="English",
default=None,
advanced=True,
)
@@ -121,7 +126,6 @@ class PostToYouTubeBlock(Block):
def __init__(self):
super().__init__(
disabled=True,
id="0082d712-ff1b-4c3d-8a8d-6c7721883b83",
description="Post to YouTube using Ayrshare",
categories={BlockCategory.SOCIAL},
@@ -219,7 +223,7 @@ class PostToYouTubeBlock(Block):
yield "error", "YouTube subtitle URL must end in .srt or .sbv"
return
if len(input_data.subtitle_name) > 150:
if input_data.subtitle_name and len(input_data.subtitle_name) > 150:
yield "error", f"YouTube subtitle name exceeds 150 character limit ({len(input_data.subtitle_name)} characters)"
return
@@ -258,7 +262,7 @@ class PostToYouTubeBlock(Block):
if not input_data.notify_subscribers:
youtube_options["notifySubscribers"] = False
if input_data.category_id > 0:
if input_data.category_id and input_data.category_id > 0:
youtube_options["categoryId"] = input_data.category_id
if input_data.contains_synthetic_media:

View File

@@ -624,7 +624,7 @@ async def get_ayrshare_sso_url(
SocialPlatform.TWITTER,
SocialPlatform.LINKEDIN,
# SocialPlatform.INSTAGRAM,
# SocialPlatform.YOUTUBE,
SocialPlatform.YOUTUBE,
# SocialPlatform.REDDIT,
# SocialPlatform.TELEGRAM,
# SocialPlatform.GOOGLE_MY_BUSINESS,