Parent Call told rooms they had never been set up. Nothing about the rooms was wrong. Read from the church broker: …/coordinator and …/pending are retained and …/mode is absent, and the coordinator's own source says why — it publishes the retained mode only in answer to an operator writing to …/mode/set, and nobody ever has. Every classroom waited a second and a half for a message that will never exist. A missing mode is now read as the coordinator's own default rather than as an unconfigured room: the room offers the call and lets the sanctuary end answer, because that is the end that knows. A mode that really is off still refuses, in the coordinator's words, and a broker that is missing or unreachable still says which of those it is. Proved against a rig broker made to match the church's exactly, with a call landing on the sanctuary mock. The card is anchored to the bottom-left corner instead of centred, by layer shell, which is what the disc player's cover has used under KWin for months. Anchoring rather than positioning is what makes it right at 1080p and at 4K without a coordinate anywhere. It falls back to the centred card if the library is missing. Notifications are sized for a room. Every Plasma measurement is a grid unit and a grid unit is the height of the general font, so the font is the lever — 15 point instead of Breeze's 10, which takes the card from about 350x110 to about 410x145 at 100% and doubles again at 200%. Text, icon, padding and buttons move together. The chime stays off. No shell package: that path cost the panel and is not coming back. And the product name is gone from everything a teacher sees. The notification card is from Classroom, the disc player's from DVD. Co-Authored-By: Claude Opus 5 <[email protected]>
156 lines
4.0 KiB
Python
Executable File
156 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""Classroom DVD control.
|
|
|
|
Discs are never played automatically. Inserting a disc only produces a
|
|
notification and changes the dock icon; playback always starts from a
|
|
deliberate press of the DVD button.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
sys.path.insert(0, "/usr/local/lib/ekklesia-classroom")
|
|
sys.path.insert(
|
|
0,
|
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lib"),
|
|
)
|
|
|
|
import classroom_optical as optical # noqa: E402
|
|
from classroom_vlc import PLAYER # noqa: E402
|
|
|
|
SESSION_TOOL = "/usr/local/bin/classroom-dvd-session"
|
|
|
|
|
|
def notify(title, message, urgency="normal"):
|
|
try:
|
|
subprocess.Popen(
|
|
[
|
|
"notify-send",
|
|
"--app-name=DVD",
|
|
f"--urgency={urgency}",
|
|
"--",
|
|
title,
|
|
message,
|
|
],
|
|
start_new_session=True,
|
|
)
|
|
except OSError:
|
|
print(f"{title}: {message}", file=sys.stderr)
|
|
|
|
|
|
def play(device):
|
|
"""Hand the disc to DVD mode.
|
|
|
|
classroom-dvd-session owns the player from here: it puts a black
|
|
surface up immediately, starts VLC behind it, and provides the
|
|
classroom's own controls.
|
|
|
|
Started directly, with nothing arranging windows on the way. A second
|
|
session cannot happen — the session takes an exclusive lock on
|
|
dvd.lock and exits if it cannot get it — and bringing an existing one
|
|
back to the front is the panel's job, not this command's. That is
|
|
what replaced the window helper.
|
|
"""
|
|
if shutil.which(PLAYER) is None:
|
|
notify(
|
|
"DVD",
|
|
"The DVD player is not installed on this classroom computer.",
|
|
)
|
|
return 1
|
|
|
|
subprocess.Popen([SESSION_TOOL], start_new_session=True)
|
|
|
|
return 0
|
|
|
|
|
|
def action_toggle(force_play=False):
|
|
device = optical.default_device()
|
|
|
|
if device is None:
|
|
notify("DVD", "No DVD drive is connected to this classroom computer.")
|
|
return 1
|
|
|
|
status = optical.drive_status(device)
|
|
|
|
if status == optical.CDS_DISC_OK:
|
|
return play(device)
|
|
|
|
if status == optical.CDS_DRIVE_NOT_READY:
|
|
notify("DVD", "Reading the disc. Please try again in a moment.")
|
|
return 1
|
|
|
|
if status == optical.CDS_TRAY_OPEN:
|
|
notify("DVD", "The disc tray is open. Insert a disc to get started.")
|
|
return 1
|
|
|
|
if force_play:
|
|
return play(device)
|
|
|
|
notify("DVD", "No disc is in the drive. Insert a disc to get started.")
|
|
return 1
|
|
|
|
|
|
def action_status():
|
|
device = optical.default_device()
|
|
|
|
if device is None:
|
|
print("drive: none")
|
|
return 1
|
|
|
|
status = optical.drive_status(device)
|
|
|
|
print(f"drive: {device}")
|
|
print(f"status: {optical.describe(status)}")
|
|
print(f"player: {shutil.which(PLAYER) or 'not installed'}")
|
|
|
|
if status == optical.CDS_DISC_OK:
|
|
print(f"disc: {optical.disc_status(device)}")
|
|
return 0
|
|
|
|
return 1
|
|
|
|
|
|
def action_eject():
|
|
device = optical.default_device()
|
|
|
|
if device is None:
|
|
notify("DVD", "No DVD drive is connected to this classroom computer.")
|
|
return 1
|
|
|
|
if not optical.eject(device):
|
|
notify("DVD", "The disc could not be ejected. It may still be playing.")
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Classroom DVD control.")
|
|
subparsers = parser.add_subparsers(dest="action", required=True)
|
|
|
|
subparsers.add_parser("toggle", help="Dock button behaviour")
|
|
subparsers.add_parser("play", help="Start playback even if the drive is unsure")
|
|
subparsers.add_parser("status", help="Report drive and disc state")
|
|
subparsers.add_parser("eject", help="Open the disc tray")
|
|
|
|
arguments = parser.parse_args()
|
|
|
|
if arguments.action == "toggle":
|
|
return action_toggle()
|
|
|
|
if arguments.action == "play":
|
|
return action_toggle(force_play=True)
|
|
|
|
if arguments.action == "status":
|
|
return action_status()
|
|
|
|
return action_eject()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|