Hopefully fixed weird gstreamer traceback.
This commit is contained in:
@@ -98,11 +98,11 @@ git status
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- **Runtime**: python3, pygobject-3.0, pluggy, AT-SPI2
|
- **Runtime**: python3, pygobject-3.0, pluggy, tomlkit, AT-SPI2
|
||||||
- **Build**: meson, ninja, gettext
|
- **Build**: meson, ninja, gettext
|
||||||
- **Optional**: dasbus (for D-Bus service), BrlTTY, speech-dispatcher, piper-tts
|
- **Optional**: dasbus (for D-Bus service), BrlTTY, speech-dispatcher, piper-tts
|
||||||
|
|
||||||
Install build dependencies on Arch Linux:
|
Install build dependencies on Arch Linux:
|
||||||
```bash
|
```bash
|
||||||
sudo pacman -S meson ninja gettext python-dasbus
|
sudo pacman -S meson ninja gettext python-dasbus python-tomlkit
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ Ensure you have the development dependencies installed:
|
|||||||
```bash
|
```bash
|
||||||
sudo pacman -S python python-gobject gtk3 at-spi2-core at-spi2-atk \
|
sudo pacman -S python python-gobject gtk3 at-spi2-core at-spi2-atk \
|
||||||
python-speechd gstreamer python-pluggy python-dasbus \
|
python-speechd gstreamer python-pluggy python-dasbus \
|
||||||
|
python-tomlkit \
|
||||||
meson ninja pkgconf intltool gettext
|
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 \
|
sudo apt install python3 python3-gi python3-gi-cairo gir1.2-gtk-3.0 \
|
||||||
at-spi2-core libatk-adaptor python3-speechd \
|
at-spi2-core libatk-adaptor python3-speechd \
|
||||||
gstreamer1.0-plugins-base python3-pluggy python3-dasbus \
|
gstreamer1.0-plugins-base python3-pluggy python3-dasbus \
|
||||||
|
python3-tomlkit \
|
||||||
meson ninja-build pkg-config intltool gettext
|
meson ninja-build pkg-config intltool gettext
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ for cmd in meson ninja python3; do
|
|||||||
fi
|
fi
|
||||||
done
|
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
|
# Check for optional dependencies
|
||||||
missingOptional=()
|
missingOptional=()
|
||||||
if ! python3 -c "import gi" 2>/dev/null; then
|
if ! python3 -c "import gi" 2>/dev/null; then
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -48,6 +48,7 @@ cthulhu_python_sources = files([
|
|||||||
'formatting.py',
|
'formatting.py',
|
||||||
'focus_manager.py',
|
'focus_manager.py',
|
||||||
'generator.py',
|
'generator.py',
|
||||||
|
'gstreamer_support.py',
|
||||||
'guilabels.py',
|
'guilabels.py',
|
||||||
'highlighter.py',
|
'highlighter.py',
|
||||||
'input_event.py',
|
'input_event.py',
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ import threading
|
|||||||
|
|
||||||
import gi
|
import gi
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
|
from . import debug
|
||||||
|
from . import gstreamer_support
|
||||||
|
from . import sound_sink
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gi.require_version('Gst', '1.0')
|
gi.require_version('Gst', '1.0')
|
||||||
@@ -38,10 +41,7 @@ try:
|
|||||||
except Exception:
|
except Exception:
|
||||||
_gstreamerAvailable = False
|
_gstreamerAvailable = False
|
||||||
else:
|
else:
|
||||||
_gstreamerAvailable, args = Gst.init_check(None)
|
_gstreamerAvailable = gstreamer_support.gst_init_check_available(Gst)
|
||||||
|
|
||||||
from . import debug
|
|
||||||
from . import sound_sink
|
|
||||||
|
|
||||||
|
|
||||||
class PiperAudioPlayer:
|
class PiperAudioPlayer:
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ import threading
|
|||||||
from typing import Any, Optional, Tuple
|
from typing import Any, Optional, Tuple
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
from . import debug
|
||||||
|
from . import gstreamer_support
|
||||||
|
from . import settings
|
||||||
|
from . import sound_sink
|
||||||
|
from .sound_generator import Icon, Tone
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gi.require_version('Gst', '1.0')
|
gi.require_version('Gst', '1.0')
|
||||||
@@ -46,12 +51,7 @@ try:
|
|||||||
except Exception:
|
except Exception:
|
||||||
_gstreamerAvailable: bool = False
|
_gstreamerAvailable: bool = False
|
||||||
else:
|
else:
|
||||||
_gstreamerAvailable, _args = Gst.init_check(None)
|
_gstreamerAvailable = gstreamer_support.gst_init_check_available(Gst)
|
||||||
|
|
||||||
from . import debug
|
|
||||||
from . import settings
|
|
||||||
from . import sound_sink
|
|
||||||
from .sound_generator import Icon, Tone
|
|
||||||
|
|
||||||
_soundSystemFailureReason: Optional[str] = None
|
_soundSystemFailureReason: Optional[str] = None
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ gi.require_version('GLib', '2.0')
|
|||||||
gi.require_version('Gst', '1.0')
|
gi.require_version('Gst', '1.0')
|
||||||
from gi.repository import GLib, Gst
|
from gi.repository import GLib, Gst
|
||||||
|
|
||||||
|
from . import gstreamer_support
|
||||||
from . import settings
|
from . import settings
|
||||||
from . import sound_sink
|
from . import sound_sink
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ class SoundWorker:
|
|||||||
"""Runs a long-lived GStreamer worker for icon and tone playback."""
|
"""Runs a long-lived GStreamer worker for icon and tone playback."""
|
||||||
|
|
||||||
def __init__(self, soundSink: Optional[str] = None) -> None:
|
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:
|
if not available:
|
||||||
raise RuntimeError("GStreamer is not available")
|
raise RuntimeError("GStreamer is not available")
|
||||||
|
|
||||||
@@ -371,7 +372,7 @@ def play_file_once(
|
|||||||
soundSink: Optional[str] = None,
|
soundSink: Optional[str] = None,
|
||||||
volume: float = 1.0,
|
volume: float = 1.0,
|
||||||
) -> int:
|
) -> int:
|
||||||
available, _args = Gst.init_check(None)
|
available = gstreamer_support.gst_init_check_available(Gst)
|
||||||
if not available:
|
if not available:
|
||||||
print("GStreamer is not available", file=sys.stderr)
|
print("GStreamer is not available", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ from __future__ import annotations
|
|||||||
from typing import Any, Optional, Tuple
|
from typing import Any, Optional, Tuple
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
from . import gstreamer_support
|
||||||
|
from . import settings
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gi.require_version('Gst', '1.0')
|
gi.require_version('Gst', '1.0')
|
||||||
@@ -26,9 +28,7 @@ try:
|
|||||||
except Exception:
|
except Exception:
|
||||||
_gstreamerAvailable: bool = False
|
_gstreamerAvailable: bool = False
|
||||||
else:
|
else:
|
||||||
_gstreamerAvailable, _args = Gst.init_check(None)
|
_gstreamerAvailable = gstreamer_support.gst_init_check_available(Gst)
|
||||||
|
|
||||||
from . import settings
|
|
||||||
|
|
||||||
_SINK_ELEMENT_BY_SETTING = {
|
_SINK_ELEMENT_BY_SETTING = {
|
||||||
settings.SOUND_SINK_PIPEWIRE: "pipewiresink",
|
settings.SOUND_SINK_PIPEWIRE: "pipewiresink",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
|
import importlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ soundGeneratorStub.Tone = _Tone
|
|||||||
sys.modules.setdefault("cthulhu.sound_generator", soundGeneratorStub)
|
sys.modules.setdefault("cthulhu.sound_generator", soundGeneratorStub)
|
||||||
|
|
||||||
from cthulhu import settings
|
from cthulhu import settings
|
||||||
|
from cthulhu import piper_audio_player
|
||||||
from cthulhu import sound
|
from cthulhu import sound
|
||||||
from cthulhu import sound_sink
|
from cthulhu import sound_sink
|
||||||
|
|
||||||
@@ -46,6 +48,23 @@ class SoundSinkTests(unittest.TestCase):
|
|||||||
self.assertGreaterEqual(len(candidates), 1)
|
self.assertGreaterEqual(len(candidates), 1)
|
||||||
self.assertEqual(candidates[0], "autoaudiosink")
|
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):
|
class PlayerRecoveryTests(unittest.TestCase):
|
||||||
def test_worker_diagnostic_marks_restart_required(self):
|
def test_worker_diagnostic_marks_restart_required(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user