podcast-generator/process.py
Jake Walker 80085fcad1
All checks were successful
ci/woodpecker/push/build Pipeline was successful
improvements
2025-01-09 11:28:18 +00:00

108 lines
3.5 KiB
Python

import queue
import shutil
import tempfile
import threading
import time
from pathlib import Path
from typing import Callable, Optional
import structlog
from ffmpeg_normalize import FFmpegNormalize
from settings import Settings
DELETE_INPUTS = Settings().delete_consume_files
CONSUME_DELAY = Settings().consume_delay
log = structlog.get_logger()
class AudioProcessor:
def __init__(self, generate_callback: Callable[[], None]):
self.queue: queue.Queue[(Path, Path)] = queue.Queue()
self.is_running = False
self.processor_thread: Optional[threading.Thread] = None
self.generate_callback = generate_callback
def add_file(self, input_filename: Path, output_filename: Path) -> None:
self.queue.put((input_filename, output_filename))
log.debug(
"Added file for processing",
input_filename=input_filename,
output_filename=output_filename,
)
def start_processing(self) -> None:
if self.is_running:
return
self.is_running = True
self.processor_thread = threading.Thread(target=self._process_queue)
self.processor_thread.daemon = True
self.processor_thread.start()
log.info("Started audio processing queue")
def stop_processing(self) -> None:
self.is_running = False
def process_audio(self, input_filename: Path, output_filename: Path) -> None:
log.info(
"Processing file",
input_filename=input_filename,
output_filename=output_filename,
)
if not input_filename.is_file():
log.error("Could not process non-file", input_filename=input_filename)
return
# wait for file to finish uploading
current_size = input_filename.stat().st_size
while True:
time.sleep(CONSUME_DELAY)
if input_filename.stat().st_size != current_size:
log.debug(
"Waiting for file to finish uploading",
input_filename=input_filename,
)
current_size = input_filename.stat().st_size
continue
break
with tempfile.TemporaryDirectory() as tmp:
input_temp_path = Path(tmp) / input_filename.name
output_temp_path = Path(tmp) / output_filename.name
# copy to temp directory
shutil.move(input_filename, input_temp_path)
ffmpeg_normalize = FFmpegNormalize(
"ebu", audio_codec="aac", audio_bitrate="192k"
)
ffmpeg_normalize.add_media_file(str(input_temp_path), str(output_temp_path))
ffmpeg_normalize.run_normalization()
shutil.move(output_temp_path, output_filename)
self.generate_callback()
def _process_queue(self) -> None:
while self.is_running:
try:
(input_filename, output_filename) = self.queue.get(timeout=1.0)
try:
self.process_audio(input_filename, output_filename)
log.info("Finished processing", output_filename=output_filename)
except Exception as e:
log.error(
"Failed processing",
input_filename=input_filename,
output_filename=output_filename,
error=e,
)
self.queue.task_done()
except queue.Empty:
continue