From a6382a9b967786d0d9b58df6d75d6bb2713cbb97 Mon Sep 17 00:00:00 2001 From: jgrogan Date: Tue, 3 Sep 2024 09:33:09 +0100 Subject: [PATCH] Flesh out audio conversion script and add initial network sync --- src/music/convert.py | 72 ++++++++++++++++++++++++++++---------------- src/network/sync.py | 70 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 src/network/sync.py diff --git a/src/music/convert.py b/src/music/convert.py index ae6781c..8befcf2 100644 --- a/src/music/convert.py +++ b/src/music/convert.py @@ -4,38 +4,58 @@ import logging import shutil from pathlib import Path import subprocess +import uuid from multiprocessing import Pool logger = logging.getLogger(__name__) -def launch_task(cmd): - subprocess.run(cmd, shell=True, check=True) - -def convert(input_dir: Path, output_dir: Path): - - logger.info("Converting files in %s", input_dir) - logger.info("Writing output to: %s", output_dir) - - os.makedirs(output_dir, exist_ok=True) - - flac_files = list(input_dir.resolve().rglob("*.flac")) - tasks = [] +def convert_audio_file(args): + cmd, output_tmp, output_path = args - for idx, path in enumerate(flac_files): + subprocess.run(cmd, shell=True) + os.makedirs(output_path.parent, exist_ok=True) + shutil.move(output_tmp, output_path) + +class AudioFileConverter(): - logger.info("Converting file %d of %d", idx, len(flac_files)) - - relative_path = path.relative_to(input_dir) - output_filename = str(path.stem) + ".mp3" + def __init__(self, input_dir: Path, output_dir: Path): + self.input_dir = input_dir + self.output_dir = output_dir + self.output_fmt = "mp3" + self.input_fmt = "flac" + + def get_converted_path(self, input_path: Path): + relative_path = input_path.relative_to(self.input_dir) + output_filename = str(input_path.stem) + f".{self.output_fmt}" output_filename.replace("'", "") + output_path = self.output_dir / relative_path.parent / output_filename + return output_path + + def convert(self): - output_path = output_dir / relative_path.parent / output_filename - - cmd = f"ffmpeg -i '{path}' -ab 320k -map_metadata 0 -id3v2_version 3 '{output_path}'" - tasks.append(cmd) + logger.info("Converting files in %s", self.input_dir) + logger.info("Writing output to: %s", self.output_dir) - with Pool(10) as p: - p.map(launch_task, tasks) + os.makedirs(self.output_dir, exist_ok=True) + + input_files = list(self.input_dir.resolve().rglob(f"*.{self.input_fmt}")) + output_files = [] + for input_file in input_files: + candidate_output = self.get_converted_path(input_file) + if not candidate_output.exists(): + output_files.append(candidate_output) + + tasks = [] + for idx, (input_path, output_path) in enumerate(zip(input_files, output_files)): + + logger.info("Converting file %d of %d", idx, len(input_files)) + + output_tmp = self.output_dir / (str(uuid.uuid4()) + f".{self.output_fmt}") + cmd = f"ffmpeg -i '{input_path}' -ab 320k -map_metadata 0 -id3v2_version 3 '{output_tmp}'" + tasks.append((cmd, output_tmp, output_path)) + + with Pool(10) as p: + p.map(convert_audio_file, tasks) def move(input_dir: Path, output_dir: Path): @@ -50,7 +70,6 @@ def move(input_dir: Path, output_dir: Path): output_path = output_dir / relative_path os.makedirs(output_path.parent, exist_ok=True) shutil.move(path, output_path) - if __name__ == "__main__": @@ -71,7 +90,8 @@ if __name__ == "__main__": logging.basicConfig() logging.getLogger().setLevel(logging.INFO) - # convert(args.input_dir.resolve(), args.output_dir.resolve()) - move(args.input_dir.resolve(), args.output_dir.resolve()) + converter = AudioFileConverter(args.input_dir.resolve(), args.output_dir.resolve()) + converter.convert() + #move(args.input_dir.resolve(), args.output_dir.resolve()) diff --git a/src/network/sync.py b/src/network/sync.py new file mode 100644 index 0000000..7b8da7c --- /dev/null +++ b/src/network/sync.py @@ -0,0 +1,70 @@ +import argparse +import os +import logging +import shutil +from pathlib import Path +import subprocess +import uuid + +from fabric import Connection + +logger = logging.getLogger(__name__) + + +def sync(source_dir: Path, target_dir: Path, host: str): + + logger.info("Syncing files in %s to %s on % s", source_dir, target_dir, host) + + cmd = f"find {target_dir} -type f" + + if host: + result = Connection(host).run(cmd, hide=True) + target_files = result.stdout.splitlines() + else: + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + target_files = result.stdout.splitlines() + + target_files = [Path(f).relative_to(target_dir) for f in target_files] + source_files = [] + for dir_path, _, files in os.walk(source_dir): + for each_file in files: + source_files.append(Path(dir_path + "/" + each_file).relative_to(source_dir)) + + sync_files = [] + for source_file in source_files: + if source_file not in target_files: + sync_files.append(source_file) + + sync_dir = Path(os.getcwd())/"sync" + os.makedirs(sync_dir, exist_ok=True) + for sync_file in sync_files: + os.makedirs((sync_dir/sync_file).parent, exist_ok=True) + shutil.copy(source_dir / sync_file, sync_dir / sync_file) + +if __name__ == "__main__": + + parser = argparse.ArgumentParser() + + parser.add_argument('--source_dir', + type=Path, + default=Path(), + help="Directory with source files to sync from.") + + parser.add_argument('--host', + type=str, + default = "", + help="Name of host system to sync with.") + + parser.add_argument('--target_dir', + type=Path, + default=Path(), + help="Directory with source files to sync to.") + + args = parser.parse_args() + + logging.basicConfig() + logging.getLogger().setLevel(logging.INFO) + + sync(args.source_dir.resolve(), args.target_dir.resolve(), args.host) + +