def ensureMplayerThreads(): global mplayerManager if not mplayerManager: mplayerManager = MplayerMonitor() mplayerManager.daemon = True mplayerManager.start() # ensure the tmpdir() exit handler is registered first so it runs # after the mplayer exit tmpdir() # clean up mplayer on exit atexit.register(stopMplayer)
def ensureMplayerThreads(): global mplayerManager if not mplayerManager: mplayerManager = MplayerMonitor() mplayerManager.daemon = True mplayerManager.start() # ensure the tmpdir() exit handler is registered first so it runs # after the mplayer exit if not aqt.mw.pm.profile.get("mpv.directAccess", True): tmpdir() #TODO: rm if testNoCache passes # clean up mplayer on exit atexit.register(stopMplayer)
def _buildImg(col, latex, fname, model): # add header/footer & convert to utf8 latex = (model["latexPre"] + "\n" + latex + "\n" + model["latexPost"]) latex = latex.encode("utf8") # it's only really secure if run in a jail, but these are the most common for bad in ("write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout"): assert bad not in latex # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texfile = file(namedtmp("tmp.tex"), "w") texfile.write(latex) texfile.close() mdir = col.media.dir() oldcwd = os.getcwd() png = namedtmp("tmp.png") try: # generate dvi os.chdir(tmpdir()) if call(latexCmd + ["tmp.tex"], stdout=log, stderr=log): return _errMsg("latex") # and png if call(latexDviPngCmd + ["tmp.dvi", "-o", "tmp.png"], stdout=log, stderr=log): return _errMsg("dvipng") # add to media shutil.copy2(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd)
def boss_key(self): cmd = self.pm.profile.get("ccbc.bossCmd","notepad.exe") if not cmd: # TODO: Write gui for this option # Use debugger to set command for ccbc.bossCmd showInfo("No external text editor was set.") return if isWin: cmd = cmd.replace('/','\\') anki.sound.clearAudioQueue() import subprocess, time from anki.utils import tmpdir fname = os.path.join(tmpdir(), "note%d.txt"%time.time()) f = open(fname, "w") f.write(self.pm.profile.get("ccbc.bossText","To Whom It May Concern:")) f.close() runHook("BOSS_KEY", True) self.hideAllCollectionWindows() self.hide() try: self.debugDiag.close() except AttributeError: pass self.pm.save() self.col.autosave() subprocess.call('''%s "%s"'''%(cmd,fname), shell=True) self.show() self.showAllCollectionWindows() runHook("BOSS_KEY", False)
def queueMplayerWithCache(path): if DEV_MODE: selfTest() s.ensureMplayerThreads() if conf.get("direct_access_mode", False): # Modern versions of mplayer seems to have this unicode bug fixed. path=path.replace("\\", "/") elif os.path.exists(path): if path in audio_cached: path=audio_cached[path] else: # mplayer on windows doesn't like the encoding, so we create a # temporary file instead. oddly, foreign characters in the dirname # don't seem to matter. key=path dir = tmpdir() name = os.path.join(dir, "audio%s%s" % ( random.randrange(0, 1000000), os.path.splitext(path)[1])) f = open(name, "wb") f.write(open(path, "rb").read()) f.close() # it wants unix paths, too! path = name.replace("\\", "/") audio_cached[key]=path s.mplayerQueue.append(path) s.mplayerEvt.set()
def queueMplayer(path, start_sec_p=0, end_sec_p=0): global media_to_play, start_sec, end_sec, stop_play_timer ensureMplayerThreads() if isWin and os.path.exists(path): # mplayer on windows doesn't like the encoding, so we create a # temporary file instead. oddly, foreign characters in the dirname # don't seem to matter. config = mw.addonManager.getConfig(__name__) if "my_mplayer_need_copying_file_to_temp" in config \ and config["my_mplayer_need_copying_file_to_temp"]: dir = tmpdir() name = os.path.join(dir, "audio%s%s" % ( random.randrange(0, 1000000), os.path.splitext(path)[1])) f = open(name, "wb") f.write(open(path, "rb").read()) f.close() # it wants unix paths, too! path = name path = path.replace("\\", "/") media_to_play = path if isinstance(start_sec_p, int) and start_sec_p >= 0: start_sec = start_sec_p else: start_sec = 0 if isinstance(end_sec_p, int) and end_sec_p > start_sec: end_sec = end_sec_p if stop_play_timer and stop_play_timer.is_alive(): stop_play_timer.cancel() stop_play_timer = threading.Timer(end_sec - start_sec, stop_as_planned, kwargs={"media_path_to_stop": media_to_play}) stop_play_timer.start() else: end_sec = 0 mplayerEvt.set()
def _buildImg(deck, latex, fname, model): # add header/footer latex = (model.conf["latexPre"] + "\n" + latex + "\n" + model.conf["latexPost"]) # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texfile = file(namedtmp("tmp.tex"), "w") texfile.write(latex) texfile.close() # make sure we have a valid mediaDir mdir = deck.media.dir(create=True) oldcwd = os.getcwd() png = namedtmp("tmp.png") try: # generate dvi os.chdir(tmpdir()) if call(latexCmd + ["tmp.tex"], stdout=log, stderr=log): return _errMsg("latex") # and png if call(latexDviPngCmd + ["tmp.dvi", "-o", "tmp.png"], stdout=log, stderr=log): return _errMsg("dvipng") # add to media shutil.copy2(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd)
def _buildImg(col, latex, fname, model): # add header/footer & convert to utf8 latex = (model["latexPre"] + "\n" + latex + "\n" + model["latexPost"]) latex = latex.encode("utf8") # it's only really secure if run in a jail, but these are the most common tmplatex = latex.replace("\\includegraphics", "") for bad in ("\\write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout"): # don't mind if the sequence is only part of a command bad_re = "\\" + bad + "[^a-zA-Z]" if re.search(bad_re, tmplatex): return _("""\ For security reasons, '%s' is not allowed on cards. You can still use \ it by placing the command in a different package, and importing that \ package in the LaTeX header instead.""") % bad # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texpath = namedtmp("tmp.tex") texfile = file(texpath, "w") texfile.write(latex) texfile.close() mdir = col.media.dir() oldcwd = os.getcwd() png = namedtmp("tmp.png") try: # generate png os.chdir(tmpdir()) for latexCmd in latexCmds: if call(latexCmd, stdout=log, stderr=log): return _errMsg(latexCmd[0], texpath) # add to media shutil.copyfile(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd)
def _buildImg(col, latex, fname, model): # add header/footer & convert to utf8 latex = (model["latexPre"] + "\n" + latex + "\n" + model["latexPost"]) latex = latex.encode("utf8") # it's only really secure if run in a jail, but these are the most common tmplatex = latex.replace("\\includegraphics", "") for bad in ("write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout"): if bad in tmplatex: return _("""\ For security reasons, '%s' is not allowed on cards. You can still use \ it by placing the command in a different package, and importing that \ package in the LaTeX header instead.""") % bad # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texpath = namedtmp("tmp.tex") texfile = file(texpath, "w") texfile.write(latex) texfile.close() mdir = col.media.dir() oldcwd = os.getcwd() png = namedtmp("tmp.png") try: # generate png os.chdir(tmpdir()) for latexCmd in latexCmds: if call(latexCmd, stdout=log, stderr=log): return _errMsg(latexCmd[0], texpath) # add to media shutil.copyfile(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd)
def _buildImg(col, latex, fname, model): # add header/footer & convert to utf8 latex = (model["latexPre"] + "\n" + latex + "\n" + model["latexPost"]) latex = latex.encode("utf8") # it's only really secure if run in a jail, but these are the most common for bad in ("write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout"): assert bad not in latex # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texfile = file(namedtmp("tmp.tex"), "w") texfile.write(latex) texfile.close() mdir = col.media.dir() oldcwd = os.getcwd() png = namedtmp("tmp.png") try: # generate dvi os.chdir(tmpdir()) if call(latexCmd + ["tmp.tex"], stdout=log, stderr=log): return _errMsg("latex") # and png if call(latexDviPngCmd + ["tmp.dvi", "-o", "tmp.png"], stdout=log, stderr=log): return _errMsg("dvipng") # add to media shutil.copyfile(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd)
def temp_file_for_tag_and_voice(self, tag: AVTag, voice: TTSVoice) -> str: """Return a hashed filename, to allow for caching generated files. No file extension is included.""" assert isinstance(tag, TTSTag) buf = f"{voice.name}-{voice.lang}-{tag.field_text}" return os.path.join(tmpdir(), f"tts-{checksum(buf)}")
def _save_latex_image( col: anki.collection.Collection, extracted: ExtractedLatex, header: str, footer: str, svg: bool, ) -> str | None: # add header/footer latex = f"{header}\n{extracted.latex_body}\n{footer}" # it's only really secure if run in a jail, but these are the most common tmplatex = latex.replace("\\includegraphics", "") for bad in ( "\\write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout", ): # don't mind if the sequence is only part of a command bad_re = f"\\{bad}[^a-zA-Z]" if re.search(bad_re, tmplatex): return col.tr.media_for_security_reasons_is_not(val=bad) # commands to use if svg: latex_cmds = svgCommands ext = "svg" else: latex_cmds = pngCommands ext = "png" # write into a temp file log = open(namedtmp("latex_log.txt"), "w", encoding="utf8") texpath = namedtmp("tmp.tex") texfile = open(texpath, "w", encoding="utf8") texfile.write(latex) texfile.close() oldcwd = os.getcwd() png_or_svg = namedtmp(f"tmp.{ext}") try: # generate png/svg os.chdir(tmpdir()) for latex_cmd in latex_cmds: if call(latex_cmd, stdout=log, stderr=log): return _err_msg(col, latex_cmd[0], texpath) # add to media with open(png_or_svg, "rb") as file: data = file.read() col.media.write_data(extracted.filename, data) os.unlink(png_or_svg) return None finally: os.chdir(oldcwd) log.close()
class MacTTSFilePlayer(MacTTSPlayer): "Generates an .aiff file, which is played using av_player." tmppath = os.path.join(tmpdir(), "tts.aiff") def _play(self, tag: AVTag) -> None: assert isinstance(tag, TTSTag) match = self.voice_for_tag(tag) assert match voice = match.voice assert isinstance(voice, MacVoice) default_wpm = 170 words_per_min = str(int(default_wpm * tag.speed)) self._process = subprocess.Popen( [ "say", "-v", voice.original_name, "-r", words_per_min, "-f", "-", "-o", self.tmppath, ], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) # write the input text to stdin self._process.stdin.write(tag.field_text.encode("utf8")) self._process.stdin.close() self._wait_for_termination(tag) def _on_done(self, ret: Future, cb: OnDoneCallback) -> None: try: ret.result() except PlayerInterrupted: # don't fire done callback when interrupted return # inject file into the top of the audio queue from aqt.sound import av_player av_player.insert_file(self.tmppath) # then tell player to advance, which will cause the file to be played cb()
def queueMplayer(path): ensureMplayerThreads() if isWin and os.path.exists(path): # mplayer on windows doesn't like the encoding, so we create a # temporary file instead. oddly, foreign characters in the dirname # don't seem to matter. dir = tmpdir() name = os.path.join(dir, "audio%s%s" % ( random.randrange(0, 1000000), os.path.splitext(path)[1])) f = open(name, "wb") f.write(open(path, "rb").read()) f.close() # it wants unix paths, too! path = name.replace("\\", "/") mplayerQueue.append(path) mplayerEvt.set()
def _buildImg(col, latex, fname, model): # add header/footer latex = (model["latexPre"] + "\n" + latex + "\n" + model["latexPost"]) # it's only really secure if run in a jail, but these are the most common tmplatex = latex.replace("\\includegraphics", "") for bad in ("\\write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout"): # don't mind if the sequence is only part of a command bad_re = "\\" + bad + "[^a-zA-Z]" if re.search(bad_re, tmplatex): return _("""\ For security reasons, '%s' is not allowed on cards. You can still use \ it by placing the command in a different package, and importing that \ package in the LaTeX header instead.""") % bad # commands to use? if model.get("latexsvg", False): latexCmds = svgCommands ext = "svg" else: latexCmds = pngCommands ext = "png" # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texpath = namedtmp("tmp.tex") texfile = open(texpath, "w", encoding="utf8") texfile.write(latex) texfile.close() mdir = col.media.dir() oldcwd = os.getcwd() png = namedtmp("tmp.%s" % ext) try: # generate png os.chdir(tmpdir()) for latexCmd in latexCmds: if call(latexCmd, stdout=log, stderr=log): return _errMsg(latexCmd[0], texpath) # add to media shutil.copyfile(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd) log.close()
def queueMplayer(path): ensureMplayerThreads() while mplayerEvt.isSet(): time.sleep(0.1) if isWin and os.path.exists(path): # mplayer on windows doesn't like the encoding, so we create a # temporary file instead. oddly, foreign characters in the dirname # don't seem to matter. dir = tmpdir().encode(sys.getfilesystemencoding()) name = os.path.join(dir, "audio"+os.path.splitext(path)[1]) f = open(name, "wb") f.write(open(path, "rb").read()) f.close() # it wants unix paths, too! path = name.replace("\\", "/") path = path.encode(sys.getfilesystemencoding()) else: path = path.encode("utf-8") mplayerQueue.append(path) mplayerEvt.set()
def review_link_handler_wrapper(reviewer, url): """Play the sound or call the original link handler.""" if url.startswith("urlplayhttp://") or url.startswith("urlplayhttps://"): if isWin: # download audio from url name = os.path.join( tmpdir(), hashlib.md5(url.encode(encoding="UTF-8")).hexdigest() + ".mp3") if not os.path.exists(name): f = open(name, "wb") f.write(requests.get(url[7:]).content) f.close() # it wants unix paths, too! path = name.replace("\\", "/") play(path) else: # use mpv directly mpvManager.command("loadfile", url[7:], "append-play") else: original_review_link_handler(reviewer, url)
def _buildImg(col, latex, fname, model): # add header/footer & convert to utf8 latex = (model["latexPre"] + "\n" + latex + "\n" + model["latexPost"]) latex = latex.encode("utf8") # it's only really secure if run in a jail, but these are the most common for bad in ("write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout"): if bad in latex: return _("""\ For security reasons, '%s' is not allowed on cards. You can still use \ it by placing the command in a different package, and importing that \ package in the LaTeX header instead.""") % bad # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texpath = namedtmp("tmp.tex") texfile = file(texpath, "w") texfile.write(latex) texfile.close() mdir = col.media.dir() oldcwd = os.getcwd() png = namedtmp("tmp.png") try: # generate dvi os.chdir(tmpdir()) if call(latexCmd + ["tmp.tex"], stdout=log, stderr=log): return _errMsg("latex", texpath) # and png if call(latexDviPngCmd + ["tmp.dvi", "-o", "tmp.png"], stdout=log, stderr=log): return _errMsg("dvipng", texpath) # add to media shutil.copyfile(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd)
def queueMplayer(path): ensureMplayerThreads() if isWin and os.path.exists(path): # mplayer on windows doesn't like the encoding, so we create a # temporary file instead. oddly, foreign characters in the dirname # don't seem to matter. if aqt.mw.pm.profile.get("mpv.directAccess", True): #TODO: testNoCache path = path.replace("\\", "/") elif path in audio_cached: path = audio_cached[path] else: key = path dir = tmpdir() name = os.path.join( dir, "audio%s%s" % (random.randrange(0, 1000000), os.path.splitext(path)[1])) f = open(name, "wb") f.write(open(path, "rb").read()) f.close() # it wants unix paths, too! path = name.replace("\\", "/") audio_cached[key] = path mplayerQueue.append(path) mplayerEvt.set()
def _buildImg(col, latex, fname, model): """Generate an image file from latex code latex's header and foot is added as in the model. The image is named fname and added to the media folder of this collection. The compiled file is in tmp.tex and its output (err and std) in latex_log.tex, replacing previous files of the same name. Both of those file are in the tmpdir as in utils.py Compiles to svg if latexsvg is set to true in the model, otherwise to png. The compilation commands are given above. In case of error, return an error message to be displayed instead of the LaTeX document. (Note that this image is not displayed in AnkiDroid. It is probably shown only in computer mode) Keyword arguments: col -- the current collection. It deals with media folder latex -- the code LaTeX to compile, as given in fields fname -- the name given to the generated png model -- the model in which is compiled the note. It deals with the header/footer, and the image file format """ # add header/footer latex = (model["latexPre"] + "\n" + latex + "\n" + model["latexPost"]) # it's only really secure if run in a jail, but these are the most common tmplatex = latex.replace("\\includegraphics", "") for bad in ("\\write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout"): # don't mind if the sequence is only part of a command bad_re = "\\" + bad + "[^a-zA-Z]" if re.search(bad_re, tmplatex): return _("""\ For security reasons, '%s' is not allowed on cards. You can still use \ it by placing the command in a different package, and importing that \ package in the LaTeX header instead.""") % bad # commands to use? if model.get("latexsvg", False): latexCmds = svgCommands ext = "svg" else: latexCmds = pngCommands ext = "png" # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texpath = namedtmp("tmp.tex") texfile = open(texpath, "w", encoding="utf8") texfile.write(latex) texfile.close() mdir = col.media.dir() oldcwd = os.getcwd() png = namedtmp("tmp.%s" % ext) try: # generate an image os.chdir(tmpdir()) for latexCmd in latexCmds: if call(latexCmd, stdout=log, stderr=log): return _errMsg(latexCmd[0], texpath) # add the image to the media folder shutil.copyfile(png, os.path.join(mdir, fname)) return finally: os.chdir(oldcwd) log.close()
def _save_latex_image( col: anki.collection.Collection, extracted: ExtractedLatex, header: str, footer: str, svg: bool, ) -> Optional[str]: # add header/footer latex = header + "\n" + extracted.latex_body + "\n" + footer # it's only really secure if run in a jail, but these are the most common tmplatex = latex.replace("\\includegraphics", "") for bad in ( "\\write18", "\\readline", "\\input", "\\include", "\\catcode", "\\openout", "\\write", "\\loop", "\\def", "\\shipout", ): # don't mind if the sequence is only part of a command bad_re = "\\" + bad + "[^a-zA-Z]" if re.search(bad_re, tmplatex): return (_("""\ For security reasons, '%s' is not allowed on cards. You can still use \ it by placing the command in a different package, and importing that \ package in the LaTeX header instead.""") % bad) # commands to use if svg: latexCmds = svgCommands ext = "svg" else: latexCmds = pngCommands ext = "png" # write into a temp file log = open(namedtmp("latex_log.txt"), "w") texpath = namedtmp("tmp.tex") texfile = open(texpath, "w", encoding="utf8") texfile.write(latex) texfile.close() oldcwd = os.getcwd() png_or_svg = namedtmp("tmp.%s" % ext) try: # generate png/svg os.chdir(tmpdir()) for latexCmd in latexCmds: if call(latexCmd, stdout=log, stderr=log): return _errMsg(latexCmd[0], texpath) # add to media with open(png_or_svg, "rb") as file: data = file.read() col.media.write_data(extracted.filename, data) os.unlink(png_or_svg) return None finally: os.chdir(oldcwd) log.close()
class WindowsRTTTSFilePlayer(TTSProcessPlayer): voice_list = None tmppath = os.path.join(tmpdir(), "tts.wav") def import_voices(self) -> None: import winrt.windows.media.speechsynthesis as speechsynthesis # type: ignore try: self.voice_list = speechsynthesis.SpeechSynthesizer.get_all_voices( ) except Exception as e: print("winrt tts voices unavailable:", e) self.voice_list = [] def get_available_voices(self) -> List[TTSVoice]: t = threading.Thread(target=self.import_voices) t.start() t.join() return list(map(self._voice_to_object, self.voice_list)) def _voice_to_object(self, voice: Any) -> TTSVoice: return WindowsRTVoice( id=voice.id, name=voice.display_name.replace(" ", "_"), lang=voice.language.replace("-", "_"), ) def _play(self, tag: AVTag) -> None: assert isinstance(tag, TTSTag) match = self.voice_for_tag(tag) assert match voice = cast(WindowsRTVoice, match.voice) self._taskman.run_on_main( lambda: gui_hooks.av_player_did_begin_playing(self, tag)) asyncio.run(self.speakText(tag, voice.id)) def _on_done(self, ret: Future, cb: OnDoneCallback): ret.result() # inject file into the top of the audio queue from aqt.sound import av_player av_player.insert_file(self.tmppath) # then tell player to advance, which will cause the file to be played cb() async def speakText(self, tag: TTSTag, voice_id): import winrt.windows.media.speechsynthesis as speechsynthesis # type: ignore import winrt.windows.storage.streams as streams # type: ignore synthesizer = speechsynthesis.SpeechSynthesizer() voices = speechsynthesis.SpeechSynthesizer.get_all_voices() voice_match = next(filter(lambda v: v.id == voice_id, voices)) assert voice_match synthesizer.voice = voice_match synthesizer.options.speaking_rate = tag.speed stream = await synthesizer.synthesize_text_to_stream_async( tag.field_text) inputStream = stream.get_input_stream_at(0) dataReader = streams.DataReader(inputStream) dataReader.load_async(stream.size) f = open(self.tmppath, "wb") for x in range(stream.size): f.write(bytes([dataReader.read_byte()])) f.close()