|
|
@ -32,21 +32,22 @@ KNOWN_EXTENSIONS: Set[str] = set([ |
|
|
|
def munge_path(path: str) -> str: |
|
|
|
def munge_path(path: str) -> str: |
|
|
|
return re.sub(r'[\:*?"|<>]+', '-', path) |
|
|
|
return re.sub(r'[\:*?"|<>]+', '-', path) |
|
|
|
|
|
|
|
|
|
|
|
if len(sys.argv) != 3: |
|
|
|
def main(): |
|
|
|
|
|
|
|
if len(sys.argv) != 3: |
|
|
|
print(f'Usage: {os.path.basename(__file__)} PATHS-FILE DESTINATION-DIR', file=sys.stderr) |
|
|
|
print(f'Usage: {os.path.basename(__file__)} PATHS-FILE DESTINATION-DIR', file=sys.stderr) |
|
|
|
sys.exit(1) |
|
|
|
sys.exit(1) |
|
|
|
|
|
|
|
|
|
|
|
paths_file: str = sys.argv[1] |
|
|
|
paths_file: str = sys.argv[1] |
|
|
|
destination_dir: str = sys.argv[2] |
|
|
|
destination_dir: str = sys.argv[2] |
|
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(paths_file): |
|
|
|
if not os.path.exists(paths_file): |
|
|
|
print(f'PATHS-FILE \'{paths_file}\' does not exist or is not readable', file=sys.stderr) |
|
|
|
print(f'PATHS-FILE \'{paths_file}\' does not exist or is not readable', file=sys.stderr) |
|
|
|
sys.exit(2) |
|
|
|
sys.exit(2) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print('[DEBUG] Gathering destination files list') |
|
|
|
print('[DEBUG] Gathering destination files list') |
|
|
|
destination_files: Set[Path] = set() |
|
|
|
destination_files: Set[Path] = set() |
|
|
|
if os.path.isdir(destination_dir): |
|
|
|
if os.path.isdir(destination_dir): |
|
|
|
for dirpath, _, files in os.walk(destination_dir): |
|
|
|
for dirpath, _, files in os.walk(destination_dir): |
|
|
|
for file in files: |
|
|
|
for file in files: |
|
|
|
path = Path(os.path.join(dirpath, file)).absolute() |
|
|
|
path = Path(os.path.join(dirpath, file)).absolute() |
|
|
@ -55,11 +56,11 @@ if os.path.isdir(destination_dir): |
|
|
|
print(f'Ignoring duplicate input entry: {file}') |
|
|
|
print(f'Ignoring duplicate input entry: {file}') |
|
|
|
else: |
|
|
|
else: |
|
|
|
destination_files.add(path) |
|
|
|
destination_files.add(path) |
|
|
|
print(f'[DEBUG] Gathered {len(destination_files)} destination files') |
|
|
|
print(f'[DEBUG] Gathered {len(destination_files)} destination files') |
|
|
|
|
|
|
|
|
|
|
|
print('[DEBUG] Gathering source files list') |
|
|
|
print('[DEBUG] Gathering source files list') |
|
|
|
source_files: Set[Path] = set() |
|
|
|
source_files: Set[Path] = set() |
|
|
|
for line in open(paths_file, 'r'): |
|
|
|
for line in open(paths_file, 'r'): |
|
|
|
trimmed_line = line.strip() |
|
|
|
trimmed_line = line.strip() |
|
|
|
if not trimmed_line: |
|
|
|
if not trimmed_line: |
|
|
|
continue |
|
|
|
continue |
|
|
@ -83,38 +84,38 @@ for line in open(paths_file, 'r'): |
|
|
|
print(f'[ERROR] Could not find source path: {path}') |
|
|
|
print(f'[ERROR] Could not find source path: {path}') |
|
|
|
sys.exit(3) |
|
|
|
sys.exit(3) |
|
|
|
|
|
|
|
|
|
|
|
if len(source_files) == 0: |
|
|
|
if len(source_files) == 0: |
|
|
|
print('No source files to transfer') |
|
|
|
print('No source files to transfer') |
|
|
|
sys.exit() |
|
|
|
sys.exit() |
|
|
|
else: |
|
|
|
else: |
|
|
|
print(f'[DEBUG] Gathered {len(source_files)} source files') |
|
|
|
print(f'[DEBUG] Gathered {len(source_files)} source files') |
|
|
|
|
|
|
|
|
|
|
|
print('[DEBUG] Finding longest common prefix of source files') |
|
|
|
print('[DEBUG] Finding longest common prefix of source files') |
|
|
|
longest_prefix = next(iter(source_files)).parent |
|
|
|
longest_prefix = next(iter(source_files)).parent |
|
|
|
for file in source_files: |
|
|
|
for file in source_files: |
|
|
|
if longest_prefix.as_posix() == '/': |
|
|
|
if longest_prefix.as_posix() == '/': |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
while not file.as_posix().startswith(longest_prefix.as_posix()): |
|
|
|
while not file.as_posix().startswith(longest_prefix.as_posix()): |
|
|
|
longest_prefix = longest_prefix.parent |
|
|
|
longest_prefix = longest_prefix.parent |
|
|
|
|
|
|
|
|
|
|
|
print('[DEBUG] Filtering files already present in destination') |
|
|
|
print('[DEBUG] Filtering files already present in destination') |
|
|
|
# NOTE: this assumes all filenames are unique, which should already be the case in my music library |
|
|
|
# NOTE: this assumes all filenames are unique, which should already be the case in my music library |
|
|
|
# verify via `find . -type f -not -name '*.jpg' -not -name '*.png' -not -name '*.txt' -exec basename {} \; | sort | uniq -d` |
|
|
|
# verify via `find . -type f -not -name '*.jpg' -not -name '*.png' -not -name '*.txt' -exec basename {} \; | sort | uniq -d` |
|
|
|
# TODO: add a hash check so that we can overwrite songs on the target if source |
|
|
|
# TODO: add a hash check so that we can overwrite songs on the target if source |
|
|
|
# has a version which is different (new metadata, or better version) |
|
|
|
# has a version which is different (new metadata, or better version) |
|
|
|
for source_path in list(source_files)[:]: |
|
|
|
for source_path in list(source_files)[:]: |
|
|
|
found_destination_path: Union[Path, None] = next((destination_file for destination_file in destination_files if destination_file.stem == munge_path(source_path.stem)), None) |
|
|
|
found_destination_path: Union[Path, None] = next((destination_file for destination_file in destination_files if destination_file.stem == munge_path(source_path.stem)), None) |
|
|
|
if found_destination_path: |
|
|
|
if found_destination_path: |
|
|
|
source_files.remove(source_path) |
|
|
|
source_files.remove(source_path) |
|
|
|
destination_files.remove(found_destination_path) |
|
|
|
destination_files.remove(found_destination_path) |
|
|
|
|
|
|
|
|
|
|
|
if len(source_files) == 0: |
|
|
|
if len(source_files) == 0: |
|
|
|
print('All source files already exist in target') |
|
|
|
print('All source files already exist in target') |
|
|
|
sys.exit() |
|
|
|
sys.exit() |
|
|
|
|
|
|
|
|
|
|
|
# TODO: improve prompt (print 10+ file paths or exit to `PAGER` to view before prompt so user has more info before confirming) |
|
|
|
# TODO: improve prompt (print 10+ file paths or exit to `PAGER` to view before prompt so user has more info before confirming) |
|
|
|
if len(destination_files) > 0: |
|
|
|
if len(destination_files) > 0: |
|
|
|
delete_selection_made = False |
|
|
|
delete_selection_made = False |
|
|
|
while not delete_selection_made: |
|
|
|
while not delete_selection_made: |
|
|
|
print(f'Files in destination not found in source files list:\n{destination_files}') |
|
|
|
print(f'Files in destination not found in source files list:\n{destination_files}') |
|
|
@ -126,11 +127,11 @@ if len(destination_files) > 0: |
|
|
|
elif delete_input[0] == 'n' or delete_input[0] == 'N': |
|
|
|
elif delete_input[0] == 'n' or delete_input[0] == 'N': |
|
|
|
delete_selection_made = True |
|
|
|
delete_selection_made = True |
|
|
|
|
|
|
|
|
|
|
|
# TODO: list both number of files in songs list & number of files to be transferred |
|
|
|
# TODO: list both number of files in songs list & number of files to be transferred |
|
|
|
print(f'Copying {len(source_files)} songs to {destination_dir}') |
|
|
|
print(f'Copying {len(source_files)} songs to {destination_dir}') |
|
|
|
print(f'[TRACE] Copying the following files to destination:\n{source_files}') |
|
|
|
print(f'[TRACE] Copying the following files to destination:\n{source_files}') |
|
|
|
|
|
|
|
|
|
|
|
for source_file in source_files: |
|
|
|
for source_file in source_files: |
|
|
|
source_copy_path = source_file.with_suffix('').as_posix().replace(longest_prefix.as_posix(), '', 1) |
|
|
|
source_copy_path = source_file.with_suffix('').as_posix().replace(longest_prefix.as_posix(), '', 1) |
|
|
|
destination_filename = munge_path(source_copy_path) |
|
|
|
destination_filename = munge_path(source_copy_path) |
|
|
|
destination_file_path = f'{destination_dir}/{destination_filename}.mp3' |
|
|
|
destination_file_path = f'{destination_dir}/{destination_filename}.mp3' |
|
|
@ -154,5 +155,8 @@ for source_file in source_files: |
|
|
|
]) |
|
|
|
]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: improve this output (number of deleted files, number of new files, correct plural of 'files') |
|
|
|
# TODO: improve this output (number of deleted files, number of new files, correct plural of 'files') |
|
|
|
print(f'Finished copying {len(source_files)} new files to {destination_dir}') |
|
|
|
print(f'Finished copying {len(source_files)} new files to {destination_dir}') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
|
|
|
main() |
|
|
|