Hopefully fixed weird gstreamer traceback.

This commit is contained in:
Storm Dragon
2026-04-04 23:04:33 -04:00
parent 20e61d6259
commit 64ad1ab3e0
10 changed files with 75 additions and 17 deletions

View File

@@ -98,11 +98,11 @@ git status
## Dependencies
- **Runtime**: python3, pygobject-3.0, pluggy, AT-SPI2
- **Runtime**: python3, pygobject-3.0, pluggy, tomlkit, AT-SPI2
- **Build**: meson, ninja, gettext
- **Optional**: dasbus (for D-Bus service), BrlTTY, speech-dispatcher, piper-tts
Install build dependencies on Arch Linux:
```bash
sudo pacman -S meson ninja gettext python-dasbus
sudo pacman -S meson ninja gettext python-dasbus python-tomlkit
```

View File

@@ -179,6 +179,7 @@ Ensure you have the development dependencies installed:
```bash
sudo pacman -S python python-gobject gtk3 at-spi2-core at-spi2-atk \
python-speechd gstreamer python-pluggy python-dasbus \
python-tomlkit \
meson ninja pkgconf intltool gettext
```
@@ -187,6 +188,7 @@ sudo pacman -S python python-gobject gtk3 at-spi2-core at-spi2-atk \
sudo apt install python3 python3-gi python3-gi-cairo gir1.2-gtk-3.0 \
at-spi2-core libatk-adaptor python3-speechd \
gstreamer1.0-plugins-base python3-pluggy python3-dasbus \
python3-tomlkit \
meson ninja-build pkg-config intltool gettext
```

View File

@@ -26,6 +26,12 @@ for cmd in meson ninja python3; do
fi
done
if ! python3 -c "import tomlkit" 2>/dev/null; then
echo "Error: Python module tomlkit is not installed"
echo "Please install: python-tomlkit"
exit 1
fi
# Check for optional dependencies
missingOptional=()
if ! python3 -c "import gi" 2>/dev/null; then

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python3
#
# Copyright (c) 2026 Stormux
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
"""Compatibility helpers for GStreamer GI bindings."""
from __future__ import annotations
from typing import Any
def gst_init_check_available(gst: Any) -> bool:
"""Return Gst.init_check(None) availability across GI variants."""
initCheckResult = gst.init_check(None)
if isinstance(initCheckResult, tuple):
return bool(initCheckResult[0])
return bool(initCheckResult)

View File

@@ -48,6 +48,7 @@ cthulhu_python_sources = files([
'formatting.py',
'focus_manager.py',
'generator.py',
'gstreamer_support.py',
'guilabels.py',
'highlighter.py',
'input_event.py',

View File

@@ -31,6 +31,9 @@ import threading
import gi
from gi.repository import GLib
from . import debug
from . import gstreamer_support
from . import sound_sink
try:
gi.require_version('Gst', '1.0')
@@ -38,10 +41,7 @@ try:
except Exception:
_gstreamerAvailable = False
else:
_gstreamerAvailable, args = Gst.init_check(None)
from . import debug
from . import sound_sink
_gstreamerAvailable = gstreamer_support.gst_init_check_available(Gst)
class PiperAudioPlayer:

View File

@@ -39,6 +39,11 @@ import threading
from typing import Any, Optional, Tuple
import gi
from . import debug
from . import gstreamer_support
from . import settings
from . import sound_sink
from .sound_generator import Icon, Tone
try:
gi.require_version('Gst', '1.0')
@@ -46,12 +51,7 @@ try:
except Exception:
_gstreamerAvailable: bool = False
else:
_gstreamerAvailable, _args = Gst.init_check(None)
from . import debug
from . import settings
from . import sound_sink
from .sound_generator import Icon, Tone
_gstreamerAvailable = gstreamer_support.gst_init_check_available(Gst)
_soundSystemFailureReason: Optional[str] = None

View File

@@ -28,6 +28,7 @@ gi.require_version('GLib', '2.0')
gi.require_version('Gst', '1.0')
from gi.repository import GLib, Gst
from . import gstreamer_support
from . import settings
from . import sound_sink
@@ -75,7 +76,7 @@ class SoundWorker:
"""Runs a long-lived GStreamer worker for icon and tone playback."""
def __init__(self, soundSink: Optional[str] = None) -> None:
available, _args = Gst.init_check(None)
available = gstreamer_support.gst_init_check_available(Gst)
if not available:
raise RuntimeError("GStreamer is not available")
@@ -371,7 +372,7 @@ def play_file_once(
soundSink: Optional[str] = None,
volume: float = 1.0,
) -> int:
available, _args = Gst.init_check(None)
available = gstreamer_support.gst_init_check_available(Gst)
if not available:
print("GStreamer is not available", file=sys.stderr)
return 1

View File

@@ -19,6 +19,8 @@ from __future__ import annotations
from typing import Any, Optional, Tuple
import gi
from . import gstreamer_support
from . import settings
try:
gi.require_version('Gst', '1.0')
@@ -26,9 +28,7 @@ try:
except Exception:
_gstreamerAvailable: bool = False
else:
_gstreamerAvailable, _args = Gst.init_check(None)
from . import settings
_gstreamerAvailable = gstreamer_support.gst_init_check_available(Gst)
_SINK_ELEMENT_BY_SETTING = {
settings.SOUND_SINK_PIPEWIRE: "pipewiresink",

View File

@@ -1,6 +1,7 @@
import sys
import types
import unittest
import importlib
from pathlib import Path
from unittest import mock
@@ -31,6 +32,7 @@ soundGeneratorStub.Tone = _Tone
sys.modules.setdefault("cthulhu.sound_generator", soundGeneratorStub)
from cthulhu import settings
from cthulhu import piper_audio_player
from cthulhu import sound
from cthulhu import sound_sink
@@ -46,6 +48,23 @@ class SoundSinkTests(unittest.TestCase):
self.assertGreaterEqual(len(candidates), 1)
self.assertEqual(candidates[0], "autoaudiosink")
def test_gstreamer_init_check_bool_result_does_not_break_imports(self):
modules = [
sound_sink,
sound,
piper_audio_player,
]
with mock.patch.object(sound_sink.Gst, "init_check", return_value=True):
reloadedModules = []
for module in modules:
reloaded = importlib.reload(module)
reloadedModules.append(reloaded)
self.assertTrue(reloaded._gstreamerAvailable)
for module in reloadedModules:
importlib.reload(module)
class PlayerRecoveryTests(unittest.TestCase):
def test_worker_diagnostic_marks_restart_required(self):