I’m working on a Discord bot that needs to show the progress of video exporting with MoviePy. I tried using an async callback function, but it’s not working as expected. The logger doesn’t seem to call the function, probably because it’s async. Any ideas on how to fix this?
Here’s a simplified version of what I’ve tried:
async def update_progress(progress):
print(f'Progress: {progress}%')
await message.edit(content=f'Processing: {progress}%')
class ProgressLogger(ProgressBarLogger):
async def log_progress(self, progress):
await update_progress(int(progress * 100))
async def process_video(clip, output):
logger = ProgressLogger()
await clip.write_videofile(output, logger=logger)
@bot.command()
async def create_video(ctx):
clip = VideoFileClip('input.mp4')
await process_video(clip, 'output.mp4')
The bot should update a message with the current progress, but it’s not working. How can I make this work correctly?
I’ve encountered a similar issue when working with MoviePy and Discord bots. The problem lies in the asynchronous nature of Discord.py and the synchronous processing of MoviePy. Here’s a workaround I’ve found effective:
Instead of using an async callback, create a separate thread for video processing. Use a queue to communicate progress between the processing thread and your Discord bot’s event loop. Then, use asyncio.sleep() to periodically check and update the progress.
Here’s a basic implementation:
import threading
import queue
import asyncio
progress_queue = queue.Queue()
def process_video_thread(clip, output):
def our_logger(progress):
progress_queue.put(progress)
clip.write_videofile(output, logger=our_logger)
@bot.command()
async def create_video(ctx):
message = await ctx.send('Processing: 0%')
clip = VideoFileClip('input.mp4')
threading.Thread(target=process_video_thread, args=(clip, 'output.mp4')).start()
while True:
try:
progress = progress_queue.get_nowait()
await message.edit(content=f'Processing: {int(progress * 100)}%')
except queue.Empty:
await asyncio.sleep(1)
if progress == 1:
break
await message.edit(content='Video processing complete!')
This approach should reliably update the progress while keeping your bot responsive.
I’ve wrestled with this exact issue before, and it can be a real pain. The key is to separate the video processing from your bot’s main event loop. Here’s what worked for me:
I used the concurrent.futures module to run the video processing in a separate thread. Then I set up a simple callback function to update a shared variable with the progress.
In the bot command, I kicked off the video processing and then used a while loop with asyncio.sleep() to periodically check and update the progress message. Something like this:
import concurrent.futures
import asyncio
progress = 0
def process_callback(curr):
global progress
progress = curr
async def create_video(ctx):
global progress
msg = await ctx.send('Starting...')
with concurrent.futures.ThreadPoolExecutor() as pool:
future = pool.submit(process_video, 'input.mp4', 'output.mp4', process_callback)
while not future.done():
await msg.edit(content=f'Progress: {progress:.0%}')
await asyncio.sleep(2)
await msg.edit(content='Video complete!')
def process_video(input, output, callback):
clip = VideoFileClip(input)
clip.write_videofile(output, logger=callback)
This approach kept my bot responsive and gave smooth progress updates. Hope it helps!
hey, i had similar problem few weeks ago. what worked for me was using a separate thread for video processing and a queue to pass progress updates. then u can use asyncio.sleep() to check n update progress regularly. keeps the bot responsive while handling the video stuff. hope this helps!