# Copyright 2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

inherit git-r3

DESCRIPTION="Kokoro-82M TTS server in Rust (CPU; OpenAI-compatible HTTP API)"
HOMEPAGE="https://github.com/lucasjinreal/Kokoros"

# Tracks upstream lucasjinreal/Kokoros directly — the lemonade-sdk fork
# diverges only in CI/release infrastructure plus a bundled copy of
# espeak-ng-data. Having app-accessibility/espeak-ng installed does NOT
# by itself make that bundle redundant: the binary never consults
# /usr/share unless pointed there, which is what the wrapper in
# src_install exists to do. Given that, the fork's bundling is plausibly
# a workaround for the same baked-path problem — untested, so treat it as
# a lead rather than a fact if the fork is ever revisited.
EGIT_REPO_URI="https://github.com/lucasjinreal/Kokoros.git"

LICENSE="Apache-2.0"
SLOT="0"
# No KEYWORDS for live ebuild.

# Cargo fetches all crates online; bundled ONNX Runtime download is also part
# of the build via `ort = { default-features = true }`. Keep network open.
PROPERTIES="live"
RESTRICT="network-sandbox"

# Edition 2024 needs rust >= 1.85; Cargo.toml workspace pins ort 2.0-rc11 etc.
BDEPEND="
	|| ( >=dev-lang/rust-1.85 >=dev-lang/rust-bin-1.85 )
	virtual/pkgconfig
"
RDEPEND="
	app-accessibility/espeak-ng
	media-libs/libogg:=
	media-libs/opus:=
	media-libs/pcaudiolib
	media-libs/sonic
	media-sound/lame:=
"
DEPEND="${RDEPEND}"

src_compile() {
	# cargo.eclass assumes a vendored CRATES list; we let cargo fetch
	# online via RESTRICT=network-sandbox, so call cargo directly.
	# espeak-rs-sys bundles espeak-ng but its build.rs doesn't emit
	# `cargo:rustc-link-lib=pcaudio`, leaving audio_object_* unresolved
	# at link time. Force the link via RUSTFLAGS.
	local RUSTFLAGS="${RUSTFLAGS} -l pcaudio"
	export RUSTFLAGS

	cargo build --release --bin koko || die "cargo build failed"
}

src_install() {
	# koko does NOT pick up the system espeak-ng-data on its own, despite
	# linking espeak-rs. espeak-rs-sys builds a *vendored* espeak-ng via the
	# cmake crate, which defaults CMAKE_INSTALL_PREFIX to $OUT_DIR, and
	# espeak-ng bakes that prefix into the binary as its compile-time
	# PATH_ESPEAK_DATA. $OUT_DIR is inside the portage work dir, so the
	# baked path evaporates at merge and every synthesis call fails to load
	# phontab.
	#
	# The failure is silent in the worst way: the error goes to koko's
	# stderr only, the process still exits 0, and it still writes a valid
	# WAV - a fixed ~0.48 s stub, byte-identical for every input, because
	# the style vector is applied to an empty phoneme sequence. Consumers
	# that proxy koko over HTTP (sci-ml/lemonade) report success throughout.
	#
	# There is no build-time fix. espeak-rs-sys-0.1.9's build.rs exposes
	# only ESPEAK_BUILD_SHARED_LIBS, ESPEAK_LIB_PROFILE, ESPEAK_STATIC_CRT
	# and CMAKE_VERBOSE - nothing for the data path, and no option to link
	# the system espeak-ng instead of vendoring. It never sets
	# CMAKE_INSTALL_PREFIX itself, so the prefix cannot be steered without
	# also breaking the -L search paths that point into $OUT_DIR.
	#
	# So redirect at runtime. espeak_ng_InitializePath() (speech.c) resolves
	# in order: explicit argument, $ESPEAK_DATA_PATH, $HOME, then the baked
	# PATH_ESPEAK_DATA. Kokoros passes no explicit path, so the env var is
	# the first hook available. check_data_path() tries "$VAR/espeak-ng-data"
	# before "$VAR" itself, so /usr/share is the canonical value - it
	# resolves to app-accessibility/espeak-ng's /usr/share/espeak-ng-data.
	#
	# A wrapper rather than documentation: the main consumer (lemonade)
	# spawns koko itself, so nobody gets to set the variable at the call
	# site.
	#
	# The wrapper VALIDATES rather than merely defaulting, which matters:
	# sci-ml/lemonade exports ESPEAK_DATA_PATH=/usr/bin/espeak-ng-data
	# before spawning koko - correct for a portable bundle that keeps its
	# data beside the binary, meaningless for a distro install where the
	# binary is in /usr/bin. A plain ${VAR:-default} would honour that dead
	# path and change nothing, which is exactly what happened on the first
	# attempt at this fix. Testing for phontab mirrors what espeak-ng
	# itself probes, so a genuine user override still wins and only a
	# broken value is replaced. verified 2026-07-28
	exeinto /usr/libexec/${PN}
	doexe target/release/koko

	newbin - koko <<-EOF
		#!/bin/sh
		# espeak-ng probes "\$ESPEAK_DATA_PATH/espeak-ng-data" then
		# "\$ESPEAK_DATA_PATH" itself; accept either, else point at the
		# system data from app-accessibility/espeak-ng.
		if [ ! -f "\${ESPEAK_DATA_PATH}/espeak-ng-data/phontab" ] &&
		   [ ! -f "\${ESPEAK_DATA_PATH}/phontab" ]; then
			ESPEAK_DATA_PATH=/usr/share
		fi
		export ESPEAK_DATA_PATH
		exec /usr/libexec/${PN}/koko "\$@"
	EOF
}

pkg_postinst() {
	elog ""
	elog "Kokoros installed.  Binary: /usr/bin/koko"
	elog ""
	elog "Model + voice files are NOT shipped with this ebuild — they total"
	elog "~330 MB and need to be downloaded from HuggingFace before first use."
	elog ""
	elog "Quickest path: drop them into ~/.cache/kokoros/ (or any dir):"
	elog "  mkdir -p ~/.cache/kokoros/checkpoints ~/.cache/kokoros/data"
	elog "  curl -L -o ~/.cache/kokoros/checkpoints/kokoro-v1.0.onnx \\"
	elog "    https://github.com/nazdridoy/kokoro-tts/releases/download/v1.0.0/kokoro-v1.0.onnx"
	elog "  curl -L -o ~/.cache/kokoros/data/voices-v1.0.bin \\"
	elog "    https://github.com/nazdridoy/kokoro-tts/releases/download/v1.0.0/voices-v1.0.bin"
	elog ""
	elog "Then run from that directory, or pass --model / --voices-bin paths."
	elog ""
	elog "/usr/bin/koko is a wrapper that sets ESPEAK_DATA_PATH=/usr/share so"
	elog "the phoneme tables from app-accessibility/espeak-ng are found. The"
	elog "real binary is /usr/libexec/${PN}/koko; calling it directly without"
	elog "that variable produces a fixed ~0.5s stub for any input, silently"
	elog "and with exit status 0."
}