173 lines
6.7 KiB
Python
173 lines
6.7 KiB
Python
"""
|
|
Bilibili uploader using biliup library
|
|
"""
|
|
import json
|
|
import asyncio
|
|
from pathlib import Path
|
|
from typing import Optional, List, Dict, Any
|
|
from datetime import datetime
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
try:
|
|
from biliup.plugins.bili_webup import BiliBili, Data
|
|
BILIUP_AVAILABLE = True
|
|
except ImportError:
|
|
BILIUP_AVAILABLE = False
|
|
|
|
from loguru import logger
|
|
from .base_uploader import BaseUploader
|
|
|
|
# Thread pool for running sync biliup code
|
|
_executor = ThreadPoolExecutor(max_workers=2)
|
|
|
|
|
|
class BilibiliUploader(BaseUploader):
|
|
"""Bilibili video uploader using biliup library"""
|
|
|
|
def __init__(
|
|
self,
|
|
title: str,
|
|
file_path: str,
|
|
tags: List[str],
|
|
publish_date: Optional[datetime] = None,
|
|
account_file: Optional[str] = None,
|
|
description: str = "",
|
|
tid: int = 122, # 分区ID: 122=国内原创
|
|
copyright: int = 1 # 1=原创, 2=转载
|
|
):
|
|
"""
|
|
Initialize Bilibili uploader
|
|
|
|
Args:
|
|
tid: Bilibili category ID (default: 122 for 国内原创)
|
|
copyright: 1 for original, 2 for repost
|
|
"""
|
|
super().__init__(title, file_path, tags, publish_date, account_file, description)
|
|
self.tid = tid
|
|
self.copyright = copyright
|
|
|
|
if not BILIUP_AVAILABLE:
|
|
raise ImportError(
|
|
"biliup library not installed. Please run: pip install biliup"
|
|
)
|
|
|
|
async def main(self) -> Dict[str, Any]:
|
|
"""
|
|
Upload video to Bilibili
|
|
|
|
Returns:
|
|
dict: Upload result
|
|
"""
|
|
# Run sync upload in thread pool to avoid asyncio.run() conflict
|
|
loop = asyncio.get_event_loop()
|
|
return await loop.run_in_executor(_executor, self._upload_sync)
|
|
|
|
def _upload_sync(self) -> Dict[str, Any]:
|
|
"""Synchronous upload logic (runs in thread pool)"""
|
|
try:
|
|
# 1. Load cookie data
|
|
if not self.account_file or not Path(self.account_file).exists():
|
|
logger.error(f"[B站] Cookie 文件不存在: {self.account_file}")
|
|
return {
|
|
"success": False,
|
|
"message": "Cookie 文件不存在,请先登录",
|
|
"url": None
|
|
}
|
|
|
|
with open(self.account_file, 'r', encoding='utf-8') as f:
|
|
cookie_data = json.load(f)
|
|
|
|
# Convert simple cookie format to biliup format if needed
|
|
if 'cookie_info' not in cookie_data and 'SESSDATA' in cookie_data:
|
|
# Transform to biliup expected format
|
|
cookie_data = {
|
|
'cookie_info': {
|
|
'cookies': [
|
|
{'name': k, 'value': v} for k, v in cookie_data.items()
|
|
]
|
|
},
|
|
'token_info': {
|
|
'access_token': cookie_data.get('access_token', ''),
|
|
'refresh_token': cookie_data.get('refresh_token', '')
|
|
}
|
|
}
|
|
logger.info("[B站] Cookie格式已转换")
|
|
|
|
# 2. Prepare video data
|
|
data = Data()
|
|
data.copyright = self.copyright
|
|
data.title = self.title
|
|
data.desc = self.description or f"标签: {', '.join(self.tags)}"
|
|
data.tid = self.tid
|
|
data.set_tag(self.tags)
|
|
data.dtime = self._get_timestamp(self.publish_date)
|
|
|
|
logger.info(f"[B站] 开始上传: {self.file_path.name}")
|
|
logger.info(f"[B站] 标题: {self.title}")
|
|
logger.info(f"[B站] 定时发布: {'是' if data.dtime > 0 else '否'}")
|
|
|
|
# 3. Upload video
|
|
with BiliBili(data) as bili:
|
|
# Login with cookies
|
|
bili.login_by_cookies(cookie_data)
|
|
bili.access_token = cookie_data.get('access_token', '')
|
|
|
|
# Upload file (3 threads, auto line selection)
|
|
video_part = bili.upload_file(
|
|
str(self.file_path),
|
|
lines='AUTO',
|
|
tasks=3
|
|
)
|
|
video_part['title'] = self.title
|
|
data.append(video_part)
|
|
|
|
# Submit
|
|
ret = bili.submit()
|
|
|
|
# Debug: log full response
|
|
logger.debug(f"[B站] API响应: {ret}")
|
|
|
|
if ret.get('code') == 0:
|
|
# Try multiple keys for bvid (API may vary)
|
|
bvid = ret.get('data', {}).get('bvid') or ret.get('bvid', '')
|
|
aid = ret.get('data', {}).get('aid') or ret.get('aid', '')
|
|
|
|
if bvid:
|
|
logger.success(f"[B站] 上传成功: {bvid}")
|
|
return {
|
|
"success": True,
|
|
"message": "上传成功" if data.dtime == 0 else "已设置定时发布",
|
|
"url": f"https://www.bilibili.com/video/{bvid}"
|
|
}
|
|
elif aid:
|
|
logger.success(f"[B站] 上传成功: av{aid}")
|
|
return {
|
|
"success": True,
|
|
"message": "上传成功" if data.dtime == 0 else "已设置定时发布",
|
|
"url": f"https://www.bilibili.com/video/av{aid}"
|
|
}
|
|
else:
|
|
# No bvid/aid but code=0, still consider success
|
|
logger.warning(f"[B站] 上传返回code=0但无bvid/aid: {ret}")
|
|
return {
|
|
"success": True,
|
|
"message": "上传成功(无法获取视频链接)",
|
|
"url": None
|
|
}
|
|
else:
|
|
error_msg = ret.get('message', '未知错误')
|
|
logger.error(f"[B站] 上传失败: {error_msg} (完整响应: {ret})")
|
|
return {
|
|
"success": False,
|
|
"message": f"上传失败: {error_msg}",
|
|
"url": None
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.exception(f"[B站] 上传异常: {e}")
|
|
return {
|
|
"success": False,
|
|
"message": f"上传异常: {str(e)}",
|
|
"url": None
|
|
}
|