From 32e56f991af84217f915378a057107d8b9ecaa38 Mon Sep 17 00:00:00 2001 From: ruscher Date: Fri, 9 Oct 2026 10:30:22 -0300 Subject: [PATCH 1/3] fix(piper): find voice packages under both spellings, AUR as a last resort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BigLinux stable has piper-voices-pt-br, testing piper-voices-pt-BR: the app asked only for pt-BR, so stable systems were told Piper was not available. - package_installer.resolve() looks for each package under both spellings: an installed one first (both ship the same files, so the other one would fail on file conflicts), then the repositories, and only then the AUR, built as the person with pamac (when its AUR support is on), paru or yay. - The Piper prompt lists where each package comes from and explains AUR. - Voice Manager: one row per voice whatever the spelling (the installed one, else lower case), and "[installed]" read with LC_ALL=C — it was only recognized in Portuguese and English. - README/PKGBUILD name the stable package. --- README.md | 15 +- locale/bg.po | 7 + locale/ca.po | 9 ++ locale/cs.po | 7 + locale/da.po | 7 + locale/de.po | 7 + locale/el.po | 7 + locale/en.po | 9 ++ locale/es.po | 7 + locale/et.po | 7 + locale/fi.po | 7 + locale/fr.po | 7 + locale/he.po | 7 + locale/hr.po | 7 + locale/hu.po | 7 + locale/is.po | 7 + locale/it.po | 7 + locale/ja.po | 7 + locale/ko.po | 7 + locale/nl.po | 7 + locale/no.po | 7 + locale/pl.po | 7 + locale/pt-BR.po | 9 ++ locale/pt.po | 7 + locale/ro.po | 7 + locale/ru.po | 7 + locale/sk.po | 7 + locale/sv.po | 7 + locale/tr.po | 7 + locale/tts-biglinux.pot | 11 ++ locale/uk.po | 7 + locale/zh.po | 7 + pkgbuild/PKGBUILD | 2 +- tests/test_package_installer.py | 131 +++++++++++++++++ tests/test_voice_manager_rows.py | 20 +++ .../services/package_installer.py | 132 +++++++++++++++++- .../biglinux/tts-biglinux/ui/main_view.py | 45 ++++-- .../tts-biglinux/ui/voice_manager_dialog.py | 20 ++- 38 files changed, 570 insertions(+), 22 deletions(-) create mode 100644 tests/test_voice_manager_rows.py diff --git a/README.md b/README.md index 0b14ccc..cab0d9a 100644 --- a/README.md +++ b/README.md @@ -337,11 +337,20 @@ sudo pacman -S rhvoice-voice-evgeniy-eng # BigLinux repositories only: sudo pacman -S biglinux-kokoro-tts # Kokoro neural voices -sudo pacman -S piper-voices-pt-BR # Brazilian Portuguese Piper voices +sudo pacman -S piper-voices-pt-br # Brazilian Portuguese Piper voices (stable) +sudo pacman -S piper-voices-pt-BR # the same voices, as named in biglinux-testing ``` Piper voices can also be downloaded in the Voice Manager. +The Piper voice packages are spelled differently in BigLinux stable +(`piper-voices-pt-br`) and testing (`piper-voices-pt-BR`); both ship the same +files, so only one can be installed. When Piper is selected, the app looks +for both spellings and never offers the second one if either is installed. +Only when neither is in the repositories does it fall back to the AUR. It +builds the AUR package as you, never as root, with `pamac build` when pamac's +AUR support is on, or with `paru`/`yay`, and it says so before installing. + ### Uninstall ```bash @@ -406,7 +415,7 @@ All from the official Arch/Manjaro repositories. | `rhvoice-voice-evgeniy-eng` | official | RHVoice English voice | | `xclip` | official | X11 selection capture when `xsel` is missing | | `biglinux-kokoro-tts` | BigLinux | Kokoro neural voices (`koko`, model, base voices) | -| `piper-voices-pt-BR` | BigLinux | Brazilian Portuguese Piper voices, preinstalled | +| `piper-voices-pt-br` | BigLinux | Brazilian Portuguese Piper voices, preinstalled (`piper-voices-pt-BR` in testing) | | `piper-tts-bin` | BigLinux | Piper command-line fallback (the native engine needs only voices) | | `rhvoice-brazilian-portuguese-complementary-dict-biglinux` | BigLinux | Extra pronunciation dictionary for RHVoice | @@ -755,7 +764,7 @@ The engines and libraries it uses keep their own licenses, among them: | espeak-ng, RHVoice | GPL-3.0-or-later | | RHVoice voice Letícia F123 | CC-BY-SA-4.0 | | Kokoro model (`biglinux-kokoro-tts`) | Apache-2.0 | -| Piper voices (`piper-voices-pt-BR`) | MIT | +| Piper voices (`piper-voices-pt-br`) | MIT | | ONNX Runtime | MIT | | GTK 4, libadwaita, PyGObject, GStreamer | LGPL-2.1-or-later | | PySide6 / Qt 6 | LGPL-3.0 (or GPL-3.0) | diff --git a/locale/bg.po b/locale/bg.po index d91d48a..8a221c8 100644 --- a/locale/bg.po +++ b/locale/bg.po @@ -1295,3 +1295,10 @@ msgstr "Това ще върне всички настройки към първ #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Възстановяване" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Пакетите, отбелязани с AUR, не са във вашите хранилища: те идват от Arch User Repository, поддържат се от общността и се компилират на този компютър, което отнема повече време." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} не можа да бъде компилиран от AUR. Вижте подробностите." diff --git a/locale/ca.po b/locale/ca.po index 9b67946..9f1fddc 100644 --- a/locale/ca.po +++ b/locale/ca.po @@ -1677,3 +1677,12 @@ msgstr "Això retornarà tots els ajustaments als seus valors originals per defe #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Restaura" + +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Els paquets marcats amb AUR no són als vostres repositoris: provenen de l'Arch User Repository, els manté la comunitat i es compilen en aquest ordinador, cosa que triga més." + +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "No s'ha pogut compilar {packages} des de l'AUR. Vegeu els detalls." diff --git a/locale/cs.po b/locale/cs.po index b60a740..eb88de5 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -1295,3 +1295,10 @@ msgstr "Tímto se vrátí všechny úpravy na původní výchozí hodnoty." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Obnovit" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Balíčky označené AUR nejsou ve vašich repozitářích: pocházejí z Arch User Repository, udržuje je komunita a sestavují se na tomto počítači, což trvá déle." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} se nepodařilo sestavit z AUR. Podívejte se na podrobnosti." diff --git a/locale/da.po b/locale/da.po index 6615943..88f56fb 100644 --- a/locale/da.po +++ b/locale/da.po @@ -1295,3 +1295,10 @@ msgstr "Dette vil gendanne alle justeringer til deres oprindelige standardværdi #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Gendan" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Pakker markeret med AUR findes ikke i dine arkiver: de kommer fra Arch User Repository, vedligeholdes af fællesskabet og bygges på denne computer, hvilket tager længere tid." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} kunne ikke bygges fra AUR. Se detaljerne." diff --git a/locale/de.po b/locale/de.po index 98fde0c..2307b86 100644 --- a/locale/de.po +++ b/locale/de.po @@ -1303,3 +1303,10 @@ msgstr "Dies setzt alle Einstellungen auf die ursprünglichen Standardwerte zur #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Wiederherstellen" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Mit AUR markierte Pakete sind nicht in Ihren Paketquellen: Sie stammen aus dem Arch User Repository, werden von der Community gepflegt und auf diesem Computer gebaut, was länger dauert." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} konnte nicht aus dem AUR gebaut werden. Siehe Details." diff --git a/locale/el.po b/locale/el.po index 1e730e1..80cade2 100644 --- a/locale/el.po +++ b/locale/el.po @@ -1297,3 +1297,10 @@ msgstr "Αυτό θα επαναφέρει όλες τις ρυθμίσεις σ #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Επαναφορά" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Τα πακέτα με την ένδειξη AUR δεν βρίσκονται στα αποθετήριά σας: προέρχονται από το Arch User Repository, συντηρούνται από την κοινότητα και μεταγλωττίζονται σε αυτόν τον υπολογιστή, κάτι που διαρκεί περισσότερο." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Δεν ήταν δυνατή η μεταγλώττιση του {packages} από το AUR. Δείτε τις λεπτομέρειες." diff --git a/locale/en.po b/locale/en.po index 3c8a815..408adf3 100644 --- a/locale/en.po +++ b/locale/en.po @@ -2114,3 +2114,12 @@ msgstr "This will return all adjustments to their original defaults." # File: tts-biglinux/usr/share/biglinux/tts-biglinux/window.py, line: 465 msgid "Restore" msgstr "Restore" + +# File: tts-biglinux/usr/share/biglinux/tts-biglinux/ui/main_view.py, line: 1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." + +# File: tts-biglinux/usr/share/biglinux/tts-biglinux/services/package_installer.py, line: 252 +# File: tts-biglinux/usr/share/biglinux/tts-biglinux/services/package_installer.py, line: 265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} could not be built from the AUR. See the details." diff --git a/locale/es.po b/locale/es.po index 1951981..1fda8fc 100644 --- a/locale/es.po +++ b/locale/es.po @@ -1303,3 +1303,10 @@ msgstr "Esto devolverá todos los ajustes a sus valores originales." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Restaurar" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Los paquetes marcados con AUR no están en sus repositorios: provienen del Arch User Repository, los mantiene la comunidad y se compilan en este equipo, lo que tarda más." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "No se pudo compilar {packages} desde el AUR. Consulte los detalles." diff --git a/locale/et.po b/locale/et.po index 7398469..26214cd 100644 --- a/locale/et.po +++ b/locale/et.po @@ -1295,3 +1295,10 @@ msgstr "See taastab kõik kohandused algsete vaikeseadeteni." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Taasta" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "AUR-iga märgitud pakette pole teie hoidlates: need pärinevad Arch User Repositoryst, neid haldab kogukond ja need ehitatakse selles arvutis, mis võtab kauem aega." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Paketti {packages} ei õnnestunud AUR-ist ehitada. Vaadake üksikasju." diff --git a/locale/fi.po b/locale/fi.po index 11e324f..5454e77 100644 --- a/locale/fi.po +++ b/locale/fi.po @@ -1295,3 +1295,10 @@ msgstr "Tämä palauttaa kaikki säädöt alkuperäisiin oletusarvoihinsa." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Palauta" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "AUR-merkityt paketit eivät ole ohjelmistolähteissäsi: ne tulevat Arch User Repositorysta, yhteisö ylläpitää niitä ja ne käännetään tällä tietokoneella, mikä kestää kauemmin." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Pakettia {packages} ei voitu kääntää AUR:sta. Katso lisätiedot." diff --git a/locale/fr.po b/locale/fr.po index 9a7e808..7d074fc 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -1303,3 +1303,10 @@ msgstr "Cela retournera tous les ajustements à leurs valeurs par défaut origin #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Restaurer" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Les paquets marqués AUR ne sont pas dans vos dépôts : ils proviennent de l'Arch User Repository, sont maintenus par la communauté et sont compilés sur cet ordinateur, ce qui prend plus de temps." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} n'a pas pu être compilé depuis l'AUR. Consultez les détails." diff --git a/locale/he.po b/locale/he.po index aeff71c..0b19bcd 100644 --- a/locale/he.po +++ b/locale/he.po @@ -1295,3 +1295,10 @@ msgstr "פעולה זו תחזיר את כל ההתאמות לברירות המ #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "שחזור" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "חבילות המסומנות ב-AUR אינן במאגרים שלך: הן מגיעות מ-Arch User Repository, מתוחזקות על ידי הקהילה ונבנות במחשב זה, מה שלוקח יותר זמן." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "לא ניתן היה לבנות את {packages} מ-AUR. ראו את הפרטים." diff --git a/locale/hr.po b/locale/hr.po index bc22759..351c46a 100644 --- a/locale/hr.po +++ b/locale/hr.po @@ -1297,3 +1297,10 @@ msgstr "Ovo će vratiti sve prilagodbe na izvorne zadane vrijednosti." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Vrati" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Paketi označeni s AUR nisu u vašim repozitorijima: dolaze iz Arch User Repositoryja, održava ih zajednica i grade se na ovom računalu, što traje dulje." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} nije moguće izgraditi iz AUR-a. Pogledajte pojedinosti." diff --git a/locale/hu.po b/locale/hu.po index 3072b56..ebf6671 100644 --- a/locale/hu.po +++ b/locale/hu.po @@ -1295,3 +1295,10 @@ msgstr "Ez visszaállítja az összes beállítást az eredeti alapértékekre." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Visszaállítás" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Az AUR jelölésű csomagok nincsenek a tárolóidban: az Arch User Repositoryból származnak, a közösség tartja karban őket, és ezen a számítógépen fordítódnak, ami tovább tart." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "A(z) {packages} nem fordítható le az AUR-ból. Lásd a részleteket." diff --git a/locale/is.po b/locale/is.po index 5007716..8eb1200 100644 --- a/locale/is.po +++ b/locale/is.po @@ -1295,3 +1295,10 @@ msgstr "Þetta mun færa allar stillingar til baka í upprunaleg sjálfgildi." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Endurstilla" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Pakkar merktir AUR eru ekki í hugbúnaðarsöfnunum þínum: þeir koma úr Arch User Repository, samfélagið viðheldur þeim og þeir eru byggðir á þessari tölvu, sem tekur lengri tíma." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Ekki tókst að byggja {packages} úr AUR. Sjá nánar." diff --git a/locale/it.po b/locale/it.po index 07a404d..04f6f1d 100644 --- a/locale/it.po +++ b/locale/it.po @@ -1303,3 +1303,10 @@ msgstr "Tutte le regolazioni torneranno ai valori predefiniti originali." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Ripristina" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "I pacchetti contrassegnati con AUR non sono nei tuoi repository: provengono dall'Arch User Repository, sono mantenuti dalla comunità e vengono compilati su questo computer, il che richiede più tempo." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Impossibile compilare {packages} dall'AUR. Vedi i dettagli." diff --git a/locale/ja.po b/locale/ja.po index c3db1d2..4b81427 100644 --- a/locale/ja.po +++ b/locale/ja.po @@ -1295,3 +1295,10 @@ msgstr "すべての設定を元のデフォルト値に戻します。" #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "復元" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "AUR と表示されたパッケージはリポジトリにありません。Arch User Repository から取得され、コミュニティが管理しており、このコンピューターでビルドされるため時間がかかります。" +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} を AUR からビルドできませんでした。詳細を確認してください。" diff --git a/locale/ko.po b/locale/ko.po index 7f6f30e..17e7538 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -1295,3 +1295,10 @@ msgstr "모든 조정이 원래 기본값으로 돌아갑니다." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "복원" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "AUR로 표시된 패키지는 저장소에 없습니다. Arch User Repository에서 제공되며, 커뮤니티가 관리하고 이 컴퓨터에서 빌드되므로 시간이 더 걸립니다." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "AUR에서 {packages}을(를) 빌드할 수 없습니다. 세부 정보를 확인하세요." diff --git a/locale/nl.po b/locale/nl.po index 481a26c..43077f4 100644 --- a/locale/nl.po +++ b/locale/nl.po @@ -1297,3 +1297,10 @@ msgstr "Dit zal alle aanpassingen terugzetten naar de oorspronkelijke standaardw #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Herstellen" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Pakketten gemarkeerd met AUR staan niet in je pakketbronnen: ze komen uit de Arch User Repository, worden door de gemeenschap onderhouden en op deze computer gebouwd, wat langer duurt." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} kon niet vanuit de AUR worden gebouwd. Zie de details." diff --git a/locale/no.po b/locale/no.po index 5952586..da7239c 100644 --- a/locale/no.po +++ b/locale/no.po @@ -1295,3 +1295,10 @@ msgstr "Dette vil tilbakestille alle justeringer til de opprinnelige standardver #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Gjenopprett" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Pakker merket AUR finnes ikke i pakkebrønnene dine: de kommer fra Arch User Repository, vedlikeholdes av fellesskapet og bygges på denne datamaskinen, noe som tar lengre tid." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} kunne ikke bygges fra AUR. Se detaljene." diff --git a/locale/pl.po b/locale/pl.po index bdccd09..ed35e9a 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -1295,3 +1295,10 @@ msgstr "Spowoduje to przywrócenie wszystkich ustawień do wartości domyślnych #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Przywróć" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Pakiety oznaczone jako AUR nie znajdują się w Twoich repozytoriach: pochodzą z Arch User Repository, są utrzymywane przez społeczność i budowane na tym komputerze, co trwa dłużej." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Nie udało się zbudować {packages} z AUR. Zobacz szczegóły." diff --git a/locale/pt-BR.po b/locale/pt-BR.po index 6e74972..eb07e70 100644 --- a/locale/pt-BR.po +++ b/locale/pt-BR.po @@ -1669,3 +1669,12 @@ msgstr "Isso retornará todos os ajustes para os seus padrões originais." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Restaurar" + +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Os pacotes marcados com AUR não estão nos seus repositórios: vêm do Arch User Repository, são mantidos pela comunidade e são compilados neste computador, o que demora mais." + +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Não foi possível compilar {packages} a partir do AUR. Veja os detalhes." diff --git a/locale/pt.po b/locale/pt.po index 7edec4c..371cc33 100644 --- a/locale/pt.po +++ b/locale/pt.po @@ -1295,3 +1295,10 @@ msgstr "Isto irá repor todos os ajustes para as predefinições originais." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Restaurar" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Os pacotes marcados com AUR não estão nos seus repositórios: vêm do Arch User Repository, são mantidos pela comunidade e são compilados neste computador, o que demora mais tempo." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Não foi possível compilar {packages} a partir do AUR. Consulte os detalhes." diff --git a/locale/ro.po b/locale/ro.po index cbe85f3..4e0edf9 100644 --- a/locale/ro.po +++ b/locale/ro.po @@ -1295,3 +1295,10 @@ msgstr "Aceasta va readuce toate ajustările la valorile implicite originale." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Restabilește" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Pachetele marcate cu AUR nu se află în depozitele tale: provin din Arch User Repository, sunt întreținute de comunitate și sunt compilate pe acest calculator, ceea ce durează mai mult." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} nu a putut fi compilat din AUR. Vezi detaliile." diff --git a/locale/ru.po b/locale/ru.po index d7107ef..8fb9303 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -1303,3 +1303,10 @@ msgstr "Все настройки будут возвращены к значе #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Сбросить" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Пакеты с пометкой AUR отсутствуют в ваших репозиториях: они берутся из Arch User Repository, поддерживаются сообществом и собираются на этом компьютере, что занимает больше времени." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Не удалось собрать {packages} из AUR. См. подробности." diff --git a/locale/sk.po b/locale/sk.po index dfd01cc..e586253 100644 --- a/locale/sk.po +++ b/locale/sk.po @@ -1295,3 +1295,10 @@ msgstr "Toto vráti všetky úpravy na pôvodné predvolené hodnoty." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Obnoviť" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Balíky označené AUR nie sú vo vašich repozitároch: pochádzajú z Arch User Repository, udržiava ich komunita a zostavujú sa na tomto počítači, čo trvá dlhšie." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} sa nepodarilo zostaviť z AUR. Pozrite si podrobnosti." diff --git a/locale/sv.po b/locale/sv.po index 3e288cb..f13115f 100644 --- a/locale/sv.po +++ b/locale/sv.po @@ -1295,3 +1295,10 @@ msgstr "Detta kommer att återställa alla justeringar till sina ursprungliga st #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Återställ" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Paket märkta med AUR finns inte i dina förråd: de kommer från Arch User Repository, underhålls av gemenskapen och byggs på den här datorn, vilket tar längre tid." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} kunde inte byggas från AUR. Se detaljerna." diff --git a/locale/tr.po b/locale/tr.po index 1c10aef..d3a2d41 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -1295,3 +1295,10 @@ msgstr "Bu, tüm ayarları orijinal varsayılan değerlerine döndürecektir." #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Geri yükle" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "AUR olarak işaretlenen paketler depolarınızda yok: Arch User Repository'den gelir, topluluk tarafından bakımı yapılır ve bu bilgisayarda derlenir; bu daha uzun sürer." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "{packages} AUR'dan derlenemedi. Ayrıntılara bakın." diff --git a/locale/tts-biglinux.pot b/locale/tts-biglinux.pot index cfeb348..da9ecdb 100644 --- a/locale/tts-biglinux.pot +++ b/locale/tts-biglinux.pot @@ -2112,3 +2112,14 @@ msgstr "" # File: tts-biglinux/usr/share/biglinux/tts-biglinux/window.py, line: 465 msgid "Restore" msgstr "" + +# +# File: tts-biglinux/usr/share/biglinux/tts-biglinux/ui/main_view.py, line: 1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "" + +# +# File: tts-biglinux/usr/share/biglinux/tts-biglinux/services/package_installer.py, line: 252 +# File: tts-biglinux/usr/share/biglinux/tts-biglinux/services/package_installer.py, line: 265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "" diff --git a/locale/uk.po b/locale/uk.po index f640082..105cb7d 100644 --- a/locale/uk.po +++ b/locale/uk.po @@ -1295,3 +1295,10 @@ msgstr "Це поверне всі налаштування до початко #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "Відновити" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "Пакунки з позначкою AUR відсутні у ваших репозиторіях: вони надходять з Arch User Repository, підтримуються спільнотою та збираються на цьому комп'ютері, що триває довше." +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "Не вдалося зібрати {packages} з AUR. Перегляньте подробиці." diff --git a/locale/zh.po b/locale/zh.po index d79d87c..1ca8c0c 100644 --- a/locale/zh.po +++ b/locale/zh.po @@ -1295,3 +1295,10 @@ msgstr "这将把所有调整恢复为原始默认值。" #: usr/share/biglinux/tts-biglinux/window.py:465 msgid "Restore" msgstr "恢复" +#: usr/share/biglinux/tts-biglinux/ui/main_view.py:1442 +msgid "Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer." +msgstr "标记为 AUR 的软件包不在您的软件源中:它们来自 Arch User Repository,由社区维护,并在此计算机上构建,耗时更长。" +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:252 +#: usr/share/biglinux/tts-biglinux/services/package_installer.py:265 +msgid "{packages} could not be built from the AUR. See the details." +msgstr "无法从 AUR 构建 {packages}。请查看详细信息。" diff --git a/pkgbuild/PKGBUILD b/pkgbuild/PKGBUILD index b4d1066..2495362 100644 --- a/pkgbuild/PKGBUILD +++ b/pkgbuild/PKGBUILD @@ -56,7 +56,7 @@ optdepends=( 'rhvoice-voice-evgeniy-eng: RHVoice English male voice' 'xclip: selection capture on X11 when xsel is not available' 'biglinux-kokoro-tts: Kokoro neural voices (BigLinux repository)' - 'piper-voices-pt-BR: Brazilian Portuguese Piper voices, preinstalled (BigLinux repository)' + 'piper-voices-pt-br: Brazilian Portuguese Piper voices, preinstalled (BigLinux repository; piper-voices-pt-BR in testing)' 'piper-tts-bin: Piper command-line fallback (BigLinux repository; the native engine needs only voices)' 'rhvoice-brazilian-portuguese-complementary-dict-biglinux: extra pronunciation dictionary for RHVoice (BigLinux repository)' ) diff --git a/tests/test_package_installer.py b/tests/test_package_installer.py index 2ed85b6..2c4409a 100644 --- a/tests/test_package_installer.py +++ b/tests/test_package_installer.py @@ -68,3 +68,134 @@ def test_unsafe_package_names_are_refused(monkeypatch): monkeypatch.setattr(pi.subprocess, "run", lambda *a, **k: (_ for _ in ()).throw(AssertionError("must not run"))) assert not pi.query("x; rm -rf /").available assert not pi.install(["--overwrite=*"]).ok + + +# ── Two spellings, then the AUR ────────────────────────────────────── +# BigLinux stable has piper-voices-pt-br, testing piper-voices-pt-BR, the AUR +# piper-voices-pt-br. Both repository packages ship the same voice files. + +def _world(monkeypatch, repo=(), installed=(), aur=(), helper=True): + asked_aur = [] + monkeypatch.setattr(pi, "is_installed", lambda n: n in installed) + monkeypatch.setattr(pi, "query", lambda n: pi.PackageInfo(n, n in repo)) + + def fake_aur(names, timeout=10.0): + asked_aur.append(list(names)) + known = {a.lower(): a for a in aur} + return {n.lower(): known[n.lower()] for n in names if n.lower() in known} + + monkeypatch.setattr(pi, "aur_names", fake_aur) + monkeypatch.setattr(pi, "aur_helper", lambda: ["pamac", "build", "--no-confirm"] if helper else None) + return asked_aur + + +def _one(wanted="piper-voices-pt-BR"): + [r] = pi.resolve([wanted]) + return r.source, r.name + + +def test_both_spellings_are_tried(): + assert pi.name_variants("piper-voices-pt-BR") == ["piper-voices-pt-br", "piper-voices-pt-BR"] + assert pi.name_variants("piper-tts-bin") == ["piper-tts-bin"] + + +def test_stable_spelling_is_found(monkeypatch): + _world(monkeypatch, repo={"piper-voices-pt-br"}) + assert _one() == ("repo", "piper-voices-pt-br") + + +def test_testing_spelling_is_found(monkeypatch): + _world(monkeypatch, repo={"piper-voices-pt-BR"}) + assert _one() == ("repo", "piper-voices-pt-BR") + + +def test_lower_case_wins_when_both_exist(monkeypatch): + _world(monkeypatch, repo={"piper-voices-pt-BR", "piper-voices-pt-br"}) + assert _one() == ("repo", "piper-voices-pt-br") + + +def test_an_installed_spelling_is_never_installed_again(monkeypatch): + # Installing the other spelling would fail on the shared voice files. + _world(monkeypatch, repo={"piper-voices-pt-br", "piper-voices-pt-BR"}, installed={"piper-voices-pt-BR"}) + assert _one() == ("installed", "piper-voices-pt-BR") + + +def test_aur_only_as_a_last_resort(monkeypatch): + asked = _world(monkeypatch, repo={"piper-voices-pt-br"}, aur={"piper-voices-pt-br"}) + assert _one() == ("repo", "piper-voices-pt-br") + assert asked == [] # found in the repositories: the AUR is not even asked + _world(monkeypatch, aur={"piper-voices-pt-br"}) + assert _one() == ("aur", "piper-voices-pt-br") + + +def test_without_an_aur_helper_the_package_is_missing(monkeypatch): + asked = _world(monkeypatch, aur={"piper-voices-pt-br"}, helper=False) + assert _one() == ("", "") + assert asked == [] # no network query for nothing + + +def test_nowhere_is_missing(monkeypatch): + _world(monkeypatch) + assert _one() == ("", "") + + +def test_aur_names_are_case_insensitive(monkeypatch): + import io + import json + + seen = {} + + def fake_urlopen(url, timeout): + seen["url"] = url + return io.BytesIO(json.dumps({"results": [{"Name": "piper-voices-pt-br"}]}).encode()) + + monkeypatch.setattr(pi.urllib.request, "urlopen", fake_urlopen) + assert pi.aur_names(["piper-voices-pt-BR"]) == {"piper-voices-pt-br": "piper-voices-pt-br"} + assert seen["url"].startswith("https://aur.archlinux.org/rpc/v5/info?") + + def offline(url, timeout): + raise OSError("no network") + + monkeypatch.setattr(pi.urllib.request, "urlopen", offline) + assert pi.aur_names(["piper-voices-pt-BR"]) == {} + + +def test_aur_helper_choice(monkeypatch, tmp_path): + conf = tmp_path / "pamac.conf" + monkeypatch.setattr(pi, "PAMAC_CONF", conf) + tools = set() + monkeypatch.setattr(pi.shutil, "which", lambda t: f"/usr/bin/{t}" if t in tools else None) + assert pi.aur_helper() is None + tools.update({"pamac", "yay"}) + conf.write_text("#EnableAUR\n") + assert pi.aur_helper()[0] == "yay" # pamac without its AUR support + conf.write_text("EnableAUR\n") + assert pi.aur_helper()[:2] == ["pamac", "build"] + tools.discard("pamac") + tools.add("paru") + helper = pi.aur_helper() + assert helper[0] == "paru" and helper[-2:] == ["--sudo", "pkexec"] # no terminal for sudo + + +def test_install_aur_runs_as_the_person(monkeypatch): + seen = {} + + def fake_run(cmd, **kw): + seen["cmd"] = cmd + return subprocess.CompletedProcess(cmd, 0, "", "") + + monkeypatch.setattr(pi.subprocess, "run", fake_run) + monkeypatch.setattr(pi, "PACMAN_LOCK", pi.Path("/nonexistent/db.lck")) + monkeypatch.setattr(pi, "aur_helper", lambda: ["pamac", "build", "--no-confirm"]) + assert pi.install_aur(["piper-voices-pt-br", "-rf"]).ok + assert seen["cmd"] == ["pamac", "build", "--no-confirm", "piper-voices-pt-br"] # not pkexec, no option-like name + + +def test_install_aur_failure_is_explained(monkeypatch): + out = "==> ERROR: Failure while downloading https://huggingface.co/x.onnx\n Aborting...\n" + monkeypatch.setattr(pi.subprocess, "run", lambda cmd, **kw: subprocess.CompletedProcess(cmd, 1, out, "")) + monkeypatch.setattr(pi, "PACMAN_LOCK", pi.Path("/nonexistent/db.lck")) + monkeypatch.setattr(pi, "aur_helper", lambda: ["pamac", "build", "--no-confirm"]) + r = pi.install_aur(["piper-voices-pt-br"]) + assert not r.ok and "piper-voices-pt-br" in r.message + assert "Failure while downloading" in r.detail diff --git a/tests/test_voice_manager_rows.py b/tests/test_voice_manager_rows.py new file mode 100644 index 0000000..7e31078 --- /dev/null +++ b/tests/test_voice_manager_rows.py @@ -0,0 +1,20 @@ +"""Voice Manager rows: one per package, whatever the repository's spelling.""" +import pytest + +gi = pytest.importorskip("gi") +gi.require_version("Gtk", "4.0") +gi.require_version("Adw", "1") +try: + from ui.voice_manager_dialog import _one_per_spelling +except Exception as e: # pragma: no cover + pytest.skip(f"GTK unavailable headless: {e}", allow_module_level=True) + + +def test_voice_manager_shows_one_row_per_spelling(): + def row(name, installed="no"): + return {"pkg": name, "installed": installed} + + rows = [row("piper-voices-pt-BR"), row("piper-voices-pt-PT"), row("piper-voices-pt-br")] + assert [r["pkg"] for r in _one_per_spelling(rows)] == ["piper-voices-pt-PT", "piper-voices-pt-br"] + rows[0]["installed"] = "yes" # the installed spelling is the one shown + assert [r["pkg"] for r in _one_per_spelling(rows)] == ["piper-voices-pt-BR", "piper-voices-pt-PT"] diff --git a/usr/share/biglinux/tts-biglinux/services/package_installer.py b/usr/share/biglinux/tts-biglinux/services/package_installer.py index c89cadb..d4d6001 100644 --- a/usr/share/biglinux/tts-biglinux/services/package_installer.py +++ b/usr/share/biglinux/tts-biglinux/services/package_installer.py @@ -12,14 +12,22 @@ translated explanation, and the raw ``error:`` lines as technical detail. (The old code showed the last stderr line, which was often a harmless warning such as "espeak-ng-1.52.0-1 is up to date -- skipping".) +- A package may be spelled two ways: BigLinux stable has + ``piper-voices-pt-br``, testing ``piper-voices-pt-BR``. resolve() tries both + (an installed spelling first: both ship the same files), and the AUR only + as a last resort, through pamac (or paru/yay) as the person, never root. """ from __future__ import annotations +import json import logging import os import re +import shutil import subprocess +import urllib.parse +import urllib.request from dataclasses import dataclass, field from pathlib import Path @@ -28,6 +36,8 @@ logger = logging.getLogger(__name__) PACMAN_LOCK = Path("/var/lib/pacman/db.lck") +PAMAC_CONF = Path("/etc/pamac.conf") +AUR_RPC = "https://aur.archlinux.org/rpc/v5/info" _SAFE_PKG = re.compile(r"^[A-Za-z0-9@._+-]+$") _C_ENV = {**os.environ, "LC_ALL": "C", "LANG": "C"} @@ -74,6 +84,80 @@ def query(name: str) -> PackageInfo: ) +@dataclass +class Resolution: + """How a wanted package can be had.""" + wanted: str + name: str = "" # the spelling to install (or the one installed) + source: str = "" # "installed" | "repo" | "aur" | "" (nowhere) + + +def name_variants(name: str) -> list[str]: + """The spellings to look for, lower case first ("piper-voices-pt-BR" → + ["piper-voices-pt-br", "piper-voices-pt-BR"]): BigLinux stable and the + AUR use lower case.""" + return list(dict.fromkeys([name.lower(), name])) + + +def aur_helper() -> list[str] | None: + """argv prefix that builds and installs AUR packages, or None. + + pamac (BigLinux's own) when its AUR support is on; otherwise paru or + yay, which ask for the password through pkexec (no terminal here). + """ + if shutil.which("pamac") and _pamac_aur_enabled(): + return ["pamac", "build", "--no-confirm"] + if shutil.which("paru"): + return ["paru", "-S", "--aur", "--needed", "--noconfirm", "--skipreview", "--sudo", "pkexec"] + if shutil.which("yay"): + return ["yay", "-S", "--aur", "--needed", "--noconfirm", + "--answerdiff", "None", "--answerclean", "None", "--sudo", "pkexec"] + return None + + +def _pamac_aur_enabled() -> bool: + try: + lines = PAMAC_CONF.read_text(encoding="utf-8", errors="replace").splitlines() + except OSError: + return False + return any(line.split("#", 1)[0].strip() == "EnableAUR" for line in lines) + + +def aur_names(names: list[str], timeout: float = 10.0) -> dict[str, str]: + """Which of ``names`` exist in the AUR (case-insensitive): lower-case + name → the AUR's spelling. Empty when the AUR cannot be reached.""" + names = [n for n in names if _SAFE_PKG.match(n)] + if not names: + return {} + query = urllib.parse.urlencode([("arg[]", n) for n in names]) + try: + with urllib.request.urlopen(f"{AUR_RPC}?{query}", timeout=timeout) as resp: + data = json.load(resp) + except (OSError, ValueError) as e: + logger.debug("AUR query failed: %s", e) + return {} + return {r["Name"].lower(): r["Name"] for r in data.get("results", []) if r.get("Name")} + + +def resolve(packages: list[str], *, allow_aur: bool = True) -> list[Resolution]: + """Find each package under either spelling: installed, then in the + repositories, then (last resort) in the AUR if a helper can build it.""" + result: list[Resolution] = [] + for wanted in packages: + variants = name_variants(wanted) + found = next((Resolution(wanted, v, "installed") for v in variants if is_installed(v)), None) + if found is None: + found = next((Resolution(wanted, v, "repo") for v in variants if query(v).available), None) + result.append(found or Resolution(wanted)) + missing = [r for r in result if not r.source] + if missing and allow_aur and aur_helper(): + in_aur = aur_names([r.wanted for r in missing]) + for r in missing: + if r.wanted.lower() in in_aur: + r.name, r.source = in_aur[r.wanted.lower()], "aur" + return result + + def is_installed(name: str) -> bool: try: return subprocess.run( @@ -140,4 +224,50 @@ def install(packages: list[str], timeout: int = 1800) -> InstallResult: return result -__all__ = ["InstallResult", "PackageInfo", "explain_failure", "install", "is_installed", "query"] +def install_aur(packages: list[str], timeout: int = 3600) -> InstallResult: + """Build and install AUR packages as the person (blocking: worker thread). + + Never as root: AUR helpers refuse it, and a PKGBUILD must not run as + root. The helper asks for the password itself (polkit) to install. + """ + # No "--" (pamac does not take it): a name can never start with "-". + packages = [p for p in packages if _SAFE_PKG.match(p) and not p.startswith("-")] + if not packages: + return InstallResult(False, _("Nothing to install.")) + helper = aur_helper() + if helper is None: + return InstallResult(False, _("{packages} is not available in your repositories. Update the system (sudo pacman -Syu) and try again.").format( + packages=", ".join(packages)), missing=packages) + if PACMAN_LOCK.exists(): + return InstallResult( + False, + _("Another program is installing or updating packages. Wait for it to finish and try again."), + ) + try: + proc = subprocess.run( + [*helper, *packages], + capture_output=True, text=True, timeout=timeout, env=_C_ENV, stdin=subprocess.DEVNULL, + ) + except FileNotFoundError: + return InstallResult(False, _("{packages} could not be built from the AUR. See the details.").format( + packages=", ".join(packages)), helper[0]) + except subprocess.TimeoutExpired: + return InstallResult(False, _("The installation took too long and was stopped.")) + if proc.returncode == 0: + return InstallResult(True) + output = (proc.stdout or "") + "\n" + (proc.stderr or "") + lines = [ln.strip() for ln in output.splitlines() if ln.strip()] + errors = [ln for ln in lines if ln.lower().startswith(("error", "==> error"))] + detail = "\n".join(errors or lines[-8:]) + if "unable to lock database" in output.lower(): + message = _("Another program is installing or updating packages. Wait for it to finish and try again.") + else: + message = _("{packages} could not be built from the AUR. See the details.").format(packages=", ".join(packages)) + logger.warning("AUR install failed (%d): %s", proc.returncode, detail) + return InstallResult(False, message, detail) + + +__all__ = [ + "InstallResult", "PackageInfo", "Resolution", "aur_helper", "aur_names", "explain_failure", + "install", "install_aur", "is_installed", "name_variants", "query", "resolve", +] diff --git a/usr/share/biglinux/tts-biglinux/ui/main_view.py b/usr/share/biglinux/tts-biglinux/ui/main_view.py index f2d77d2..028c919 100644 --- a/usr/share/biglinux/tts-biglinux/ui/main_view.py +++ b/usr/share/biglinux/tts-biglinux/ui/main_view.py @@ -1398,39 +1398,48 @@ def _revert_to_rhvoice(self) -> None: if self._catalog: self._on_voices_discovered(self._catalog) - def _piper_package_plan(self) -> tuple[list[str], list[str]]: - """Piper packages to install and those not in the repositories (worker).""" + def _piper_package_plan(self) -> list: + """Where each Piper package can come from (worker). + + Each package under either spelling (BigLinux stable has + piper-voices-pt-br, testing piper-voices-pt-BR), the AUR last. + """ from services import package_installer lang = get_system_language() wanted = ["piper-tts-bin", self._PIPER_VOICE_PACKAGES.get(lang, "piper-voices-en-US")] if "piper-voices-pt-BR" not in wanted: wanted.append("piper-voices-pt-BR") # BigLinux default language - available, missing = [], [] - for name in wanted: - (available if package_installer.query(name).available else missing).append(name) - return available, missing + return package_installer.resolve(wanted) def _ask_install_piper(self) -> None: """Offer to install Piper — only the packages that really exist.""" run_in_thread(self._piper_package_plan, on_done=self._show_piper_install_dialog) - def _show_piper_install_dialog(self, plan: tuple[list[str], list[str]]) -> None: - available, missing = plan - voices = [p for p in available if p.startswith("piper-voices-")] - if not voices: + def _show_piper_install_dialog(self, plan: list) -> None: + voices = [r for r in plan if r.wanted.startswith("piper-voices-")] + if not any(r.source for r in voices): dialog = Adw.AlertDialog.new( _("Piper is not available"), _("No Piper voice package was found in your repositories ({packages}). Update the system (sudo pacman -Syu) and try again.").format( - packages=", ".join(p for p in missing if p.startswith("piper-voices-")) + packages=", ".join(r.wanted for r in voices) ), ) dialog.add_response("ok", _("OK")) dialog.connect("response", lambda *_a: self._revert_to_rhvoice()) dialog.present(self._root_window()) return + repo = [r.name for r in plan if r.source == "repo"] + aur = [r.name for r in plan if r.source == "aur"] + if not repo and not aur: # everything is installed already + from services.package_installer import InstallResult + + self._on_piper_installed(InstallResult(True)) + return body = _("Piper provides natural neural voices. These packages will be installed:") + "\n" - body += "\n".join(f"• {name}" for name in available) + body += "\n".join([f"• {name}" for name in repo] + [f"• {name} (AUR)" for name in aur]) + if aur: + body += "\n\n" + _("Packages marked AUR are not in your repositories: they come from the Arch User Repository, are maintained by the community and are built on this computer, which takes longer.") body += "\n\n" + _("This requires administrator permissions.") dialog = Adw.AlertDialog.new(_("Install Piper Neural TTS?"), body) dialog.add_response("cancel", _("Cancel")) @@ -1439,16 +1448,22 @@ def _show_piper_install_dialog(self, plan: tuple[list[str], list[str]]) -> None: dialog.set_default_response("install") dialog.set_close_response("cancel") + def _install(): + from services import package_installer + + result = package_installer.install(repo) if repo else package_installer.InstallResult(True) + if result.ok and aur: + result = package_installer.install_aur(aur) + return result + def _on_response(_d, response: str) -> None: if response != "install": self._revert_to_rhvoice() return - from services import package_installer - self._run_install_with_progress( title=_("Installing Piper TTS"), status_text=_("Downloading and installing packages…"), - worker=lambda: package_installer.install(available), + worker=_install, on_done=self._on_piper_installed, ) diff --git a/usr/share/biglinux/tts-biglinux/ui/voice_manager_dialog.py b/usr/share/biglinux/tts-biglinux/ui/voice_manager_dialog.py index e932a9d..6926df5 100644 --- a/usr/share/biglinux/tts-biglinux/ui/voice_manager_dialog.py +++ b/usr/share/biglinux/tts-biglinux/ui/voice_manager_dialog.py @@ -180,6 +180,7 @@ def _query_packages(search_term: str, prefix: str) -> list[dict[str, str]]: capture_output=True, text=True, timeout=10, + env={**os.environ, "LC_ALL": "C"}, # "[installed]" in every language ) if proc.returncode != 0: return packages @@ -200,7 +201,7 @@ def _query_packages(search_term: str, prefix: str) -> list[dict[str, str]]: continue pkg_name = m.group(1) version = m.group(2) - installed = "[instalado]" in header or "[installed]" in header + installed = "[installed]" in header if not pkg_name.startswith(prefix): continue @@ -275,6 +276,21 @@ def _annotate_sizes(data: dict[str, list[dict[str, str]]]) -> None: by_name[name]["size"] = sz +def _one_per_spelling(pkgs: list[dict[str, str]]) -> list[dict[str, str]]: + """One row per package whatever its spelling: BigLinux stable has + piper-voices-pt-br, testing piper-voices-pt-BR, and both ship the same + files (installing the second would fail). The installed one wins, then + the lower-case spelling (stable, AUR).""" + best: dict[str, dict[str, str]] = {} + for p in pkgs: + key = p["pkg"].lower() + cur = best.get(key) + rank = (p.get("installed") == "yes", p["pkg"] == key) + if cur is None or rank > (cur.get("installed") == "yes", cur["pkg"] == key): + best[key] = p + return [p for p in pkgs if best[p["pkg"].lower()] is p] + + def _query_all_voice_packages() -> dict[str, list[dict[str, str]]]: """Query pacman for all voice packages across all engines. @@ -301,7 +317,7 @@ def _query_all_voice_packages() -> dict[str, list[dict[str, str]]]: # ── Piper voices ── piper_pkgs = _query_packages("piper-voices-", "piper-voices-") # Filter out "piper-voices-common" - piper_pkgs = [p for p in piper_pkgs if p["pkg"] != "piper-voices-common"] + piper_pkgs = _one_per_spelling([p for p in piper_pkgs if p["pkg"] != "piper-voices-common"]) for pkg in piper_pkgs: locale = pkg["pkg"].removeprefix("piper-voices-") pkg["voice_name"] = locale From 9ea3d7299e6303a450ec333ecae728b8f23d2b85 Mon Sep 17 00:00:00 2001 From: ruscher Date: Fri, 9 Oct 2026 10:38:44 -0300 Subject: [PATCH 2/3] fix(history): "Show as grid" really shows a grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every card stayed in one column. A wrapping label asks for its whole text on one line, so each card wanted the full width, and the page was clamped to a list's 760 px. - Grid cards have a fixed text width (22 characters): equal columns, as many as fit — 1 on a phone, 2 from ~760 px, 3 on wide windows. - In grid mode the page may grow to 1280 px; back in list mode it is 760. - Rows are as tall as their own tallest card: "Show more" on one card no longer stretches all of them. --- tests/test_history_view.py | 40 +++++++++++++++++++ .../biglinux/tts-biglinux/ui/history_view.py | 22 ++++++++-- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/tests/test_history_view.py b/tests/test_history_view.py index 6a0ac0f..88d2630 100644 --- a/tests/test_history_view.py +++ b/tests/test_history_view.py @@ -188,3 +188,43 @@ def test_history_is_always_in_the_main_menu(monkeypatch): for j in range(section.get_n_items()): labels.append(section.get_item_attribute_value(j, "label", None).get_string()) assert hv._("History") in labels + + +def _child_boxes(flowbox): + boxes, child = [], flowbox.get_first_child() + while child: + ok, rect = child.compute_bounds(flowbox) + boxes.append((round(rect.get_x()), round(rect.get_y()), round(rect.get_width()))) + child = child.get_next_sibling() + return boxes + + +def test_grid_puts_cards_side_by_side_in_equal_columns(page, tmp_path): + view, _settings, window = page + window.set_default_size(1000, 700) + for i in range(4): + _add("Um texto bem mais longo, para ocupar várias linhas no cartão. " * (i + 1), tmp=tmp_path) + _loaded(view) + view._view_toggle.set_active(True) + _pump(lambda: len(_child_boxes(view._grid)) == 4 and _child_boxes(view._grid)[1][0] > 0) + boxes = _child_boxes(view._grid) + assert view._grid.get_visible() and not view._list.get_visible() + assert boxes[0][1] == boxes[1][1] and boxes[1][0] > boxes[0][0] # same row, side by side + assert abs(boxes[0][2] - boxes[1][2]) <= 2 # whatever the text length + assert view._clamp.get_maximum_size() > 760 # the grid may use the window's width + view._view_toggle.set_active(False) + _pump(lambda: view._list.get_first_child() is not None) + assert view._list.get_visible() and not view._grid.get_visible() + assert view._clamp.get_maximum_size() == 760 + + +def test_grid_is_one_column_on_a_narrow_window(page, tmp_path): + view, _settings, window = page + window.set_default_size(400, 700) + for i in range(2): + _add(f"Leitura {i}.", tmp=tmp_path) + _loaded(view) + view._view_toggle.set_active(True) + _pump(lambda: len(_child_boxes(view._grid)) == 2 and _child_boxes(view._grid)[1][1] > 0) + first, second = _child_boxes(view._grid) + assert second[1] > first[1] and second[0] == first[0] # one under the other diff --git a/usr/share/biglinux/tts-biglinux/ui/history_view.py b/usr/share/biglinux/tts-biglinux/ui/history_view.py index 8f6bdc0..a58ae0e 100644 --- a/usr/share/biglinux/tts-biglinux/ui/history_view.py +++ b/usr/share/biglinux/tts-biglinux/ui/history_view.py @@ -88,6 +88,12 @@ def _show_in_file_manager(file_path: Path | None) -> None: # ── One entry ───────────────────────────────────────────────────────── +# Grid: card text width (characters) and how wide the page may get. +_GRID_TEXT_CHARS = 22 +_LIST_WIDTH = 760 +_GRID_WIDTH = 1280 + + class HistoryCard(Gtk.Box): """A history entry: text, details, player and actions.""" @@ -117,6 +123,12 @@ def __init__(self, entry: dict, view: HistoryView) -> None: self._text.set_ellipsize(Pango.EllipsizeMode.END) self._text.set_selectable(True) self._text.add_css_class("history-text-preview") + if view._grid_mode: + # A wrapping label asks for its whole text on one line: a fixed + # width lets several cards fit side by side, in equal columns. + self.add_css_class("grid") + self._text.set_width_chars(_GRID_TEXT_CHARS) + self._text.set_max_width_chars(_GRID_TEXT_CHARS) if not text: self._text.add_css_class("dim-label") text_box.append(self._text) @@ -406,9 +418,12 @@ def _build_ui(self) -> None: self._list.update_property([Gtk.AccessibleProperty.LABEL], [_("History entries")]) self._grid = Gtk.FlowBox() self._grid.set_selection_mode(Gtk.SelectionMode.NONE) - self._grid.set_homogeneous(True) + # Not homogeneous: each row is as tall as its own tallest card (one + # expanded text must not stretch every card of the page). + self._grid.set_homogeneous(False) + self._grid.set_valign(Gtk.Align.START) self._grid.set_min_children_per_line(1) - self._grid.set_max_children_per_line(3) + self._grid.set_max_children_per_line(4) self._grid.set_column_spacing(8) self._grid.set_row_spacing(8) self._grid.set_visible(False) @@ -424,7 +439,7 @@ def _build_ui(self) -> None: self._more_spinner.set_visible(False) content.append(self._more_spinner) self._clamp = Adw.Clamp() - self._clamp.set_maximum_size(760) + self._clamp.set_maximum_size(_LIST_WIDTH) self._clamp.set_child(content) self._scroll = Gtk.ScrolledWindow() self._scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) @@ -659,6 +674,7 @@ def _on_view_toggled(self, button: Gtk.ToggleButton) -> None: self._grid_mode = button.get_active() self._list.set_visible(not self._grid_mode) self._grid.set_visible(self._grid_mode) + self._clamp.set_maximum_size(_GRID_WIDTH if self._grid_mode else _LIST_WIDTH) button.set_tooltip_text(_("Show as list") if self._grid_mode else _("Show as grid")) button.set_icon_name("format-justify-fill-symbolic" if self._grid_mode else "view-grid-symbolic") self.reload() From ef097e80d29095ddb0de020ec841b49390707ca9 Mon Sep 17 00:00:00 2001 From: ruscher Date: Fri, 9 Oct 2026 10:38:44 -0300 Subject: [PATCH 3/3] build: pkgrel 3 --- pkgbuild/PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgbuild/PKGBUILD b/pkgbuild/PKGBUILD index 2495362..a174b21 100644 --- a/pkgbuild/PKGBUILD +++ b/pkgbuild/PKGBUILD @@ -7,7 +7,7 @@ pkgname=tts-biglinux # vercmp); the epoch makes 4.1.0 an upgrade over them. Never remove it. epoch=1 pkgver=4.1.0 -pkgrel=2 +pkgrel=3 arch=('x86_64') license=('GPL-3.0-or-later') url="https://github.com/biglinux/tts-biglinux"