135 lines
3.8 KiB
Python
135 lines
3.8 KiB
Python
# 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",
|
|
}
|