CONFIG_FILE: str = "libconv.ini" CUE_SHEET_EXTENSION: str = ".cue" import os import shutil import logging import subprocess from configparser import ConfigParser from copy import deepcopy from pathlib import Path from typing import List, Optional, Tuple LOGGER = logging.getLogger(__name__) config = ConfigParser() config.read(CONFIG_FILE) source_folder: str = config.get("libconv", "source_folder") destination_folder: str = config.get("libconv", "destination_folder") source_file_extensions: List[str] = config.get("libconv", "source_file_extensions").split(",") destination_extension: str = config.get("libconv", "destination_extension") ffmpeg_options: str = config.get("libconv", "ffmpeg_options") ffmpeg_location: str = config.get("libconv", "ffmpeg_location") folder_icon_extensions: List[str] = config.get("libconv", "folder_icon_extensions").split(",") overwrite: bool = config.get("libconv", "overwrite_files") == "true" dry_run: bool = config.get("libconv", "dry_run") != "false" # ----------------------------------------------------------------- # # file and folder operations def copy(src: str, dst: str): if (dry_run): LOGGER.debug(f"copy {src} to {dst}") else: shutil.copyfile(src, dst, follow_symlinks=True) def mkdir(path: str): if (dry_run): LOGGER.debug(f"mkdir -r {path}") else: os.makedirs(path, exist_ok=True) def execute(command: str): if (dry_run): LOGGER.debug(f"execute: {command}") else: subprocess.run(command) # ----------------------------------------------------------------- # # cue sheet processing def cue_sheet_processor(path: str): dst_folder = prepare_destination(path) sheet = get_files_with_ext(path, [CUE_SHEET_EXTENSION]) if (len(sheet) != 1): LOGGER.debug(f"ERR: Expected exactly one but {path} contains {len(sheet)} cue sheets") exit(-1) # ----------------------------------------------------------------- # # basic "folder contains audio files" processing def file_processor(path: str): dst_folder = prepare_destination(path) files = get_files_with_ext(path, source_file_extensions) for file in files: filename = get_filename(file, path) execute(f"{ffmpeg_location} -i {file} {ffmpeg_options} {dst_folder + filename}.{destination_extension}") # ----------------------------------------------------------------- # # Iteration over library folders and preparation def contains_extension(path: str, extensions: List[str]) -> bool: for root, dirs, files in os.walk(path): for file in files: for ext in extensions: if file.endswith(ext): return True return False def get_files_with_ext(path: str, extensions: List[str]) -> List[str]: files = [] for root, dirs, files in os.walk(path): for file in files: for ext in extensions: if file.endswith(ext): files.append(os.path.join(root, file)) return files def prepare_destination(path: str) -> str: images = get_files_with_ext(path, folder_icon_extensions) sub_path = path.replace(source_folder, "") new_path = destination_folder + sub_path mkdir(new_path) for img in images: img_name = img.replace(path, "") new_img_path = new_path + img_name copy(img, new_img_path) return new_path def get_filename(file: str, path: str) -> str: fname = file.replace(path, "") for ext in source_file_extensions: fname = fname.replace("." + ext, "") return fname def scan_folder(path: str): for root, dirs, files in os.walk(path): for directory in dirs: process_folder(os.path.join(root, directory)) def process_folder(path: str): if contains_extension(path, [CUE_SHEET_EXTENSION]): cue_sheet_processor(path) elif contains_extension(path, source_file_extensions): file_processor(path) else: scan_folder(path) if __name__ == "__main__": process_folder(source_folder)