Search code examples
pythonaudiodiscorddiscord.py

how do i skip `x` seconds in discord.py audio source


my discord.py bot has a music function and today i wanted to add seek command which would allow to skip back or forward x amt of seconds, although i keep running into issues and cant find much abt it, i tried ai and stuff, but im just confused lol, this is pretty much my first time working with audio stuff in discord so yeah, im sorry if this is a well known question

anyway, this is the audio source i use :

class YTDLSource(discord.PCMVolumeTransformer):
    def __init__(
        self,
        source: discord.AudioSource,
        *,
        data: dict[typing.Any, typing.Any],
        volume: float = const.MUSIC_DEFAULT_VOL,
    ) -> None:
        super().__init__(source, volume)  # type: ignore
        self.data: dict[typing.Any, typing.Any] = data

    @classmethod
    async def from_url(
        cls,
        url: str,
        ytdl: yt_dlp.YoutubeDL,
        *,
        loop: typing.Optional[asyncio.AbstractEventLoop] = None,
    ) -> typing.Any:
        loop = loop or asyncio.get_event_loop()

        data: typing.Any = await loop.run_in_executor(
            None, lambda: ytdl.extract_info(url, download=False)  # type: ignore
        )

        if data is None:
            return

        if "entries" in data:
            data: typing.Any = data["entries"][0]

        return cls(
            discord.PCMVolumeTransformer(
                discord.FFmpegPCMAudio(data["url"], **const.FFMPEG_OPTIONS)  # type: ignore
            ),
            data=data,
        )

my ffmpeg and ytdl options :

FFMPEG_OPTIONS: Final[dict[str, str]] = {
    "before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5 -nostdin",
    "options": "-vn",
}
YTDL_OPTIONS: Final[dict[str, Any]] = {
    "format": "bestaudio/best",
    "extract_flat": "in_playlist",
    "ignoreerrors": True,
    "logtostderr": False,
    "quiet": True,
    "no_warnings": True,
    "default_search": "auto",
    "source_address": "0.0.0.0",
    "noprogress": True,
    "socket_timeout": 5,
    "socket_io_timeout": 10,
    "sleep_interval": 1,
    "max_sleep_interval": 5,
    "retries": 10,
    "fragment_retries": 10,
    "threads": 6,
    "nocheckcertificate": True,
    "geo_bypass": True,
}

how would i skip forward for example 5 seconds ( seek 5 ) and how would i skip backward 5 seconds ( seek -5 ) using this source ?

thanks for answers in advance


Solution

  • First, you need to keep track of the length of the music that has been played. You can do this by subclassing the FFmpegPCMAudio class and add a counter under read() function. (Each read() will take 20ms of audio data)

    # some subclassing like this
    class myAudioSubclass(discord.FFmpegPCMAudio):
      def __init__(self, pass_time, ...):
        # initialization
        # also initialize counter
        # pass_time means the seeked time
        self.counter : int = 0
    
      def read(self) -> bytes:
        # add the time here
        self.counter += 20
    
        return super().read()
    
      # custom method to return the current play-time
      def check_time(self) -> float:
        return self.counter / 1000 + pass_time
    
    

    Then, after you keep track of time, you could do some calculation and use FFmpeg (before option) -ss {time_you_want_to_seek} to start playing at the point you want.
    P.S: why we need sth like pass_time? because you need to correctly keep track of the time if voice.play() a audio-source that is not started in the beginning.