linux.sipsoftphone/sipwebapp.spec
data 48ddb4b4af Initiales Release: SIP Softphone für FreePBX/Asterisk
PySide6/PJSUA2-basiertes Desktop-Softphone mit:
- SIP-Telefonie (Anrufe, DTMF, Halten, Transfer, Konferenz)
- CardDAV-Kontaktsync mit Write-Back (Multi-Account)
- Kontaktverwaltung mit Suche und Bearbeiten-Dialog
- Anrufliste mit Namensauflösung
- Favoriten-Panel (manuell + häufig angerufen)
- BLF-Panel (SIP Presence Monitoring)
- Responsives Layout (kompakt/erweitert)
- Click-to-Call (tel:/sip: URI via D-Bus)
- KDE-Integration (Tray, Benachrichtigungen)
- PipeWire/PulseAudio Audio-Routing
- AppImage Build-Support (PyInstaller + appimagetool)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:18:54 +01:00

127 lines
3.2 KiB
Python

# -*- mode: python ; coding: utf-8 -*-
"""PyInstaller Spec-Datei für SIP Softphone AppImage.
Bündelt Python-App + pjsua2 + Qt + alle nativen Abhängigkeiten
zu einem komplett unabhängigen Paket.
"""
import os
import subprocess
import re
# === Pfade ===
APP_DIR = os.path.dirname(os.path.abspath(SPEC))
PJSUA2_SO = "/tmp/sipwebapp_run/_pjsua2.cpython-314-x86_64-linux-gnu.so"
# === pjsua2 native Bibliotheken sammeln ===
# Alle Shared Libraries die _pjsua2.so braucht (nicht-triviale)
def pjsua2_libs_sammeln():
"""Alle pjsua2-Abhängigkeiten aus ldd extrahieren."""
ergebnis = subprocess.run(
["ldd", PJSUA2_SO], capture_output=True, text=True)
libs = []
# System-Basis-Libs die überall vorhanden sind (nicht bündeln)
skip = {
"linux-vdso", "ld-linux", "libc.so", "libm.so",
"libdl.so", "libpthread.so", "librt.so",
}
for zeile in ergebnis.stdout.splitlines():
match = re.match(r"\s+(\S+)\s+=>\s+(/\S+)", zeile)
if match:
name, pfad = match.groups()
if not any(s in name for s in skip) and os.path.exists(pfad):
libs.append((pfad, "."))
return libs
pjsua2_binaries = pjsua2_libs_sammeln()
# dbus native Libs
dbus_libs = []
for lib_name in ["libdbus-1.so.3", "libglib-2.0.so.0", "libgio-2.0.so.0",
"libgobject-2.0.so.0", "libgmodule-2.0.so.0"]:
for pfad in [f"/usr/lib/{lib_name}", f"/usr/lib/x86_64-linux-gnu/{lib_name}"]:
if os.path.exists(pfad):
dbus_libs.append((pfad, "."))
break
# === pjsua2 Python-Bindings ===
pjsua2_daten = [
(PJSUA2_SO, "."),
("/tmp/sipwebapp_run/pjsua2.py", "."),
]
# === dbus Python-Bindings ===
SYS_SITE = "/usr/lib/python3.14/site-packages"
dbus_daten = []
if os.path.isdir(f"{SYS_SITE}/dbus"):
dbus_daten.append((f"{SYS_SITE}/dbus", "dbus"))
for so in ["_dbus_bindings", "_dbus_glib_bindings"]:
for f in os.listdir(SYS_SITE):
if f.startswith(so) and f.endswith(".so"):
dbus_daten.append((f"{SYS_SITE}/{f}", "."))
# === Ressourcen ===
ressourcen = [
(os.path.join(APP_DIR, "resources"), "resources"),
]
# === Hidden Imports (Module die PyInstaller nicht automatisch findet) ===
hidden_imports = [
"pjsua2",
"_pjsua2",
"dbus",
"dbus.service",
"dbus.mainloop.glib",
"dbus._dbus",
"dbus.bus",
"dbus.connection",
"dbus.decorators",
"dbus.exceptions",
"dbus.lowlevel",
"dbus.proxies",
"dbus.types",
"vobject",
"vobject.vcard",
"vobject.base",
]
# === Analysis ===
a = Analysis(
[os.path.join(APP_DIR, "main.py")],
pathex=[APP_DIR],
binaries=pjsua2_binaries + dbus_libs + pjsua2_daten,
datas=ressourcen + dbus_daten,
hiddenimports=hidden_imports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
"tkinter", "unittest", "test", "xmlrpc",
"pydoc", "doctest", "lib2to3",
],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="sipwebapp",
debug=False,
bootloader_ignore_signals=False,
strip=True,
upx=False,
console=False, # Kein Terminal-Fenster
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=True,
upx=False,
name="sipwebapp",
)