Commit 13b787b

benny-dou <60535774+benny-dou@users.noreply.github.com>
2025-10-17 04:03:00
feat(ffmpeg): add `/h264` video conversion
1 parent 411b707
Changed files (3)
src/others/ffmpeg.py
@@ -15,10 +15,13 @@ from messages.parser import parse_msg
 from messages.progress import modify_progress
 from messages.sender import send2tg
 from messages.utils import equal_prefix, remove_prefix, startswith_prefix
-from multimedia import generate_cover, parse_media_info
+from multimedia import convert_to_h264, generate_cover, parse_media_info
 from utils import readable_size, seconds_to_time
 
-HELP = f"""✂️**视频/音频切片**
+
+async def ffmpeg_cut(client: Client, message: Message, **kwargs):
+    """Cut video or audio file."""
+    docs = f"""✂️**视频/音频切片**
 该功能需要使用`{PREFIX.FFMPEG_CUT}`命令回复一条视频或音频消息
 
 **命令格式**:
@@ -37,25 +40,21 @@ HELP = f"""✂️**视频/音频切片**
 ⚠️注意: 切片使用ffmpeg对视频流或音频流进行copy, 不会重新进行编码
 copy操作使用关键帧作为分割点, 因此切片的开始或结束位置并非精确匹配请求时间点
 """
-
-
-async def ffmpeg_cut(client: Client, message: Message, **kwargs):
-    """Cut video or audio file."""
     if not startswith_prefix(message.content, prefix=PREFIX.FFMPEG_CUT):
         return
     # send docs if message == "/cut"
     if equal_prefix(message.content, prefix=PREFIX.FFMPEG_CUT):
-        await send2tg(client, message, texts=HELP, **kwargs)
+        await send2tg(client, message, texts=docs, **kwargs)
         return
     media_message = message.reply_to_message if message.reply_to_message else message
     info = parse_msg(media_message)
 
     if info["mtype"] not in ["video", "audio"]:
-        await send2tg(client, message, texts="❌请回复一条视频或音频消息\n\n" + HELP, **kwargs)
+        await send2tg(client, message, texts="❌请回复一条视频或音频消息\n\n" + docs, **kwargs)
         return
     times = remove_prefix(message.content, prefix=PREFIX.FFMPEG_CUT).split()
     if len(times) not in [1, 2]:
-        await send2tg(client, message, texts="❌时间格式错误\n\n" + HELP, **kwargs)
+        await send2tg(client, message, texts="❌时间格式错误\n\n" + docs, **kwargs)
         return
 
     fpath = Path(DOWNLOAD_DIR).joinpath(f"{info['mid']}-{info['cid']}-{info['file_name']}").as_posix()
@@ -150,3 +149,34 @@ def sanitize_time(t: str) -> str:
         else:
             time_str += f".{microseconds:06d}".rstrip("0")
     return time_str
+
+
+async def ffmpeg_h264(client: Client, message: Message, **kwargs):
+    """Convert video to h264."""
+    if not startswith_prefix(message.content, prefix=PREFIX.FFMPEG_H264):
+        return
+    # send docs if message == "/h264"
+    if equal_prefix(message.content, prefix=PREFIX.FFMPEG_H264) and not message.reply_to_message:
+        await send2tg(client, message, texts=f"🔄使用 `{PREFIX.FFMPEG_H264}` 回复视频消息将其转换为H264编码", **kwargs)
+        return
+    media_message = message.reply_to_message if message.reply_to_message else message
+    info = parse_msg(media_message)
+    if info["mtype"] != "video":
+        await send2tg(client, message, texts="❌请回复一条视频消息", **kwargs)
+        return
+    fpath = Path(DOWNLOAD_DIR).joinpath(f"{info['mid']}-{info['cid']}-{info['file_name']}").as_posix()
+    if not Path(fpath).is_file():
+        kwargs["progress"] = kwargs.get("progress", await message.reply_text("⏬正在下载视频文件...", quote=True))
+        fpath: str = await client.download_media(media_message, file_name=fpath)  # type: ignore
+    if not Path(fpath).is_file():
+        await modify_progress(texts="❌视频文件下载失败", force_update=True, **kwargs)
+        return
+
+    media_info = await parse_media_info(fpath)
+    if media_info["video_codec"] == "h264" and not equal_prefix(message.content, prefix=f"{PREFIX.FFMPEG_CUT} force"):
+        await modify_progress(texts=f"⚠️该视频已经是H264编码, 无需转换\n使用 `{PREFIX.FFMPEG_H264} force` 强制进行转换", force_update=True, **kwargs)
+        return
+
+    h264_path = await convert_to_h264(fpath, force_re_encoding=True)
+    await send2tg(client, media_message, media=[{"video": h264_path}], **kwargs)
+    await modify_progress(del_status=True, **kwargs)
src/config.py
@@ -103,6 +103,7 @@ class PREFIX:
     QUOTLY = os.getenv("PREFIX_QUOTLY", "/quote").lower()
     TMDB = os.getenv("PREFIX_TMDB", "/tmdb").lower()
     FFMPEG_CUT = os.getenv("PREFIX_FFMPEG_CUT", "/cut").lower()
+    FFMPEG_H264 = os.getenv("PREFIX_FFMPEG_H264", "/h264").lower()
 
 
 class API:
src/handler.py
@@ -23,7 +23,7 @@ from others.convert_chinese import chinese_conversion
 from others.download_external import download_url_in_message
 from others.extract_audio import extract_audio_file
 from others.favorite import save_favorite, send_favorite
-from others.ffmpeg import ffmpeg_cut
+from others.ffmpeg import ffmpeg_cut, ffmpeg_h264
 from others.raw_img_file import convert_raw_img_file
 from others.search_google import search_google
 from others.search_ytb import search_youtube
@@ -152,6 +152,7 @@ async def handle_utilities(
         await convert_raw_img_file(client, message, **kwargs)
     if ffmpeg:
         await ffmpeg_cut(client, message, **kwargs)
+        await ffmpeg_h264(client, message, **kwargs)
 
 
 async def handle_social_media(
@@ -437,6 +438,7 @@ def get_social_media_help(chat_id: int | str, ctype: str, prefix: str):
         msg += f"\n🔄**简繁转换**: `{PREFIX.CONVERT_TO_SC}` 或 `{PREFIX.CONVERT_TO_TC}`"
     if permission["ffmpeg"]:
         msg += f"\n✂️**视频切片**: `{PREFIX.FFMPEG_CUT}` 回复视频消息"
+        msg += f"\n🎬**视频转码**: `{PREFIX.FFMPEG_H264}` 回复视频消息"
 
     msg += "\n\n单独发送每个命令前缀本身可查看该命令详细使用说明"
     return msg