Skip to content

Commit a935e80

Browse files
committed
Added queuemanager to sounds.ext
1 parent 1f12f4a commit a935e80

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed

docs/changelog.rst

+18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
:orphan:
22

33

4+
2.10.1
5+
=======
6+
- ext.sounds
7+
- Additions
8+
- Added :class:`Twitchio.ext.sounds.AudioQueueManager` to manage a queue of audio files to be played sequentially with optional repeat functionality.
9+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.add_audio` to add a new audio file to the queue.
10+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.play_next` to play the next audio file in the queue.
11+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.skip_audio` to stop the currently playing audio file.
12+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.stop_audio` to stop the currently playing audio file and reset the playing flag.
13+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.pause_audio` to pause the currently playing audio file.
14+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.resume_audio` to resume the currently paused audio file.
15+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.clear_queue` to clear all audio files from the queue.
16+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.pause_queue` to pause the processing of the queue.
17+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.resume_queue` to resume the processing of the queue.
18+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.get_queue_contents` to retrieve the current contents of the queue.
19+
- Added :method:`Twitchio.ext.sounds.AudioQueueManager.queue_loop` to continuously check the queue and play the next audio file if not currently playing and not paused.
20+
21+
422
2.10.0
523
=======
624
- TwitchIO

examples/music_queue.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import asyncio
2+
from twitchio.ext import commands, sounds
3+
from twitchio.ext.sounds import queuemanager
4+
5+
6+
class Bot(commands.Bot):
7+
8+
def __init__(self):
9+
super().__init__(token="TOKEN", prefix="!", initial_channels=["CHANNEL"])
10+
self.audio_manager = queuemanager.AudioQueueManager()
11+
12+
# Adding sound files paths to the queue for uses to choose from
13+
song_dict = {
14+
"song_one": "\\PATH\\TO\\FILE.mp3",
15+
"song_two": "\\PATH\\TO\\FILE.mp3",
16+
"song_three": "\\PATH\\TO\\FILE.mp3",
17+
}
18+
19+
async def event_ready(self):
20+
loop = asyncio.get_event_loop()
21+
22+
# Start the queue loop
23+
self.task = loop.create_task(self.audio_manager.queue_loop())
24+
25+
@commands.command(name="sr")
26+
async def addsound(self, ctx: commands.Context, sound: str):
27+
sound_path = self.song_dict[sound]
28+
await self.audio_manager.add_audio(sound_path)
29+
await ctx.send(f"Added sound to queue: {sound_path}")
30+
31+
@commands.command(name="skip")
32+
async def skip(self, ctx: commands.Context):
33+
await ctx.send(f"Skipped the current sound. {self.audio_manager.current_sound}")
34+
await self.audio_manager.skip_audio()
35+
36+
@commands.command(name="pause")
37+
async def pause(self, ctx: commands.Context):
38+
await self.audio_manager.pause_audio()
39+
40+
@commands.command(name="resume")
41+
async def resume(self, ctx: commands.Context):
42+
await self.audio_manager.resume_audio()
43+
44+
@commands.command(name="queue")
45+
async def queue(self, ctx: commands.Context):
46+
queue_contents = await self.audio_manager.get_queue_contents()
47+
await ctx.send(f"Queue contents: {queue_contents}")
48+
49+
# Override close method to gracefully cancel the task
50+
async def close(self):
51+
self.task.cancel()
52+
await super().close()
53+
54+
55+
if __name__ == "__main__":
56+
bot = Bot()
57+
bot.run()

twitchio/ext/sounds/queuemanager.py

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import asyncio
2+
from twitchio.ext import sounds
3+
4+
5+
class AudioQueueManager:
6+
"""
7+
Manages a queue of audio files to be played sequentially with optional repeat and pause functionalities.
8+
9+
Attributes:
10+
queue (asyncio.Queue[str]): A queue to hold paths of audio files to be played.
11+
is_playing (bool): Indicates whether an audio file is currently being played.
12+
repeat_queue (bool): If True, adds the current playing audio file back to the queue after playing.
13+
queue_paused (bool): If True, pauses the processing of the queue.
14+
player (sounds.AudioPlayer): An instance of AudioPlayer to play audio files.
15+
current_sound (str): Path of the currently playing audio file.
16+
"""
17+
18+
def __init__(self):
19+
"""
20+
Initializes an instance of AudioQueueManager with an empty queue and default settings.
21+
"""
22+
self.queue: asyncio.Queue[str] = asyncio.Queue()
23+
self.is_playing: bool = False
24+
self.repeat_queue: bool = True
25+
self.queue_paused: bool = False
26+
self.player: sounds.AudioPlayer = sounds.AudioPlayer(
27+
callback=self.player_done)
28+
self.current_sound: str = ""
29+
30+
async def player_done(self) -> None:
31+
"""
32+
Callback method called when the player finishes playing an audio file.
33+
Resets the is_playing flag and marks the current task as done in the queue.
34+
"""
35+
await asyncio.sleep(0.1)
36+
self.is_playing = False
37+
self.queue.task_done()
38+
39+
async def add_audio(self, sound_path: str) -> None:
40+
"""
41+
Adds a new audio file to the queue.
42+
43+
Args:
44+
sound_path (str): Path of the audio file to add to the queue.
45+
"""
46+
await asyncio.sleep(0.1)
47+
await self.queue.put(sound_path)
48+
49+
async def play_next(self) -> None:
50+
"""
51+
Plays the next audio file in the queue if the queue is not empty and not paused.
52+
Sets the is_playing flag, retrieves the next audio file from the queue, and plays it.
53+
If repeat_queue is True, adds the current audio file back to the queue after playing.
54+
"""
55+
await asyncio.sleep(0.1)
56+
if not self.queue.empty() and not self.queue_paused:
57+
self.is_playing = True
58+
sound_path = await self.queue.get()
59+
self.current_sound = sound_path
60+
sound = sounds.Sound(source=sound_path)
61+
self.player.play(sound)
62+
if self.repeat_queue:
63+
await self.queue.put(self.current_sound)
64+
65+
async def skip_audio(self) -> None:
66+
"""
67+
Stops the currently playing audio file if there is one.
68+
"""
69+
await asyncio.sleep(0.1)
70+
if self.is_playing:
71+
self.player.stop()
72+
self.is_playing = False
73+
74+
async def stop_audio(self) -> None:
75+
"""
76+
Stops the currently playing audio file.
77+
Resets the playing flag but leaves the queue intact.
78+
"""
79+
await asyncio.sleep(0.1)
80+
if self.is_playing:
81+
self.player.stop()
82+
self.is_playing = False
83+
84+
async def pause_audio(self) -> None:
85+
"""
86+
Pauses the currently playing audio file.
87+
"""
88+
await asyncio.sleep(0.1)
89+
self.player.pause()
90+
91+
async def resume_audio(self) -> None:
92+
"""
93+
Resumes the currently paused audio file.
94+
"""
95+
await asyncio.sleep(0.1)
96+
self.player.resume()
97+
98+
async def clear_queue(self) -> None:
99+
"""
100+
Clears all audio files from the queue.
101+
"""
102+
await asyncio.sleep(0.1)
103+
while not self.queue.empty():
104+
await self.queue.get()
105+
self.queue.task_done()
106+
107+
async def pause_queue(self) -> None:
108+
"""
109+
Pauses the processing of the queue.
110+
"""
111+
await asyncio.sleep(0.1)
112+
self.queue_paused = True
113+
114+
async def resume_queue(self) -> None:
115+
"""
116+
Resumes the processing of the queue.
117+
"""
118+
await asyncio.sleep(0.1)
119+
self.queue_paused = False
120+
121+
async def get_queue_contents(self) -> list:
122+
"""
123+
Retrieves the current contents of the queue.
124+
125+
Returns:
126+
list: List of paths of audio files in the queue.
127+
"""
128+
await asyncio.sleep(0.1)
129+
return list(self.queue._queue)
130+
131+
async def queue_loop(self) -> None:
132+
"""
133+
Continuously checks the queue and plays the next audio file if not currently playing and not paused.
134+
"""
135+
try:
136+
while True:
137+
await asyncio.sleep(0.2)
138+
if not self.is_playing and not self.queue.empty() and not self.queue_paused:
139+
await self.play_next()
140+
finally:
141+
return

0 commit comments

Comments
 (0)