Discord机器人如何下载聊天最新GIF/MP4?解决文件损坏问题
Discord机器人下载GIF文件损坏的修复方案
问题描述
我希望Discord机器人能下载聊天中最新发送的GIF,支持保存为GIF格式或可转换为GIF的MP4格式。现有代码能正常定位到目标GIF的URL,但下载生成的downloaded.gif文件始终损坏,无法正常使用。
现有代码:
@client.command() async def giffy(ctx): channel = ctx.channel async for message in channel.history(limit=100): if message.attachments: for attachment in message.attachments: if attachment.filename.lower().endswith(('.gif', '.gifv')): gif_url = attachment.url response = requests.get(gif_url) if response.status_code == 200: with open("downloaded.gif", "wb") as f: f.write(response.content) else: print("error") return elif message.content: content = message.content.lower() if content.endswith(('.gif', '.gifv')) or content.startswith(('http://', 'https://')): gif_url = content response = requests.get(gif_url) if response.status_code == 200: with open("downloaded.gif", "wb") as f: f.write(response.content) else: print("error") return await ctx.send("No GIF found in the channel's history.")
问题原因及修复方案
1. Discord的GIF链接实际返回MP4格式
Discord上标记为.gif或.gifv的附件,很多时候实际返回的是MP4视频文件(为了节省带宽)。强行用.gif后缀保存MP4数据,会导致系统无法识别文件,看起来像是“损坏”。
2. 同步请求在异步环境下的风险
requests.get是同步阻塞请求,在Discord.py的异步运行环境中,可能导致数据未完全接收就写入文件,造成文件损坏。建议改用异步HTTP库aiohttp。
修复后的代码
import aiohttp from discord.ext import commands @client.command() async def giffy(ctx): channel = ctx.channel async for message in channel.history(limit=100): # 处理附件中的GIF/GIFV if message.attachments: for attachment in message.attachments: if attachment.filename.lower().endswith(('.gif', '.gifv')): await download_media(attachment.url) await ctx.send("最新GIF/MP4已下载完成") return # 处理消息内容中的链接 elif message.content: content = message.content.strip() if (content.lower().endswith(('.gif', '.gifv')) or content.startswith(('http://', 'https://'))): await download_media(content) await ctx.send("最新GIF/MP4已下载完成") return await ctx.send("频道历史中未找到GIF/MP4") async def download_media(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: if response.status == 200: # 获取响应的内容类型,判断实际文件格式 content_type = response.headers.get('Content-Type', '') if 'image/gif' in content_type: filename = "downloaded.gif" elif 'video/mp4' in content_type: filename = "downloaded.mp4" else: # 默认按原链接后缀保存 filename = url.split('/')[-1] # 完整写入文件 with open(filename, "wb") as f: f.write(await response.read()) else: print(f"下载失败,状态码:{response.status}")
额外优化:MP4转GIF(可选)
如果需要将下载的MP4转换为GIF,可以使用moviepy库,添加以下转换函数:
from moviepy.editor import VideoFileClip def convert_mp4_to_gif(mp4_path, gif_path, fps=10): clip = VideoFileClip(mp4_path) clip.write_gif(gif_path, fps=fps) clip.close() # 在download_media函数中添加调用: # 当保存为mp4后,调用convert_mp4_to_gif("downloaded.mp4", "converted.gif")
内容的提问来源于stack exchange,提问作者Baby Yoda
相关产品推荐
相关产品推荐

