metube3.sh
· 7.8 KiB · Bash
Brut
#!/usr/bin/env bash
# MeTube installation script with offline Deno installation
# Place this script and deno-x86_64-unknown-linux-gnu.zip in the same directory
set -e
APP="MeTube"
APP_DIR="/opt/metube"
DENO_INSTALL_DIR="/usr/local"
DENO_ZIP="deno-x86_64-unknown-linux-gnu.zip"
GITHUB_REPO="alexta69/metube"
SERVICE_USER="root"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
step() { echo -e "${BLUE}[STEP]${NC} $1"; }
# Check if running as root
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root"
fi
# Check for local Deno archive
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DENO_ARCHIVE_PATH="${SCRIPT_DIR}/${DENO_ZIP}"
if [[ ! -f "$DENO_ARCHIVE_PATH" ]]; then
error "Deno archive not found at $DENO_ARCHIVE_PATH"
fi
info "Found Deno archive: $DENO_ARCHIVE_PATH"
# Install dependencies
step "Installing system dependencies"
apt-get update
apt-get install -y curl wget unzip git build-essential python3 python3-pip python3-venv
# Install FFmpeg
step "Installing FFmpeg"
apt-get install -y ffmpeg
if command -v ffmpeg >/dev/null 2>&1; then
info "FFmpeg installed successfully: $(ffmpeg -version | head -n1)"
else
error "FFmpeg installation failed"
fi
step "Installing NODEJS"
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
apt install -y nodejs
npm install -g pm2
# Install Deno from local archive
step "Installing Deno from local archive"
if [[ -d "${DENO_INSTALL_DIR}/deno" ]]; then
rm -rf "${DENO_INSTALL_DIR}/deno"
fi
unzip -o "$DENO_ARCHIVE_PATH" -d /tmp/deno_extract
chmod +x /tmp/deno_extract/deno
mv /tmp/deno_extract/deno "${DENO_INSTALL_DIR}/bin/deno"
rm -rf /tmp/deno_extract
if command -v deno >/dev/null 2>&1; then
info "Deno installed successfully: $(deno --version)"
else
error "Deno installation failed"
fi
# Install pnpm
step "Installing pnpm"
curl -fsSL https://get.pnpm.io/install.sh | sh -
# Download and setup MeTube
step "Downloading MeTube from GitHub"
LATEST_URL=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep "tarball_url" | cut -d '"' -f 4)
if [[ -z "$LATEST_URL" ]]; then
error "Failed to get latest release URL"
fi
if [[ -d "$APP_DIR" ]]; then
warn "Existing installation found, backing up..."
mv "$APP_DIR" "${APP_DIR}_bak_$(date +%Y%m%d_%H%M%S)"
fi
mkdir -p "$APP_DIR"
cd "$APP_DIR"
curl -L "$LATEST_URL" -o metube.tar.gz
tar -xzf metube.tar.gz --strip-components=1
rm metube.tar.gz
# Build frontend
step "Building frontend"
if [[ -d "$APP_DIR/ui" ]]; then
cd "$APP_DIR/ui"
/root/.local/share/pnpm/pnpm install --frozen-lockfile
/root/.local/share/pnpm/pnpm run build
else
error "UI directory not found"
fi
# Setup Python backend
step "Setting up Python backend"
cd "$APP_DIR"
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
# Install required Python packages
step "Installing Python packages"
pip install yt-dlp aiohttp python-dotenv python-socketio watchfiles
# Check for requirements.txt and install if exists
if [[ -f "$APP_DIR/requirements.txt" ]]; then
pip install -r requirements.txt
fi
# Configure yt-dlp to use Deno for JS challenges
step "Configuring yt-dlp for JavaScript challenges"
mkdir -p /root/.config/yt-dlp
# Create yt-dlp config to enable remote components
cat > /root/.config/yt-dlp/config <<EOF
# Enable Deno for JS challenge solving
--remote-components ejs:github
EOF
info "yt-dlp configured to use Deno for JavaScript challenges"
# Create .env file with yt-dlp options in JSON format
step "Creating configuration"
if [[ ! -f "$APP_DIR/.env" ]]; then
cat > "$APP_DIR/.env" <<'EOF'
# MeTube Configuration
DOWNLOAD_DIR=/downloads
STATE_DIR=/opt/metube/state
URL_PREFIX=
HTTP_USERNAME=
HTTP_PASSWORD=
YTDL_OPTIONS={"extractor_args":{"youtube":{"skip":[["hls","dash","live"]],"player_client":["android","web"]}},"remote_components":"ejs:github"}
EOF
else
# Check if YTDL_OPTIONS already exists in .env
if ! grep -q "YTDL_OPTIONS" "$APP_DIR/.env"; then
echo "" >> "$APP_DIR/.env"
echo "# yt-dlp options for JS challenge solving" >> "$APP_DIR/.env"
echo 'YTDL_OPTIONS={"extractor_args":{"youtube":{"skip":[["hls","dash","live"]],"player_client":["android","web"]}},"remote_components":"ejs:github"}' >> "$APP_DIR/.env"
fi
fi
# Create download and state directories
mkdir -p /downloads
mkdir -p "$APP_DIR/state"
chown -R root:root /downloads
chown -R root:root "$APP_DIR"
# Fix potential missing app/main.py structure
step "Checking MeTube application structure"
if [[ ! -f "$APP_DIR/app/main.py" ]]; then
warn "app/main.py not found, checking alternative locations..."
if [[ -f "$APP_DIR/main.py" ]]; then
warn "Found main.py in root, creating app directory structure"
mkdir -p "$APP_DIR/app"
mv "$APP_DIR/main.py" "$APP_DIR/app/"
else
error "Cannot find main.py. MeTube structure may be different than expected"
fi
fi
# Create systemd service
step "Creating systemd service"
cat > /etc/systemd/system/metube.service <<EOF
[Unit]
Description=MeTube - YouTube Downloader
After=network.target
[Service]
Type=simple
WorkingDirectory=${APP_DIR}
EnvironmentFile=${APP_DIR}/.env
Environment="PATH=/usr/local/bin:/usr/bin:/bin:${APP_DIR}/.venv/bin"
ExecStartPre=/bin/bash -c 'echo "Starting MeTube with YTDL_OPTIONS=\${YTDL_OPTIONS}"'
ExecStart=${APP_DIR}/.venv/bin/python3 ${APP_DIR}/app/main.py
Restart=always
User=root
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# Test the Python application manually before starting service
step "Testing Python application manually"
cd "$APP_DIR"
source .venv/bin/activate
timeout 5 python3 -c "
import os
import json
os.environ['DOWNLOAD_DIR'] = '/downloads'
os.environ['STATE_DIR'] = '/opt/metube/state'
ytdl_options_str = '{\"extractor_args\":{\"youtube\":{\"skip\":[[\"hls\",\"dash\",\"live\"]],\"player_client\":[\"android\",\"web\"]}},\"remote_components\":\"ejs:github\"}'
try:
ytdl_options = json.loads(ytdl_options_str)
print(f'YTDL_OPTIONS parsed successfully: {ytdl_options}')
except Exception as e:
print(f'Error parsing YTDL_OPTIONS: {e}')
exit(1)
" || warn "Manual test failed, but continuing..."
# Reload systemd and start service
step "Starting MeTube service"
systemctl daemon-reload
systemctl enable metube
systemctl start metube
# Check if service is running
sleep 5
if systemctl is-active --quiet metube; then
info "MeTube service started successfully"
else
warn "MeTube service failed to start. Checking logs..."
journalctl -u metube --no-pager -n 30
echo ""
echo "Trying to run MeTube directly to see error:"
cd "$APP_DIR"
source .venv/bin/activate
timeout 10 python3 app/main.py || true
error "MeTube service failed to start. Check logs with: journalctl -u metube"
fi
# Get IP address
IP_ADDRESS=$(hostname -I | awk '{print $1}')
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}MeTube installation completed!${NC}"
echo -e "${GREEN}========================================${NC}"
echo -e "Access MeTube at: ${YELLOW}http://${IP_ADDRESS}:8081${NC}"
echo ""
echo -e "Commands:"
echo -e " Start: ${BLUE}systemctl start metube${NC}"
echo -e " Stop: ${BLUE}systemctl stop metube${NC}"
echo -e " Status: ${BLUE}systemctl status metube${NC}"
echo -e " Logs: ${BLUE}journalctl -u metube -f${NC}"
echo ""
echo -e "Download directory: ${YELLOW}/downloads${NC}"
echo -e "Config file: ${YELLOW}${APP_DIR}/.env${NC}"
echo -e "yt-dlp config: ${YELLOW}/root/.config/yt-dlp/config${NC}"
echo ""
echo -e "${GREEN}Note:${NC} yt-dlp has been configured to use Deno for JavaScript challenge solving"
echo -e "This helps bypass YouTube's anti-bot protection for downloading videos"
| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | # MeTube installation script with offline Deno installation |
| 4 | # Place this script and deno-x86_64-unknown-linux-gnu.zip in the same directory |
| 5 | |
| 6 | set -e |
| 7 | |
| 8 | APP="MeTube" |
| 9 | APP_DIR="/opt/metube" |
| 10 | DENO_INSTALL_DIR="/usr/local" |
| 11 | DENO_ZIP="deno-x86_64-unknown-linux-gnu.zip" |
| 12 | GITHUB_REPO="alexta69/metube" |
| 13 | SERVICE_USER="root" |
| 14 | |
| 15 | # Colors for output |
| 16 | RED='\033[0;31m' |
| 17 | GREEN='\033[0;32m' |
| 18 | YELLOW='\033[1;33m' |
| 19 | BLUE='\033[0;34m' |
| 20 | NC='\033[0m' |
| 21 | |
| 22 | info() { echo -e "${GREEN}[INFO]${NC} $1"; } |
| 23 | warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } |
| 24 | error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } |
| 25 | step() { echo -e "${BLUE}[STEP]${NC} $1"; } |
| 26 | |
| 27 | # Check if running as root |
| 28 | if [[ $EUID -ne 0 ]]; then |
| 29 | error "This script must be run as root" |
| 30 | fi |
| 31 | |
| 32 | # Check for local Deno archive |
| 33 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 34 | DENO_ARCHIVE_PATH="${SCRIPT_DIR}/${DENO_ZIP}" |
| 35 | |
| 36 | if [[ ! -f "$DENO_ARCHIVE_PATH" ]]; then |
| 37 | error "Deno archive not found at $DENO_ARCHIVE_PATH" |
| 38 | fi |
| 39 | |
| 40 | info "Found Deno archive: $DENO_ARCHIVE_PATH" |
| 41 | |
| 42 | # Install dependencies |
| 43 | step "Installing system dependencies" |
| 44 | apt-get update |
| 45 | apt-get install -y curl wget unzip git build-essential python3 python3-pip python3-venv |
| 46 | |
| 47 | # Install FFmpeg |
| 48 | step "Installing FFmpeg" |
| 49 | apt-get install -y ffmpeg |
| 50 | if command -v ffmpeg >/dev/null 2>&1; then |
| 51 | info "FFmpeg installed successfully: $(ffmpeg -version | head -n1)" |
| 52 | else |
| 53 | error "FFmpeg installation failed" |
| 54 | fi |
| 55 | |
| 56 | step "Installing NODEJS" |
| 57 | curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - |
| 58 | apt install -y nodejs |
| 59 | npm install -g pm2 |
| 60 | |
| 61 | # Install Deno from local archive |
| 62 | step "Installing Deno from local archive" |
| 63 | if [[ -d "${DENO_INSTALL_DIR}/deno" ]]; then |
| 64 | rm -rf "${DENO_INSTALL_DIR}/deno" |
| 65 | fi |
| 66 | |
| 67 | unzip -o "$DENO_ARCHIVE_PATH" -d /tmp/deno_extract |
| 68 | chmod +x /tmp/deno_extract/deno |
| 69 | mv /tmp/deno_extract/deno "${DENO_INSTALL_DIR}/bin/deno" |
| 70 | rm -rf /tmp/deno_extract |
| 71 | |
| 72 | if command -v deno >/dev/null 2>&1; then |
| 73 | info "Deno installed successfully: $(deno --version)" |
| 74 | else |
| 75 | error "Deno installation failed" |
| 76 | fi |
| 77 | |
| 78 | # Install pnpm |
| 79 | step "Installing pnpm" |
| 80 | curl -fsSL https://get.pnpm.io/install.sh | sh - |
| 81 | |
| 82 | # Download and setup MeTube |
| 83 | step "Downloading MeTube from GitHub" |
| 84 | LATEST_URL=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep "tarball_url" | cut -d '"' -f 4) |
| 85 | |
| 86 | if [[ -z "$LATEST_URL" ]]; then |
| 87 | error "Failed to get latest release URL" |
| 88 | fi |
| 89 | |
| 90 | if [[ -d "$APP_DIR" ]]; then |
| 91 | warn "Existing installation found, backing up..." |
| 92 | mv "$APP_DIR" "${APP_DIR}_bak_$(date +%Y%m%d_%H%M%S)" |
| 93 | fi |
| 94 | |
| 95 | mkdir -p "$APP_DIR" |
| 96 | cd "$APP_DIR" |
| 97 | |
| 98 | curl -L "$LATEST_URL" -o metube.tar.gz |
| 99 | tar -xzf metube.tar.gz --strip-components=1 |
| 100 | rm metube.tar.gz |
| 101 | |
| 102 | # Build frontend |
| 103 | step "Building frontend" |
| 104 | if [[ -d "$APP_DIR/ui" ]]; then |
| 105 | cd "$APP_DIR/ui" |
| 106 | /root/.local/share/pnpm/pnpm install --frozen-lockfile |
| 107 | /root/.local/share/pnpm/pnpm run build |
| 108 | else |
| 109 | error "UI directory not found" |
| 110 | fi |
| 111 | |
| 112 | # Setup Python backend |
| 113 | step "Setting up Python backend" |
| 114 | cd "$APP_DIR" |
| 115 | python3 -m venv .venv |
| 116 | source .venv/bin/activate |
| 117 | pip install --upgrade pip |
| 118 | |
| 119 | # Install required Python packages |
| 120 | step "Installing Python packages" |
| 121 | pip install yt-dlp aiohttp python-dotenv python-socketio watchfiles |
| 122 | |
| 123 | # Check for requirements.txt and install if exists |
| 124 | if [[ -f "$APP_DIR/requirements.txt" ]]; then |
| 125 | pip install -r requirements.txt |
| 126 | fi |
| 127 | |
| 128 | # Configure yt-dlp to use Deno for JS challenges |
| 129 | step "Configuring yt-dlp for JavaScript challenges" |
| 130 | mkdir -p /root/.config/yt-dlp |
| 131 | |
| 132 | # Create yt-dlp config to enable remote components |
| 133 | cat > /root/.config/yt-dlp/config <<EOF |
| 134 | # Enable Deno for JS challenge solving |
| 135 | --remote-components ejs:github |
| 136 | EOF |
| 137 | |
| 138 | info "yt-dlp configured to use Deno for JavaScript challenges" |
| 139 | |
| 140 | # Create .env file with yt-dlp options in JSON format |
| 141 | step "Creating configuration" |
| 142 | if [[ ! -f "$APP_DIR/.env" ]]; then |
| 143 | cat > "$APP_DIR/.env" <<'EOF' |
| 144 | # MeTube Configuration |
| 145 | DOWNLOAD_DIR=/downloads |
| 146 | STATE_DIR=/opt/metube/state |
| 147 | URL_PREFIX= |
| 148 | HTTP_USERNAME= |
| 149 | HTTP_PASSWORD= |
| 150 | YTDL_OPTIONS={"extractor_args":{"youtube":{"skip":[["hls","dash","live"]],"player_client":["android","web"]}},"remote_components":"ejs:github"} |
| 151 | EOF |
| 152 | else |
| 153 | # Check if YTDL_OPTIONS already exists in .env |
| 154 | if ! grep -q "YTDL_OPTIONS" "$APP_DIR/.env"; then |
| 155 | echo "" >> "$APP_DIR/.env" |
| 156 | echo "# yt-dlp options for JS challenge solving" >> "$APP_DIR/.env" |
| 157 | echo 'YTDL_OPTIONS={"extractor_args":{"youtube":{"skip":[["hls","dash","live"]],"player_client":["android","web"]}},"remote_components":"ejs:github"}' >> "$APP_DIR/.env" |
| 158 | fi |
| 159 | fi |
| 160 | |
| 161 | # Create download and state directories |
| 162 | mkdir -p /downloads |
| 163 | mkdir -p "$APP_DIR/state" |
| 164 | chown -R root:root /downloads |
| 165 | chown -R root:root "$APP_DIR" |
| 166 | |
| 167 | # Fix potential missing app/main.py structure |
| 168 | step "Checking MeTube application structure" |
| 169 | if [[ ! -f "$APP_DIR/app/main.py" ]]; then |
| 170 | warn "app/main.py not found, checking alternative locations..." |
| 171 | if [[ -f "$APP_DIR/main.py" ]]; then |
| 172 | warn "Found main.py in root, creating app directory structure" |
| 173 | mkdir -p "$APP_DIR/app" |
| 174 | mv "$APP_DIR/main.py" "$APP_DIR/app/" |
| 175 | else |
| 176 | error "Cannot find main.py. MeTube structure may be different than expected" |
| 177 | fi |
| 178 | fi |
| 179 | |
| 180 | # Create systemd service |
| 181 | step "Creating systemd service" |
| 182 | cat > /etc/systemd/system/metube.service <<EOF |
| 183 | [Unit] |
| 184 | Description=MeTube - YouTube Downloader |
| 185 | After=network.target |
| 186 | |
| 187 | [Service] |
| 188 | Type=simple |
| 189 | WorkingDirectory=${APP_DIR} |
| 190 | EnvironmentFile=${APP_DIR}/.env |
| 191 | Environment="PATH=/usr/local/bin:/usr/bin:/bin:${APP_DIR}/.venv/bin" |
| 192 | ExecStartPre=/bin/bash -c 'echo "Starting MeTube with YTDL_OPTIONS=\${YTDL_OPTIONS}"' |
| 193 | ExecStart=${APP_DIR}/.venv/bin/python3 ${APP_DIR}/app/main.py |
| 194 | Restart=always |
| 195 | User=root |
| 196 | StandardOutput=journal |
| 197 | StandardError=journal |
| 198 | |
| 199 | [Install] |
| 200 | WantedBy=multi-user.target |
| 201 | EOF |
| 202 | |
| 203 | # Test the Python application manually before starting service |
| 204 | step "Testing Python application manually" |
| 205 | cd "$APP_DIR" |
| 206 | source .venv/bin/activate |
| 207 | timeout 5 python3 -c " |
| 208 | import os |
| 209 | import json |
| 210 | os.environ['DOWNLOAD_DIR'] = '/downloads' |
| 211 | os.environ['STATE_DIR'] = '/opt/metube/state' |
| 212 | ytdl_options_str = '{\"extractor_args\":{\"youtube\":{\"skip\":[[\"hls\",\"dash\",\"live\"]],\"player_client\":[\"android\",\"web\"]}},\"remote_components\":\"ejs:github\"}' |
| 213 | try: |
| 214 | ytdl_options = json.loads(ytdl_options_str) |
| 215 | print(f'YTDL_OPTIONS parsed successfully: {ytdl_options}') |
| 216 | except Exception as e: |
| 217 | print(f'Error parsing YTDL_OPTIONS: {e}') |
| 218 | exit(1) |
| 219 | " || warn "Manual test failed, but continuing..." |
| 220 | |
| 221 | # Reload systemd and start service |
| 222 | step "Starting MeTube service" |
| 223 | systemctl daemon-reload |
| 224 | systemctl enable metube |
| 225 | systemctl start metube |
| 226 | |
| 227 | # Check if service is running |
| 228 | sleep 5 |
| 229 | if systemctl is-active --quiet metube; then |
| 230 | info "MeTube service started successfully" |
| 231 | else |
| 232 | warn "MeTube service failed to start. Checking logs..." |
| 233 | journalctl -u metube --no-pager -n 30 |
| 234 | echo "" |
| 235 | echo "Trying to run MeTube directly to see error:" |
| 236 | cd "$APP_DIR" |
| 237 | source .venv/bin/activate |
| 238 | timeout 10 python3 app/main.py || true |
| 239 | error "MeTube service failed to start. Check logs with: journalctl -u metube" |
| 240 | fi |
| 241 | |
| 242 | # Get IP address |
| 243 | IP_ADDRESS=$(hostname -I | awk '{print $1}') |
| 244 | |
| 245 | echo "" |
| 246 | echo -e "${GREEN}========================================${NC}" |
| 247 | echo -e "${GREEN}MeTube installation completed!${NC}" |
| 248 | echo -e "${GREEN}========================================${NC}" |
| 249 | echo -e "Access MeTube at: ${YELLOW}http://${IP_ADDRESS}:8081${NC}" |
| 250 | echo "" |
| 251 | echo -e "Commands:" |
| 252 | echo -e " Start: ${BLUE}systemctl start metube${NC}" |
| 253 | echo -e " Stop: ${BLUE}systemctl stop metube${NC}" |
| 254 | echo -e " Status: ${BLUE}systemctl status metube${NC}" |
| 255 | echo -e " Logs: ${BLUE}journalctl -u metube -f${NC}" |
| 256 | echo "" |
| 257 | echo -e "Download directory: ${YELLOW}/downloads${NC}" |
| 258 | echo -e "Config file: ${YELLOW}${APP_DIR}/.env${NC}" |
| 259 | echo -e "yt-dlp config: ${YELLOW}/root/.config/yt-dlp/config${NC}" |
| 260 | echo "" |
| 261 | echo -e "${GREEN}Note:${NC} yt-dlp has been configured to use Deno for JavaScript challenge solving" |
| 262 | echo -e "This helps bypass YouTube's anti-bot protection for downloading videos" |