_resolve_title replaced with _resolve_tracks using --flat-playlist to enumerate playlist entries. cmd_play enqueues each track individually, with truncation when the queue is nearly full. Single-video behavior unchanged.
ctypes libopus encoder (src/derp/opus.py), voice varint/packet builder and stream_audio method on MumbleBot (src/derp/mumble.py), music plugin with play/stop/skip/queue/np/volume commands (plugins/music.py). Audio pipeline: yt-dlp|ffmpeg subprocess -> PCM -> Opus -> UDPTunnel. 67 new tests (1561 total).