peegen4k/peegen4k.py
2022-05-04 18:05:09 +02:00

163 lines
5.5 KiB
Python

import argparse, os, hashlib, random, mutagen, progressbar, ffmpeg, time
# get input
parser = argparse.ArgumentParser(description="automatically create shuffled music playlist file from folder")
parser.add_argument("input", help="input folder", metavar="input", type=str)
parser.add_argument("output", help="output file", metavar="output", type=str)
parser.add_argument("-d", "--dedup", help="deduplication mode (sha1/filename)", required=False, default="sha1", type=str)
parser.add_argument("-t", "--outputtype", help="output type (onefile/folder)", required=False, default="onefile", type=str)
parser.add_argument("-f", "--force", help="overwrite without asking", required=False, type=bool, default=False)
args = parser.parse_args()
# make sure arguments are valid
if (not os.path.isdir(args.input)):
print("input folder does not exist")
exit(1)
if (args.dedup != "sha1" and args.dedup != "filename"):
print("deduplication mode is not valid (sha1/filename)")
exit(1)
if (args.outputtype != "onefile" and args.outputtype != "folder"):
print("output type is not valid (onefile/folder)")
exit(1)
# check if output exists already, warn user about potential overwriting
if (args.outputtype == "onefile"):
if (os.path.isfile(args.output) and not args.force):
print("output file already exists, overwrite? (y/n)")
answer = input()
if (answer.lower() != "y"):
exit()
else:
if (os.path.isdir(args.output) and not args.force):
print("output folder already exists, overwrite files inside? (y/n)")
answer = input()
if (answer.lower() != "y"):
exit()
print("fyi: this doesnt delete files in the folder, only overwrites songs already present")
# scan input folder
f_files = []
f_special = []
allowedfiletypes = ["mp3", "opus", "flac", "wav", "aac", "m4a"] # what else?
print("scanning folders...")
pbar = progressbar.ProgressBar(max_value=progressbar.UnknownLength)
def scanfolder(folder):
# goes through a folder. checks all the files in it
# then recursively goes through all subfolders
progress = 0
dirs_to_recurse = []
global f_files, f_special
for file in os.listdir(folder):
if (os.path.isfile(folder + "/" + file)):
# make sure file is audio file
if (file.split(".")[-1].lower() in allowedfiletypes):
# verify not already in list
if (args.dedup == "sha1"):
hash = hashlib.sha1(open(folder + "/" + file, "rb").read()).hexdigest()
if (hash not in f_special):
f_special.append(hash)
f_files.append(folder + "/" + file)
progress += 1
elif (args.dedup == "filename"):
if (file not in f_special):
f_special.append(file)
f_files.append(folder + "/" + file)
elif (os.path.isdir(folder + "/" + file)):
dirs_to_recurse.append(folder + "/" + file)
pbar.update(progress)
for dir in dirs_to_recurse:
scanfolder(dir)
scanfolder(args.input)
pbar.finish()
del f_special
if (len(f_files) == 0):
print("no (supported) audio files found in input folder")
exit(1)
random.shuffle(f_files)
# scan through files. count song length.
# save every scanned file to a new array.
# once we finish scanning, or total length exceeds eight hours, finish
maxlength = 8 * 60 * 60 # 8 hours
length = 0.0
f_final = []
print("generating playlist...")
pbar = progressbar.ProgressBar(max_value=maxlength, redirect_stdout=True)
for x in range(len(f_files)):
try:
audio = mutagen.File(f_files[x])
length += audio.info.length
f_final.append(f_files[x])
except:
print("skipping " + f_files[x] + " (not valid file according to mutagen)")
if (length >= maxlength):
pbar.update(maxlength)
break
else:
pbar.update(int(length))
pbar.finish()
del f_files
if (length < maxlength):
print("warning: not enough songs to fill 8 hours")
# create output folder if needed
if (args.outputtype == "folder"):
if (not os.path.isdir(args.output)):
os.mkdir(args.output)
# start encoding
if (args.outputtype == "onefile"):
f_inputs = []
print("encoding file...")
for file in f_final:
f_inputs.append(ffmpeg.input(file))
stream = ffmpeg.concat(*f_inputs, v=0, a=1)
# make sure args.output contains a file extension
if (args.output.split(".")[-1] not in allowedfiletypes):
stream = ffmpeg.output(stream, args.output + ".opus")
else:
stream = ffmpeg.output(stream, args.output)
ffmpeg.run(stream, overwrite_output=True, quiet=True)
else:
filestoremove = []
print("encoding files to folder...")
for file in f_final:
try:
stream = ffmpeg.input(file)
stream = ffmpeg.output(stream, args.output + "/" + os.path.basename(file) + ".opus")
ffmpeg.run(stream, overwrite_output=True, quiet=True)
except:
print("skipping " + file + " (not valid file according to ffmpeg)")
filestoremove.append(args.output + "/" + os.path.basename(file) + ".opus")
if (len(filestoremove) > 0):
print("removing invalid files (interactive)...")
for file in filestoremove:
if (os.path.isfile(file)):
q = input("remove " + file + "? (y/n) ")
if (q.lower() == "y"):
os.remove(file)
# done
print("all done! :3")