-
-
Notifications
You must be signed in to change notification settings - Fork 166
Audio queue manager, solution for #292 #462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sockheadrps
wants to merge
19
commits into
PythonistaGuild:main
Choose a base branch
from
sockheadrps:AudioQueueManager
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
a6a0b18
Added the ability to set rate and channels on Sound
sockheadrps 20ba986
Added the ability to set rate and channels on Sound
sockheadrps 03e52ba
typo in docs
sockheadrps 2bf418c
typo in docs
sockheadrps 215e5b2
add to changelog
sockheadrps e7e2047
Added tinytag dependency to retrieve audio file metadata
sockheadrps a2176dc
Updated Sounds Docs to reflect auto-detect of sample rate etc
sockheadrps 16834d7
Merge branch 'master' into sounds_setters
sockheadrps 7bb21c2
Merge branch 'master' into sounds_setters
IAmTomahawkx 0c46ba7
Added new dependency for resolving stereo channels and sample rate fr…
sockheadrps 62ff356
Merge remote-tracking branch 'upstream/maintain/2.x' into sound_setters
sockheadrps 1f12f4a
Added new dependency for resolving stereo channels and sample rate fr…
sockheadrps a935e80
Added queuemanager to sounds.ext
sockheadrps 2a739ed
Fixed docstrings, removed unnecessary async definitions for methods t…
sockheadrps f5532d8
typo in example
sockheadrps 23ba393
Changes for PR review
sockheadrps 3a6c8e4
resolved conflicts from upstream changelog
sockheadrps be2bed0
removed duplicate __all__
sockheadrps ca49519
Fixed docs
sockheadrps File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,5 @@ sphinxext-opengraph | |
Pygments | ||
furo | ||
pyaudio==0.2.11 | ||
yt-dlp>=2022.2.4 | ||
yt-dlp>=2022.2.4 | ||
tinytag>=1.9.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import asyncio | ||
from twitchio.ext import commands, sounds | ||
from twitchio.ext.sounds import queuemanager | ||
|
||
|
||
class Bot(commands.Bot): | ||
|
||
def __init__(self): | ||
super().__init__(token="TOKEN", prefix="!", initial_channels=["CHANNEL"]) | ||
self.audio_manager = queuemanager.AudioQueueManager() | ||
|
||
# Adding sound files paths to the queue for uses to choose from | ||
song_dict = { | ||
"song_one": "\\PATH\\TO\\FILE.mp3", | ||
"song_two": "\\PATH\\TO\\FILE.mp3", | ||
"song_three": "\\PATH\\TO\\FILE.mp3", | ||
} | ||
|
||
async def event_ready(self): | ||
loop = asyncio.get_event_loop() | ||
|
||
# Start the queue loop | ||
self.task = loop.create_task(self.audio_manager.queue_loop()) | ||
|
||
@commands.command(name="sr") | ||
async def addsound(self, ctx: commands.Context, sound: str): | ||
sound_path = self.song_dict[sound] | ||
await self.audio_manager.add_audio(sound_path) | ||
await ctx.send(f"Added sound to queue: {sound_path}") | ||
|
||
@commands.command(name="skip") | ||
async def skip(self, ctx: commands.Context): | ||
await ctx.send(f"Skipped the current sound. {self.audio_manager.current_sound}") | ||
await self.audio_manager.skip_audio() | ||
|
||
@commands.command(name="pause") | ||
async def pause(self, ctx: commands.Context): | ||
await self.audio_manager.pause_audio() | ||
|
||
@commands.command(name="resume") | ||
async def resume(self, ctx: commands.Context): | ||
await self.audio_manager.resume_audio() | ||
|
||
@commands.command(name="queue") | ||
async def queue(self, ctx: commands.Context): | ||
queue_contents = await self.audio_manager.get_queue_contents() | ||
await ctx.send(f"Queue contents: {queue_contents}") | ||
|
||
# Override close method to gracefully cancel the task | ||
async def close(self): | ||
self.task.cancel() | ||
await super().close() | ||
|
||
|
||
if __name__ == "__main__": | ||
bot = Bot() | ||
bot.run() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import asyncio | ||
from twitchio.ext import sounds | ||
|
||
|
||
class AudioQueueManager: | ||
""" | ||
Manages a queue of audio files to be played sequentially with optional repeat and pause functionalities. | ||
|
||
Attributes: | ||
queue (asyncio.Queue[str]): A queue to hold paths of audio files to be played. | ||
is_playing (bool): Indicates whether an audio file is currently being played. | ||
repeat_queue (bool): If True, adds the current playing audio file back to the queue after playing. | ||
queue_paused (bool): If True, pauses the processing of the queue. | ||
player (sounds.AudioPlayer): An instance of AudioPlayer to play audio files. | ||
current_sound (str): Path of the currently playing audio file. | ||
""" | ||
|
||
def __init__(self): | ||
sockheadrps marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
Initializes an instance of AudioQueueManager with an empty queue and default settings. | ||
""" | ||
self.queue: asyncio.Queue[str] = asyncio.Queue() | ||
self.is_playing: bool = False | ||
self.repeat_queue: bool = True | ||
self.queue_paused: bool = False | ||
self.player: sounds.AudioPlayer = sounds.AudioPlayer( | ||
callback=self.player_done) | ||
self.current_sound: str = "" | ||
|
||
async def player_done(self) -> None: | ||
""" | ||
Callback method called when the player finishes playing an audio file. | ||
Resets the is_playing flag and marks the current task as done in the queue. | ||
""" | ||
await asyncio.sleep(0.1) | ||
self.is_playing = False | ||
self.queue.task_done() | ||
|
||
async def add_audio(self, sound_path: str) -> None: | ||
""" | ||
Adds a new audio file to the queue. | ||
|
||
Args: | ||
sound_path (str): Path of the audio file to add to the queue. | ||
""" | ||
await asyncio.sleep(0.1) | ||
await self.queue.put(sound_path) | ||
|
||
async def play_next(self) -> None: | ||
""" | ||
Plays the next audio file in the queue if the queue is not empty and not paused. | ||
Sets the is_playing flag, retrieves the next audio file from the queue, and plays it. | ||
If repeat_queue is True, adds the current audio file back to the queue after playing. | ||
""" | ||
await asyncio.sleep(0.1) | ||
if not self.queue.empty() and not self.queue_paused: | ||
self.is_playing = True | ||
sound_path = await self.queue.get() | ||
self.current_sound = sound_path | ||
sound = sounds.Sound(source=sound_path) | ||
self.player.play(sound) | ||
if self.repeat_queue: | ||
await self.queue.put(self.current_sound) | ||
|
||
async def skip_audio(self) -> None: | ||
""" | ||
Stops the currently playing audio file if there is one. | ||
""" | ||
await asyncio.sleep(0.1) | ||
if self.is_playing: | ||
self.player.stop() | ||
self.is_playing = False | ||
|
||
async def stop_audio(self) -> None: | ||
""" | ||
Stops the currently playing audio file. | ||
Resets the playing flag but leaves the queue intact. | ||
""" | ||
await asyncio.sleep(0.1) | ||
if self.is_playing: | ||
self.player.stop() | ||
self.is_playing = False | ||
|
||
async def pause_audio(self) -> None: | ||
""" | ||
Pauses the currently playing audio file. | ||
""" | ||
await asyncio.sleep(0.1) | ||
self.player.pause() | ||
|
||
async def resume_audio(self) -> None: | ||
""" | ||
Resumes the currently paused audio file. | ||
""" | ||
await asyncio.sleep(0.1) | ||
self.player.resume() | ||
|
||
async def clear_queue(self) -> None: | ||
""" | ||
Clears all audio files from the queue. | ||
""" | ||
await asyncio.sleep(0.1) | ||
while not self.queue.empty(): | ||
await self.queue.get() | ||
self.queue.task_done() | ||
|
||
async def pause_queue(self) -> None: | ||
""" | ||
Pauses the processing of the queue. | ||
""" | ||
await asyncio.sleep(0.1) | ||
self.queue_paused = True | ||
|
||
async def resume_queue(self) -> None: | ||
""" | ||
Resumes the processing of the queue. | ||
""" | ||
await asyncio.sleep(0.1) | ||
self.queue_paused = False | ||
|
||
async def get_queue_contents(self) -> list: | ||
""" | ||
Retrieves the current contents of the queue. | ||
|
||
Returns: | ||
list: List of paths of audio files in the queue. | ||
sockheadrps marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
await asyncio.sleep(0.1) | ||
return list(self.queue._queue) | ||
sockheadrps marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
async def queue_loop(self) -> None: | ||
""" | ||
Continuously checks the queue and plays the next audio file if not currently playing and not paused. | ||
""" | ||
try: | ||
while True: | ||
await asyncio.sleep(0.2) | ||
if not self.is_playing and not self.queue.empty() and not self.queue_paused: | ||
await self.play_next() | ||
finally: | ||
return |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.