Skip to content

Surface track playback errors to users and support yt-dlp JS runtimes#53

Open
Firestone82 wants to merge 4 commits into
masterfrom
claude/add-js-runtime-env-var-r0BVA
Open

Surface track playback errors to users and support yt-dlp JS runtimes#53
Firestone82 wants to merge 4 commits into
masterfrom
claude/add-js-runtime-env-var-r0BVA

Conversation

@Firestone82
Copy link
Copy Markdown
Owner

Summary

This PR improves error visibility and adds support for external JavaScript runtimes in yt-dlp. When a track fails to play, users now see a descriptive error message in the voice channel's text chat instead of the track silently skipping. Additionally, the bot can now forward a YT_DLP_JS_RUNTIMES environment variable to all yt-dlp invocations, enabling hosts to use native JS engines (Deno, Node) when the bundled interpreter struggles with YouTube signature extraction.

Key Changes

  • Error Handler Enhancement: ErrorHandler now captures playback error details from Songbird's EventContext, extracts the track title from the player state, and sends a formatted PlaybackErrorEmbed to the voice channel's integrated text chat. This surfaces why tracks were skipped (typically yt-dlp signature/extraction failures) to users.

  • Channel Service Utilities: Added bot_in_voice() and bot_voice_channel() helpers to safely determine the bot's current voice channel without relying on stale cached GuildChannel references. This prevents announcing leaves that already happened when the bot was kicked or dragged out of voice.

  • Embed Service Extension: Added send_channel_id() method to the SendEmbed trait and a corresponding send_channel_id_embed() function to support sending embeds to channels identified by raw ChannelId rather than cached GuildChannel objects. This is necessary for writing into voice channel text chats looked up through the bot's voice state.

  • yt-dlp JS Runtime Support: Created utils/yt_dlp_utils.rs with an extra_args() function that reads the YT_DLP_JS_RUNTIMES environment variable and returns --js-runtimes <value> CLI arguments. Integrated this into all yt-dlp invocations: cache_service::cache_track(), cache_service::probe_track(), Track::get_input(), and YoutubeClient::fetch_playlist().

  • Inactivity Leave Notices: Updated cmd_stop.rs and queue_handler.rs to use bot_voice_channel() instead of relying on potentially stale guild_channel references, ensuring inactivity leave notices are only sent when the bot is actually still in voice.

  • Documentation: Updated .env.example with guidance on the new YT_DLP_JS_RUNTIMES configuration option and linked to the yt-dlp wiki for context.

Implementation Details

  • Error extraction uses pattern matching on EventContext::Track to find the PlayMode::Errored state and convert it to a user-friendly string.
  • The ErrorHandler is now instantiated with serenity_ctx, player, and guild_channel at event registration time in channel_service::join_user_channel(), allowing it to send embeds and read current track metadata.
  • Voice channel lookups bypass the cache and query the bot's own voice state, making them resilient to cache staleness or the bot being forcibly moved/removed.
  • The yt-dlp extra args are applied consistently across download, probe, playlist enumeration, and streaming input contexts so all operations stay in sync.

https://claude.ai/code/session_013G7EXeh6SyhGywqvugD1t9

claude added 3 commits May 20, 2026 20:16
Hosts where yt-dlp's bundled JS interpreter can't decipher YouTube
signatures need an external runtime (Deno/Node) via --js-runtimes. The
new YT_DLP_JS_RUNTIMES env var is appended to the download, probe,
playlist-scrape, and streaming YoutubeDl input so the workaround
applies everywhere yt-dlp runs.

See https://github.com/yt-dlp/yt-dlp/wiki/EJS#deno
Track errors now reach the text channel: `ErrorHandler` carries the
serenity context, the player, and the guild channel so it can read the
current track and post a `PlaybackErrorEmbed` with the underlying
PlayError (e.g. yt-dlp signature/extraction failure). Songbird still
fires TrackEvent::End after the error, so QueueHandler keeps the queue
advancing — we only add the user-facing notice here.

The 5-minute inactivity timer (queue_handler + cmd_stop) used to fire
unconditionally, including after the bot had already been kicked or
dragged out of voice. It now checks `channel_service::bot_in_voice`
before announcing the leave, so a vacated bot doesn't post "leaving
voice channel" from a channel it isn't in anymore.
The 5-minute inactivity leave used to land in whatever text channel
issued the first !play, which gets noisy when the music conversation
has moved on. Route it through the bot's current voice channel
instead — Discord voice channels have an integrated text chat on the
same ChannelId, so messages there sit next to the voice activity they
describe.

Adds `channel_service::bot_voice_channel` (looks up the bot's current
voice channel via cache) and a `send_channel_id` variant on the
SendEmbed trait so we can post to a raw ChannelId without first
resolving a GuildChannel. The kicked/dragged-out check from the
previous commit folds into the same lookup — `None` already means
"not in voice", so the timer bails for free.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6de85320a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/service/channel_service.rs Outdated
`manager.join` is idempotent — `!play` calls it on every invocation
even when the bot is already connected, and songbird stacks global
event listeners on the same Call. With the previous unit-struct
ErrorHandler that just logged, this produced duplicate log lines;
now that the handler posts an embed, every prior `!play` would
contribute another duplicate PlaybackErrorEmbed on a single failure.

Check `manager.get(guild_id)` before joining and only register the
handler on the first connect.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants