CloudTube is an alternative YouTube web frontend developed by cadence. This is in my opinion much welcomed as the experience of browsing YouTube was heavily degraded recently (the lighter classic layout was removed in March 2020, and Google keep adding popups nagging you to log in and accept tracking cookies).
CloudTube is a better alternative to Invidious, a similar and older project, for the following reasons:
- decoupled frontend CloudTube and backend NewLeaf (reimplementation of Invidious API ; originally named Second)
- respectively in nodejs and python, making them easier to attract external contributors (Invidious being developed in the Crystal programming language, people are often seen blocked by the language itself when they want to help the project) and
- simple to deploy, as detailed below
- the backend “NewLeaf” leverages the youtube-dlc library to obtain videos information instead of rolling its own implementation (that would then need to be fixed manually each time YouTube JS changes instead of waiting for the broader OSS community to implement a fix)
- better polished UX in general
Nevertheless, keep in mind that it’s a young project and as of today in December 2020, this is not a full replacement for Invidious (or YouTube interface), at least for the features I’m interested in. Notably, comments are not displayed under videos, deployment documentation is lacking, autoplay with URL parameter is not implemented, video embeds are not working, some shortcuts for playback conflict with browser navigation.
Even without documentation, and because it doesn’t use anything fancy (python, nodejs+sqlite), I could deploy it on my server in less than 1 hour. Here’s how I’ve deployed the service, hopefully it will serve as an example for the official documentation.
Prerequisites
- a server you can access via the command line (ssh if remote), with sudo rights. Here, a Debian 9 armv7 host.
- an existing nginx deployment (nginx install won’t be detailed here)
- nodejs and python3 installed (I usually download manually nodejs to the homedir and add its
bin
dir to the user$PATH
) - systemd is assumed as init
Preparing the install with a limited user
We will create a limited user so not everything runs as root or my usual account which is in the sudoers.
sudo adduser cloudtube
No need to specify a lot of things, Ctrl-C as soon as it starts asking boring questions (the user will still be created).
Installing NewLeaf and CloudTube on the host
We will then sudo to this account
sudo -u cloudtube -i
… and retrieve the source code of NewLeaf and CloudTube in cloudtube
homedir
git clone https://git.sr.ht/~cadence/NewLeaf
git clone https://git.sr.ht/~cadence/cloudtube
We are then going to install CloudTube and NewLeaf.
CloudTube
As cloudtube
user
cd ~/cloudtube
npm install # takes a while on an arm host, as it needs to compile sqlite. You can continue to the NewLeaf section in the meantime
Add config
cd ~/cloudtube
cat <<EOF > config/config.js
module.exports = {
/*
Override options from `utils/constants.js`.
*/
user_settings: {
instance: {
type: "string",
default: "https://yt.example.com"
}
}
}
EOF
You can then check that CloudTube frontend starts up with npm start
and check curl localhost:10412
Add a systemd service so CloudTube (re)starts automatically. As an account with sudo rights (not cloudtube
)
sudo cat <<EOF | sudo tee /etc/systemd/system/cloudtube.service >/dev/null
[Unit]
Description=CloudTube
After=network.target
[Service]
Type=simple
ExecStart=/home/cloudtube/node-v12.18.3-linux-armv7l/bin/node /home/cloudtube/cloudtube/server.js
WorkingDirectory=/home/cloudtube/cloudtube
# Restart timing
Restart=always
RestartSec=60
# Disable logs
StandardOutput=null
StandardError=null
SyslogIdentifier=cloudtube
# User & group to run service as
User=cloudtube
Group=cloudtube
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload && sudo systemctl start cloudtube.service
Check that CloudTube runs with sudo systemctl status cloudtube.service
and with the previous curl
command.
NewLeaf
Create a virtualenv so NewLeaf dependencies are installed only for this application and not globally
Still as cloudtube
user
cd ~
python3 -m venv newleaf-venv # install apt dependencies if needed
. newleaf-venv/bin/activate
pip3 install -r NewLeaf/requirements.txt
You can then check that NewLeaf starts up without issues with python3 index.py
, and check curl localhost:3000
(you will see a CherryPy stacktrace as /
is not an implemented route).
Add config
cd ~/NewLeaf
cat <<EOF > configuration.py
# ==============================
# You MUST set these settings.
# ==============================
# A URL that this site can be accessed on. Do not include a trailing slash.
website_origin = "https://yt.example.com"
EOF
Add a systemd service so NewLeaf (re)starts automatically. As an account with sudo rights (not cloudtube
)
sudo cat <<EOF | sudo tee /etc/systemd/system/newleaf.service >/dev/null
[Unit]
Description=NewLeaf (CloudTube backend)
After=network.target
[Service]
Type=simple
ExecStart=/home/cloudtube/newleaf-venv/bin/python3 /home/cloudtube/NewLeaf/index.py
WorkingDirectory=/home/cloudtube/NewLeaf
# Restart timing
Restart=always
RestartSec=60
# Disable logs
StandardOutput=null
StandardError=null
SyslogIdentifier=newleaf
# User & group to run service as
User=cloudtube
Group=cloudtube
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload && sudo systemctl start newleaf.service
Check that NewLeaf runs with sudo systemctl status newleaf.service
and with the previous curl
command.
nginx reverse proxy
We previously added a CNAME to the subdomain we want cloudtube to be accessible from, yt.example.com
. We will be hosting both CloudTube frontend and NewLeaf HTTP API on the same domain with some nginx magic.
This is an older nginx configuration back form when certbot was only supporting auto-configuring Apache, today you should use certbot nginx plugin.
Create nginx config and then enable it. As an account with sudo rights (not cloudtube
)
cat <<EOF | sudo tee /etc/nginx/sites-available/yt.example.com >/dev/null
server {
listen 80;
server_name yt.example.com;
root /var/www/yt.example.com/public_html/;
location /.well-known/acme-challenge {
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name yt.example.com;
access_log /var/www/yt.example.com/logs/access.log;
error_log /var/www/yt.example.com/logs/error.log;
ssl_certificate /etc/letsencrypt/live/yt.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yt.example.com/privkey.pem;
include /etc/nginx/ssl_params;
location / {
include /etc/nginx/security-headers;
add_header X-Robots-Tag "noindex, nofollow";
more_set_headers Content-Security-Policy "";
proxy_pass http://127.0.0.1:10412;
}
location ~ "^/embed/([A-Za-z0-9_\-]{11})(\?.+)?$" { # handle Privacy Redirect /embed/$ID links
rewrite "^/embed/([A-Za-z0-9_\-]{11})(\?.+)?$" /watch?v=$1$2 redirect;
}
location /api/ {
include /etc/nginx/security-headers;
add_header X-Robots-Tag "noindex, nofollow";
more_set_headers Content-Security-Policy "";
proxy_pass http://127.0.0.1:3000;
}
}
EOF
cd /etc/nginx/sites-enabled
sudo ln -s ../sites-available/yt.example.com
sudo nginx -t && sudo nginx -s reload
You have to comment out the SSL part the 1st time though (certificates are not there yet), and then enable certbot with sudo certbot certonly --webroot -w /var/www/yt.example.com/public_html/ -d yt.example.com
,
and then enable the SSL part and reload nginx.
You should now have a running instance on https://yt.example.com/, enjoy!
Updates
Updates should be as simple as the following commands with the cloudtube
user
cd ~/NewLeaf
git pull
# update dependencies?
. ~/newleaf-venv/bin/activate
pip3 install -U -r requirements.txt
cd ~/cloudtube
git pull
npm install
And then restarting both services with a user with sudoers rights (not cloudtube
)
sudo systemctl restart cloudtube.service
sudo systemctl restart newleaf.service
Troubleshooting
Nothing specific to CloudTube really, check out logs with sudo journalctl -u cloudtube.service -f
, sudo journalctl -u newleaf.service -f
, run the service interactively like before, etc.