Plex, Youtube Video and Discord
In my precedent article, I’ve proposed a way to subscribe to your favorite Youtube videos with the RSS feed and download them to a Plex media server. But, a possible use-case that came later in my case was : OK, but if I want to fetch a unitary video ? Or if when I receive some friend at home they want to play from their smartphone similar to a Chromecast or equivalent ?
I don’t have a Chromecast, or Firestick, or whatever else like this. The Plex smartphone app supports streaming to one of these device, and they even support it on the Plex HTPC application for desktop. I use it at home when I want to stream a playlist from the smartphone app (because playing directly with the app on Bluetooth is just crap, the playlist fail after some titles, don’t understand why…).
So, I’ve thinked to a solution with the following constraints :
- I don’t want to maintain a web service with all the security equired
- I want it to be simple and stupid
- I want to to be accessible to friends the same way they can connect to a Chromecast
The idea came out : why not making a Discord bot that will just run the yt-dlp
command and add the video on the Plex server ? Because my friends are using Discord, the simple part is checked. And I’ve experimented some integration with Sonarr and Radarr on a Discord server. I was hoping it to notify me when new episodes of series came out or using their built-in calendar to remind me a movie I want to see, but the notification works only if you download them. I don’t do that, so it was basically useless and I was just notified when the application got updated.
For the last item, Discord is available anywhere and I don’t have to maintain the Web part, because the bot is a program that connects and authenticates itself on Discord too.
Create the Discord bot
First, you need to connect to the Discord developer portal to setup the whole thing.
Then, you create a new application. I’ve kept the default parameters.
Following that, go the the Bot settings. You can customise the bot name, but the most important thing is to enable “Message content intent”. I must admit I don’t really remember the permissions calculated below, but The numeric value was 68672 (read messages, add reaction, and something else).
Then, the last part is to add your bot to the server you want. For this, I’ve been in the Installation part and copied the Discord provided link. Append the URL with the permissions and scope : https://discord.com/oauth2/authorize?client_id=<clientID>&permissions=68672&scope=bot%20applications.commands
Activate it on your server and the Bot should now be listed in the members.
The bot’s code
Integrating our app in Discord was cool, but so far it does nothing. Basically, we’ve just setup the authentication. Now, we need to code our bot. And here is the one I’ve made (with some AI help, I must admit, I don’t really know the Discord SDK).
Just like the previous one, I won’t put it on GitHub.
import discord
import subprocess
import os
# A bot that will listen a link an launch yt-dlp
# Released under MIT license
# Copyright 2024 Seb - https://zedas.fr
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# The discord token
TOKEN = 'YOURTOKEN'
filepath = '/path/to/plex/youtube'
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f'We have logged in as {client.user}')
print(f'Connected to the following guilds: {[guild.name for guild in client.guilds]}')
@client.event
async def on_message(message):
if message.author == client.user:
return
print(message.content)
action = message.content.split(' ', 1)[1]
print(action)
if action.startswith('https'):
await message.add_reaction('👍')
thread = await message.create_thread(name='Work in progress !')
try:
command = f'yt-dlp -v -o "{filepath}/%(uploader)s/%(uploader)s - %(title)s - %(id)s.%(ext)s" --sponsorblock-mark all --embed-metadata --parse-metadata "%(uploader|)s:%(meta_artist)s" "{action}"'
subprocess.run(command, shell=True)
await thread.send('Done !')
await message.add_reaction('👍')
except subprocess.CalledProcessError as e:
await thread.send(f'Fail : {e}')
await message.add_reaction('😵')
else:
await message.channel.send('Hello ! Just send me a message with the link and let the magic be done !')
client.run(TOKEN)
So, what this little thing do ?
First, it authenticates on Discord using the Token you can generate on the Bot page. Yes it’s crappy to do it like this, but it’s a simple idea. Then, it joins the server and listen the messages. If you notify the bot on Discord, it will use the content of the message and pass it to yt-dlp
if it’s a link starting with https. Or answer some help.
Well, actually, the @bot doesn’t really work. The bot will read all messages and answer or put a stack in the script output even if you don’t address directly. So far, I don’t really care. In fact, the code will just split the message you send using space as a separator, and use the second part.
If it’s a link, the bot will thumb-up your message to notify it’s been taken into account. Then, it will open a thread saying “work in progress”. Once the video is downloaded, it will confirm it by answering “Done” in the thread, so you will have the notification. The yt-dlp command is similar to the one for the RSS subscription, so it will automatically create a folder for the Youtuber and put the video inside with a naming convention.
Create a requirement.txt
file with discord
as a dependency.
Now, we need to start the bot, I’ve made a basic shell script :
#!/usr/bin/env bash
# A bot that will listen a link an launch yt-dlp
# Released under MIT license
# Copyright 2024 Seb - https://zedas.fr
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if [ -d venv ]; then
rm -rf venv
fi
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt
python ./bot.py > bot.log 2>&1 &
And now, it will work in background. Now you should see your bot online on the server. You can check its activity in the log file.
Et voilà, simple and stupid, but it works fine. Feel free to enhance it if you want :)