+
+### Support This Project
+
+> ✨ This guide is **free, open-source, and community-run**. If it helped: starring, reporting corrections, or using a referral link helps sustain maintenance (at no extra cost to you).
+
+| Service | Referral | Direct Signup |
+| :------ | :------- | :------------ |
+| AllDebrid | [Referral](https://alldebrid.com/?uid=3wvya&lang=en) | [Signup](https://alldebrid.com/register/) |
+| Real-Debrid | [Referral](https://real-debrid.com/?id=10990901) | [Signup](https://real-debrid.com/) |
+| TorBox | – | [Signup](https://torbox.app/login) |
+| Premiumize | – | [Signup](https://www.premiumize.me/register) |
+| LinkSnappy | [Referral](https://linksnappy.com/?ref=774668) | [Signup](https://linksnappy.com/home#Register) |
+| Debrid-Link | [Referral](https://debrid-link.com/id/7B3BO) | [Signup](https://debrid-link.com/webapp/register) |
+| Mega-Debrid | – | [Signup](https://www.mega-debrid.eu/index.php?page=freeregister) |
+| Deepbrid | [Referral](https://www.deepbrid.com/aff/go/upward1971) | [Signup](https://www.deepbrid.com/signup) |
+| Offcloud | [Referral](https://offcloud.com/?=c44b1fa5) | [Signup](https://offcloud.com/register) |
+| High-Way | – | [Signup](https://high-way.me/login/login) |
+
+> [!NOTE]
+> No financial ties to services. Referrals offset hosting & data validation costs only.
+
+
+
+[(↑ Back to Top)](#table-of-contents)
+
+
diff --git a/docs/policies.md b/docs/policies.md
new file mode 100644
index 0000000..0368aea
--- /dev/null
+++ b/docs/policies.md
@@ -0,0 +1,6 @@
+---
+layout: home
+title: Policies
+titleTemplate: Debrid Compare
+description: "Refund, privacy, and usage policies for leading debrid/multi-hoster services."
+---
\ No newline at end of file
diff --git a/docs/pricing.md b/docs/pricing.md
new file mode 100644
index 0000000..f5d6b5a
--- /dev/null
+++ b/docs/pricing.md
@@ -0,0 +1,62 @@
+---
+layout: home
+title: Pricing
+titleTemplate: Debrid Compare
+description: "Comprehensive pricing comparison of leading debrid/multi-hoster services including plans, costs, and value analysis."
+---
+
+
diff --git a/dist/favicon.ico b/docs/public/favicon.ico
similarity index 100%
rename from dist/favicon.ico
rename to docs/public/favicon.ico
diff --git a/dist/favicon.svg b/docs/public/favicon.svg
similarity index 100%
rename from dist/favicon.svg
rename to docs/public/favicon.svg
diff --git a/dist/images/android-chrome-192x192.png b/docs/public/images/android-chrome-192x192.png
similarity index 100%
rename from dist/images/android-chrome-192x192.png
rename to docs/public/images/android-chrome-192x192.png
diff --git a/dist/images/android-chrome-512x512.png b/docs/public/images/android-chrome-512x512.png
similarity index 100%
rename from dist/images/android-chrome-512x512.png
rename to docs/public/images/android-chrome-512x512.png
diff --git a/dist/images/apple-touch-icon.png b/docs/public/images/apple-touch-icon.png
similarity index 100%
rename from dist/images/apple-touch-icon.png
rename to docs/public/images/apple-touch-icon.png
diff --git a/dist/images/favicon-16x16.png b/docs/public/images/favicon-16x16.png
similarity index 100%
rename from dist/images/favicon-16x16.png
rename to docs/public/images/favicon-16x16.png
diff --git a/dist/images/favicon-32x32.png b/docs/public/images/favicon-32x32.png
similarity index 100%
rename from dist/images/favicon-32x32.png
rename to docs/public/images/favicon-32x32.png
diff --git a/dist/image.png b/docs/public/images/og.png
similarity index 100%
rename from dist/image.png
rename to docs/public/images/og.png
diff --git a/dist/robots.txt b/docs/public/robots.txt
similarity index 100%
rename from dist/robots.txt
rename to docs/public/robots.txt
diff --git a/dist/rzd7n8dtydkn3rusq48azrfacbx5zapr.txt b/docs/public/rzd7n8dtydkn3rusq48azrfacbx5zapr.txt
similarity index 100%
rename from dist/rzd7n8dtydkn3rusq48azrfacbx5zapr.txt
rename to docs/public/rzd7n8dtydkn3rusq48azrfacbx5zapr.txt
diff --git a/dist/site.webmanifest b/docs/public/site.webmanifest
similarity index 100%
rename from dist/site.webmanifest
rename to docs/public/site.webmanifest
diff --git a/dist/sitemap.xml b/docs/public/sitemap.xml
similarity index 100%
rename from dist/sitemap.xml
rename to docs/public/sitemap.xml
diff --git a/docs/tools.md b/docs/tools.md
new file mode 100644
index 0000000..b8bc1de
--- /dev/null
+++ b/docs/tools.md
@@ -0,0 +1,6 @@
+---
+layout: home
+title: Tools
+titleTemplate: Debrid Compare
+description: "Tools and integrations for leading debrid/multi-hoster services including browser extensions, download managers, and media center plugins."
+---
\ No newline at end of file
diff --git a/package.json b/package.json
index 74c9b37..2e89b8b 100644
--- a/package.json
+++ b/package.json
@@ -1,24 +1,30 @@
{
- "name": "@fynks/debrid-service-comparison",
- "version": "2.0.1",
- "description": "A concise comparison of pricing and supported hosts for popular debrid services including AllDebrid, Real-Debrid, LinkSnappy, Premiumize, Debrid-Link, TorBox, and Mega-Debrid.",
- "main": "index.js",
- "homepage": "",
+ "name": "debrid-services-comparison",
+ "version": "1.1.0",
+ "description": "Community‑maintained, up-to-date comparison of leading debrid / multi-hoster services for pricing, host coverage, policies & tools etc.",
+ "private": true,
+ "homepage": "https://awesome-android-root.org",
"repository": {
"type": "git",
- "url": ""
+ "url": "https://github.com/fynks/debrid-services-comparison.git"
+ },
+ "bugs": {
+ "url": "https://github.com/fynks/debrid-services-comparison/issues"
},
"scripts": {
- "minify:css": "cleancss -o dist/css/styles-min.css dist/css/styles.css",
- "minify:js": "terser dist/js/app.js -o dist/js/app-min.js --compress --mangle",
- "optimize-json": "chmod +x scripts/json-optimizer.sh && scripts/json-optimizer.sh dist/json/file-hosts.json dist/json/file-hosts-optimized.json && scripts/json-optimizer.sh dist/json/adult-hosts.json dist/json/adult-hosts-optimized.json",
- "build": "npm run minify:css && npm run minify:js && npm run optimize-json && workbox generateSW workbox-config.js",
- "postbuild": "node scripts/replace-assets.js"
+ "docs:dev": "vitepress dev docs",
+ "docs:build": "scripts/build-docs.sh && vitepress build docs",
+ "docs:preview": "vitepress preview docs"
},
- "author": "fynks",
- "dependencies": {
- "clean-css-cli": "^5.6.3",
- "terser": "^5.44.0",
- "workbox-cli": "^7.3.0"
- }
+ "devDependencies": {
+ "vitepress": "^1.6.4",
+ "terser": "^5.44.0"
+ },
+ "keywords": [
+ ],
+ "author": {
+ "name": "debrid-services-comparison",
+ "url": "https://debridcompare.pages.dev"
+ },
+ "license": "MIT"
}
\ No newline at end of file
diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh
new file mode 100755
index 0000000..9c18a1a
--- /dev/null
+++ b/scripts/build-docs.sh
@@ -0,0 +1,147 @@
+#!/bin/bash
+
+# Exit on any error and treat unset variables as errors
+set -euo pipefail
+
+# Colors for output
+readonly GREEN='\033[0;32m'
+readonly NC='\033[0m' # No Color
+readonly RED='\033[0;31m'
+readonly BLUE='\033[0;34m'
+readonly YELLOW='\033[1;33m'
+
+# Script info
+readonly SCRIPT_NAME="$(basename "$0")"
+readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# Error handling function with line number
+handle_error() {
+ local line_number="${2:-unknown}"
+ echo -e "${RED}Error on line ${line_number}: $1${NC}" >&2
+ exit 1
+}
+
+# Trap errors and show line number
+trap 'handle_error "Script failed" ${LINENO}' ERR
+
+# Logging functions
+log_info() {
+ echo -e "${GREEN}✓ $1${NC}"
+}
+
+log_warn() {
+ echo -e "${YELLOW}⚠ $1${NC}"
+}
+
+log_error() {
+ echo -e "${RED}✗ $1${NC}" >&2
+}
+
+echo -e "${GREEN}Starting build-docs process...${NC}"
+
+# 1. Check required files and directories
+[ ! -d "docs" ] && handle_error "'docs' directory not found."
+[ ! -f "README.md" ] && handle_error "'README.md' not found."
+
+# 2. Function to extract div content with specific class from README.md
+extract_div_content() {
+ local class_name="$1"
+ local start_line end_line
+
+ # Find the start line of the div with the specified class
+ start_line=$(grep -n "
]*class=\"${class_name}\"" README.md | head -1 | cut -d: -f1)
+
+ if [ -z "$start_line" ]; then
+ log_warn "No div with class '${class_name}' found in README.md"
+ return 1
+ fi
+
+ # Find the corresponding closing
after the start line
+ end_line=$(tail -n +$((start_line + 1)) README.md | grep -n "" | head -1 | cut -d: -f1)
+
+ if [ -z "$end_line" ]; then
+ log_warn "No closing found for class '${class_name}'"
+ return 1
+ fi
+
+ # Calculate the actual end line number
+ end_line=$((start_line + end_line))
+
+ # Extract the content between start and end lines
+ sed -n "${start_line},${end_line}p" README.md
+}
+
+# 3. Function to copy div content to docs file
+copy_div_to_docs() {
+ local class_name="$1"
+ local target_file="$2"
+ local content
+
+ content=$(extract_div_content "$class_name")
+
+ if [ $? -eq 0 ] && [ -n "$content" ]; then
+ if [ -f "$target_file" ]; then
+ # Append to existing file with separator
+ {
+ cat "$target_file"
+ echo ""
+ echo "$content"
+ } > "${target_file}.tmp" && mv "${target_file}.tmp" "$target_file" || handle_error "Failed appending content to $target_file"
+ log_info "Appended '$class_name' div content to $target_file"
+ else
+ # Create new file with content
+ echo "$content" > "$target_file" || handle_error "Failed creating $target_file"
+ log_info "Created $target_file with '$class_name' div content"
+ fi
+
+ # Adjust relative links for docs directory structure
+ adjust_links_for_docs "$target_file"
+ else
+ log_warn "Failed to extract content for class '$class_name'"
+ fi
+}
+
+# 4. Function to adjust links for docs directory structure
+adjust_links_for_docs() {
+ local file="$1"
+
+ # Adjust relative links (skip absolute URLs)
+ sed -i '/http[s]*:\/\/\//! s|\.\/docs/|\.\/|g' "$file" || handle_error "Failed adjusting links in $file"
+
+ # Adjust image paths
+ sed -i 's|docs/public/images/|public/images/|g' "$file" || handle_error "Failed adjusting image paths in $file"
+ sed -i 's|\./dist/|public/images/|g' "$file" || handle_error "Failed adjusting dist image paths in $file"
+
+ log_info "Adjusted links and image paths in $file"
+}
+
+# 5. Copy div sections to respective docs files
+copy_div_to_docs "support" "docs/index.md"
+copy_div_to_docs "pricing" "docs/pricing.md"
+copy_div_to_docs "hosts" "docs/hosts.md"
+copy_div_to_docs "policies" "docs/policies.md"
+copy_div_to_docs "tools" "docs/tools.md"
+
+# 6. Append speedtest content to the bottom of tools.md
+speedtest_content=$(extract_div_content "speedtest")
+if [ $? -eq 0 ] && [ -n "$speedtest_content" ]; then
+ {
+ cat "docs/tools.md"
+ echo ""
+ echo "---"
+ echo ""
+ echo "$speedtest_content"
+ } > "docs/tools.md.tmp" && mv "docs/tools.md.tmp" "docs/tools.md" || handle_error "Failed appending speedtest content to docs/tools.md"
+ log_info "Appended speedtest content to bottom of docs/tools.md"
+ adjust_links_for_docs "docs/tools.md"
+else
+ log_warn "Failed to extract speedtest content"
+fi
+
+# 7. Remove standalone speedtest.md file if it exists
+if [ -f "docs/speedtest.md" ]; then
+ rm "docs/speedtest.md" || handle_error "Failed removing docs/speedtest.md"
+ log_info "Removed standalone docs/speedtest.md file"
+fi
+
+echo -e "${GREEN}Documentation build process completed successfully.${NC}"
diff --git a/scripts/json-optimizer.sh b/scripts/json-optimizer.sh
deleted file mode 100755
index ab25304..0000000
--- a/scripts/json-optimizer.sh
+++ /dev/null
@@ -1,191 +0,0 @@
-#!/bin/bash
-
-# JSON Optimizer Script - Converts host data to indexed format
-# Usage: ./scripts/json-optimizer.sh file-hosts.json
-# ./scripts/json-optimizer.sh adult-hosts.json
-# ./scripts/json-optimizer.sh pricing.json
-
-# Colors for output
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-NC='\033[0m' # No Color
-
-# Function to print colored output
-print_status() {
- echo -e "${BLUE}[INFO]${NC} $1"
-}
-
-print_success() {
- echo -e "${GREEN}[SUCCESS]${NC} $1"
-}
-
-print_warning() {
- echo -e "${YELLOW}[WARNING]${NC} $1"
-}
-
-print_error() {
- echo -e "${RED}[ERROR]${NC} $1"
-}
-
-# Default values
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-DEFAULT_INPUT_DIR="."
-DEFAULT_OUTPUT_DIR="."
-
-# Check if at least one argument is provided
-if [ $# -eq 0 ]; then
- print_error "No input file specified"
- echo "Usage: $0 [output-file.json]"
- echo "Examples:"
- echo " $0 file-hosts.json"
- echo " $0 ./json/adult-hosts.json"
- echo " $0 pricing.json pricing-optimized.json"
- exit 1
-fi
-
-# Get input file
-INPUT_FILE="$1"
-
-# Generate output filename if not provided
-if [ $# -eq 2 ]; then
- OUTPUT_FILE="$2"
-else
- # Extract filename without path and extension
- FILENAME=$(basename "$INPUT_FILE")
- NAME="${FILENAME%.*}"
- EXT="${FILENAME##*.}"
- OUTPUT_FILE="${NAME}-optimized.${EXT}"
-fi
-
-# Check if input file exists
-if [ ! -f "$INPUT_FILE" ]; then
- print_error "Input file '$INPUT_FILE' not found"
- exit 1
-fi
-
-# Create output directory if it doesn't exist
-OUTPUT_DIR=$(dirname "$OUTPUT_FILE")
-if [ ! -d "$OUTPUT_DIR" ] && [ "$OUTPUT_DIR" != "." ]; then
- print_status "Creating output directory: $OUTPUT_DIR"
- mkdir -p "$OUTPUT_DIR"
-fi
-
-print_status "Processing: $INPUT_FILE"
-print_status "Output will be saved to: $OUTPUT_FILE"
-
-# Python script to handle the conversion
-python3 -c "
-import json
-import sys
-import os
-from collections import defaultdict
-
-def convert_to_indexed_format(input_file, output_file):
- try:
- # Read the input JSON
- print('Reading input file...')
- with open(input_file, 'r') as f:
- data = json.load(f)
-
- host_count = len(data)
- print(f'Loaded {host_count:,} hosts')
-
- # Handle empty data
- if not data:
- print('Warning: Input file is empty')
- output_data = {'services': [], 'supported': {}}
- with open(output_file, 'w') as f:
- json.dump(output_data, f, separators=(',', ':'))
- return True
-
- # Extract all services (keys from entries)
- all_services = set()
- for host_data in data.values():
- all_services.update(host_data.keys())
-
- services = sorted(list(all_services))
- service_count = len(services)
- print(f'Found {service_count} services: {\", \".join(services[:5])}{\"...\" if service_count > 5 else \"\"}')
-
- # Create service to index mapping
- service_to_index = {service: idx for idx, service in enumerate(services)}
-
- # Convert data to indexed format
- print('Converting data to indexed format...')
- supported = {}
- total_support_instances = 0
-
- for host, host_data in data.items():
- # Get indices of services with 'yes' values (case insensitive)
- supported_indices = [
- service_to_index[service]
- for service, value in host_data.items()
- if str(value).lower() == 'yes'
- ]
- supported[host] = sorted(supported_indices)
- total_support_instances += len(supported_indices)
-
- # Create output structure
- output_data = {
- 'services': services,
- 'supported': supported
- }
-
- # Write optimized JSON (compact format)
- print('Writing optimized JSON...')
- with open(output_file, 'w') as f:
- json.dump(output_data, f, separators=(',', ':'))
-
- # Get file sizes for comparison
- original_size = os.path.getsize(input_file)
- new_size = os.path.getsize(output_file)
- reduction = ((original_size - new_size) / original_size) * 100 if original_size > 0 else 0
-
- print('✅ Conversion successful!')
- print(f'📁 Original size: {original_size:,} bytes')
- print(f'📁 New size: {new_size:,} bytes')
- print(f'📉 Size reduction: {reduction:.1f}%')
- print(f'💾 Output saved to: {output_file}')
-
- # Show statistics
- avg_support_per_host = total_support_instances / host_count if host_count > 0 else 0
-
- print('📈 Statistics:')
- print(f' - Hosts: {host_count:,}')
- print(f' - Services: {service_count:,}')
- print(f' - Total support instances: {total_support_instances:,}')
- print(f' - Average support per host: {avg_support_per_host:.1f}')
-
- return True
-
- except json.JSONDecodeError as e:
- print(f'❌ JSON parsing error: {str(e)}')
- return False
- except Exception as e:
- print(f'❌ Error during conversion: {str(e)}')
- return False
-
-# Run the conversion
-success = convert_to_indexed_format('$INPUT_FILE', '$OUTPUT_FILE')
-
-if not success:
- sys.exit(1)
-" || {
- print_error "Python conversion failed. Please ensure Python 3 is installed."
- exit 1
-}
-
-print_success "Conversion completed successfully!"
-
-# Optional: Show file size comparison in bash as well
-if command -v stat >/dev/null 2>&1; then
- ORIGINAL_SIZE=$(stat -f%z "$INPUT_FILE" 2>/dev/null || stat -c%s "$INPUT_FILE" 2>/dev/null)
- NEW_SIZE=$(stat -f%z "$OUTPUT_FILE" 2>/dev/null || stat -c%s "$OUTPUT_FILE" 2>/dev/null)
-
- if [ $? -eq 0 ] && [ $ORIGINAL_SIZE -gt 0 ]; then
- REDUCTION_PERCENTAGE=$(( (ORIGINAL_SIZE - NEW_SIZE) * 100 / ORIGINAL_SIZE ))
- print_status "Final size reduction: ${REDUCTION_PERCENTAGE}%"
- fi
-fi
\ No newline at end of file
diff --git a/scripts/replace-assets.js b/scripts/replace-assets.js
deleted file mode 100644
index 0185bd9..0000000
--- a/scripts/replace-assets.js
+++ /dev/null
@@ -1,158 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-
-// Improved script with better error handling and focused head tag processing
-function replaceAssets() {
- const file = path.resolve(__dirname, '..', 'dist', 'index.html');
-
- // Check if dist/index.html exists
- if (!fs.existsSync(file)) {
- console.error('Error: dist/index.html not found');
- process.exit(1);
- }
-
- try {
- let html = fs.readFileSync(file, 'utf8');
- console.log('Original HTML loaded successfully');
-
- // Extract the head section
- const headMatch = html.match(/(]*>)([\s\S]*?)(<\/head>)/i);
- if (!headMatch) {
- console.error('Error: Could not find tag in HTML');
- process.exit(1);
- }
-
- const [fullHeadTag, headStart, headContent, headEnd] = headMatch;
- console.log('Head section found, processing assets...');
-
- let updatedHeadContent = headContent;
- let cssReplaced = 0;
- let jsReplaced = 0;
-
- // Compute simple query hash for cache-busting if files exist
- const cssMinPath = path.resolve(__dirname, '..', 'dist', 'css', 'styles-min.css');
- const jsMinPath = path.resolve(__dirname, '..', 'dist', 'js', 'app-min.js');
- const safeHash = (p) => {
- try {
- const buf = fs.readFileSync(p);
- let hash = 0;
- for (let i = 0; i < buf.length; i += Math.ceil(buf.length / 128) || 1) {
- hash = (hash * 31 + buf[i]) >>> 0;
- }
- return hash.toString(36);
- } catch {
- return null;
- }
- };
- const cssHash = safeHash(cssMinPath);
- const jsHash = safeHash(jsMinPath);
- const cssTarget = cssHash ? `href="./css/styles-min.css?v=${cssHash}"` : 'href="./css/styles-min.css"';
- const jsTarget = jsHash ? `src="js/app-min.js?v=${jsHash}"` : 'src="js/app-min.js"';
-
- // Replace CSS links in head - more flexible patterns
- const cssPatterns = [
- /href=["']\.\/css\/styles\.css["']/g,
- /href=["']css\/styles\.css["']/g,
- /href=["'][^"']*\/styles\.css["']/g
- ];
-
- cssPatterns.forEach(pattern => {
- const matches = updatedHeadContent.match(pattern);
- if (matches) {
- updatedHeadContent = updatedHeadContent.replace(pattern, cssTarget);
- cssReplaced += matches.length;
- }
- });
-
- // Ensure existing preload to styles-min.css carries the same version hash
- if (cssHash) {
- const preloadMinCssPattern = /href=["']\/?css\/styles-min\.css["']/g;
- if (preloadMinCssPattern.test(updatedHeadContent)) {
- updatedHeadContent = updatedHeadContent.replace(preloadMinCssPattern, `href="/css/styles-min.css?v=${cssHash}"`);
- }
- }
-
- // Replace JS script src in head - more flexible patterns
- const jsPatterns = [
- /src=["']\.\/js\/app\.js["']/g,
- /src=["']js\/app\.js["']/g,
- /src=["'][^"']*\/app\.js["']/g
- ];
-
- jsPatterns.forEach(pattern => {
- const matches = updatedHeadContent.match(pattern);
- if (matches) {
- updatedHeadContent = updatedHeadContent.replace(pattern, jsTarget);
- jsReplaced += matches.length;
- }
- });
-
- // Also check for script tags at the end of body (common pattern)
- const bodyMatch = html.match(/(]*>)([\s\S]*?)(<\/body>)/i);
- if (bodyMatch) {
- const [fullBodyTag, bodyStart, bodyContent, bodyEnd] = bodyMatch;
- let updatedBodyContent = bodyContent;
-
- jsPatterns.forEach(pattern => {
- const matches = updatedBodyContent.match(pattern);
- if (matches) {
- updatedBodyContent = updatedBodyContent.replace(pattern, jsTarget);
- jsReplaced += matches.length;
- }
- });
-
- // Reconstruct the body if changes were made
- if (updatedBodyContent !== bodyContent) {
- html = html.replace(fullBodyTag, bodyStart + updatedBodyContent + bodyEnd);
- }
- }
-
- // Reconstruct the HTML with updated head
- const updatedHeadTag = headStart + updatedHeadContent + headEnd;
- html = html.replace(fullHeadTag, updatedHeadTag);
-
- // Log results
- console.log(`CSS replacements made: ${cssReplaced}`);
- console.log(`JS replacements made: ${jsReplaced}`);
-
- if (cssReplaced === 0 && jsReplaced === 0) {
- console.warn('Warning: No asset links were found to replace');
- console.log('Checking for existing patterns in head section:');
-
- // Debug: show what's actually in the head
- const linkTags = headContent.match(/]*>/gi) || [];
- const scriptTags = headContent.match(/