107 lines
4.2 KiB
Python
107 lines
4.2 KiB
Python
from utils import format_time_short
|
|
|
|
HELP_TEXT = """
|
|
<b>Available Commands:</b><br>
|
|
• <b>/play</b>, <b>/pause</b> - Control playback<br>
|
|
• <b>/seek [time]</b> - Seek (e.g., 1:23, +30s, -1m)<br>
|
|
• <b>/tag [now|time] [name]</b> - Create a highlight<br>
|
|
• <b>/time</b> - Show current timestamp<br>
|
|
• <b>/help</b> - Show this message<br><br>
|
|
<b>Keyboard Shortcuts:</b><br>
|
|
• <b>Space</b>: Play/Pause<br>
|
|
• <b>F</b>: Fullscreen | <b>M</b>: Mute<br>
|
|
• <b>Left/Right</b>: Seek ±5s<br>
|
|
• <b>Up/Down</b>: Volume ±5%<br>
|
|
• <b>Enter</b>: Focus chat | <b>Esc</b>: Clear focus
|
|
"""
|
|
|
|
|
|
def parse_time_arg(arg: str, current_ms: int, length_ms: int):
|
|
"""Parses a time string (absolute time, offset like +30s, or 'now') and returns the target time in MS. Returns None on error."""
|
|
if arg == "now":
|
|
return current_ms
|
|
|
|
try:
|
|
target_ms = 0
|
|
if arg.startswith('+') or arg.startswith('-'):
|
|
modifier = 1 if arg.startswith('+') else -1
|
|
num_str = arg[1:]
|
|
if num_str.endswith('s'): val = float(num_str[:-1]) * 1000
|
|
elif num_str.endswith('m'): val = float(num_str[:-1]) * 60 * 1000
|
|
elif num_str.endswith('h'): val = float(num_str[:-1]) * 3600 * 1000
|
|
else: val = float(num_str) * 1000
|
|
target_ms = current_ms + (val * modifier)
|
|
elif ":" in arg:
|
|
parts = arg.split(":")
|
|
parts.reverse()
|
|
if len(parts) > 0: target_ms += float(parts[0]) * 1000
|
|
if len(parts) > 1: target_ms += float(parts[1]) * 60 * 1000
|
|
if len(parts) > 2: target_ms += float(parts[2]) * 3600 * 1000
|
|
else:
|
|
if arg.endswith('s'): target_ms = float(arg[:-1]) * 1000
|
|
elif arg.endswith('m'): target_ms = float(arg[:-1]) * 60 * 1000
|
|
elif arg.endswith('h'): target_ms = float(arg[:-1]) * 3600 * 1000
|
|
else: target_ms = float(arg) * 1000
|
|
|
|
if length_ms > 0:
|
|
return max(0, min(target_ms, length_ms))
|
|
return max(0, target_ms)
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def handle_chat_command(cmd: str, parts: list, widget):
|
|
"""Handle a slash command. Returns True if the command was handled."""
|
|
if cmd == "/play":
|
|
if not widget.vlc_player.is_playing:
|
|
widget.toggle_playback()
|
|
widget.add_system_message(" ".join(parts))
|
|
return True
|
|
|
|
elif cmd == "/pause":
|
|
if widget.vlc_player.is_playing:
|
|
widget.toggle_playback()
|
|
widget.add_system_message(" ".join(parts))
|
|
return True
|
|
|
|
elif cmd == "/seek":
|
|
if len(parts) > 1:
|
|
if widget._handle_seek_command(parts[1]):
|
|
widget.add_system_message(" ".join(parts))
|
|
else:
|
|
widget.add_system_message("Invalid time format. Use: 1:23, +30s, -1m")
|
|
else:
|
|
widget.add_system_message("Usage: /seek [time]")
|
|
return True
|
|
|
|
elif cmd == "/tag":
|
|
if len(parts) > 1:
|
|
time_arg = parts[1].lower()
|
|
tag_name = " ".join(parts[2:])
|
|
|
|
current_ms = widget.vlc_player.current_time_ms
|
|
length_ms = widget.vlc_player.get_length()
|
|
target_ms = parse_time_arg(time_arg, current_ms, length_ms)
|
|
|
|
if target_ms is not None:
|
|
time_str = format_time_short(target_ms)
|
|
msg = f"{time_str} {tag_name}".strip()
|
|
widget.chat_message_ready.emit(msg)
|
|
else:
|
|
widget.add_system_message("Invalid time format. Use: now, 1:23, +30s, -1m")
|
|
else:
|
|
widget.add_system_message("Usage: /tag [now|time] [name]")
|
|
return True
|
|
|
|
elif cmd == "/time":
|
|
current_ms = widget.vlc_player.current_time_ms
|
|
time_str = format_time_short(current_ms)
|
|
widget.add_system_message(f"Current time: <a href='seek:{time_str}' style='color: #66b3ff; text-decoration: none;'>{time_str}</a>")
|
|
return True
|
|
|
|
elif cmd == "/help":
|
|
widget.add_system_message(HELP_TEXT)
|
|
return True
|
|
|
|
return False
|