From 599e76d44b7ac7a15580d7b825a21367cd564286 Mon Sep 17 00:00:00 2001 From: grglcy Date: Sat, 12 Jul 2025 14:23:50 +0100 Subject: [PATCH] implement transcoder as layer - copy worker over note: originally removed in commit 641abcdd90 --- src/layers/__init__.py | 2 ++ src/layers/transcoder.py | 45 ++++++++++++++++++++++++++ src/layers/worker.py | 70 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 src/layers/transcoder.py create mode 100644 src/layers/worker.py diff --git a/src/layers/__init__.py b/src/layers/__init__.py index 9230f8e..e55e1e0 100644 --- a/src/layers/__init__.py +++ b/src/layers/__init__.py @@ -1,2 +1,4 @@ from .layer import Layer +from .worker import Worker from .transcoder import Transcoder +from .dedupe import Dedupe diff --git a/src/layers/transcoder.py b/src/layers/transcoder.py new file mode 100644 index 0000000..eadc940 --- /dev/null +++ b/src/layers/transcoder.py @@ -0,0 +1,45 @@ +from pathlib import Path +from multiprocessing import Pool, Manager, set_start_method +from log import Log, LogCat +from .worker import Worker +from dir import Root +from .layer import Layer + + +class Transcoder(Layer): + def __init__(self, encoder: Path, extension: str, output_root: Path, log: Log, log_path: Path): + super().__init__(log, 'TCD') + self.encoder = encoder + self.extension = extension + self.output_root = output_root + + self.log_path = log_path + + def _process(self, root: Root): + transcode_list = root.all_files + self._transcode(transcode_list, self.encoder) + + def _transcode(self, transcode_list: list, encoder: Path, workers=16): + manager = Manager() + queue = manager.Queue() + log = Log(self.log_path, queue) + logcat = LogCat(log.queue, "TCD") + args = [(str(self.output_root), self.extension, track, encoder, logcat) for track in transcode_list] + with Pool(workers) as pool: + pool.starmap(self.worker, args) + pool.close() + pool.join() + log.stop() + + def _transcode_single_thread(self, transcode_list: list, encoder: Path): + log = Log(self.log_path) + logcat = LogCat(log.queue, "TCD") + worker_args = [(track, encoder) for track in transcode_list] + for track, encoder in worker_args: + self.worker(str(self.output_root), self.extension, track, encoder, logcat) + log.stop() + + @staticmethod + def worker(output_root, extension, track, encoder, log): + w = Worker(output_root, extension) + w.transcode_worker(track, encoder, log) diff --git a/src/layers/worker.py b/src/layers/worker.py new file mode 100644 index 0000000..24ceb20 --- /dev/null +++ b/src/layers/worker.py @@ -0,0 +1,70 @@ +import shutil +import subprocess +from pathlib import Path +from dir.file import File + +class Worker: + def __init__(self, output_root, extension): + self.output_root = Path(output_root) + self.extension = extension + + def transcode_worker(self, track: File, encoder, log): + if track.is_art: + return self.copy_album_art(track, log) + elif track.is_audio: + return self.transcode_audio(track, encoder, log) + else: + log.info("WRK", f"File {track.path} ignored") + + def copy_album_art(self, file: File, log): + self.create_directories(file) + output_path = file.output_file(self.output_root, file.extension[1:]) + if output_path.exists(): + log.info("ART", f"Skipped {output_path}") + return + try: + shutil.copy(file.path, output_path) + log.info("ART", f"Successfully copied {output_path}") + except FileExistsError: + log.warning("ART", f"File exists: {output_path}") + except Exception: + log.error("ART", f"{file.path} failed to copy") + + def transcode_audio(self, track, encoder, log): + track_output = track.output_file(self.output_root, self.extension) + if track_output.exists(): + log.info("AUD", f"Skipped {track_output}") + return + self.create_directories(track) + encoder_args = self.encoder_args(encoder, track, log) + try: + subprocess.run(encoder_args, capture_output=True, text=True, check=True) + log.info("AUD", f"Transcoded '{track}' successfully.") + except Exception: + log.error("AUD", f"ERROR: Transcoding of '{track}' failed.") + + def encoder_args(self, encoder, track, log): + track_output = track.output_file(self.output_root, self.extension) + enc_filename = encoder.parts[-1] + encoder_args = "" + if enc_filename == "opusenc.exe": + additional_args = ('--bitrate', '128', '--music') + encoder_args = (str(encoder),) + additional_args + (str(track.path), str(track_output)) + elif enc_filename == "qaac64.exe": + additional_args = (str(track), '-o', str(track_output), '--threading') + encoder_args = (str(encoder),) + additional_args + else: + log.error("AUD", f"Encoder {encoder} not recognised.") + return encoder_args + + def create_directories(self, track: File): + if not track.artist_out(self.output_root).exists(): + try: + track.artist_out(self.output_root).mkdir() + except FileExistsError: + pass + if not track.album_out(self.output_root).exists(): + try: + track.album_out(self.output_root).mkdir() + except FileExistsError: + pass \ No newline at end of file