108 lines
3.5 KiB
Python
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
|