You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在discord.py音频源中实现x秒seek(跳转)功能?

实现discord.py音乐机器人的Seek跳转命令

要实现向前/向后跳转指定秒数的seek命令,核心是利用FFmpeg的-ss参数重新生成音频流,同时跟踪当前播放进度。下面结合你的YTDLSource类给出具体实现步骤:

1. 完善YTDLSource类,增加进度跟踪

首先确保你的YTDLSource能获取音频总时长,并记录播放起始时间,这样才能计算当前播放位置:

import time
import asyncio
import discord
from discord.ext import commands

# 你的YTDL和FFmpeg配置(假设已定义)
ytdl_options = {
    'format': 'bestaudio/best',
    'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
    'restrictfilenames': True,
    'noplaylist': True,
    'nocheckcertificate': True,
    'ignoreerrors': False,
    'logtostderr': False,
    'quiet': True,
    'no_warnings': True,
    'default_search': 'auto',
    'source_address': '0.0.0.0'
}

ffmpeg_options = {
    'options': '-vn',
    'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5'
}

ytdl = discord.FFmpegPCMAudio.ytdl  # 或你自行初始化的youtube_dl.YoutubeDL实例

class YTDLSource(discord.PCMVolumeTransformer):
    def __init__(self, source, *, data, volume=0.5):
        super().__init__(source, volume)
        self.data = data
        self.title = data.get('title')
        self.url = data.get('url')
        self.duration = data.get('duration')  # 从YTDL获取音频总时长
        self.start_time = None  # 记录播放器开始播放的时间戳

    @classmethod
    async def from_url(cls, url, *, loop=None, stream=False):
        loop = loop or asyncio.get_event_loop()
        data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))

        if 'entries' in data:
            data = data['entries'][0]

        filename = data['url'] if stream else ytdl.prepare_filename(data)
        return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)

2. 播放时记录起始时间

在你的play命令中,给YTDLSource实例设置start_time,用于后续计算播放进度:

@bot.command()
async def play(ctx, url):
    if not ctx.voice_client:
        if ctx.author.voice:
            await ctx.author.voice.channel.connect()
        else:
            await ctx.send('你不在语音频道里!')
            return

    voice_client = ctx.voice_client
    source = await YTDLSource.from_url(url, loop=bot.loop, stream=True)
    source.start_time = time.time()  # 记录开始播放的时间
    voice_client.play(source)
    await ctx.send(f'正在播放:{source.title}')

3. 实现Seek命令

编写seek命令,计算新的播放位置,重新生成带跳转参数的FFmpeg流:

@bot.command()
async def seek(ctx, offset: int):
    voice_client = ctx.voice_client
    # 检查是否在播放
    if not voice_client or not voice_client.is_playing():
        await ctx.send('当前没有播放音频!')
        return

    current_source = voice_client.source
    # 检查当前源是否是YTDLSource
    if not isinstance(current_source, YTDLSource):
        await ctx.send('当前播放的内容不支持跳转!')
        return

    # 计算当前已播放时长
    elapsed = time.time() - current_source.start_time
    new_position = elapsed + offset

    # 边界校验,防止超出音频范围
    if new_position < 0:
        new_position = 0
        await ctx.send('已跳转到音频开头')
    elif new_position > current_source.duration:
        new_position = current_source.duration
        await ctx.send('已跳转到音频末尾')

    # 重新生成带seek参数的FFmpeg源
    ffmpeg_seek_opts = ffmpeg_options.copy()
    # 给FFmpeg添加-ss参数指定起始位置
    ffmpeg_seek_opts['before_options'] = f'-ss {new_position} ' + ffmpeg_seek_opts.get('before_options', '')
    
    new_ffmpeg_source = discord.FFmpegPCMAudio(current_source.url, **ffmpeg_seek_opts)
    # 包装成YTDLSource,保留原音量和元数据
    new_source = YTDLSource(new_ffmpeg_source, data=current_source.data, volume=current_source.volume)
    # 修正start_time,让后续进度计算准确
    new_source.start_time = time.time() - new_position

    # 替换音频源并继续播放
    voice_client.stop()
    voice_client.play(new_source)
    await ctx.send(f'已跳转到 {int(new_position)} 秒位置')

# 处理参数错误
@seek.error
async def seek_error(ctx, error):
    if isinstance(error, commands.BadArgument):
        await ctx.send('请输入整数秒数,例如`seek 5`(向前跳5秒)或`seek -5`(向后跳5秒)')

关键注意事项

  • 确保YTDL能正确获取duration字段:如果是YouTube视频,YTDL默认会返回该字段;如果是本地文件,需要确保FFmpeg能识别时长(YTDL处理本地文件也会返回duration)。
  • 部分流媒体(比如直播)没有固定时长,此时current_source.duration会是None,可以在seek命令里加判断,提示用户无法跳转。
  • 有些音频流可能不支持-ss跳转(比如部分加密流),这种情况会导致FFmpeg报错,你可以加try-except块捕获异常并提示用户。

内容的提问来源于stack exchange,提问作者user17411100

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 18:22:23