# Copyright (C) 2025 AIDC-AI # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Publishing service for multi-platform video distribution. Supports: - Format conversion + export (Douyin/Kuaishou) - API-based upload (Bilibili/YouTube) """ from abc import ABC, abstractmethod from dataclasses import dataclass, field from enum import Enum from typing import Optional, List, Dict, Any from datetime import datetime class Platform(Enum): """Supported publishing platforms""" EXPORT = "export" # Format conversion only DOUYIN = "douyin" # 抖音 (via export or CDP) KUAISHOU = "kuaishou" # 快手 (via export or CDP) BILIBILI = "bilibili" # B站 (API) YOUTUBE = "youtube" # YouTube (API) class PublishStatus(Enum): """Publishing task status""" PENDING = "pending" CONVERTING = "converting" UPLOADING = "uploading" PROCESSING = "processing" PUBLISHED = "published" FAILED = "failed" @dataclass class VideoMetadata: """Video metadata for publishing""" title: str description: str = "" tags: List[str] = field(default_factory=list) category: Optional[str] = None cover_path: Optional[str] = None privacy: str = "public" # public, private, unlisted # Platform-specific options platform_options: Dict[str, Any] = field(default_factory=dict) @dataclass class PublishResult: """Result of a publishing operation""" success: bool platform: Platform status: PublishStatus # On success video_url: Optional[str] = None platform_video_id: Optional[str] = None # On failure error_message: Optional[str] = None # Export result export_path: Optional[str] = None # Timestamps started_at: Optional[datetime] = None completed_at: Optional[datetime] = None @dataclass class PublishTask: """A publishing task for background processing""" id: str video_path: str platform: Platform metadata: VideoMetadata status: PublishStatus = PublishStatus.PENDING result: Optional[PublishResult] = None created_at: datetime = field(default_factory=datetime.now) updated_at: Optional[datetime] = None class Publisher(ABC): """Abstract base class for platform publishers""" platform: Platform @abstractmethod async def publish( self, video_path: str, metadata: VideoMetadata, progress_callback: Optional[callable] = None ) -> PublishResult: """ Publish a video to the platform. Args: video_path: Path to the video file metadata: Video metadata (title, description, tags, etc.) progress_callback: Optional callback for progress updates Returns: PublishResult with success/failure details """ pass @abstractmethod async def validate_credentials(self) -> bool: """Check if platform credentials are valid""" pass def get_platform_requirements(self) -> Dict[str, Any]: """Get platform-specific requirements (dimensions, file size, etc.)""" return { "max_file_size_mb": 128, "max_duration_seconds": 900, # 15 minutes "supported_formats": ["mp4", "webm"], "recommended_resolution": (1080, 1920), # Portrait 9:16 "recommended_codec": "h264", }