Fix line-endings; Increase portability; Add speedtyper.py

This commit is contained in:
olari
2019-05-26 23:05:28 +03:00
parent 661a5984a3
commit 63a1b4f501
33 changed files with 1447 additions and 1341 deletions

View File

@@ -1,257 +1,257 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# overly complicated flac reencoder originally written for the big touhou music torrent on nyaa.si
# contains example of process calling and multithreading
# not recommended to use (probably destructive)
import os, re, time
import subprocess
import wave
from queue import Queue
from threading import Thread
f = open("errlog", "w", encoding="UTF-8")
def remove_if_exists(filename):
if os.path.exists(filename):
os.remove(filename)
def opus_enc(queue, split_track_filename, track, quality=140.0):
subprocess.call([
'opusenc',
'--vbr',
'--bitrate', str(quality),
#'--comp', 10, #default
#'--framesize', '60', # default 20
'--artist', track.performer,
'--comment', 'tracknumber={}'.format(track.index),
'--title', track.title,
'--date', track.cd_date,
'--genre', track.cd_genre,
'--album', track.cd_title,
split_track_filename,
'{}.opus'.format(os.path.splitext(split_track_filename)[0]),
])
queue.get()
class Track():
def __init__(self, track_index, filename, parent):
for member in ('cd_performer', 'cd_title', 'cd_date', 'cd_genre'):
setattr(self, member, getattr(parent, member))
self.filename = filename
self.filepath = filename[:filename.rfind('\\')+1]
self.title = ''
self.index = track_index
self.performer = self.cd_performer
self.time = { 1:0.0 }
def __str__(self):
return "{} - {} - {}".format(self.index, self.title, self.time)
class CueSheet():
def __init__(self, filename):
self.filename = filename
self.filepath = filename[:filename.rfind('\\')+1]
self.cd_performer = ''
self.cd_title = ''
self.cd_genre = ''
self.cd_date = ''
self.current_file = ''
self.tracks = []
self.regex_lst = (
(re.compile(r'PERFORMER\s(.+)'), self.__performer),
(re.compile(r'REM DATE\s(.+)'), self.__date),
(re.compile(r'REM GENRE\s(.+)'), self.__genre),
(re.compile(r'TITLE\s(.+)'), self.__title),
(re.compile(r'FILE\s(.+)\sWAVE'), self.__file),
(re.compile(r'TRACK\s(\d{2})\sAUDIO'), self.__track),
(re.compile(r'INDEX\s(\d{2})\s(\d{1,3}:\d{2}:\d{2})'), self.__index),
)
def __str__(self):
value = "Title: {}\nPerformer: {}\nGenre: {}\nDate: {}\n".format(self.cd_title, self.cd_performer, self.cd_genre, self.cd_date)
for track in self.tracks:
value += ' ' + str(track) + '\n'
return value
def read(self):
with open(self.filename, 'r', encoding='utf-8-sig') as f:
for line in f:
for regex, handler in self.regex_lst:
mobj = regex.match(line.strip())
if mobj:
handler(*self.unquote(mobj.groups()))
def split(self):
encoding_queue = multiprocessing.Queue(multiprocessing.cpu_count())
cds = set()
tracks = set()
for i, track in enumerate(self.tracks):
# FATAL: sheet is not for .tta file
if track.filename[-4:] != '.tta':
f.write("\nFilename isn't .tta ({}):\n{}\n".format(track.filename, str(self)))
return
track_path = track.filepath + ' - '.join((track.index, track.title)).replace('?', '').replace('\\', '').replace('\\', '').replace(':', '')
track_opus = track_path + '.opus'
track_wav = track_path + '.wav'
if os.path.exists(track_opus):
f.write("File already exists, continuing... ({})".format(track_opus))
remove_if_exists(track_wav)
continue
cd_wav = track.filename[:-4] + '.wav'
# decode .tta if needed
if not os.path.exists(cd_wav):
# FATAL: no file to decode
if not os.path.exists(track.filename):
f.write("\nFile doesn't exist ({}):\n{}\n".format(track.filename, str(self)))
return
result = subprocess.call([
'tta', #'ttaenc',
'-d',
track.filename,
#'-o',
cd_wav
])
# FATAL: .tta decode failed
if result != 0:
f.write("Failed to decode .tta ({}):\n{}\n\n".format(track.index, str(self)))
return
# remove .tta
remove_if_exists(track.filename)
# split .wav into track
if not os.path.exists(track_wav):
wafi = wave.open(cd_wav, 'rb')
param_names = ('nchannels', 'sampwidth', 'framerate', 'nframes', 'comptype', 'compname')
params = wafi.getparams()
param_dict = dict(zip(param_names, params))
start = int(param_dict['framerate'] * track.time[1])
stop = param_dict['nframes']
if len(sheet.tracks) > i+1 and sheet.tracks[i+1].filename == track.filename:
stop = int(param_dict['framerate'] * sheet.tracks[i+1].time.get(0, sheet.tracks[i+1].time[1]))
wafi_write = wave.open(track_wav, 'wb')
newparams = list(params)
newparams[3] = 0
wafi_write.setparams( tuple(newparams) )
wafi.setpos(start)
wafi_write.writeframes(wafi.readframes(stop-start))
wafi_write.close()
wafi.close()
encoding_queue.put(track_wav)
p = multiprocessing.Process(
target=opus_enc,
args=(
encoding_queue,
track_wav,
track
)
)
p.start()
if cd_wav not in cds:
cds.add(cd_wav)
tracks.add(track_wav)
while not encoding_queue.empty():
time.sleep(0.2)
for cd in cds:
remove_if_exists(cd)
for track in tracks:
remove_if_exists(track)
remove_if_exists(self.filename)
print(self.filename, "done!")
def __performer(self, s):
if not self.tracks:
self.cd_performer = s
else:
self.tracks[-1].performer = s
def __title(self, s):
if not self.tracks:
self.cd_title = s
else:
self.tracks[-1].title = s
def __genre(self, s):
self.cd_genre = s
def __date(self, s):
self.cd_date = s
def __file(self, s):
self.current_file = s
def __track(self, s):
self.tracks.append( Track(s, self.filepath + self.current_file, self) )
def __index(self, idx, s):
idx = int(idx)
self.tracks[-1].time[idx] = self.index_split(s)
@staticmethod
def index_split(s):
t = s.split(':')
return float(t[0])*60 + float(t[1]) + float(t[2]) / 75.0
@staticmethod
def dqstrip(s):
if s[0] == '"' and s[-1] == '"': return s[1:-1]
return s
@staticmethod
def unquote(t):
return tuple([CueSheet.dqstrip(s.strip()) for s in t])
class SplitterWorker(Thread):
def __init__(self, queue, filename):
Thread.__init__(self)
self.queue = queue
self.filename = filename
def run(self):
sheet = CueSheet(self.filename)
sheet.read()
sheet.split()
if __name__ == '__main__':
queue = Queue()
for root, dirs, files in os.walk('.'):
for name in files:
if name[-4:].lower() == '.cue':
worker = SplitterWorker(queue, root + '\\' + name)
worker.daemon = True
worker.start()
if os.path.exists('./stop'):
exit(1)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# overly complicated flac reencoder originally written for the big touhou music torrent on nyaa.si
# contains example of process calling and multithreading
# not recommended to use (probably destructive)
import os, re, time
import subprocess
import wave
from queue import Queue
from threading import Thread
f = open("errlog", "w", encoding="UTF-8")
def remove_if_exists(filename):
if os.path.exists(filename):
os.remove(filename)
def opus_enc(queue, split_track_filename, track, quality=140.0):
subprocess.call([
'opusenc',
'--vbr',
'--bitrate', str(quality),
#'--comp', 10, #default
#'--framesize', '60', # default 20
'--artist', track.performer,
'--comment', 'tracknumber={}'.format(track.index),
'--title', track.title,
'--date', track.cd_date,
'--genre', track.cd_genre,
'--album', track.cd_title,
split_track_filename,
'{}.opus'.format(os.path.splitext(split_track_filename)[0]),
])
queue.get()
class Track():
def __init__(self, track_index, filename, parent):
for member in ('cd_performer', 'cd_title', 'cd_date', 'cd_genre'):
setattr(self, member, getattr(parent, member))
self.filename = filename
self.filepath = filename[:filename.rfind('\\')+1]
self.title = ''
self.index = track_index
self.performer = self.cd_performer
self.time = { 1:0.0 }
def __str__(self):
return "{} - {} - {}".format(self.index, self.title, self.time)
class CueSheet():
def __init__(self, filename):
self.filename = filename
self.filepath = filename[:filename.rfind('\\')+1]
self.cd_performer = ''
self.cd_title = ''
self.cd_genre = ''
self.cd_date = ''
self.current_file = ''
self.tracks = []
self.regex_lst = (
(re.compile(r'PERFORMER\s(.+)'), self.__performer),
(re.compile(r'REM DATE\s(.+)'), self.__date),
(re.compile(r'REM GENRE\s(.+)'), self.__genre),
(re.compile(r'TITLE\s(.+)'), self.__title),
(re.compile(r'FILE\s(.+)\sWAVE'), self.__file),
(re.compile(r'TRACK\s(\d{2})\sAUDIO'), self.__track),
(re.compile(r'INDEX\s(\d{2})\s(\d{1,3}:\d{2}:\d{2})'), self.__index),
)
def __str__(self):
value = "Title: {}\nPerformer: {}\nGenre: {}\nDate: {}\n".format(self.cd_title, self.cd_performer, self.cd_genre, self.cd_date)
for track in self.tracks:
value += ' ' + str(track) + '\n'
return value
def read(self):
with open(self.filename, 'r', encoding='utf-8-sig') as f:
for line in f:
for regex, handler in self.regex_lst:
mobj = regex.match(line.strip())
if mobj:
handler(*self.unquote(mobj.groups()))
def split(self):
encoding_queue = multiprocessing.Queue(multiprocessing.cpu_count())
cds = set()
tracks = set()
for i, track in enumerate(self.tracks):
# FATAL: sheet is not for .tta file
if track.filename[-4:] != '.tta':
f.write("\nFilename isn't .tta ({}):\n{}\n".format(track.filename, str(self)))
return
track_path = track.filepath + ' - '.join((track.index, track.title)).replace('?', '').replace('\\', '').replace('\\', '').replace(':', '')
track_opus = track_path + '.opus'
track_wav = track_path + '.wav'
if os.path.exists(track_opus):
f.write("File already exists, continuing... ({})".format(track_opus))
remove_if_exists(track_wav)
continue
cd_wav = track.filename[:-4] + '.wav'
# decode .tta if needed
if not os.path.exists(cd_wav):
# FATAL: no file to decode
if not os.path.exists(track.filename):
f.write("\nFile doesn't exist ({}):\n{}\n".format(track.filename, str(self)))
return
result = subprocess.call([
'tta', #'ttaenc',
'-d',
track.filename,
#'-o',
cd_wav
])
# FATAL: .tta decode failed
if result != 0:
f.write("Failed to decode .tta ({}):\n{}\n\n".format(track.index, str(self)))
return
# remove .tta
remove_if_exists(track.filename)
# split .wav into track
if not os.path.exists(track_wav):
wafi = wave.open(cd_wav, 'rb')
param_names = ('nchannels', 'sampwidth', 'framerate', 'nframes', 'comptype', 'compname')
params = wafi.getparams()
param_dict = dict(zip(param_names, params))
start = int(param_dict['framerate'] * track.time[1])
stop = param_dict['nframes']
if len(sheet.tracks) > i+1 and sheet.tracks[i+1].filename == track.filename:
stop = int(param_dict['framerate'] * sheet.tracks[i+1].time.get(0, sheet.tracks[i+1].time[1]))
wafi_write = wave.open(track_wav, 'wb')
newparams = list(params)
newparams[3] = 0
wafi_write.setparams( tuple(newparams) )
wafi.setpos(start)
wafi_write.writeframes(wafi.readframes(stop-start))
wafi_write.close()
wafi.close()
encoding_queue.put(track_wav)
p = multiprocessing.Process(
target=opus_enc,
args=(
encoding_queue,
track_wav,
track
)
)
p.start()
if cd_wav not in cds:
cds.add(cd_wav)
tracks.add(track_wav)
while not encoding_queue.empty():
time.sleep(0.2)
for cd in cds:
remove_if_exists(cd)
for track in tracks:
remove_if_exists(track)
remove_if_exists(self.filename)
print(self.filename, "done!")
def __performer(self, s):
if not self.tracks:
self.cd_performer = s
else:
self.tracks[-1].performer = s
def __title(self, s):
if not self.tracks:
self.cd_title = s
else:
self.tracks[-1].title = s
def __genre(self, s):
self.cd_genre = s
def __date(self, s):
self.cd_date = s
def __file(self, s):
self.current_file = s
def __track(self, s):
self.tracks.append( Track(s, self.filepath + self.current_file, self) )
def __index(self, idx, s):
idx = int(idx)
self.tracks[-1].time[idx] = self.index_split(s)
@staticmethod
def index_split(s):
t = s.split(':')
return float(t[0])*60 + float(t[1]) + float(t[2]) / 75.0
@staticmethod
def dqstrip(s):
if s[0] == '"' and s[-1] == '"': return s[1:-1]
return s
@staticmethod
def unquote(t):
return tuple([CueSheet.dqstrip(s.strip()) for s in t])
class SplitterWorker(Thread):
def __init__(self, queue, filename):
Thread.__init__(self)
self.queue = queue
self.filename = filename
def run(self):
sheet = CueSheet(self.filename)
sheet.read()
sheet.split()
if __name__ == '__main__':
queue = Queue()
for root, dirs, files in os.walk('.'):
for name in files:
if name[-4:].lower() == '.cue':
worker = SplitterWorker(queue, root + '\\' + name)
worker.daemon = True
worker.start()
if os.path.exists('./stop'):
exit(1)