macOS launcher improvements

- do mpv initial run in parallel
- improve messages, show dots regularly
This commit is contained in:
Damien Elmes 2025-06-22 20:57:53 +07:00
parent eb6c977e08
commit d2f818fad2
4 changed files with 81 additions and 44 deletions

View file

@ -23,47 +23,45 @@ def first_run_setup() -> None:
if not is_mac:
return
def _dot():
print(".", flush=True, end="")
_dot()
import anki.collection
_dot()
import PyQt6.sip
_dot()
import PyQt6.QtCore
_dot()
import PyQt6.QtGui
_dot()
import PyQt6.QtNetwork
_dot()
import PyQt6.QtQuick
_dot()
import PyQt6.QtWebChannel
_dot()
import PyQt6.QtWebEngineCore
_dot()
import PyQt6.QtWebEngineWidgets
_dot()
# Import anki_audio first and spawn commands
import anki_audio
import PyQt6.QtWidgets
audio_pkg_path = Path(anki_audio.__file__).parent
# Invoke mpv and lame
cmd = [Path(""), "--version"]
# Start mpv and lame commands concurrently
processes = []
for cmd_name in ["mpv", "lame"]:
_dot()
cmd[0] = audio_pkg_path / cmd_name
subprocess.run([str(cmd[0]), str(cmd[1])], check=True, capture_output=True)
cmd_path = audio_pkg_path / cmd_name
proc = subprocess.Popen(
[str(cmd_path), "--version"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
processes.append(proc)
print()
# Continue with other imports while commands run
import concurrent.futures
import bs4
import flask
import flask_cors
import markdown
import PyQt6.QtCore
import PyQt6.QtGui
import PyQt6.QtNetwork
import PyQt6.QtQuick
import PyQt6.QtWebChannel
import PyQt6.QtWebEngineCore
import PyQt6.QtWebEngineWidgets
import PyQt6.QtWidgets
import PyQt6.sip
import requests
import waitress
import anki.collection
from . import _macos_helper
# Wait for both commands to complete
for proc in processes:
proc.wait()

View file

@ -47,8 +47,8 @@ done
codesign -vvv "$APP_LAUNCHER"
spctl -a "$APP_LAUNCHER"
# Notarize
./notarize.sh "$OUTPUT_DIR"
# Bundle
./dmg/build.sh "$OUTPUT_DIR"
# Notarize and bundle (skip if NODMG is set)
if [ -z "$NODMG" ]; then
./notarize.sh "$OUTPUT_DIR"
./dmg/build.sh "$OUTPUT_DIR"
fi

View file

@ -172,6 +172,8 @@ fn run() -> Result<()> {
command.env("UV_PRERELEASE", "allow");
}
println!("\x1B[1mUpdating Anki...\x1B[0m\n");
match command.ensure_success() {
Ok(_) => {
// Sync succeeded, break out of loop

View file

@ -3,6 +3,11 @@
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use anki_process::CommandExt as AnkiCommandExt;
use anyhow::Context;
@ -25,6 +30,9 @@ pub fn launch_anki_detached(anki_bin: &std::path::Path, _config: &crate::Config)
.process_group(0)
.ensure_spawn()?;
std::mem::forget(child);
println!("Anki will start shortly.");
println!("\x1B[1mYou can close this window.\x1B[0m\n");
Ok(())
}
@ -34,6 +42,10 @@ pub fn ensure_terminal_shown() -> Result<()> {
// If launched from GUI, relaunch in Terminal.app
relaunch_in_terminal()?;
}
// Set terminal title to "Anki Launcher"
print!("\x1b]0;Anki Launcher\x07");
Ok(())
}
@ -47,12 +59,37 @@ fn relaunch_in_terminal() -> Result<()> {
}
pub fn handle_first_launch(anki_bin: &std::path::Path) -> Result<()> {
use std::io::Write;
use std::io::{
self,
};
// Pre-validate by running --version to trigger any Gatekeeper checks
println!("\n\x1B[1mThis may take a few minutes. Please wait...\x1B[0m");
print!("\n\x1B[1mThis may take a few minutes. Please wait\x1B[0m");
io::stdout().flush().unwrap();
// Start progress indicator
let running = Arc::new(AtomicBool::new(true));
let running_clone = running.clone();
let progress_thread = thread::spawn(move || {
while running_clone.load(Ordering::Relaxed) {
print!(".");
io::stdout().flush().unwrap();
thread::sleep(Duration::from_secs(1));
}
});
let _ = Command::new(anki_bin)
.env("ANKI_FIRST_RUN", "1")
.arg("--version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.ensure_success();
// Stop progress indicator
running.store(false, Ordering::Relaxed);
progress_thread.join().unwrap();
println!(); // New line after dots
Ok(())
}