KarelWintersky ревизий этого фрагмента 1 week ago. К ревизии
1 file changed, 262 insertions
metube3.sh(файл создан)
| @@ -0,0 +1,262 @@ | |||
| 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" | |
Новее
Позже