#!/usr/bin/env python3 # external modules from argparse import ArgumentParser from mutagen.flac import FLAC from os import cpu_count, path, walk from pathlib import Path from shutil import copy from subprocess import check_output, Popen from sys import exit as s_exit from time import sleep # utility functions def scan_source(_source): _artist_dirs, _album_dirs, _full_paths = [], [], [] for _root, _directories, _files in walk(path.expanduser(_source)): for _dir in sorted(_directories): if not _root.endswith(path.expanduser(_source)): _artist_dirs.append(_root) _artist_dirs = list(set(_artist_dirs)) for _artist in sorted(_artist_dirs): for _root, _directories, _files in walk(path.expanduser(_artist)): if not _root.endswith(path.expanduser(_artist)): _album_dirs.append(_root) _album_dirs = list(set(_album_dirs)) for _album in sorted(_album_dirs): for _root, _directories, _files in walk(path.expanduser(_album)): for _filename in _files: if _filename.endswith('.flac') or _filename.endswith('.mp3') or _filename.endswith('.mp4') \ or _filename.endswith('.m4a'): _full_paths.append(f"{_album}/{_filename}") return _album_dirs, _full_paths def create_destination(): for _dir in source_directories[0]: Path(path.expanduser(_dir.replace(path.expanduser(source), path.expanduser(destination))))\ .mkdir(0o700, parents=True, exist_ok=True) def scan_destination_convert(): _i, _i_total, _total_files = 0, 0, len(source_directories[1]) for _file in source_directories[1]: _i_total += 1 _progress = round((_i_total / _total_files) * 100, 2) if not Path(f"{(_file.replace(path.expanduser(source), path.expanduser(destination)))[:-5]}.m4a").is_file()\ and not Path(f"{(_file.replace(path.expanduser(source), path.expanduser(destination)))}").is_file(): if not _file.endswith('.flac'): print(f"[{_file}] Not a FLAC file, copying...") copy(f"{_file}", f"{_file.replace(path.expanduser(source), path.expanduser(destination))}") else: _file_s = _file.replace(' ', '\\ ').replace("'", "\\'").replace(')', '\\)').replace('(', '\\(') \ .replace(']', '\\]').replace('[', '\\[').replace('&', '\\&').replace('`', '\\`') \ .replace('$', '\\$') _file_d = _file.replace(path.expanduser(source), path.expanduser(destination)).replace(' ', '\\ ') \ .replace("'", "\\'").replace(')', '\\)').replace('(', '\\(').replace(']', '\\]') \ .replace('[', '\\[').replace('&', '\\&').replace('`', '\\`').replace('$', '\\$')[ :-5] + '.m4a' _active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1 if _active_processes >= cpu_count(): while _active_processes >= cpu_count(): print(f"\nThere are already {cpu_count()} processes running...\n") sleep(1) _active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1 _i += 1 print(f"\n{_progress}% | \u001b[38;5;0;48;5;15mConverting {_file_s} to {_file_d}...\u001b[0m\n") # get relevant ReplayGain info _audio = FLAC(f"{_file}") if args.albumgain: if _audio.get('REPLAYGAIN_ALBUM_GAIN'): _rggain = float(_audio.get('REPLAYGAIN_ALBUM_GAIN')[0][:-3]) elif _audio.get('replaygain_album_gain'): _rggain = float(_audio.get('replaygain_album_gain')[0][:-3]) else: _rggain = None if _audio.get('REPLAYGAIN_ALBUM_PEAK'): _rgpeak = float(_audio.get('REPLAYGAIN_ALBUM_PEAK')[0]) elif _audio.get('replaygain_album_peak'): _rgpeak = float(_audio.get('replaygain_album_peak')[0]) else: _rgpeak = None else: if _audio.get('REPLAYGAIN_TRACK_GAIN'): _rggain = float(_audio.get('REPLAYGAIN_TRACK_GAIN')[0][:-3]) elif _audio.get('replaygain_track_gain'): _rggain = float(_audio.get('replaygain_track_gain')[0][:-3]) else: _rggain = None if _audio.get('REPLAYGAIN_TRACK_PEAK'): _rgpeak = float(_audio.get('REPLAYGAIN_TRACK_PEAK')[0]) elif _audio.get('replaygain_track_peak'): _rgpeak = float(_audio.get('replaygain_track_peak')[0]) else: _rgpeak = None # determine ffmpeg arguments if args.bake and _rggain is not None: _bake_args, _flac2pod_rg_tags = '-filter:a "volume=' + str(_rggain) + 'dB"', '' elif _rggain is not None and _rgpeak is not None: _bake_args, _flac2pod_rg_tags = '', f"-metadata comment='FLAC2PODRG#{_rggain}#{_rgpeak}#'" else: _bake_args, _flac2pod_rg_tags = '', '' # generate the appropriate ffmpeg command _cmd = f"screen -DmS flac2pod{_i} ffmpeg -i {_file_s} -c:a aac -ab 256k {_bake_args} -map_metadata 0 " \ f"{_flac2pod_rg_tags} -aac_pns 0 -movflags +faststart -vn {_file_d}