import os
import re
import sys
import subprocess
try:
	import natsort
	from mutagen.mp3 import MP3
except ModuleNotFoundError as e:
	module = e.msg.split("'")[-2]
	print(e, ' Install it by running the command: ', f'pip3 install {module}', sep='\n')


def files(path, out_path):
	print(f'Converting files in: {path}')
	out_path = out_path or path
	infos = []
	tags = {}
	for file in natsort.natsorted(os.listdir(path)):
		if file.endswith('.mp3'):
			mutagen = MP3(os.path.join(path, file))
			length = int(mutagen.info.length*10**9)
			if not tags:
				tags = {
					'title': mutagen.get('TIT2'),
					'artist': mutagen.get('TPE1'),
					'author': mutagen.get('TPE1'),
					'album_artist': mutagen.get('TPE2') or mutagen.get('TPE1'),
					'album': mutagen.get('TALB'),
					'composer': mutagen.get('TCOM'),
					'date': mutagen.get('TDRC'),
					'year': mutagen.get('TDRC'),
					'comment': mutagen.get('COMM::eng') or next(iter([v for k,v in mutagen.tags.items() if k.startswith('COMM')]), None),
					'description': mutagen.get('COMM::eng') or next(iter([v for k,v in mutagen.tags.items() if k.startswith('COMM')]), None),
					'genre': mutagen.get('TCON'),
				}
			infos.append((file, length))
	if not infos:
		return

	name = '{}{}'.format(
		str(tags.get('title').text[0]) if tags.get('title') else '',
		' - '+str(tags.get('album').text[0]) if tags.get('album') and tags.get('album').text != tags.get('title').text else ''
	)
	name = re.sub(r'[<>\"/\\|:?*]', '_', name) or 'book'
			
	with open(os.path.join(path, 'files.txt'), 'w', encoding='utf-8') as f:
		for info in infos:
			fixed = info[0].replace("'", "'\\''")
			f.write(f"file '{fixed}'\n")
			
	with open(os.path.join(path, 'FFMETADATAFILE'), 'w', encoding='utf-8') as f:
		start = 0
		metadata = ';FFMETADATA1\n'
		for k, v in tags.items():
			if v:
				metadata += f'{k}={", ".join([str(x) for x in v.text])}\n'
		for i, info in enumerate(infos, 1):
			metadata += '\n[CHAPTER]\nSTART={}\nSTOP={}\ntitle={}\n'.format(
				start,
				start + info[1],
				f'Chapter {i}'
			)
			start += info[1]
		f.write(metadata)
		
	null = subprocess.DEVNULL
	if os.path.isfile(os.path.join(path, 'cover.jpg')):
		cover = [
			'-i', os.path.join(path, 'cover.jpg'),
			'-disposition:v:0', 'attached_pic'
		]
	else:
		subprocess.run([
			'ffmpeg',
			'-i', os.path.join(path, infos[0][0]),
			'-an',
			'-c:v', 'copy',
			'-map', '0:v:0?',
			os.path.join(path, 'cover.jpg')
		], stderr=null, stdout=null)
		if os.path.isfile(os.path.join(path, 'cover.jpg')):
			cover = [
				'-i', os.path.join(path, 'cover.jpg'),
				'-disposition:v:0', 'attached_pic'
			]
		else:
			cover = []
		
	if path != out_path:
		try:
			os.mkdir(os.path.join(out_path, name))
		except: pass
		output_name = os.path.join(out_path, name, name+'.m4b')
	else:
		output_name = os.path.join(out_path, name+'.m4b')
		
	subprocess.run([
		'ffmpeg',
		'-y',
		'-f', 'concat',
		'-safe', '0',
		'-i', os.path.join(path, 'files.txt'),
		'-i', os.path.join(path, 'FFMETADATAFILE'),
		*cover,
		'-map_metadata', '1',
		'-c', 'copy',
		'-f', 'mp4',
		output_name
	], stderr=null, stdout=null)
	
	os.remove(os.path.join(path, 'files.txt'))
	os.remove(os.path.join(path, 'FFMETADATAFILE'))
	# if cover:
		# os.remove(os.path.join(path, 'cover.jpg'))
	if delete:
		for info in infos:
			os.remove(os.path.join(path, info[0]))

def folders(path, out_path):
	for (p, d, f) in os.walk(path):
		if [x for x in f if '.mp3' in x]:
			files(p, out_path)
	

if __name__ == '__main__':
	#import pdb; pdb.set_trace()
	path = None
	out_path = None
	delete = False
	recursive = False
	listFiles = False
	for i, arg in enumerate(sys.argv[1:], 1):
		if arg in ['-h', '--help']:
			print('python3 convert-mp3-m4b.py{}'.format('\n'.join([
				' [OPTIONS] -i, --in "path"',
				' -h, --help               prints this message',
				' -r, --recursive          prints files in order they\'re processed in',
				' -l, --list               prints files in order they\'re processed in',
				' -d, --delete             deletes .mp3 files after conversion',
				' -o, --out "path"         path to output folder, defaults to folder where .mp3 files are found',
				' -i, --in "path"          required, path to folder containing the .mp3 files',
				'                          folder name is used as a book name',
				'\n !!! The order of files is dependant how your OS sorts files by name !!!'
				' written   by   demcooki.es'
			])))
			sys.exit()
		elif arg in ['-l', '--list']:
			listFiles = True
		elif arg in ['-r', '--recursive']:
			recursive = True
		elif arg in ['-d', '--delete']:
			delete = True
		elif arg in ['-i', '--in']:
			path = sys.argv[i+1]
		elif arg in ['-o', '--out']:
			out_path = sys.argv[i+1]
			
	if not path:
		path = input('mp3 folder path: ')
		if not path:
			sys.exit()
	if listFiles:
		if recursive:
			fileList = [dp+':\n '+'\n '.join([f for f in fn if f.endswith('.mp3')]) for (dp, _, fn) in os.walk(path) if [f for f in fn if f.endswith('.mp3')]]
		else:
			fileList = [x for x in os.listdir(path) if x.endswith('.mp3')]
		fileList = natsort.natsorted(fileList)
		print('The files in .m4b will be in the following order: ', *fileList, sep='\n')
	else:
		if recursive:
			folders(path, out_path)
		else:
			files(path, out_path)
