def serve(port=8765, interface='0.0.0.0', installSignalHandlers=0, nthreads=4, ntranscriptionthreads=2, data_dir=get_datadir('webdata')): logging.info("SERVE %d, %s, %d", port, interface, installSignalHandlers) if not os.path.exists(data_dir): os.makedirs(data_dir) zip_dir = os.path.join(data_dir, 'zip') if not os.path.exists(zip_dir): os.makedirs(zip_dir) f = File(data_dir) f.putChild(b'', File(get_resource('www/index.html'))) f.putChild(b'status.html', File(get_resource('www/status.html'))) f.putChild(b'preloader.gif', File(get_resource('www/preloader.gif'))) trans = Transcriber(data_dir, nthreads=nthreads, ntranscriptionthreads=ntranscriptionthreads) trans_ctrl = TranscriptionsController(trans) f.putChild(b'transcriptions', trans_ctrl) trans_zippr = TranscriptionZipper(zip_dir, trans) f.putChild(b'zip', trans_zippr) s = Site(f) logging.info("about to listen") reactor.listenTCP(port, s, interface=interface) logging.info("listening") reactor.run(installSignalHandlers=installSignalHandlers)
def serve(port=8765, interface='0.0.0.0', installSignalHandlers=0, nthreads=4, ntranscriptionthreads=2, data_dir=get_datadir('webdata')): logging.info("SERVE %d, %s, %d", port, interface, installSignalHandlers) if not os.path.exists(data_dir): os.makedirs(data_dir) zip_dir = os.path.join(data_dir, 'zip') if not os.path.exists(zip_dir): os.makedirs(zip_dir) f = File(data_dir) f.putChild('', File(get_resource('www/index.html'))) f.putChild('status.html', File(get_resource('www/status.html'))) f.putChild('preloader.gif', File(get_resource('www/preloader.gif'))) trans = Transcriber(data_dir, nthreads=nthreads, ntranscriptionthreads=ntranscriptionthreads) trans_ctrl = TranscriptionsController(trans) f.putChild('transcriptions', trans_ctrl) trans_zippr = TranscriptionZipper(zip_dir, trans) f.putChild('zip', trans_zippr) s = Site(f) logging.info("about to listen") reactor.listenTCP(port, s, interface=interface) logging.info("listening") reactor.run(installSignalHandlers=installSignalHandlers)
def serve(port=8765, interface='0.0.0.0', installSignalHandlers=0, nthreads=4, ntranscriptionthreads=2, data_dir=get_datadir('webdata'), modelDir='exp'): logging.info("SERVE %d, %s, %d", port, interface, installSignalHandlers) if not os.path.exists(data_dir): os.makedirs(data_dir) zip_dir = os.path.join(data_dir, 'zip') if not os.path.exists(zip_dir): os.makedirs(zip_dir) f = File(data_dir) f.putChild('', File(get_resource('www/index.html'))) f.putChild('status.html', File(get_resource('www/status.html'))) f.putChild('preloader.gif', File(get_resource('www/preloader.gif'))) resources = gentle.Resources(modelDir) trans = Transcriber(data_dir, nthreads=nthreads, ntranscriptionthreads=ntranscriptionthreads, modelDir=modelDir) config = trans.config logging.info("CONFIG: samplerate %d, silencephones %s, context-width %s", config['samplerate'], config['silencephones'], config['context-width']) trans_ctrl = TranscriptionsController(trans) f.putChild('transcriptions', trans_ctrl) trans_zippr = TranscriptionZipper(zip_dir, trans) f.putChild('zip', trans_zippr) s = Site(f) logging.info("about to listen") reactor.listenTCP(port, s, interface=interface) logging.info("listening") reactor.run(installSignalHandlers=installSignalHandlers)
def render_POST(self, req): uid = self.transcriber.next_id() tran = req.args.get(b'transcript', [b''])[0].decode() audio = req.args[b'audio'][0] disfluency = True if b'disfluency' in req.args else False conservative = True if b'conservative' in req.args else False kwargs = { 'disfluency': disfluency, 'conservative': conservative, 'disfluencies': set(['uh', 'um']) } async = True if b'async' in req.args and req.args[b'async'][0] == b'false': async = False # We need to make the transcription directory here, so that # when we redirect the user we are sure that there's a place # for them to go. outdir = os.path.join(self.transcriber.data_dir, 'transcriptions', uid) os.makedirs(outdir) # Copy over the HTML shutil.copy(get_resource('www/view_alignment.html'), os.path.join(outdir, 'index.html')) result_promise = threads.deferToThreadPool(reactor, reactor.getThreadPool(), self.transcriber.transcribe, uid, tran, audio, async, **kwargs) if not async: def write_result(result): '''Write JSON to client on completion''' req.setHeader("Content-Type", "application/json") req.setHeader("X-Transcription-Id", uid) req.write(result.to_json(uid=uid, indent=2).encode()) req.finish() result_promise.addCallback(write_result) result_promise.addErrback(lambda _: None) # ignore errors req.notifyFinish().addErrback(lambda _: result_promise.cancel()) return NOT_DONE_YET req.setResponseCode(FOUND) req.setHeader(b"Location", "/transcriptions/%s" % (uid)) return b''
def render_POST(self, req): uid = self.transcriber.next_id() tran = req.args.get(b'transcript', [b''])[0].decode() audio = req.args[b'audio'][0] disfluency = True if b'disfluency' in req.args else False conservative = True if b'conservative' in req.args else False kwargs = {'disfluency': disfluency, 'conservative': conservative, 'disfluencies': set(['uh', 'um'])} async = True if b'async' in req.args and req.args[b'async'][0] == b'false': async = False # We need to make the transcription directory here, so that # when we redirect the user we are sure that there's a place # for them to go. outdir = os.path.join(self.transcriber.data_dir, 'transcriptions', uid) os.makedirs(outdir) # Copy over the HTML shutil.copy(get_resource('www/view_alignment.html'), os.path.join(outdir, 'index.html')) result_promise = threads.deferToThreadPool( reactor, reactor.getThreadPool(), self.transcriber.transcribe, uid, tran, audio, async, **kwargs) if not async: def write_result(result): '''Write JSON to client on completion''' req.setHeader("Content-Type", "application/json") req.write(result.to_json(indent=2).encode()) req.finish() result_promise.addCallback(write_result) result_promise.addErrback(lambda _: None) # ignore errors req.notifyFinish().addErrback(lambda _: result_promise.cancel()) return NOT_DONE_YET req.setResponseCode(FOUND) req.setHeader(b"Location", "/transcriptions/%s" % (uid)) return b''
class Transcriber(): def __init__(self, data_dir, nthreads=4, ntranscriptionthreads=2): self.data_dir = data_dir self.nthreads = nthreads self.ntranscriptionthreads = ntranscriptionthreads self.resources = gentle.Resources() self.full_transcriber = gentle.FullTranscriber(self.resources, nthreads=ntranscriptionthreads) self._status_dicts = {} def get_status(self, uid): return self._status_dicts.setdefault(uid, {}) def out_dir(self, uid): return os.path.join(self.data_dir, 'transcriptions', uid) # TODO(maxhawkins): refactor so this is returned by transcribe() def next_id(self): uid = None while uid is None or os.path.exists(os.path.join(self.data_dir, uid)): uid = uuid.uuid4().get_hex()[:8] return uid def transcribe(self, uid, transcript, audio, async, **kwargs): status = self.get_status(uid) status['status'] = 'STARTED' output = { 'transcript': transcript } outdir = os.path.join(self.data_dir, 'transcriptions', uid) tran_path = os.path.join(outdir, 'transcript.txt') with open(tran_path, 'w') as tranfile: tranfile.write(transcript) audio_path = os.path.join(outdir, 'upload') with open(audio_path, 'w') as wavfile: wavfile.write(audio) status['status'] = 'ENCODING' wavfile = os.path.join(outdir, 'a.wav') if gentle.resample(os.path.join(outdir, 'upload'), wavfile) != 0: status['status'] = 'ERROR' status['error'] = "Encoding failed. Make sure that you've uploaded a valid media file." # Save the status so that errors are recovered on restart of the server # XXX: This won't work, because the endpoint will override this file with open(os.path.join(outdir, 'status.json'), 'w') as jsfile: json.dump(status, jsfile, indent=2) return #XXX: Maybe we should pass this wave object instead of the # file path to align_progress wav_obj = wave.open(wavfile, 'r') status['duration'] = wav_obj.getnframes() / float(wav_obj.getframerate()) status['status'] = 'TRANSCRIBING' def on_progress(p): for k,v in p.items(): status[k] = v if len(transcript.strip()) > 0: trans = gentle.ForcedAligner(self.resources, transcript, nthreads=self.nthreads, **kwargs) elif self.full_transcriber.available: trans = self.full_transcriber else: status['status'] = 'ERROR' status['error'] = 'No transcript provided and no language model for full transcription' return output = trans.transcribe(wavfile, progress_cb=on_progress, logging=logging) # ...remove the original upload os.unlink(os.path.join(outdir, 'upload')) # Save with open(os.path.join(outdir, 'align.json'), 'w') as jsfile: jsfile.write(output.to_json(indent=2)) with open(os.path.join(outdir, 'align.csv'), 'w') as csvfile: csvfile.write(output.to_csv()) # Inline the alignment into the index.html file. htmltxt = open(get_resource('www/view_alignment.html')).read() htmltxt = htmltxt.replace("var INLINE_JSON;", "var INLINE_JSON=%s;" % (output.to_json())); open(os.path.join(outdir, 'index.html'), 'w').write(htmltxt) status['status'] = 'OK' logging.info('done with transcription.') return output
def transcribe(self, uid, transcript, audio, async_mode, **kwargs): status = self.get_status(uid) status['status'] = 'STARTED' output = {'transcript': transcript} outdir = os.path.join(self.data_dir, 'transcriptions', uid) tran_path = os.path.join(outdir, 'transcript.txt') with open(tran_path, 'w') as tranfile: tranfile.write(transcript) if not isinstance(audio, str): audio_path = os.path.join(outdir, 'upload') with open(audio_path, 'wb') as wavfile: wavfile.write(audio) status['status'] = 'ENCODING' wavfile = os.path.join(outdir, 'a.wav') # if ((not isinstance(audio, str)) and gentle.resample(os.path.join(outdir, 'upload'), wavfile) != 0) or gentle.resample(audio, wavfile) != 0: if (not isinstance(audio, str)) and gentle.resample( os.path.join(outdir, 'upload'), wavfile) != 0: status['status'] = 'ERROR' status[ 'error'] = "Encoding failed. Make sure that you've uploaded a valid media file." # Save the status so that errors are recovered on restart of the server # XXX: This won't work, because the endpoint will override this file with open(os.path.join(outdir, 'status.json'), 'w') as jsfile: json.dump(status, jsfile, indent=2) return if isinstance(audio, str) and gentle.resample(audio, wavfile) != 0: status['status'] = 'ERROR' status[ 'error'] = "Encoding failed. Make sure that you've referenced a valid media URL." # Save the status so that errors are recovered on restart of the server # XXX: This won't work, because the endpoint will override this file with open(os.path.join(outdir, 'status.json'), 'w') as jsfile: json.dump(status, jsfile, indent=2) return # XXX: Maybe we should pass this wave object instead of the # file path to align_progress if not isinstance(audio, str): wav_obj = wave.open(wavfile, 'rb') status['duration'] = wav_obj.getnframes() / \ float(wav_obj.getframerate()) status['status'] = 'TRANSCRIBING' def on_progress(p): print(p) for k, v in p.items(): status[k] = v if len(transcript.strip()) > 0: trans = gentle.ForcedAligner(self.resources, transcript, nthreads=self.nthreads, **kwargs) elif self.full_transcriber.available: trans = self.full_transcriber else: status['status'] = 'ERROR' status[ 'error'] = 'No transcript provided and no language model for full transcription' return output = trans.transcribe(wavfile, progress_cb=on_progress, logging=logging) # ...remove the original upload if not isinstance(audio, str): os.unlink(os.path.join(outdir, 'upload')) # Save with open(os.path.join(outdir, 'align.json'), 'w') as jsfile: jsfile.write(output.to_json(indent=2)) with open(os.path.join(outdir, 'align.csv'), 'w') as csvfile: csvfile.write(output.to_csv()) # Inline the alignment into the index.html file. htmltxt = open(get_resource('www/view_alignment.html')).read() htmltxt = htmltxt.replace("var INLINE_JSON;", "var INLINE_JSON=%s;" % (output.to_json())) open(os.path.join(outdir, 'index.html'), 'w').write(htmltxt) status['status'] = 'OK' logging.info('done with transcription.') return output
def render_POST(self, req): def quitGentle(restart): req.setHeader("Content-Type", "application/json") req.write("{ message: PROCESS_ENDED }") req.finish() time.sleep(0.5) KILL_CMD_K3 = "taskkill /F /IM gentleK3.exe /T" if os.name == "nt" else "pkill -f gentleK3" KILL_CMD_M3 = "taskkill /F /IM gentleM3.exe /T" if os.name == "nt" else "pkill -f gentleM3" RESTART_CMD_WIN = [ "timeout", "1", "&&", "START", "/min", "cursesearch.exe" ] RESTART_CMD_MAC = [ "sleep 1 && nohup ./cursesearch </dev/null >/dev/null 2>&1 &" ] os.system(KILL_CMD_K3) os.system(KILL_CMD_M3) reactor.stop() if restart is True: if os.name == "nt": subprocess.Popen(RESTART_CMD_WIN, shell=True, stdin=None, stdout=None, stderr=None, close_fds=True, cwd=os.path.dirname(sys.executable)) else: subprocess.Popen(RESTART_CMD_MAC, shell=True, cwd=os.path.dirname(sys.executable)) sys.exit(0) if "stop" in req.args and req.args["stop"][0]: quitGentle(True) if "stopNoRestart" in req.args and req.args["stopNoRestart"][0]: quitGentle(False) free_space = psutil.disk_usage(".").free / (1024 * 1024) if free_space < 250: shutil.rmtree( os.path.join(self.transcriber.data_dir, 'transcriptions')) uid = self.transcriber.next_id() tran = req.args.get('transcript', [''])[0] audio = req.args['audio'][0] disfluency = True if 'disfluency' in req.args else False conservative = True if 'conservative' in req.args else False kwargs = { 'disfluency': disfluency, 'conservative': conservative, 'disfluencies': set(['uh', 'um']) } async = True if 'async' in req.args and req.args['async'][0] == 'false': async = False # We need to make the transcription directory here, so that # when we redirect the user we are sure that there's a place # for them to go. outdir = os.path.join(self.transcriber.data_dir, 'transcriptions', uid) os.makedirs(outdir) # Copy over the HTML shutil.copy(get_resource('www/view_alignment.html'), os.path.join(outdir, 'index.html')) result_promise = threads.deferToThreadPool(reactor, reactor.getThreadPool(), self.transcriber.transcribe, uid, tran, audio, async, **kwargs) if not async: def write_result(result): '''Write JSON to client on completion''' req.setHeader("Content-Type", "application/json") req.write(result.to_json(indent=2)) req.finish() result_promise.addCallback(write_result) result_promise.addErrback(lambda _: None) # ignore errors req.notifyFinish().addErrback(lambda _: result_promise.cancel()) return NOT_DONE_YET req.setResponseCode(FOUND) req.setHeader(b"Location", "/transcriptions/%s" % (uid)) return ''
class Transcriber(): def __init__(self, data_dir, nthreads=4, ntranscriptionthreads=2): self.data_dir = data_dir self.nthreads = nthreads self.ntranscriptionthreads = ntranscriptionthreads self.resources = gentle.Resources() self.full_transcriber = gentle.FullTranscriber( self.resources, nthreads=ntranscriptionthreads) self._status_dicts = {} def get_status(self, uid): return self._status_dicts.setdefault(uid, {}) def out_dir(self, uid): return os.path.join(self.data_dir, 'transcriptions', uid) # TODO(maxhawkins): refactor so this is returned by transcribe() def next_id(self): uid = None while uid is None or os.path.exists(os.path.join(self.data_dir, uid)): uid = uuid.uuid4().get_hex()[:8] return uid def transcribe(self, uid, transcript, audio, async, **kwargs): status = self.get_status(uid) status['status'] = 'STARTED' output = {'transcript': transcript} outdir = os.path.join(self.data_dir, 'transcriptions', uid) tran_path = os.path.join(outdir, 'transcript.txt') with open(tran_path, 'w') as tranfile: tranfile.write(transcript) audio_path = os.path.join(outdir, 'upload') with open(audio_path, 'w') as wavfile: wavfile.write(audio) status['status'] = 'ENCODING' wavfile = os.path.join(outdir, 'a.wav') if gentle.resample(os.path.join(outdir, 'upload'), wavfile) != 0: status['status'] = 'ERROR' status[ 'error'] = "Encoding failed. Make sure that you've uploaded a valid media file." # Save the status so that errors are recovered on restart of the server # XXX: This won't work, because the endpoint will override this file with open(os.path.join(outdir, 'status.json'), 'w') as jsfile: json.dump(status, jsfile, indent=2) return #XXX: Maybe we should pass this wave object instead of the # file path to align_progress wav_obj = wave.open(wavfile, 'r') status['duration'] = wav_obj.getnframes() / float( wav_obj.getframerate()) status['status'] = 'TRANSCRIBING' def on_progress(p): for k, v in p.items(): status[k] = v if len(transcript.strip()) > 0: trans = gentle.ForcedAligner(self.resources, transcript, nthreads=self.nthreads, **kwargs) elif self.full_transcriber.available: trans = self.full_transcriber else: status['status'] = 'ERROR' status[ 'error'] = 'No transcript provided and no language model for full transcription' return output = trans.transcribe(wavfile, progress_cb=on_progress, logging=logging) # ...remove the original upload os.unlink(os.path.join(outdir, 'upload')) # Save with open(os.path.join(outdir, 'align.json'), 'w') as jsfile: jsfile.write(output.to_json(indent=2)) with open(os.path.join(outdir, 'align.csv'), 'w') as csvfile: csvfile.write(output.to_csv()) # add file datas sens_end_index = trans.ms.get_sentences_index() res = output.to_json() res = json.loads(res, encoding='utf-8', strict=True) time_sentences_index = [] ss_dot = 0 s_pos = None time_pos = 0 try: for i, w in enumerate(res['words']): if w["case"] != "success": continue end_v = w['endOffset'] start_v = w['startOffset'] if s_pos is None: s_pos = start_v time_pos = i if end_v >= sens_end_index[ss_dot]: ss_dot += 1 time_sentences_index.append( (res['words'][time_pos]["start"], res['words'][i]["end"])) time_pos = i s_pos = end_v if len(sens_end_index) != len(time_sentences_index): time_sentences_index.append( (res['words'][time_pos]["start"], res['words'][-1]["end"])) #print sens_end_index, len(sens_end_index) #print time_sentences_index, len(time_sentences_index) sens_str = trans.ms.get_sentences_string() save_ss = "" for i, t in enumerate(time_sentences_index): #print "{{time}}%s/%s{{end}}" % (str(round(float(t[0]), 2)), str(round(float(t[1]), 2))) #print "{{raw}}%s{{end}}" % (str(sens_str[i])) save_ss += "{{time}}" + str(round(float(t[0]), 2)) + "/" + str( round(float(t[1]), 2)) + "{{end}}\n" save_ss += "{{raw}}" + sens_str[i] + "{{end}}\n" with open(os.path.join(outdir, 'time.csv'), 'w') as timefile: timefile.write(save_ss) except Exception as e: print traceback.format_exc() # Inline the alignment into the index.html file. htmltxt = open(get_resource('www/view_alignment.html')).read() htmltxt = htmltxt.replace("var INLINE_JSON;", "var INLINE_JSON=%s;" % (output.to_json())) open(os.path.join(outdir, 'index.html'), 'w').write(htmltxt) status['status'] = 'OK' logging.info('done with transcription.') return output
# Start a thread for the web server. webthread = threading.Thread(target=serve.serve, args=(PORT, )) webthread.start() def open_browser(): webbrowser.open("http://localhost:%d/" % (PORT)) def open_about(): webbrowser.open("https://lowerquality.com/gentle") dl = None progress = None fstpath = get_resource('exp/tdnn_7b_chain_online/graph_pp/HCLG.fst') def download_full_language_model(): global progress, dl fstdir = os.path.dirname(fstpath) try: os.makedirs(fstdir) except OSError: pass progress = QtWidgets.QProgressBar() progress.setRange(0, 100)
def render_POST(self, req): uid = self.transcriber.next_id() content = json.loads(req.content.read().decode("utf-8")) tran = content["transcript"] audioUrl = content["audioUrl"] trim = content["trim"] audioResult = requests.get(audioUrl, allow_redirects=True) audio = audioResult.content disfluency = True if b'disfluency' in req.args else False conservative = True if b'conservative' in req.args else False kwargs = {'disfluency': disfluency, 'conservative': conservative, 'disfluencies': set(['uh', 'um'])} async_mode = True if b'async' in req.args and req.args[b'async'][0] == b'false': async_mode = False # We need to make the transcription directory here, so that # when we redirect the user we are sure that there's a place # for them to go. outdir = os.path.join(self.transcriber.data_dir, 'transcriptions', uid) os.makedirs(outdir) # Copy over the HTML shutil.copy(get_resource('www/view_alignment.html'), os.path.join(outdir, 'index.html')) result_promise = threads.deferToThreadPool( reactor, reactor.getThreadPool(), self.transcriber.transcribe, uid, tran, audio, async_mode, trim, **kwargs) if not async_mode: def write_result(result): '''Write JSON to client on completion''' req.setHeader("Content-Type", "application/json") req.setHeader("Access-Control-Allow-Origin", "*") req.setHeader("Access-Control-Allow-Credentials", "True") req.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") req.write(result.to_json(indent=2).encode()) req.finish() result_promise.addCallback(write_result) result_promise.addErrback(lambda _: None) # ignore errors req.notifyFinish().addErrback(lambda _: result_promise.cancel()) return NOT_DONE_YET req.setHeader("Content-Type", "application/json") req.setHeader("Access-Control-Allow-Origin", "*") req.setHeader("Access-Control-Allow-Credentials", "True") req.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") result = { "uid": uid } req.write(json.dumps(result).encode('utf-8')) req.finish() return NOT_DONE_YET
PORT = get_open_port(8765) # Start a thread for the web server. webthread = threading.Thread(target=serve.serve, args=(PORT,)) webthread.start() def open_browser(): webbrowser.open("http://localhost:%d/" % (PORT)) def open_about(): webbrowser.open("https://lowerquality.com/gentle") dl = None progress = None fstpath = get_resource('exp/tdnn_7b_chain_online/graph_pp/HCLG.fst') def download_full_language_model(): global progress, dl fstdir = os.path.dirname(fstpath) try: os.makedirs(fstdir) except OSError: pass progress = QtWidgets.QProgressBar() progress.setRange(0, 100) trans.hide()