2023-08-17 14:16:53 +00:00
|
|
|
|
import contextlib
|
|
|
|
|
import os
|
|
|
|
|
import time
|
|
|
|
|
from asyncio import create_subprocess_shell, subprocess, Lock
|
2023-08-18 02:15:23 +00:00
|
|
|
|
from io import BytesIO
|
|
|
|
|
from typing import Tuple, Dict, Union, Optional
|
2023-08-17 14:16:53 +00:00
|
|
|
|
|
|
|
|
|
import aiofiles
|
|
|
|
|
from bilibili_api import HEADERS
|
2023-08-18 10:18:12 +00:00
|
|
|
|
from bilibili_api.audio import Audio
|
2023-08-17 14:16:53 +00:00
|
|
|
|
from bilibili_api.video import Video, VideoDownloadURLDataDetecter, VideoQuality
|
2024-04-03 16:42:07 +00:00
|
|
|
|
from httpx import AsyncClient, Response, URL
|
2023-08-18 09:26:57 +00:00
|
|
|
|
from pyrogram.enums import ParseMode
|
2023-08-17 14:16:53 +00:00
|
|
|
|
from pyrogram.types import Message
|
|
|
|
|
|
|
|
|
|
from defs.request import cache_dir
|
2023-08-18 02:15:23 +00:00
|
|
|
|
from init import bot, logger, request
|
2023-08-18 14:13:02 +00:00
|
|
|
|
from models.models.bilifav import BiliFav
|
|
|
|
|
from models.services.bilifav import BiliFavAction
|
2023-08-17 14:16:53 +00:00
|
|
|
|
|
|
|
|
|
FFMPEG_PATH = "ffmpeg"
|
2023-08-18 01:55:15 +00:00
|
|
|
|
FFPROBE_PATH = "ffprobe"
|
2023-08-17 14:16:53 +00:00
|
|
|
|
LOCK = Lock()
|
|
|
|
|
EDIT_TEMP_SECONDS = 10.0
|
|
|
|
|
MESSAGE_MAP: Dict[int, float] = {}
|
|
|
|
|
UPLOAD_MESSAGE_MAP: Dict[int, int] = {}
|
2024-04-03 16:42:07 +00:00
|
|
|
|
CDN = [
|
|
|
|
|
"",
|
|
|
|
|
"upos-hz-mirrorakam.akamaized.net",
|
|
|
|
|
"upos-sz-mirroraliov.bilivideo.com",
|
|
|
|
|
"cn-hk-eq-01-09.bilivideo.com",
|
|
|
|
|
"cn-sccd-cu-01-08.bilivideo.com",
|
|
|
|
|
]
|
2023-08-17 14:16:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BilibiliDownloaderError(Exception):
|
|
|
|
|
"""Bilibili 下载器错误"""
|
|
|
|
|
|
|
|
|
|
MSG = "Bilibili 下载器错误"
|
|
|
|
|
|
|
|
|
|
def __init__(self, msg: str = None):
|
|
|
|
|
self.MSG = msg or self.MSG
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FileTooBig(BilibiliDownloaderError):
|
|
|
|
|
"""文件过大,超过2GB"""
|
|
|
|
|
|
|
|
|
|
MSG = "文件过大,超过2GB"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FileNoSize(BilibiliDownloaderError):
|
|
|
|
|
"""文件大小未知"""
|
|
|
|
|
|
|
|
|
|
MSG = "文件大小未知"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FFmpegError(BilibiliDownloaderError):
|
|
|
|
|
"""FFmpeg 转换失败"""
|
|
|
|
|
|
|
|
|
|
MSG = "FFmpeg 转换失败"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def should_edit(m: Message) -> bool:
|
|
|
|
|
if m.id in MESSAGE_MAP:
|
|
|
|
|
last_time = MESSAGE_MAP[m.id]
|
|
|
|
|
if last_time + EDIT_TEMP_SECONDS < time.time():
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def format_bytes(size: Union[int, float]) -> str:
|
|
|
|
|
"""格式化文件大小"""
|
|
|
|
|
power = 1024
|
|
|
|
|
n = 0
|
|
|
|
|
power_labels = {0: "", 1: "K", 2: "M", 3: "G", 4: "T"}
|
|
|
|
|
while size > power:
|
|
|
|
|
size /= power
|
|
|
|
|
n += 1
|
|
|
|
|
if n > 4:
|
|
|
|
|
n = 4
|
|
|
|
|
return f"{round(size, 2)} {power_labels[n]}B"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def format_seconds(seconds: Union[int, float]) -> str:
|
|
|
|
|
"""格式化秒数"""
|
|
|
|
|
m, s = divmod(seconds, 60)
|
|
|
|
|
h, m = divmod(m, 60)
|
|
|
|
|
s = round(s, 2)
|
|
|
|
|
text = ""
|
|
|
|
|
if h > 0:
|
|
|
|
|
text += f" {h} 小时"
|
|
|
|
|
if m > 0:
|
|
|
|
|
text += f" {m} 分钟"
|
|
|
|
|
if s > 0:
|
|
|
|
|
text += f" {s} 秒"
|
|
|
|
|
return text.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def safe_edit(m: Message, text: str):
|
|
|
|
|
try:
|
|
|
|
|
await m.edit_text(text)
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def fail_edit(m: Message, text: str):
|
|
|
|
|
try:
|
|
|
|
|
await m.edit_text(text)
|
|
|
|
|
except Exception:
|
|
|
|
|
with contextlib.suppress(Exception):
|
|
|
|
|
await m.reply(text, quote=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def execute(command: str) -> Tuple[str, int]:
|
|
|
|
|
process = await create_subprocess_shell(
|
|
|
|
|
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE
|
|
|
|
|
)
|
|
|
|
|
stdout, stderr = await process.communicate()
|
|
|
|
|
try:
|
|
|
|
|
result = str(stdout.decode().strip()) + str(stderr.decode().strip())
|
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
|
result = str(stdout.decode("gbk").strip()) + str(stderr.decode("gbk").strip())
|
|
|
|
|
return result, process.returncode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def safe_remove(path: str):
|
|
|
|
|
if os.path.exists(path):
|
|
|
|
|
os.remove(path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def message_edit(
|
2023-08-17 15:23:06 +00:00
|
|
|
|
length: int,
|
|
|
|
|
total_downloaded: int,
|
|
|
|
|
temp_downloaded: int,
|
|
|
|
|
chunk_time: float,
|
|
|
|
|
m: Message,
|
|
|
|
|
t: str,
|
2023-08-17 14:16:53 +00:00
|
|
|
|
):
|
|
|
|
|
speed = temp_downloaded / (chunk_time if chunk_time > 0 else 1)
|
|
|
|
|
text = (
|
|
|
|
|
f"{t}进度\n\n"
|
|
|
|
|
f"{format_bytes(total_downloaded)} / {format_bytes(length)} "
|
|
|
|
|
f"({round(total_downloaded / length * 100.0, 2)}%)\n\n"
|
2023-08-17 15:23:06 +00:00
|
|
|
|
f"传输区间速度:{format_bytes(speed)}/s\n"
|
2023-08-17 14:16:53 +00:00
|
|
|
|
f"预计剩余时间:{format_seconds((length - total_downloaded) / speed)}"
|
|
|
|
|
)
|
|
|
|
|
await safe_edit(m, text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def download_url(url: str, out: str, m: Message, start: str):
|
2024-04-03 16:42:07 +00:00
|
|
|
|
for i in CDN:
|
|
|
|
|
if i:
|
|
|
|
|
u = URL(url).host
|
|
|
|
|
url = url.replace(u, i)
|
|
|
|
|
async with AsyncClient(headers=HEADERS, timeout=60) as sess:
|
|
|
|
|
head = await sess.head(url)
|
|
|
|
|
if head.status_code != 200:
|
|
|
|
|
if i == CDN[-1]:
|
|
|
|
|
raise BilibiliDownloaderError("下载链接异常,请尝试重新下载")
|
|
|
|
|
logger.warning(f"下载链接异常,使用 CDN {i} 失败,尝试使用其他 CDN")
|
|
|
|
|
continue
|
|
|
|
|
async with sess.stream("GET", url) as resp:
|
|
|
|
|
logger.info(f"Downloading {start}")
|
|
|
|
|
resp: Response
|
|
|
|
|
length = resp.headers.get("content-length")
|
|
|
|
|
if not length:
|
|
|
|
|
raise FileNoSize
|
|
|
|
|
length = int(length)
|
|
|
|
|
if length > 1.9 * 1024 * 1024 * 1024:
|
|
|
|
|
raise FileTooBig
|
|
|
|
|
total_downloaded = 0
|
|
|
|
|
temp_downloaded = 0
|
|
|
|
|
MESSAGE_MAP[m.id] = time.time() - EDIT_TEMP_SECONDS
|
|
|
|
|
async with aiofiles.open(out, "wb") as f:
|
|
|
|
|
async for chunk in resp.aiter_bytes(1024):
|
|
|
|
|
if not chunk:
|
|
|
|
|
break
|
|
|
|
|
chunk_len = len(chunk)
|
|
|
|
|
total_downloaded += chunk_len
|
|
|
|
|
temp_downloaded += chunk_len
|
|
|
|
|
async with LOCK:
|
|
|
|
|
_should_edit = should_edit(m)
|
|
|
|
|
if _should_edit:
|
|
|
|
|
now = time.time()
|
|
|
|
|
chunk_time = now - MESSAGE_MAP[m.id]
|
|
|
|
|
MESSAGE_MAP[m.id] = now
|
2023-08-17 14:16:53 +00:00
|
|
|
|
if _should_edit:
|
2024-04-03 16:42:07 +00:00
|
|
|
|
bot.loop.create_task(
|
|
|
|
|
message_edit(
|
|
|
|
|
length,
|
|
|
|
|
total_downloaded,
|
|
|
|
|
temp_downloaded,
|
|
|
|
|
chunk_time,
|
|
|
|
|
m,
|
|
|
|
|
f"{start}下载",
|
|
|
|
|
)
|
2023-08-17 14:16:53 +00:00
|
|
|
|
)
|
2024-04-03 16:42:07 +00:00
|
|
|
|
temp_downloaded = 0
|
|
|
|
|
await f.write(chunk)
|
2023-08-17 14:16:53 +00:00
|
|
|
|
|
|
|
|
|
|
2023-08-18 01:55:15 +00:00
|
|
|
|
async def get_video_duration(path: str) -> float:
|
|
|
|
|
"""获取视频时长"""
|
|
|
|
|
video_duration, code = await execute(
|
|
|
|
|
f"{FFPROBE_PATH} -v error -select_streams v:0 -show_entries format=duration "
|
|
|
|
|
f"-of default=noprint_wrappers=1:nokey=1 {path}"
|
|
|
|
|
)
|
|
|
|
|
if code != 0:
|
|
|
|
|
raise FFmpegError("视频时长获取失败")
|
2023-08-18 02:00:30 +00:00
|
|
|
|
return round(float(video_duration.split("[")[0].strip()), 2)
|
2023-08-18 01:55:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_video_height_width(path: str) -> Tuple[int, int]:
|
|
|
|
|
"""获取视频高度和宽度"""
|
|
|
|
|
result, code = await execute(
|
|
|
|
|
f"{FFPROBE_PATH} -v error -select_streams v:0 -show_entries stream=width,height "
|
|
|
|
|
f"-of csv=s=x:p=0 {path}"
|
|
|
|
|
)
|
|
|
|
|
if code != 0:
|
|
|
|
|
raise FFmpegError("视频宽高度获取失败")
|
2023-08-18 02:00:30 +00:00
|
|
|
|
video_width, video_height = result.split("[")[0].split("x")
|
2023-08-18 01:55:15 +00:00
|
|
|
|
return int(video_height), int(video_width)
|
|
|
|
|
|
|
|
|
|
|
2023-08-18 09:26:57 +00:00
|
|
|
|
async def take_screenshot(info: Dict) -> Optional[BytesIO]:
|
2023-08-18 02:15:23 +00:00
|
|
|
|
"""获取视频封面"""
|
|
|
|
|
try:
|
|
|
|
|
pic_get = (await request.get(info["pic"])).content
|
|
|
|
|
pic = BytesIO(pic_get)
|
|
|
|
|
pic.name = "screenshot.jpg"
|
|
|
|
|
return pic
|
|
|
|
|
except Exception:
|
|
|
|
|
logger.exception("获取视频封面失败")
|
|
|
|
|
return None
|
2023-08-18 01:55:15 +00:00
|
|
|
|
|
|
|
|
|
|
2023-08-18 14:13:02 +00:00
|
|
|
|
def gen_audio_caption(a: Audio, info: Dict) -> str:
|
|
|
|
|
intro = info.get("intro", "")
|
|
|
|
|
if intro:
|
|
|
|
|
text = f"<b>{info['title']}</b>\n\n{intro}\n\nhttps://www.bilibili.com/audio/au{a.get_auid()}"
|
|
|
|
|
if len(text) > 800:
|
|
|
|
|
text = f"<b>{info['title']}</b>\n\n简介过长,无法显示\n\nhttps://www.bilibili.com/audio/au{a.get_auid()}"
|
|
|
|
|
else:
|
|
|
|
|
text = (
|
|
|
|
|
f"<b>{info['title']}</b>\n\nhttps://www.bilibili.com/audio/au{a.get_auid()}"
|
|
|
|
|
)
|
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
|
2023-08-18 13:28:16 +00:00
|
|
|
|
async def audio_download(
|
|
|
|
|
a: Audio, m: Message, push_id: int = None
|
|
|
|
|
) -> Optional[Message]:
|
2023-08-18 10:18:12 +00:00
|
|
|
|
try:
|
|
|
|
|
info = await a.get_info()
|
|
|
|
|
download_url_data = await a.get_download_url()
|
|
|
|
|
async with AsyncClient(headers=HEADERS, timeout=60) as client:
|
|
|
|
|
r = await client.get(download_url_data["cdns"][0])
|
|
|
|
|
media = BytesIO(r.content)
|
2023-08-18 10:22:07 +00:00
|
|
|
|
ext = download_url_data["cdns"][0].split("?")[0].split(".")[-1]
|
|
|
|
|
if ext:
|
|
|
|
|
media.name = f"{info['title']}.{ext}"
|
|
|
|
|
else:
|
|
|
|
|
media.name = f"{info['title']}.mp3"
|
2023-08-18 10:18:12 +00:00
|
|
|
|
media.seek(0)
|
|
|
|
|
if info.get("cover"):
|
|
|
|
|
r_ = await client.get(info.get("cover"))
|
|
|
|
|
thumb = BytesIO(r_.content)
|
|
|
|
|
thumb.seek(0)
|
|
|
|
|
else:
|
|
|
|
|
thumb = None
|
2023-08-18 14:13:02 +00:00
|
|
|
|
caption = gen_audio_caption(a, info)
|
2023-08-18 13:28:16 +00:00
|
|
|
|
msg = await bot.send_audio(
|
|
|
|
|
chat_id=push_id or m.chat.id,
|
2023-08-18 10:18:12 +00:00
|
|
|
|
audio=media,
|
2023-08-18 14:13:02 +00:00
|
|
|
|
caption=caption,
|
2023-08-18 10:18:12 +00:00
|
|
|
|
parse_mode=ParseMode.HTML,
|
2023-08-18 13:28:16 +00:00
|
|
|
|
reply_to_message_id=m.reply_to_message_id if not push_id else None,
|
2023-08-18 10:18:12 +00:00
|
|
|
|
thumb=thumb,
|
2023-08-18 10:25:33 +00:00
|
|
|
|
title=info.get("title"),
|
2023-08-18 10:18:12 +00:00
|
|
|
|
duration=info.get("duration"),
|
|
|
|
|
performer=info.get("author"),
|
|
|
|
|
)
|
2023-08-18 14:13:02 +00:00
|
|
|
|
if info.get("id") and (not await BiliFavAction.get_by_id(info.get("id"))):
|
|
|
|
|
audio_db = BiliFav(
|
|
|
|
|
id=info.get("id", 0),
|
|
|
|
|
bv_id=info.get("bvid").lower(),
|
|
|
|
|
type=12,
|
|
|
|
|
title=info.get("title", ""),
|
|
|
|
|
cover=info.get("cover", ""),
|
2023-08-18 15:14:22 +00:00
|
|
|
|
message_id=msg.id if push_id else 0,
|
2023-08-18 14:13:02 +00:00
|
|
|
|
file_id=msg.audio.file_id,
|
|
|
|
|
timestamp=int(time.time()),
|
|
|
|
|
)
|
|
|
|
|
await BiliFavAction.add_bili_fav(audio_db)
|
2023-08-18 10:18:12 +00:00
|
|
|
|
except BilibiliDownloaderError as e:
|
|
|
|
|
await fail_edit(m, e.MSG)
|
2023-08-18 13:28:16 +00:00
|
|
|
|
return
|
2023-08-18 10:18:12 +00:00
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.exception("Downloading audio failed")
|
|
|
|
|
await fail_edit(m, f"下载/上传失败:{e}")
|
2023-08-18 13:28:16 +00:00
|
|
|
|
return
|
|
|
|
|
with contextlib.suppress(Exception):
|
|
|
|
|
await m.delete()
|
|
|
|
|
return msg
|
2023-08-18 10:18:12 +00:00
|
|
|
|
|
|
|
|
|
|
2023-08-18 13:28:16 +00:00
|
|
|
|
async def go_download(v: Video, p_num: int, m: Message, task: bool = True):
|
2023-08-17 14:16:53 +00:00
|
|
|
|
video_path = cache_dir / f"{v.get_aid()}_{p_num}.mp4"
|
|
|
|
|
safe_remove(video_path)
|
|
|
|
|
flv_temp_path = cache_dir / f"{v.get_aid()}_{p_num}_temp.flv"
|
|
|
|
|
video_temp_path = cache_dir / f"{v.get_aid()}_{p_num}_video.m4s"
|
|
|
|
|
audio_temp_path = cache_dir / f"{v.get_aid()}_{p_num}_audio.m4s"
|
|
|
|
|
# 有 MP4 流 / FLV 流两种可能
|
|
|
|
|
try:
|
|
|
|
|
# 获取视频下载链接
|
|
|
|
|
download_url_data = await v.get_download_url(p_num)
|
|
|
|
|
# 解析视频下载信息
|
|
|
|
|
detector = VideoDownloadURLDataDetecter(data=download_url_data)
|
|
|
|
|
streams = detector.detect_best_streams(
|
|
|
|
|
video_max_quality=VideoQuality._1080P_60, # noqa
|
|
|
|
|
)
|
|
|
|
|
if not streams:
|
|
|
|
|
raise BilibiliDownloaderError("无法获取下载链接")
|
|
|
|
|
if detector.check_flv_stream():
|
|
|
|
|
# FLV 流下载
|
|
|
|
|
await download_url(streams[0].url, flv_temp_path, m, "视频 FLV ")
|
|
|
|
|
# 转换文件格式
|
2024-07-17 12:16:19 +00:00
|
|
|
|
_, result = await execute(
|
|
|
|
|
f'{FFMPEG_PATH} -i "{flv_temp_path}" "{video_path}"'
|
|
|
|
|
)
|
2023-08-17 14:16:53 +00:00
|
|
|
|
else:
|
|
|
|
|
if len(streams) < 2:
|
|
|
|
|
raise BilibiliDownloaderError("获取下载链接异常")
|
|
|
|
|
# MP4 流下载
|
2024-04-03 16:42:07 +00:00
|
|
|
|
video_url = streams[0].url
|
|
|
|
|
audio_url = streams[1].url
|
|
|
|
|
await download_url(video_url, video_temp_path, m, "视频 m4s ")
|
|
|
|
|
await download_url(audio_url, audio_temp_path, m, "音频 m4s ")
|
2023-08-17 14:16:53 +00:00
|
|
|
|
# 混流
|
2023-08-17 15:23:06 +00:00
|
|
|
|
logger.info("Merging video and audio")
|
2023-08-17 14:16:53 +00:00
|
|
|
|
_, result = await execute(
|
2023-08-17 15:23:06 +00:00
|
|
|
|
f'{FFMPEG_PATH} -i "{video_temp_path}" -i "{audio_temp_path}" '
|
2023-08-18 01:55:15 +00:00
|
|
|
|
f"-c:v copy -c:a copy -movflags +faststart "
|
2023-08-17 15:23:06 +00:00
|
|
|
|
f'-y "{video_path}"'
|
2023-08-17 14:16:53 +00:00
|
|
|
|
)
|
|
|
|
|
if result != 0:
|
|
|
|
|
raise FFmpegError
|
2023-08-18 13:28:16 +00:00
|
|
|
|
if task:
|
|
|
|
|
bot.loop.create_task(go_upload(v, p_num, m))
|
2023-08-17 14:16:53 +00:00
|
|
|
|
except BilibiliDownloaderError as e:
|
|
|
|
|
await fail_edit(m, e.MSG)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.exception("Downloading video failed")
|
|
|
|
|
await fail_edit(m, f"下载失败:{e}")
|
|
|
|
|
finally:
|
|
|
|
|
# 删除临时文件
|
|
|
|
|
safe_remove(flv_temp_path)
|
|
|
|
|
safe_remove(video_temp_path)
|
|
|
|
|
safe_remove(audio_temp_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def go_upload_progress(current: int, total: int, m: Message):
|
|
|
|
|
if current == 0:
|
|
|
|
|
return
|
|
|
|
|
async with LOCK:
|
|
|
|
|
_should_edit = should_edit(m)
|
|
|
|
|
if _should_edit:
|
2023-08-17 15:23:06 +00:00
|
|
|
|
now = time.time()
|
|
|
|
|
chunk_time = now - MESSAGE_MAP[m.id]
|
|
|
|
|
MESSAGE_MAP[m.id] = now
|
2023-08-17 14:16:53 +00:00
|
|
|
|
if _should_edit:
|
|
|
|
|
t = UPLOAD_MESSAGE_MAP[m.id] if m.id in UPLOAD_MESSAGE_MAP else 0
|
|
|
|
|
UPLOAD_MESSAGE_MAP[m.id] = current
|
|
|
|
|
chunk = current - t
|
|
|
|
|
chunk = chunk if chunk > 0 else 0
|
2023-08-17 15:23:06 +00:00
|
|
|
|
await message_edit(total, current, chunk, chunk_time, m, "上传")
|
2023-08-17 14:16:53 +00:00
|
|
|
|
|
|
|
|
|
|
2023-08-18 14:13:02 +00:00
|
|
|
|
def gen_video_caption(v: Video, info: Dict) -> str:
|
|
|
|
|
caption = (
|
|
|
|
|
f"<b>{info['title']}</b>\n\n{info['desc']}\n\nhttps://b23.tv/{v.get_bvid()}"
|
|
|
|
|
)
|
|
|
|
|
if len(caption) > 800:
|
2024-11-27 09:09:58 +00:00
|
|
|
|
caption = f"<b>{info['title']}</b>\n\n简介过长,无法显示\n\nhttps://b23.tv/{v.get_bvid()}"
|
2023-08-18 14:13:02 +00:00
|
|
|
|
return caption
|
|
|
|
|
|
|
|
|
|
|
2023-08-18 13:28:16 +00:00
|
|
|
|
async def go_upload(
|
|
|
|
|
v: Video, p_num: int, m: Message, push_id: int = None
|
|
|
|
|
) -> Optional[Message]:
|
2023-08-17 14:16:53 +00:00
|
|
|
|
video_path = cache_dir / f"{v.get_aid()}_{p_num}.mp4"
|
|
|
|
|
if not video_path.exists():
|
|
|
|
|
await fail_edit(m, "视频文件不存在")
|
|
|
|
|
return
|
|
|
|
|
try:
|
2023-08-18 01:55:15 +00:00
|
|
|
|
video_duration = await get_video_duration(video_path)
|
|
|
|
|
video_height, video_width = await get_video_height_width(video_path)
|
2023-08-18 09:26:57 +00:00
|
|
|
|
try:
|
|
|
|
|
info = await v.get_info()
|
|
|
|
|
video_jpg = await take_screenshot(info)
|
2023-08-18 14:13:02 +00:00
|
|
|
|
caption = gen_video_caption(v, info)
|
2023-08-18 09:26:57 +00:00
|
|
|
|
except Exception:
|
2023-08-18 14:13:02 +00:00
|
|
|
|
info = None
|
2023-08-18 09:26:57 +00:00
|
|
|
|
video_jpg = None
|
|
|
|
|
caption = f"https://b23.tv/{v.get_bvid()}"
|
2023-08-17 14:16:53 +00:00
|
|
|
|
logger.info(f"Uploading {video_path}")
|
2023-08-18 13:28:16 +00:00
|
|
|
|
msg = await bot.send_video(
|
|
|
|
|
chat_id=push_id or m.chat.id,
|
2023-08-17 14:16:53 +00:00
|
|
|
|
video=str(video_path),
|
2023-08-18 09:26:57 +00:00
|
|
|
|
caption=caption,
|
|
|
|
|
parse_mode=ParseMode.HTML,
|
2023-08-18 02:15:23 +00:00
|
|
|
|
duration=int(video_duration),
|
2023-08-18 01:55:15 +00:00
|
|
|
|
width=video_width,
|
|
|
|
|
height=video_height,
|
2023-08-18 02:15:23 +00:00
|
|
|
|
thumb=video_jpg,
|
2023-08-17 14:16:53 +00:00
|
|
|
|
supports_streaming=True,
|
|
|
|
|
progress=go_upload_progress,
|
|
|
|
|
progress_args=(m,),
|
2023-08-18 13:28:16 +00:00
|
|
|
|
reply_to_message_id=m.reply_to_message_id if not push_id else None,
|
2023-08-17 14:16:53 +00:00
|
|
|
|
)
|
2023-08-18 14:13:02 +00:00
|
|
|
|
if (
|
|
|
|
|
(not await BiliFavAction.get_by_bv_id(v.get_bvid()))
|
|
|
|
|
and info is not None
|
|
|
|
|
and info.get("aid")
|
|
|
|
|
):
|
|
|
|
|
video_db = BiliFav(
|
|
|
|
|
id=info.get("aid", 0),
|
|
|
|
|
bv_id=info.get("bvid").lower(),
|
|
|
|
|
type=2,
|
|
|
|
|
title=info.get("title", ""),
|
|
|
|
|
cover=info.get("pic", ""),
|
2023-08-18 15:14:22 +00:00
|
|
|
|
message_id=msg.id if push_id else 0,
|
2023-08-18 14:13:02 +00:00
|
|
|
|
file_id=msg.video.file_id,
|
|
|
|
|
timestamp=int(time.time()),
|
|
|
|
|
)
|
|
|
|
|
await BiliFavAction.add_bili_fav(video_db)
|
2023-08-17 14:16:53 +00:00
|
|
|
|
logger.info(f"Upload {video_path} success")
|
2023-08-18 01:55:15 +00:00
|
|
|
|
except BilibiliDownloaderError as e:
|
|
|
|
|
await fail_edit(m, e.MSG)
|
|
|
|
|
return
|
2023-08-17 14:16:53 +00:00
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.exception("Uploading video failed")
|
|
|
|
|
await fail_edit(m, f"上传失败:{e}")
|
|
|
|
|
return
|
|
|
|
|
finally:
|
|
|
|
|
safe_remove(video_path)
|
|
|
|
|
if m.id in MESSAGE_MAP:
|
|
|
|
|
del MESSAGE_MAP[m.id]
|
|
|
|
|
if m.id in UPLOAD_MESSAGE_MAP:
|
|
|
|
|
del UPLOAD_MESSAGE_MAP[m.id]
|
|
|
|
|
with contextlib.suppress(Exception):
|
|
|
|
|
await m.delete()
|
2023-08-18 13:28:16 +00:00
|
|
|
|
return msg
|