manuel-rw / jellyfin-discord-music-bot

A simple and leightweight Discord Bot, that integrates with your Jellyfin Media server and enables you to listen to your favourite music directly from discord.
MIT License
75 stars 9 forks source link

Bot crashes on summon/play #238

Closed sssionek closed 10 months ago

sssionek commented 11 months ago

Describe the bug The bot can clearly start and shows up as active on Discord. But upon summon or play, it joins the voice channel with audio disabled and crashes.

To Reproduce Steps to reproduce the behavior:

  1. Start bot manually (e.g docker run ...) with host networking for communication with Jellyfin
  2. Once it becomes active on Discord, summon into voice channel or try to play anything
  3. Bot joins voice channel without audio and the backend crashes

Expected behavior Well, it would be nice for it to play anything ;)

Screenshots Log from the crash, ran with the latest dev container.

[Nest] 28  - 10/14/2023, 7:10:10 PM   DEBUG [JellyinPlaystateService] Reported playback capabilities sucessfully
[Nest] 28  - 10/14/2023, 7:10:11 PM     LOG [NestApplication] Nest application successfully started +706ms
[Nest] 28  - 10/14/2023, 7:10:16 PM   DEBUG [JellyfinWebSocketService] Opening WebSocket with client id jellyfin-discord-bot...
node:events:368
      throw er; // Unhandled 'error' event
      ^

Error: Unexpected server response: 302
    at ClientRequest.<anonymous> (/app/node_modules/ws/lib/websocket.js:896:7)
    at ClientRequest.emit (node:events:390:28)
    at HTTPParser.parserOnIncomingClient (node:_http_client:623:27)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
    at Socket.socketOnData (node:_http_client:487:22)
    at Socket.emit (node:events:390:28)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
    at Socket.Readable.push (node:internal/streams/readable:228:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:199:23)
Emitted 'error' event on WebSocket instance at:
    at emitErrorAndClose (/app/node_modules/ws/lib/websocket.js:1016:13)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Additional context One of the first lines of the trace is Error: Unexpected server response: 302. I have my jellyfin instance in a subdir to play along with other stuff I have on the server, so I suspect that this scenario wasn't tested?

I tested getting a root request with curl (for me that would be http://127.0.0.1:8096/jellyfin for example), I got a 302 but that's expected for WebUI to work.

sssionek commented 11 months ago
[Nest] 28  - 10/14/2023, 7:23:30 PM   DEBUG [JellyfinWebSocketService] Opening WebSocket with client id jellyfin-discord-bot...
[Nest] 28  - 10/14/2023, 7:23:30 PM   DEBUG [PlayItemCommand] Extracted 1 tracks from the search item
[Nest] 28  - 10/14/2023, 7:23:30 PM   DEBUG [PlayItemCommand] Adding 1 tracks with a duration of 267833.472 ticks
[Nest] 28  - 10/14/2023, 7:23:30 PM   DEBUG [JellyinPlaystateService] Reporting playback start on track 'xxx'
[Nest] 28  - 10/14/2023, 7:23:30 PM   DEBUG [JellyfinStreamBuilderService] Building stream for 'xxx' with bitrate 96000
[Nest] 28  - 10/14/2023, 7:23:31 PM   DEBUG [DiscordVoiceService] Playing audio resource with volume 1
[Nest] 28  - 10/14/2023, 7:23:31 PM   DEBUG [DiscordVoiceService] Initialized new instance of AudioPlayer because it has not been defined yet
[Nest] 28  - 10/14/2023, 7:23:31 PM   DEBUG [DiscordVoiceService] Audio player changed state from idle to buffering
[Nest] 28  - 10/14/2023, 7:23:31 PM VERBOSE [DiscordVoiceService] Reporting progress: 0 on track 4d78c6084308de2c8de5b7e32a58c434
[Nest] 28  - 10/14/2023, 7:23:31 PM     LOG [RegisterCommandService] All global commands are registered!
[Nest] 28  - 10/14/2023, 7:23:31 PM   DEBUG [DiscordVoiceService] Audio player changed state from buffering to playing
node:events:368
      throw er; // Unhandled 'error' event
      ^

Error: Unexpected server response: 302
    at ClientRequest.<anonymous> (/app/node_modules/ws/lib/websocket.js:896:7)
    at ClientRequest.emit (node:events:390:28)
    at HTTPParser.parserOnIncomingClient (node:_http_client:623:27)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
    at Socket.socketOnData (node:_http_client:487:22)
    at Socket.emit (node:events:390:28)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
    at Socket.Readable.push (node:internal/streams/readable:228:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:199:23)
Emitted 'error' event on WebSocket instance at:
    at emitErrorAndClose (/app/node_modules/ws/lib/websocket.js:1016:13)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.

Bottom of the logs from playback.

manuel-rw commented 11 months ago

Hi, what is hosted on /jellyfin? Port 8096 sounds like it runs Jellyfin directly. Therefore, you'd have to remove it if that's the case...

sssionek commented 11 months ago

Yeah, it runs Jellyfin directly but to make reverse proxy and other stuff I have on this server happy - it serves everything from a /jellyfin directory.

When I try to default on just the base path (JELLYFIN_SERVER_ADDRESS='http://127.0.0.1:8096'), I get this:

[Nest] 28  - 10/16/2023, 11:11:27 PM   ERROR [JellyfinService] Failed to authenticate with response code 200: '<!doctype html><html class="preload"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"><link rel="manifest" href="64d966784cd77b03a79c.json"><meta name="format-detection" content="telephone=no"><meta name="msapplication-tap-highlight" content="no"><meta http-equiv="X-UA-Compatibility" content="IE=Edge"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="mobile-web-app-capable" content="yes"><meta name="application-name" content="Jellyfin"><meta name="robots" content="noindex, nofollow, noarchive"><meta name="referrer" content="no-referrer"><meta property="og:title" content="Jellyfin"><meta property="og:site_name" content="Jellyfin"><meta property="og:url" content="http://jellyfin.org"><meta property="og:description" content="The Free Software Media System"><meta property="og:type" content="article"><meta id="themeColor" name="theme-color" content="#202020"><link rel="apple-touch-icon" sizes="180x180" href="f5bbb798cb2c65908633.png"><link href="6a2e2e6b4186720e5d4f.png" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="eb8bef4f19b6ad227f46.png" media="screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="3fa90c593184d5737eb3.png" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="23a72f5d56f82554aeab.png" media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="d28a57b1e61f9f0dabd9.png" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="16fc81178d1aee54f6cc.png" media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="f94ebf203ea0c91a47c6.png" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="522fa270807b7b12a9ba.png" media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="0df719b48efcaef953df.png" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="0b37f660ac0f7f01ab41.png" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="d0e56683308a17dba86d.png" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="baafa93a783b76e667ec.png" media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="379bab68d056910336f9.png" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="d31413d3f03c0873ccbb.png" media="screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="49d14d0eb7bcdf6f2d1b.png" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="bbb3e6d43389ba0d436c.png" media="screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="142d834c201895a46a01.png" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="e62987a12a58b24f383a.png" media="screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" rel="apple-touch-startup-image"/><link href="3f3fe0fd3a0b637b5030.png" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" rel="apple-touch-startup-image"/><link href="a962662957ebbb8eb436.png" media="screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" rel="apple-touch-startup-image"/><link rel="shortcut icon" href="bc8d51405ec040305a87.ico"><meta name="msapplication-TileImage" content="39209dd2362c0db7c673.png"><meta name="msapplication-TileColor" content="#333333"><title>Jellyfin</title><style>.backgroundContainer-transparent:not(.withBackdrop),.transparentDocument{background:0 0!important;background-color:transparent!important}.layout-tv .mouseIdle,.layout-tv .mouseIdle a,.layout-tv .mouseIdle button,.layout-tv .mouseIdle input,.layout-tv .mouseIdle label,.layout-tv .mouseIdle select,.layout-tv .mouseIdle textarea,.screensaver-noScroll.mouseIdle,.screensaver-noScroll.mouseIdle a,.screensaver-noScroll.mouseIdle button,.screensaver-noScroll.mouseIdle input,.screensaver-noScroll.mouseIdle label,.screensaver-noScroll.mouseIdle select,.screensaver-noScroll.mouseIdle textarea,.transparentDocument .mouseIdle,.transparentDocument .mouseIdle a,.transparentDocument .mouseIdle button,.transparentDocument .mouseIdle input,.transparentDocument .mouseIdle label,.transparentDocument .mouseIdle select,.transparentDocument .mouseIdle textarea{cursor:none!important}.preload{background-color:#101010}.hide,.mouseIdle .hide-mouse-idle,.mouseIdle-tv .hide-mouse-idle-tv{display:none!important}.mainDrawerHandle{position:fixed;top:0;left:0;bottom:0;z-index:1;width:.8em}@-webkit-keyframes fadein{from{opacity:0}to{opacity:1}}@keyframes fadein{from{opacity:0}to{opacity:1}}.splashLogo{-webkit-animation:fadein .5s;animation:fadein .5s;width:30%;height:30%;background-image:url(assets/img/icon-transparent.png);background-position:center center;background-repeat:no-repeat;background-size:contain;position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}@media screen and (min-device-width:992px){.splashLogo{background-image:url(assets/img/banner-light.png)}}</style><script defer="defer" src="main.jellyfin.bundle.js?dafe991ea4b0f4a6e0a5"></script></head><body><div class="backdropContainer"></div><div class="backgroundContainer"></div><div class="mainDrawer hide"><div class="mainDrawer-scrollContainer scrollContainer focuscontainer-y"></div></div><div class="skinHeader focuscontainer-x"></div><div class="mainAnimatedPages skinBody"><div class="splashLogo"></div></div><div class="mainDrawerHandle"></div></body></html>'

So the bot tries to directly hit the WebUI?

manuel-rw commented 11 months ago

Looking at your response there I think removing it would be correct here. It's normal that all web UI requests might work with your reverse proxy but the bot will not necessarily use the same endpoint.

I am a bit confused because the response clearly comes from Jellyfin. And as the documentation states, you should always use the base name without any additional paths. I'm not quite certain why that's not the case here. Do you proxy the request? If you do, please ensure that you allow authentication headers and websocket.

sssionek commented 11 months ago

The request is not proxied, I run the bot on the same machine as the Jellyfin server. I do have a base url set in Jellyfin to /jellyfin for reverse proxying but that's outside of the scope of this issue.

So even though Jellyfin listens on 127.0.0.1:8096, it seems to fully require the requests to be using the address + base url. My poking around (without base url) with curl always results in a redirect, maybe the bot tries to access something without the base url and when it gets a redirect - that's from where the error pops out?

manuel-rw commented 11 months ago

I'm pretty sure that the bot doesn't allow redirects. I think it's disabled by default. I think you'll have to experiment with a few URLs and see what happens. For me, using Jellyfin's base name works perfectly fine and I think you're the first person reporting this specific issue.

sssionek commented 11 months ago

Alright, I set up mitmproxy on my laptop (so it goes like this (laptop) bot -> (laptop) mitmproxy -> (server) Jellyfin) - it's a janky setup but I think I got the info I needed.

I have my Jellyfin set up with base path /jellyfin. Mitmproxy listens on 37812, rewrites and forwards stuff to Jellyfin.

This is what happened with JELLYFIN_SERVER_ADDRESS='http://IP:8096':

Ok, that was to be expected as the path is outside the base url, so it got redirected. But then I ran the bot again with JELLYFIN_SERVER_ADDRESS='http://IP:8096/jellyfin': Other requests go through, but a request to the socket falls outside of the base url. It doesn't look right to me, but I haven't been able to find any description of this endpoint.

Just in case, I confirmed that the proxy hasn't mangled anything by retrying the same requests locally on the server with curl.

sssionek commented 11 months ago

Tried some more trickery with Jellyfin's server debug logs - seems like both approaches get resolved somewhat correctly?

[2023-10-24 11:00:18.465 +02:00] [DBG] [31] Jellyfin.Server.Middleware.BaseUrlRedirectionMiddleware: Normalizing an URL at "/socket" <--- with JELLYFIN_SERVER_ADDRESS='http://IP:8096/jellyfin'
[2023-10-24 11:02:40.011 +02:00] [DBG] [42] Jellyfin.Server.Middleware.BaseUrlRedirectionMiddleware: Normalizing an URL at "/Users/AuthenticateByName"
[2023-10-24 11:03:09.164 +02:00] [DBG] [48] Jellyfin.Server.Middleware.BaseUrlRedirectionMiddleware: Normalizing an URL at "/socket" <--- with JELLYFIN_SERVER_ADDRESS='http://IP:8096'

Additionally, looking through #64, this seems like the exact same issue that @Lurk390 has.

sssionek commented 10 months ago

@manuel-rw Any updates on this?