/ Docs

Installation guide

Requirements:

  • a VPS
  • Fully qualified domain name

OPTION 1. Install script ( Recommended )

This part is covered in docs/self-host-khofly VPS section, the ./scripts/install.sh script installs and runs both the web client and the API. If you've already run that script you probably don't need this page.

OPTION 2. Manual installation

Follow these steps only if you've manually installed Khofly web client.

1. Install dependencies

apt update && apt upgrade
apt install nodejs npm build-essential libssl-dev unzip nginx certbot python3-certbot-nginx ffmpeg

2. Install yt-dlp

# Install from binaries since the apt version is insanely out of date
# You can also update it with yt-dlp -U when installing from binaries
curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/bin/yt-dlp

chmod a+rx /usr/bin/yt-dlp

3. Install gallery-dl

# Install from binaries since the apt version is insanely out of date
# You can also update it with gallery-dl -U when installing from binaries
curl -L https://github.com/mikf/gallery-dl/releases/latest/download/gallery-dl.bin -o /usr/bin/gallery-dl

chmod a+rx /usr/bin/gallery-dl

4. Install pm2

npm install pm2 -g
source ~/.bashrc

5. Install Bun

curl -fsSL https://bun.sh/install | bash

6. Create an empty folder in your home directory, ex. mkdir khofly.

7. cd khofly and type git clone https://github.com/cufta22/khofly.git .

7.1. Pick a branch, by default it will be on master but if you want more frequent updates git fetch origin staging and git checkout -b staging origin/staging

8. Build and run API

cd api

# First create the .env.local file from example file
cp .env.example .env.local

# Edit the values per provided comments
nano .env.local

# Then we can install dependencies
bun install

9. Create the ecosystem.config file for pm2

# Create the config
touch ecosystem.config.js

# Paste the base config from below, edit whatever you want
nano ecosystem.config.js

# Start the API with pm2
pm2 start
module.exports = {
  apps : [{
    name: 'api',
    script: 'bun',
    args: 'run start',
  }]
};

10. Create Nginx config for api, don't forget to update the server_name to your domain name.

cd /etc/nginx/sites-available/

# Create the config for the API
touch api

# Paste the base config from below, edit whatever you want
nano api

# Link that file to /sites-enabled
ln -s /etc/nginx/sites-available/api /etc/nginx/sites-enabled/
server {
    server_name example.com;

    root /root/api;

    location / {
        # Proxy to pm2 server on 4000 for api

        proxy_pass http://localhost:4000/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header Origin $http_origin;
        proxy_cache_bypass $http_upgrade;
    }


    listen 80;
}

11. Add SSL certificate for your domain certbot --nginx

12. sudo systemctl reload nginx

Updating

To update the API run ./scripts/redeploy-api.sh api, make sure to replace "api" with pm2 instance name for your API.

To get a list of all pm2 instances run pm2 ls

You should alse keep yt-dlp up to date with yt-dlp -U

yt-dlp issues

Sometimes when hosting API on a server YouTube can block download requests with captcha. To try and avoid this we can pass cookies and PO Token arguments to yt-dlp.

1. Cookies

Read more about providing cookies on yt-dlp official wiki.

TL;DR With a browser extension like cookies.txt on Firefox or Get cookies.txt LOCALLY on Chormium you can extract your YouTube cookies and add it to a cookies-yt.txt file in the root of /api folder. So the path to your extracted cookies file should be /api/cookies-yt.txt. Also make sure to use a burner google account instead of your main one.

2. PO Token

Read more about providing PO Token on yt-dlp official wiki.

TL;DR

  • Go to YouTube Music in an incognito tab
  • Click on a video
  • Open DevTools (F12) - "Network" tab
  • Filter requests by googlevideo.com
  • From the most recent googlevideo.com request, extract the pot query parameter value from the URL

Paste that pot value in /api/.env.local - YT_DLP_PO_TOKEN. API will now use that PO Token when calling yt-dlp command.

gallery-dl issues

Sometimes when hosting API on a server Instagram can block download requests with captcha. To try and avoid this we can pass cookies argument to gallery-dl.

1. Instagram Cookies

Read more about providing cookies on yt-dlp official wiki.

TL;DR With a browser extension like cookies.txt on Firefox or Get cookies.txt LOCALLY on Chormium you can extract your Instagram cookies and add it to a cookies-ig.txt file in the root of /api folder. So the path to your extracted cookies file should be /api/cookies-ig.txt. Also make sure to use a burner IG account instead of your main one.

Read more