Files
Classroom_Desktop/scripts/classroom-dvd
T
ekklesiaandClaude Opus 5 c81fc1b0b9 feat: dress VLC down to an appliance screen
Stock VLC looks like a desktop media player, which is wrong for a room
where the TV is the whole interface. VLC stays the backend because its
dvdnav module is what provides the disc menus, and menus are the part
that matters on a commercial disc.

It is dressed down with documented VLC 3.x options, all verified against
the 3.0.23 build in Debian Trixie: minimal view, dark palette, no cone,
no tray, no now-playing popups, no resume prompt, no recent-disc
history, no title banner. sway draws no border on the window either, so
there is nothing above the video.

The auto-hiding fullscreen controller is deliberately kept: it is not
persistent chrome, and it is the only way a volunteer with a mouse can
pause or change chapter.

No skin is involved. Skins replace the interface wholesale and take the
keyboard and mouse handling that DVD menu navigation depends on with
them, as would --intf dummy.

Also drops --no-qt-updates-notif, which was not an option in Debian's
VLC at all. It made VLC exit before it started, so the DVD button could
never have launched a disc.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-04 21:33:22 -05:00

195 lines
5.3 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
PLAYER = "vlc"
WINDOW_TOOL = "/usr/local/bin/classroom-window"
def notify(title, message, urgency="normal"):
try:
subprocess.Popen(
[
"notify-send",
"--app-name=Ekklesia Classroom",
f"--urgency={urgency}",
"--",
title,
message,
],
start_new_session=True,
)
except OSError:
print(f"{title}: {message}", file=sys.stderr)
def player_command(device):
"""VLC, dressed down to look like an appliance rather than a desktop app.
All of these are documented VLC 3.x options, verified against the
3.0.23 build in Debian Trixie. No skin is involved: skins replace the
interface wholesale and break DVD menu input, which is the one thing
that has to keep working.
Note that --no-qt-updates-notif, which earlier builds passed, is not
an option in Debian's VLC and made it refuse to start at all.
"""
return [
PLAYER,
# Fullscreen first: the disc opens straight onto the TV.
"--fullscreen",
# No menu bar or extended panels, and a dark window instead of
# the default light grey one.
"--qt-minimal-view",
"--qt-dark-palette",
# No orange cone, and no album-art panel, behind the video.
"--no-qt-bgcone",
# Nothing that behaves like a desktop application.
"--no-qt-system-tray",
"--no-qt-name-in-title",
"--no-qt-video-autoresize",
"--qt-notification=0",
# Never offer to resume, and never accumulate a list of the
# discs a classroom has played.
"--qt-continue=0",
"--no-qt-recentplay",
# No first-run privacy dialog, no title banner over the video.
"--no-qt-privacy-ask",
"--no-video-title-show",
# dvd:// selects VLC's dvdnav module, which is what provides the
# disc menus. The Qt interface is kept for exactly that reason:
# --intf dummy would be cleaner still but takes the keyboard and
# mouse handling that menu navigation depends on with it.
f"dvd://{device}",
]
def play(device):
"""Hand the disc to the player through the normal window toggle."""
if shutil.which(PLAYER) is None:
notify(
"DVD",
"The DVD player is not installed on this classroom computer.",
)
return 1
subprocess.run(
[
WINDOW_TOOL,
"toggle",
"--match",
"(?i)vlc",
"--",
*player_command(device),
]
)
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())