
In this article, I will explain the implementation details of a Telegram Bot I devised: home-downloader.
home-downloader allows users to painlessly download videos from the internet and, then, to watch them on any devices of their liking. It is triggered by a simple request by a user, who sends it to the chosen URL. Upon receiving the text, home-downloader checks for unwanted users. Then, it downloads the associated video into a directory, which is stored on a Raspberry Pi, and is accessible by a local network. Finally, the Pi will broadcast the video to a screen of choice, while the user indulges in some well-deserved pop-corns.
The Problem
I have two TVs at home, one is smart and the second…not so much. In order to help the second one (mainly used by my girlfriend) keep up with the new fancy technologies, I bought a Xiaomi Mi Box S. However, something was still missing: my girlfriend was unimpressed, even though she had access to any media services ever invented. The issue: she wanted to broadcast videos she found on the web, but some of them are way too heavy to be opened by the Mi Box. Nor Chromecast was a viable option, as it would not allow her to cast videos from such websites!
Crap! That put me in a corner…what will I do when my brand-new PS5 arrives? And who will claim ownership of the smart TV?!
So I resolved to fix the issue and told her so
The Solution
Ok, let’s establish a few constraints/requirements:
- I don’t wanna spend money on it, and I would like to use what I have at home.
- Any kind of user interaction should be as smooth as possible, my girlfriend is not a computer scientist nor a tech enthusiast 🙂
- Python!
The solution I set out to engineer had to address the following questions:
- Given a URL, will I be able to download the video?
- How will the Android Box access the downloaded video?
- How can the above two points be automated?
- What’s the best user interface?
It turned out the easiest way to download an internet video is youtube-dl. One of the strengths of this piece of software is that it doesn’t only work with YouTube 🙂
tbh, it doesn’t work with every website, but fortunately, the list of compatible websites is quite large. Yeah.
I decided to run youtube-dl from my Raspberry Pi (RPI) and save the video in a folder hosted on the RPI, and shared with Samba. This folder is visible to the Android Box (AB), which is located in the same local network.
One important piece is still missing now: reading the video from the AB. At this point, I was pretty sure VLC would do the trick. Accessing the Samba directory from the AB is quite trivial…only if you are using VLC desktop! Consequently, trying to combine AB with VLC was a huge mess…and in fact, I found out this is a very renowned, still unresolved issue. But then KODI came out to help me: easy as pie 🙂
At this point, it dawned on me that home-downloader should be a Telegram Bot. I was too lazy to implement any interface/API, but not so much as to avoid outsourcing the job to Telegram :D! Consequently, I decided I would have the user interact with a Telegram Bot, i.e., through a simple chat. After all, all the necessary APIs to implement this are already provided by Telegram.
You can see a picture of complete architecture below:

Implementation
The language I chose is Python 3.8.
The project consists of two .py scripts and one .json configuration file:
- config.json it stores the Bot token and some other pieces of info that we don’t want to hardcode into the python scripts.
- modules.py it contains three functions.
- home_downloader.py the main.
Before getting into the nitty-gritty of the code, here is my set up:
- I used a nice library called python-telegram-bot, which provides a friendly interface to the raw Telegram APIs.
- I run Arch Arm on my Raspberry Pi 2 Model B and I set up Samba by the guidelines provided by this ArchWiki.
- I previously registered a Telegram Bot with the BotFather Bot, the purpose of which is to guide a user through the registration. The official guide will give you a pretty thorough introduction.
Configuration File
Here, we save: a) the home-downloader Bot’s Secret Token; b) the list of users who can interact with the bot, and c) the ID of the chat between the Owner (me, in this case) and the Bot.
Regarding c), for privacy concerns I wouldn’t want to let in any unauthorized users! Therefore, when a new user starts chatting with home-downloader, it checks whether the user belongs to a pre-decided whitelist.
Remark: before deploying the bot, the owner will have to read and save into the config file the chat ID between him and the bot. This is the only way to send a direct message from the Bot to the Owner every time a new user initiates a chat with the Bot: Telegram Bots cannot send direct messages given a username.
To find out the chat ID, simply start a new conversation with the Bot and print this attribute from inside one of the functions we are going to see:
update.message.chat_id
Modules
The project consists of three main functions.
First, we set up a logger: when you run stuff in the background for days, it’s useful to retrieve information about the execution of your scripts! Secondly, we open the .json configuration, which is thus going to be visible by all the functions of the project.
The following function is a wrapper for youtube-dl, which in turn is executed by subprocess. The subprocess library allows you to run a bash command from Python and returns the output. The result of the execution is used to determine whether the video has been correctly downloaded or not.
Now we have to bind the chat actions and the commands which will trigger the Telegram Bot. As I mentioned above, the bot should be triggered whenever a new user starts a conversation, and when a new message is sent.
The welcome function prints a welcome message every time a user begins to chat (that is, by the customary /start command). Moreover, the username of the user currently chatting is then sent to the owner (me!), defined in the config.json file.
Finally, the incoming_message_action function encodes the handling of new incoming messages. Each message is parsed, to unearth the URL (lines 8–11).
If the URL is approved, the function checks the website domain (line 14): in fact, I might want to allow only certain websites to be used as sources. The reason is simple: I wouldn’t want my RPI to download gigabytes and gigabytes of material without my permission!
You probably noticed that the functions incoming_message_action and welcome have something in common. Firstly of all, they accept two parameters named update and context, which contain information about the user who is triggering the bot, and the chat messages. Secondly, they both share this piece of code:
if update.message.chat.username not in config["valid_users"]:
return False
The purpose of the code snippet above is to check whether the user, who is interacting with the chat, is allowed to do so.
This is the only way to restrict the bot usage to a specific set of users. Hopefully, Telegram will add a proper feature to do that.
Home Downloader
The home_downloader.py puts together what we have seen so far.
The Bot is initiated and the handlers based on the functions we have seen so far created.
Finally, the bot is started by calling the function start_polling.
Conclusion
In this article, I wanted to show how I dealt with a real-life problem and the solution I implemented. I tried to highlight the creative process behind each decision.
Here is the GitHub link to the project. Since it’s a work in progress, some code bits may appear a bit different than in this article: I’m already working on some new features and improvements. Anyway, the code above is all you need to build your own Bot and customize it based on your needs.
Special thanks to people who read and commented on drafts of this post:
Gabriele Iommazzo and Christian Contreras-Campana