Download workflows/audio_queue.py from uri410/NovelReader_translate_tool: direct link, hf CLI and curl.
- Browser
- Download file 2.41 kB
-
https://huggingface.co/spaces/uri410/NovelReader_translate_tool/resolve/main/workflows/audio_queue.py
- Command line
-
hf download hf://spaces/uri410/NovelReader_translate_tool/workflows/audio_queue.py
-
curl -L -o audio_queue.py https://huggingface.co/spaces/uri410/NovelReader_translate_tool/resolve/main/workflows/audio_queue.py
2.41 kB
| """Audio consumer queue, bounded backlog, multi-worker support, and deterministic shutdown. | |
| Only the translation producer mutates the book scope. The job stays active until | |
| close() joins the consumer workers, so reset/delete/rename cannot race audio output. | |
| """ | |
| import queue | |
| import threading | |
| class AudioQueue: | |
| def __init__(self, consume, checkpoint, cancel, maxsize=2, workers=1): | |
| self.consume = consume | |
| self.checkpoint = checkpoint | |
| self.cancel = cancel | |
| self.queue = queue.Queue(maxsize=maxsize) | |
| self.error = None | |
| self.closed = False | |
| self.workers = [] | |
| for i in range(max(1, int(workers))): | |
| t = threading.Thread(target=self._run, name=f'chapter-audio-{i}', daemon=True) | |
| t.start() | |
| self.workers.append(t) | |
| def worker(self): | |
| """Backward compatibility for checks like `q.worker.is_alive()`.""" | |
| return self.workers[0] if self.workers else None | |
| def submit(self, item): | |
| if self.closed: | |
| raise RuntimeError('Audio queue is closed') | |
| while any(w.is_alive() for w in self.workers): | |
| if self.error: | |
| raise RuntimeError('Audio worker failed') from self.error | |
| if not self.checkpoint(): | |
| self.cancel(item) | |
| return False | |
| try: | |
| self.queue.put(item, timeout=0.2) | |
| return True | |
| except queue.Full: | |
| continue | |
| raise RuntimeError('Audio worker stopped unexpectedly') from self.error | |
| def _run(self): | |
| while True: | |
| item = self.queue.get() | |
| try: | |
| if item is None: | |
| return | |
| if self.error is not None or not self.checkpoint(): | |
| self.cancel(item) | |
| else: | |
| self.consume(item) | |
| except Exception as exc: | |
| # Do not abandon a full queue and deadlock its producer. | |
| self.error = exc | |
| finally: | |
| self.queue.task_done() | |
| def close(self): | |
| if not self.closed: | |
| self.closed = True | |
| for _ in self.workers: | |
| self.queue.put(None) | |
| for w in self.workers: | |
| w.join() | |
| if self.error: | |
| raise RuntimeError('Audio worker failed') from self.error | |