2022-02-06 15:51:42 +00:00
|
|
|
from musicutil.utils.do_replaygain import do_replaygain
|
2022-02-04 13:35:01 +00:00
|
|
|
from ..types import File
|
|
|
|
from ..utils.scan_for_music import scan_for_music
|
|
|
|
from ..utils.load_tag_information import load_tag_information
|
2022-02-06 15:20:12 +00:00
|
|
|
from ..transcode_presets import print_transcode_presets, transcode_presets_list
|
|
|
|
from ..utils.transcoder import TranscodeConfig, transcode, get_transcode_config
|
2022-02-04 13:32:22 +00:00
|
|
|
|
|
|
|
from os import makedirs as make_directories
|
|
|
|
from os.path import exists as path_exists
|
|
|
|
from shutil import copy as copy_file
|
|
|
|
from copy import deepcopy as deep_copy
|
|
|
|
|
|
|
|
|
2022-02-04 13:54:10 +00:00
|
|
|
class CopyCommandState:
|
|
|
|
files: list[File] = []
|
|
|
|
transcoded_files: list[File] = []
|
|
|
|
|
2022-02-06 16:18:14 +00:00
|
|
|
|
2022-02-06 15:20:12 +00:00
|
|
|
class CopyCommandArgs():
|
|
|
|
src: str
|
|
|
|
dest: str
|
|
|
|
transcode_preset: str
|
|
|
|
custom_transcoder_config: str
|
|
|
|
single_directory: bool
|
|
|
|
skip_existing: bool
|
2022-02-06 15:51:42 +00:00
|
|
|
skip_replaygain: bool
|
2022-02-06 15:20:12 +00:00
|
|
|
|
2022-02-06 16:18:14 +00:00
|
|
|
|
2022-02-06 15:20:12 +00:00
|
|
|
def add_copy_command(subparsers):
|
|
|
|
copy_parser = subparsers.add_parser('copy')
|
|
|
|
copy_parser.add_argument(
|
|
|
|
'src',
|
|
|
|
type=str,
|
|
|
|
help='src base music directory')
|
|
|
|
copy_parser.add_argument(
|
|
|
|
'dest',
|
|
|
|
type=str,
|
|
|
|
help='dest music directory')
|
|
|
|
copy_parser.add_argument(
|
|
|
|
'--transcode-preset',
|
|
|
|
type=str,
|
|
|
|
help='transcode preset (copy = no transcode)',
|
|
|
|
default="copy")
|
|
|
|
copy_parser.add_argument(
|
|
|
|
'--custom-transcoder-config',
|
|
|
|
type=str,
|
|
|
|
help='custom transcoder config')
|
|
|
|
copy_parser.add_argument(
|
|
|
|
'--skip-existing',
|
|
|
|
action='store_true')
|
2022-02-06 15:51:42 +00:00
|
|
|
copy_parser.add_argument(
|
|
|
|
'--skip-replaygain',
|
|
|
|
action='store_true')
|
2022-02-06 15:20:12 +00:00
|
|
|
copy_parser.add_argument(
|
|
|
|
'--single-directory',
|
|
|
|
action='store_true')
|
|
|
|
|
2022-02-06 16:18:14 +00:00
|
|
|
|
2022-02-06 15:20:12 +00:00
|
|
|
def get_copy_args(args) -> CopyCommandArgs:
|
|
|
|
command_args = CopyCommandArgs()
|
|
|
|
command_args.src = args.src
|
|
|
|
command_args.dest = args.dest
|
|
|
|
command_args.transcode_preset = args.transcode_preset
|
|
|
|
command_args.custom_transcoder_config = args.custom_transcoder_config
|
|
|
|
command_args.single_directory = args.single_directory
|
|
|
|
command_args.skip_existing = args.skip_existing
|
2022-02-06 15:51:42 +00:00
|
|
|
command_args.skip_replaygain = args.skip_replaygain
|
2022-02-06 15:20:12 +00:00
|
|
|
return command_args
|
|
|
|
|
2022-02-06 16:18:14 +00:00
|
|
|
|
2022-02-04 13:54:10 +00:00
|
|
|
class CopyCommand():
|
2022-02-06 15:20:12 +00:00
|
|
|
def __init__(self, args: CopyCommandArgs):
|
|
|
|
self.args = args
|
2022-02-04 13:54:10 +00:00
|
|
|
self.state = CopyCommandState()
|
|
|
|
|
|
|
|
def run(self):
|
2022-02-06 15:20:12 +00:00
|
|
|
if self.args.transcode_preset == "list":
|
|
|
|
print_transcode_presets()
|
2022-02-06 09:57:47 +00:00
|
|
|
exit()
|
|
|
|
|
2022-02-04 13:54:10 +00:00
|
|
|
print("Copying")
|
|
|
|
self.scan_for_music()
|
|
|
|
self.load_tag_information()
|
2022-02-06 15:20:12 +00:00
|
|
|
if self.args.single_directory:
|
2022-02-04 13:54:10 +00:00
|
|
|
self.check_for_collisions()
|
|
|
|
self.transcode_files()
|
2022-02-06 15:20:12 +00:00
|
|
|
if self.args.single_directory:
|
2022-02-04 16:19:11 +00:00
|
|
|
self.create_mappings()
|
2022-02-06 15:51:42 +00:00
|
|
|
if not self.args.skip_replaygain:
|
|
|
|
self.add_replaygain_tags()
|
2022-02-04 13:54:10 +00:00
|
|
|
|
|
|
|
def scan_for_music(self):
|
|
|
|
print("Scanning For Music")
|
2022-02-06 15:20:12 +00:00
|
|
|
self.state.files = scan_for_music(self.args.src)
|
2022-02-04 13:54:10 +00:00
|
|
|
|
|
|
|
def load_tag_information(self):
|
|
|
|
print("Loading Tag Information")
|
|
|
|
|
|
|
|
for file in self.state.files:
|
|
|
|
file.tags = load_tag_information(file)
|
|
|
|
|
|
|
|
def check_for_collisions(self):
|
|
|
|
print("Checking For Colisions")
|
|
|
|
seen = set()
|
|
|
|
dupes = []
|
|
|
|
|
|
|
|
for file in self.state.files:
|
|
|
|
filename = file.filename
|
|
|
|
if filename in seen:
|
|
|
|
dupes.append(filename)
|
|
|
|
else:
|
|
|
|
seen.add(filename)
|
|
|
|
|
|
|
|
if len(dupes) > 0:
|
|
|
|
print("Dupes Found:", dupes)
|
|
|
|
print("Cannot continue using --single-directory")
|
|
|
|
print("Please rename or remove duplicates")
|
|
|
|
exit()
|
|
|
|
|
|
|
|
def _transcode_copy(self, file: File):
|
|
|
|
src = file.join_path_to()
|
|
|
|
dest = file.join_filename(
|
2022-02-06 15:20:12 +00:00
|
|
|
) if self.args.single_directory else file.join_path_from_src()
|
|
|
|
dest = self.args.dest + "/" + dest
|
2022-02-04 13:54:10 +00:00
|
|
|
|
|
|
|
exists = path_exists(dest)
|
|
|
|
|
2022-02-06 16:35:39 +00:00
|
|
|
should_skip = False
|
|
|
|
if self.args.skip_existing and exists:
|
|
|
|
should_skip = True
|
|
|
|
|
|
|
|
if should_skip:
|
2022-02-04 13:54:10 +00:00
|
|
|
print("Copying", src, "to", dest)
|
|
|
|
copy_file(
|
|
|
|
src,
|
|
|
|
dest,
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
print(
|
|
|
|
"Skipping",
|
|
|
|
src,
|
|
|
|
"as already is copied at",
|
|
|
|
dest)
|
|
|
|
|
|
|
|
self.state.transcoded_files.append(file)
|
|
|
|
|
2022-02-06 16:18:14 +00:00
|
|
|
def _transcode_with_config(
|
|
|
|
self,
|
|
|
|
file: File,
|
|
|
|
trans_config: TranscodeConfig):
|
2022-02-04 13:54:10 +00:00
|
|
|
src = file.join_path_to()
|
|
|
|
|
|
|
|
new_file = deep_copy(file)
|
2022-02-04 16:19:11 +00:00
|
|
|
new_file.extension = trans_config.file_extension
|
2022-02-04 13:54:10 +00:00
|
|
|
|
2022-02-06 16:35:39 +00:00
|
|
|
dest_filepath = ""
|
|
|
|
|
|
|
|
if self.args.single_directory:
|
|
|
|
dest_filepath = new_file.join_filename()
|
|
|
|
else:
|
|
|
|
dest_filepath = new_file.join_path_from_src()
|
|
|
|
|
2022-02-06 15:20:12 +00:00
|
|
|
dest_filepath = self.args.dest + "/" + dest_filepath
|
2022-02-04 13:54:10 +00:00
|
|
|
|
2022-02-06 16:35:39 +00:00
|
|
|
should_skip_transcode = self.args.skip_existing and path_exists(
|
|
|
|
dest_filepath)
|
|
|
|
|
|
|
|
if should_skip_transcode:
|
2022-02-04 13:54:10 +00:00
|
|
|
print("Skipping transcoding", dest_filepath)
|
|
|
|
self.state.transcoded_files.append(new_file)
|
|
|
|
return
|
|
|
|
|
|
|
|
print("Transcoding", src, "to", dest_filepath)
|
|
|
|
|
2022-02-04 17:13:25 +00:00
|
|
|
transcode(file, trans_config, dest_filepath)
|
2022-02-04 13:54:10 +00:00
|
|
|
|
|
|
|
self.state.transcoded_files.append(new_file)
|
|
|
|
|
|
|
|
def transcode_files(self):
|
|
|
|
print("Transcoding Files")
|
|
|
|
|
2022-02-06 15:20:12 +00:00
|
|
|
if not self.args.single_directory:
|
2022-02-06 16:35:39 +00:00
|
|
|
# set can't contain duplicates
|
|
|
|
# so it only creates a dir once
|
2022-02-04 13:54:10 +00:00
|
|
|
directories = set()
|
|
|
|
for file in self.state.files:
|
|
|
|
directories.add(file.path_from_src)
|
|
|
|
for dir in directories:
|
|
|
|
make_directories(
|
2022-02-06 16:18:14 +00:00
|
|
|
self.args.dest + "/" + dir,
|
|
|
|
exist_ok=True)
|
2022-02-04 13:54:10 +00:00
|
|
|
|
2022-02-06 15:20:12 +00:00
|
|
|
is_transcode_config_set = self.args.custom_transcoder_config is not None
|
|
|
|
|
|
|
|
if self.args.transcode_preset == "copy" and not is_transcode_config_set:
|
2022-02-04 13:54:10 +00:00
|
|
|
for file in self.state.files:
|
|
|
|
self._transcode_copy(file)
|
|
|
|
return
|
2022-02-06 15:20:12 +00:00
|
|
|
else:
|
|
|
|
global trans_config
|
|
|
|
trans_config = None
|
|
|
|
if is_transcode_config_set:
|
|
|
|
with open(self.args.custom_transcoder_config, "r+") as f:
|
|
|
|
trans_config = TranscodeConfig()
|
|
|
|
trans_config.load_from_file(f)
|
|
|
|
else:
|
2022-02-06 16:18:14 +00:00
|
|
|
trans_config = get_transcode_config(
|
|
|
|
self.args.transcode_preset)
|
2022-02-06 15:20:12 +00:00
|
|
|
|
2022-02-04 13:54:10 +00:00
|
|
|
for file in self.state.files:
|
2022-02-06 15:20:12 +00:00
|
|
|
self._transcode_with_config(
|
|
|
|
file, trans_config)
|
2022-02-04 16:19:11 +00:00
|
|
|
|
|
|
|
def create_mappings(self):
|
|
|
|
with open(self.dest + "/" + "mappings.txt", "w") as f:
|
|
|
|
f.write("\n".join([
|
|
|
|
f"{file.path_from_src} <- {file.filename}" for file in self.state.files
|
|
|
|
]))
|
2022-02-06 15:51:42 +00:00
|
|
|
|
|
|
|
def add_replaygain_tags(self):
|
|
|
|
print("Adding ReplayGain Tags")
|
|
|
|
|
|
|
|
for file in self.state.files:
|
2022-02-06 16:18:14 +00:00
|
|
|
print(
|
|
|
|
f"Adding ReplayGain Tags to \"{file.filename}.{file.extension}\"")
|
2022-02-06 15:51:42 +00:00
|
|
|
|
2022-02-06 16:18:14 +00:00
|
|
|
do_replaygain(file)
|