diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 7eaac67f2..ba5125f33 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -245,7 +245,6 @@ class Collection(DeprecatedNamesMixin): self._clear_caches() self._backend.close_collection(downgrade_to_schema11=downgrade) self.db = None - self.media.close() def close_for_full_sync(self) -> None: # save and cleanup, but backend will take care of collection close @@ -253,7 +252,6 @@ class Collection(DeprecatedNamesMixin): self.save(trx=False) self._clear_caches() self.db = None - self.media.close() def rollback(self) -> None: self._clear_caches() @@ -285,8 +283,6 @@ class Collection(DeprecatedNamesMixin): media_db_path=media_db, log_path=log_path, ) - else: - self.media.connect() self.db = DBProxy(weakref.proxy(self._backend)) self.db.begin() diff --git a/pylib/anki/media.py b/pylib/anki/media.py index 25b34ccb9..941fe3920 100644 --- a/pylib/anki/media.py +++ b/pylib/anki/media.py @@ -29,11 +29,6 @@ def media_paths_from_col_path(col_path: str) -> tuple[str, str]: CheckMediaResponse = media_pb2.CheckMediaResponse -# fixme: look into whether we can drop chdir() below -# - need to check aa89d06304fecd3597da4565330a3e55bdbb91fe -# - and audio handling code - - class MediaManager(DeprecatedNamesMixin): sound_regexps = [r"(?i)(\[sound:(?P[^]]+)\])"] @@ -51,45 +46,19 @@ class MediaManager(DeprecatedNamesMixin): def __init__(self, col: anki.collection.Collection, server: bool) -> None: self.col = col.weakref() - self._dir: str | None = None if server: return # media directory self._dir = media_paths_from_col_path(self.col.path)[0] if not os.path.exists(self._dir): os.makedirs(self._dir) - try: - self._oldcwd = os.getcwd() - except OSError: - # cwd doesn't exist - self._oldcwd = None - try: - os.chdir(self._dir) - except OSError as exc: - raise Exception("invalidTempFolder") from exc def __repr__(self) -> str: dict_ = dict(self.__dict__) del dict_["col"] return f"{super().__repr__()} {pprint.pformat(dict_, width=300)}" - def connect(self) -> None: - if self.col.server: - return - os.chdir(self._dir) - - def close(self) -> None: - if self.col.server: - return - # change cwd back to old location - if self._oldcwd: - try: - os.chdir(self._oldcwd) - except: - # may have been deleted - pass - - def dir(self) -> str | None: + def dir(self) -> str: return self._dir def force_resync(self) -> None: diff --git a/qt/aqt/main.py b/qt/aqt/main.py index ed99c1d59..3d2a9c083 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -192,7 +192,6 @@ class AnkiQt(QMainWindow): self.setupKeys() self.setupThreads() self.setupMediaServer() - self.setupSound() self.setupSpellCheck() self.setupProgress() self.setupStyle() @@ -449,6 +448,7 @@ class AnkiQt(QMainWindow): if not self.loadCollection(): return + self.setup_sound() self.flags = FlagManager(self) # show main window if self.pm.profile["mainWindowState"]: @@ -486,6 +486,7 @@ class AnkiQt(QMainWindow): self.unloadCollection(callback) def _unloadProfile(self) -> None: + self.cleanup_sound() saveGeom(self, "mainWindow") saveState(self, "mainWindow") self.pm.save() @@ -519,8 +520,11 @@ class AnkiQt(QMainWindow): # Sound/video ########################################################################## - def setupSound(self) -> None: - aqt.sound.setup_audio(self.taskman, self.pm.base) + def setup_sound(self) -> None: + aqt.sound.setup_audio(self.taskman, self.pm.base, self.col.media.dir()) + + def cleanup_sound(self) -> None: + aqt.sound.cleanup_audio() def _add_play_buttons(self, text: str) -> str: "Return card text with play buttons added, or stripped." diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 1e2e39448..abf8ec320 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -3,7 +3,6 @@ from __future__ import annotations -import atexit import os import platform import re @@ -271,8 +270,9 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method args: list[str] = [] env: dict[str, str] | None = None - def __init__(self, taskman: TaskManager) -> None: + def __init__(self, taskman: TaskManager, media_folder: str | None = None) -> None: self._taskman = taskman + self._media_folder = media_folder self._terminate_flag = False self._process: subprocess.Popen | None = None self._warned_about_missing_player = False @@ -292,6 +292,7 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method self._process = subprocess.Popen( self.args + [tag.filename], env=self.env, + cwd=self._media_folder, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) @@ -359,8 +360,10 @@ class SimpleMpvPlayer(SimpleProcessPlayer, VideoPlayer): ] ) - def __init__(self, taskman: TaskManager, base_folder: str) -> None: - super().__init__(taskman) + def __init__( + self, taskman: TaskManager, base_folder: str, media_folder: str + ) -> None: + super().__init__(taskman, media_folder) self.args += [f"--config-dir={base_folder}"] @@ -381,7 +384,8 @@ class MpvManager(MPV, SoundOrVideoPlayer): "--input-media-keys=no", ] - def __init__(self, base_path: str) -> None: + def __init__(self, base_path: str, media_folder: str) -> None: + self.media_folder = media_folder mpvPath, self.popenEnv = _packagedCmd(["mpv"]) self.executable = mpvPath[0] self._on_done: OnDoneCallback | None = None @@ -407,7 +411,7 @@ class MpvManager(MPV, SoundOrVideoPlayer): assert isinstance(tag, SoundOrVideoTag) self._on_done = on_done filename = hooks.media_file_filter(tag.filename) - path = os.path.join(os.getcwd(), filename) + path = os.path.join(self.media_folder, filename) self.command("loadfile", path, "replace", "pause=no") gui_hooks.av_player_did_begin_playing(self, tag) @@ -446,8 +450,9 @@ class MpvManager(MPV, SoundOrVideoPlayer): class SimpleMplayerSlaveModePlayer(SimpleMplayerPlayer): - def __init__(self, taskman: TaskManager) -> None: - super().__init__(taskman) + def __init__(self, taskman: TaskManager, media_folder: str) -> None: + self.media_folder = media_folder + super().__init__(taskman, media_folder) self.args.append("-slave") def _play(self, tag: AVTag) -> None: @@ -458,6 +463,7 @@ class SimpleMplayerSlaveModePlayer(SimpleMplayerPlayer): self._process = subprocess.Popen( self.args + [filename], env=self.env, + cwd=self.media_folder, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, @@ -819,12 +825,12 @@ def play_clicked_audio(pycmd: str, card: Card) -> None: ########################################################################## -def setup_audio(taskman: TaskManager, base_folder: str) -> None: +def setup_audio(taskman: TaskManager, base_folder: str, media_folder: str) -> None: # legacy global var global mpvManager try: - mpvManager = MpvManager(base_folder) + mpvManager = MpvManager(base_folder, media_folder) except FileNotFoundError: print("mpv not found, reverting to mplayer") except aqt.mpv.MPVProcessError: @@ -834,10 +840,10 @@ def setup_audio(taskman: TaskManager, base_folder: str) -> None: av_player.players.append(mpvManager) if is_win: - mpvPlayer = SimpleMpvPlayer(taskman, base_folder) + mpvPlayer = SimpleMpvPlayer(taskman, base_folder, media_folder) av_player.players.append(mpvPlayer) else: - mplayer = SimpleMplayerSlaveModePlayer(taskman) + mplayer = SimpleMplayerSlaveModePlayer(taskman, media_folder) av_player.players.append(mplayer) # tts support @@ -857,5 +863,6 @@ def setup_audio(taskman: TaskManager, base_folder: str) -> None: if int(platform.version().split(".")[-1]) >= 17763: av_player.players.append(WindowsRTTTSFilePlayer(taskman)) - # cleanup at shutdown - atexit.register(av_player.shutdown) + +def cleanup_audio() -> None: + av_player.shutdown()