From 7e3a5a26bb3f384717dc687d77b1a05d022f210a Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sat, 14 Jun 2025 13:00:11 +0700 Subject: [PATCH] Use Python wheel for mpv/lame on Windows/Mac This is convenient, but suboptimal on a Mac at the moment. The first run of mpv will take a number of seconds for security checks to run, and our mpv code ends up timing out, repeating the process each time. Our installer stub will need to invoke mpv once first to get it validated. We could address this by distributing the audio with the installer/stub, or perhaps by putting the binaries in a .pkg file that's notarized+stapled and then included in the wheel. --- build/configure/src/python.rs | 2 +- qt/aqt/sound.py | 24 ++++++++++++++++++------ qt/pyproject.toml | 3 +++ uv.lock | 18 ++++++++++++++++-- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/build/configure/src/python.rs b/build/configure/src/python.rs index e69304807..31e559911 100644 --- a/build/configure/src/python.rs +++ b/build/configure/src/python.rs @@ -39,7 +39,7 @@ pub fn setup_venv(build: &mut Build) -> Result<()> { "qt/pyproject.toml", "uv.lock" ], - extra_args: "--all-packages --extra qt", + extra_args: "--all-packages --extra qt --extra audio", extra_binary_exports, }, )?; diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 386767a30..50f816964 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -279,12 +279,24 @@ def _packagedCmd(cmd: list[str]) -> tuple[Any, dict[str, str]]: if "LD_LIBRARY_PATH" in env and "SNAP" not in env: del env["LD_LIBRARY_PATH"] - if is_win: - packaged_path = Path(sys.prefix) / (cmd[0] + ".exe") - elif is_mac: - packaged_path = Path(sys.prefix) / ".." / "Resources" / cmd[0] - else: - packaged_path = Path(sys.prefix) / cmd[0] + # Try to find binary in anki-audio package for Windows/Mac + if is_win or is_mac: + try: + import anki_audio + audio_pkg_path = Path(anki_audio.__file__).parent + if is_win: + packaged_path = audio_pkg_path / (cmd[0] + ".exe") + else: # is_mac + packaged_path = audio_pkg_path / cmd[0] + + if packaged_path.exists(): + cmd[0] = str(packaged_path) + return cmd, env + except ImportError: + # anki-audio not available, fall back to old behavior + pass + + packaged_path = Path(sys.prefix) / cmd[0] if packaged_path.exists(): cmd[0] = str(packaged_path) diff --git a/qt/pyproject.toml b/qt/pyproject.toml index f30fd2249..7db26e8e4 100644 --- a/qt/pyproject.toml +++ b/qt/pyproject.toml @@ -26,6 +26,9 @@ dependencies = [ ] [project.optional-dependencies] +audio = [ + "anki-audio==0.1.0; sys.platform == 'win32' or sys.platform == 'darwin'", +] qt66 = [ "pyqt6==6.6.1", "pyqt6-qt6==6.6.2", diff --git a/uv.lock b/uv.lock index adfb38823..053fa2eca 100644 --- a/uv.lock +++ b/uv.lock @@ -80,6 +80,16 @@ requires-dist = [ { name = "typing-extensions" }, ] +[[package]] +name = "anki-audio" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/c7/b4c86d89c51d5bdcfc21bffc58be96b84075cff24b6d6fa0276a699084ff/anki_audio-0.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:249e3f7837366f8da3414139282f85df6fe65def2e1f76c2360ea88e03868f6b", size = 29453537, upload-time = "2025-06-13T10:48:32.337Z" }, + { url = "https://files.pythonhosted.org/packages/c8/38/af4dd671296cf68fb7b793d7f16845b074f5662f8e8653146ae950a149a0/anki_audio-0.1.0-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:a0b383880eaa8e27a028aa6ae50c4b95f6079044af5ec8a89ee870def21df9a5", size = 22586305, upload-time = "2025-06-13T10:48:45.557Z" }, + { url = "https://files.pythonhosted.org/packages/74/2b/5dd9b82faa27e04c9052232171de78ea4434dc384df859aa84e6dae8d468/anki_audio-0.1.0-py3-none-win_amd64.whl", hash = "sha256:b48b2537879769e03e9f4b87d7c37ef9d9fa2e5470e2116471d709666b615773", size = 31996219, upload-time = "2025-06-13T10:48:57.562Z" }, +] + [[package]] name = "anki-dev" version = "0.0.0" @@ -154,6 +164,9 @@ dependencies = [ ] [package.optional-dependencies] +audio = [ + { name = "anki-audio", marker = "sys_platform == 'darwin' or sys_platform == 'win32' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt68') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt68')" }, +] qt = [ { name = "pyqt6", version = "6.7.1", source = { registry = "https://pypi.org/simple" } }, { name = "pyqt6-qt6", version = "6.7.3", source = { registry = "https://pypi.org/simple" } }, @@ -178,6 +191,7 @@ qt68 = [ [package.metadata] requires-dist = [ + { name = "anki-audio", marker = "(sys_platform == 'darwin' and extra == 'audio') or (sys_platform == 'win32' and extra == 'audio')", specifier = "==0.1.0" }, { name = "beautifulsoup4", specifier = "==4.12.3" }, { name = "flask" }, { name = "flask-cors" }, @@ -213,7 +227,7 @@ requires-dist = [ { name = "types-waitress" }, { name = "waitress", specifier = ">=2.0.0" }, ] -provides-extras = ["qt", "qt66", "qt68"] +provides-extras = ["audio", "qt", "qt66", "qt68"] [[package]] name = "astroid" @@ -544,7 +558,7 @@ name = "importlib-metadata" version = "8.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp" }, + { name = "zipp", marker = "python_full_version < '3.10' or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt66') or (extra == 'extra-3-aqt-qt' and extra == 'extra-3-aqt-qt68') or (extra == 'extra-3-aqt-qt66' and extra == 'extra-3-aqt-qt68')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } wheels = [