Skip to content

groknt/AIO-Proxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AIO Proxy

A high-performance, security-hardened media proxy server with automatic format detection, transcoding, and HLS/DASH playlist rewriting.

Features

  • Multi-format Support: Video (MP4, MKV, AVI, WebM, WMV, FLV, MPEG, VOB, TS, HLS, DASH) and Audio (MP3, AAC, M4A, FLAC, OGG, WAV, AIFF, WMA, AC3)
  • Automatic Transcoding: Converts incompatible formats to web-compatible MP4/MP3
  • HLS/DASH Proxy: Rewrites playlists and manifests with proxied segment URLs, preserving headers
  • Security Hardened: SSRF protection, DNS rebinding prevention, header injection sanitization
  • Production Ready: Rate limiting, CORS support, request logging, graceful shutdown

Quick Start

# Build and run
go build -o aio-proxy && ./aio-proxy

Docker

docker build -t aio-proxy .
docker run -p 8080:8080 aio-proxy

Configuration

All settings are configurable via environment variables, allowing runtime tuning without rebuilding.

Environment Variable Default Description
PORT 8080 Server port
BASE_URL http://127.0.0.1:PORT Public URL for playlist rewriting
ALLOW_INTERNAL_NETWORKS false Allow proxying to private IPs (dangerous)
ENABLE_YTDLP true Enable type=ytdlp support
ENABLE_HLS true Enable HLS playlist proxying and rewriting
ENABLE_DASH true Enable DASH manifest proxying and rewriting
ENABLE_DEFAULT_HEADERS true Add browser-like headers to upstream requests
PROXY_MEDIA_URLS true Proxy embedded URLs in playlists (segments, subtitles, audio, keys). When false, converts to absolute URLs only
CACHE_ENABLED false Enable caching for HLS segments
CACHE_TYPE memory Cache backend: memory or disk
CACHE_PATH /tmp/aio-proxy-cache Directory for disk cache
CACHE_MAX_SIZE 512M Maximum cache size (e.g., 512M, 1G)
CACHE_TTL 300 Cache entry TTL in seconds
MAX_CONCURRENT_STREAMS 100 Maximum simultaneous proxy connections (global)
MAX_STREAMS_PER_IP 10 Maximum simultaneous streams per client IP
MAX_BANDWIDTH_PER_IP (unlimited) Bandwidth limit per client IP (e.g., 10M, 500K, 1G)
MAX_CONCURRENT_TRANSCODE 5 Maximum simultaneous ffmpeg processes (global)
MAX_TRANSCODE_PER_IP 2 Maximum simultaneous transcodes per client IP
HW_ACCEL auto Hardware acceleration: auto, vaapi, nvenc, qsv, off
API_RATE_LIMIT 0 Requests per minute per IP for /probe and /proxy (0 = unlimited, 60-300 recommended for production)
API_RATE_BURST 10 Burst allowance for API rate limiting
TRANSCODE_QUEUE_TIMEOUT 30 Seconds to wait for transcoder slot before 503
READ_TIMEOUT 30 HTTP server read timeout (seconds)
IDLE_TIMEOUT 120 HTTP server idle timeout (seconds)
DIAL_TIMEOUT 30 Upstream connection timeout (seconds)
HTTP_TIMEOUT 0 Overall HTTP client timeout (0 = unlimited)
MAX_REQUEST_BODY_SIZE 0 Max request body size (e.g., 1M, 10K; 0 = unlimited)
CORS_ORIGINS * CORS allowed origins (e.g., https://example.com)
TRUSTED_PROXIES (none) Comma-separated IPs/CIDRs to trust for client IP headers
LOG_LEVEL info Logging verbosity: error, warn, info, debug

Security Note: Proxy headers (CF-Connecting-IP, X-Forwarded-For, X-Real-IP) are only trusted from IPs matching TRUSTED_PROXIES. Direct connections always use the TCP source IP (unspoofable).

Example

# High-capacity server with longer timeouts
MAX_CONCURRENT_TRANSCODE=10 \
TRANSCODE_QUEUE_TIMEOUT=60 \
DIAL_TIMEOUT=60 \
./aio-proxy

# Enable memory caching (default, fast but lost on restart)
docker run -p 8080:8080 \
  -e CACHE_ENABLED=true \
  -e CACHE_MAX_SIZE=1G \
  -e CACHE_TTL=600 \
  aio-proxy

# Enable disk caching (persists across restarts, good for NVMe SSDs)
docker run -p 8080:8080 \
  -v /mnt/cache:/cache \
  -e CACHE_ENABLED=true \
  -e CACHE_TYPE=disk \
  -e CACHE_PATH=/cache \
  -e CACHE_MAX_SIZE=10G \
  aio-proxy

# Limit bandwidth to 10 MB/s per stream (prevents single user hogging all bandwidth)
docker run -p 8080:8080 \
  -e MAX_BANDWIDTH_PER_IP=10M \
  -e MAX_STREAMS_PER_IP=5 \
  aio-proxy

# Behind nginx at 10.0.0.1
docker run -p 8080:8080 \
  -e TRUSTED_PROXIES=10.0.0.1 \
  -e MAX_TRANSCODE_PER_IP=3 \
  aio-proxy

# Behind Cloudflare (trust their IP ranges)
docker run -p 8080:8080 \
  -e TRUSTED_PROXIES="173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22" \
  aio-proxy

# Hardware acceleration with VA-API (AMD/Intel on Linux)
docker run -p 8080:8080 \
  --device=/dev/dri:/dev/dri \
  aio-proxy

# Hardware acceleration with NVIDIA (requires glibc-based image, not Alpine)
# docker run -p 8080:8080 \
#   --runtime=nvidia \
#   --gpus all \
#   your-nvenc-image

# Force CPU encoding (disable hardware acceleration)
docker run -p 8080:8080 \
  -e HW_ACCEL=off \
  aio-proxy

Hardware Acceleration: The default Alpine image supports VA-API (AMD/Intel). For NVIDIA NVENC, you'll need a glibc-based image (e.g., Ubuntu) with NVIDIA drivers.

API

Probe Endpoint

GET /probe?url=<target>&headers=<json>&h=<base64>&type=<mode>

Returns media metadata including duration, video, audio, and subtitle track information.

Parameter Description
url Target media URL (required)
headers Custom headers as JSON
h Custom headers as base64-encoded JSON
type Probe mode: native, fast, or full (default)
# Full probe - all formats, all metadata (slowest)
curl "http://localhost:8080/probe?url=https://example.com/video.mkv"
curl "http://localhost:8080/probe?url=https://example.com/video.mkv&type=full"

# Fast probe - ffprobe with reduced buffer
curl "http://localhost:8080/probe?url=https://example.com/video.mkv&type=fast"

# Native probe - in-process parsing, no ffprobe spawn (fastest, MKV/MP4 only)
curl "http://localhost:8080/probe?url=https://example.com/video.mkv&type=native"
Mode Time* Formats Returns
full ~400ms All Duration + all tracks
fast ~350ms All Duration + all tracks
native ~150ms MKV, MP4 Duration only

*Times vary based on network latency and file structure

Response:

{
  "url": "https://example.com/video.mkv",
  "duration": 3847.234,
  "duration_formatted": "1:04:07",
  "video": [
    { "index": 0, "codec_name": "hevc", "width": 1920, "height": 1080 }
  ],
  "audio": [
    {
      "index": 1,
      "codec_name": "aac",
      "language": "eng",
      "title": "English",
      "default": true
    },
    { "index": 2, "codec_name": "aac", "language": "spa", "title": "Spanish" }
  ],
  "subtitles": [
    {
      "index": 3,
      "codec_name": "subrip",
      "language": "eng",
      "title": "English"
    }
  ]
}

Proxy Endpoint

GET /proxy?url=<target>&type=<type>&headers=<json>&name=<n>&method=<method>&download=<bool>&seek=<seconds>&raw=<bool>

Parameters

Parameter Required Description
url Yes* Target URL to proxy
d Yes* Segment params for HLS/DASH (see format below)
type No Force type: web, video, audio, hls, dash, ytdlp
headers No JSON object of headers to send upstream
h No Base64-encoded headers (alternative to headers)
name No Media name for playlist metadata and download filename
method No HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD
body No Base64-encoded request body (for POST/PUT/PATCH, max ~6KB due to URL limits)
download No Set to true for download mode with attachment header
stream No Set to true for HLS/DASH→MP4 streaming mode (not seekable, but starts immediately)
media No For type=ytdlp: video (default) or audio
seek No Start position in seconds (e.g., seek=3600 for 1 hour)
raw No Skip transcoding, pass through original format. Works with download=true for raw file downloads

*Either url or d is required.

d= parameter format: <base64>,<key>=<value>,...

  • Base64 part: JSON with u (url), h (headers), n (name), t (token)
  • Template vars: comma-separated key=value pairs that replace $key$ in the URL
  • Example: d=eyJ1Ijo...,Number=$Number$ → player substitutes → d=eyJ1Ijo...,Number=5
  • Uses comma separator to avoid & which causes XML parsing issues in DASH manifests

Response Headers

Header Description
X-Content-Duration Media duration in seconds (when available)
Content-Type video/mp4 / audio/mpeg (transcoded), or original format type when raw=true

Note: The body parameter is transmitted via URL query string, which has browser/server limits (typically 2-8KB). For large payloads, make a direct POST request to the proxy endpoint with the body in the request itself.

Examples

# Video streaming (returns X-Content-Duration header)
curl -I "http://localhost:8080/proxy?url=https://example.com/video.mp4"

# Seek to 1 hour into movie
curl "http://localhost:8080/proxy?url=https://example.com/movie.mkv&download=true&seek=3600"

# HLS with custom headers (default: native HLS, seekable)
curl "http://localhost:8080/proxy?type=hls&url=https://cdn.example.com/stream.m3u8&headers={\"Referer\":\"https://example.com\"}"

# HLS streaming mode (converts to MP4, not seekable but starts immediately)
curl "http://localhost:8080/proxy?type=hls&url=https://cdn.example.com/stream.m3u8&stream=true"

# DASH with custom headers (native DASH, seekable)
curl "http://localhost:8080/proxy?type=dash&url=https://cdn.example.com/manifest.mpd&headers={\"Referer\":\"https://example.com\"}"

# DASH download mode (converts to MP4)
curl -o video.mp4 "http://localhost:8080/proxy?type=dash&url=https://cdn.example.com/manifest.mpd&download=true"

# Download video
curl -o movie.mp4 "http://localhost:8080/proxy?url=https://example.com/video.mkv&download=true&name=Movie"

# yt-dlp: Stream YouTube/supported sites as MP4
curl "http://localhost:8080/proxy?type=ytdlp&url=https://www.youtube.com/watch?v=dQw4w9WgXcQ"

# yt-dlp: Extract audio only as MP3
curl "http://localhost:8080/proxy?type=ytdlp&media=audio&url=https://www.youtube.com/watch?v=dQw4w9WgXcQ"

# POST request via proxy
curl "http://localhost:8080/proxy?type=web&method=POST&url=https://api.example.com/data&body=eyJrZXkiOiJ2YWx1ZSJ9"

# Download raw MKV file (no transcoding)
curl -O "http://localhost:8080/proxy?url=https://example.com/movie.mkv&download=true&raw=true"

Nested URL Unwrapping: If the url parameter points back to the proxy itself (e.g., from a redirect or embedded player), it will be automatically unwrapped. Parameters from the inner URL override the outer request. Supports one level of nesting.

HLS/DASH Playback Modes

Mode Parameter Browser Seekable Notes
Native HLS/DASH (default) (none) Player required ✅ Yes Playlist/manifest URLs rewritten to proxy
Streaming MP4 stream=true Native ❌ No Starts immediately, no seeking
Download MP4 download=true Native ✅ Yes Must buffer fully first

Default (Native HLS/DASH): Returns a rewritten playlist/manifest with proxied segment URLs. Works with hls.js, dash.js, VLC, mpv, and any compatible player. Fully seekable.

http://localhost:8080/proxy?type=hls&url=https://cdn.example.com/stream.m3u8
http://localhost:8080/proxy?type=dash&url=https://cdn.example.com/manifest.mpd

Streaming Mode: Converts HLS/DASH to fragmented MP4 on-the-fly. Plays in any browser but cannot seek (duration unknown during transcoding).

http://localhost:8080/proxy?type=hls&url=https://cdn.example.com/stream.m3u8&stream=true
http://localhost:8080/proxy?type=dash&url=https://cdn.example.com/manifest.mpd&stream=true

Download Mode: Converts HLS/DASH to complete MP4 file. Seekable after fully buffered.

http://localhost:8080/proxy?type=hls&url=https://cdn.example.com/stream.m3u8&download=true
http://localhost:8080/proxy?type=dash&url=https://cdn.example.com/manifest.mpd&download=true

Health Check

GET /

Returns:

{
  "status": "healthy",
  "service": "AIO Proxy",
  "version": "0.0.1"
}

Format Detection

The proxy automatically detects media format using:

  1. URL file extension
  2. Content-Type header (via HEAD request)
  3. Magic bytes (file signature)

Supported Formats

Video (output: MP4)

  • Transmux (fast, lossless): Any container with H.264/H.265/VP9/AV1 video + AAC/MP3/Opus/FLAC/Vorbis audio
  • Transcode (slower, uses HW accel): Containers with incompatible codecs (MPEG-2, WMV, AC3, etc.)

Audio (output: MP3)

  • Transmux: MP3 sources only
  • Transcode: AAC, FLAC, OGG, WAV, AIFF, WMA, M4A, Opus, Vorbis

HLS

  • Playlist rewriting with proxied segments
  • Supports encrypted streams (KEY URIs rewritten)
  • Multi-track audio/subtitle passthrough

DASH

  • Manifest rewriting with proxied segments
  • Supports SegmentTemplate with $Number$, $Time$, $Bandwidth$, $RepresentationID$
  • BaseURL, initialization, and media URL rewriting
  • Multi-track audio/subtitle passthrough

Transmux vs Transcode

The proxy automatically detects if the source codecs are web-compatible:

Operation Description Speed Quality
Transmux Change container only, copy streams Fast Lossless
Transcode Re-encode streams Slower Slight loss

Web-compatible video codecs: H.264, H.265/HEVC, VP8, VP9, AV1 Web-compatible audio codecs: AAC, MP3, Opus, FLAC, Vorbis

When the source has compatible codecs, streams are copied (transmuxed) into MP4. When codecs are incompatible, hardware-accelerated encoding is used (transcode).

Security

SSRF Protection

  • Blocks connections to private IP ranges (10.x, 172.16-31.x, 192.168.x)
  • Blocks localhost and link-local addresses
  • Blocks cloud metadata endpoints (169.254.169.254)
  • DNS rebinding prevention via connection-time IP validation

Rate Limiting

Stream limits (all proxy connections):

  • Global limit: Maximum concurrent streams (default: 100, via MAX_CONCURRENT_STREAMS)
  • Per-IP limit: Maximum streams per client (default: 10, via MAX_STREAMS_PER_IP)
  • Bandwidth limit: Maximum bytes/sec per client IP across all their connections (via MAX_BANDWIDTH_PER_IP)

Transcode limits (ffmpeg processes only):

  • Global limit: Maximum concurrent transcodes (default: 5, via MAX_CONCURRENT_TRANSCODE)
  • Per-IP limit: Maximum transcodes per client (default: 2, via MAX_TRANSCODE_PER_IP)
  • Queue timeout: 503 response if slot unavailable (default: 30s, via TRANSCODE_QUEUE_TIMEOUT)

HLS/DASH segments: Segment requests (identified by the d= parameter) bypass per-IP stream limits but remain subject to bandwidth limits. This allows HLS/DASH playback with many concurrent segment requests without triggering rate limits.

Bandwidth format accepts: 10M (10 MB/s), 500K (500 KB/s), 1G (1 GB/s), or raw bytes. The bandwidth limit is shared across all of a client's connections, preventing abuse even with many concurrent requests.

Stream limits protect connection count; bandwidth limits protect network saturation; transcode limits protect CPU. Set any *_PER_IP to 0 to disable per-IP limiting.

Header Sanitization

  • CRLF injection prevention in forwarded headers
  • Playlist name sanitization (control characters, quotes removed)

Client IP Protection

The proxy strips all IP-revealing headers before forwarding to upstream:

  • X-Forwarded-For, X-Real-IP, CF-Connecting-IP, True-Client-IP, X-Client-IP
  • Forwarded, Via, X-Forwarded-Host, X-Forwarded-Proto

This means upstream servers only see the proxy's IP address, not your clients' IPs. Useful for scenarios like shared streaming accounts where the upstream service restricts to a single IP.

Building

# Standard build
go build -o aio-proxy

# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build -o aio-proxy-linux

Version Management

The version is defined in main.go:

var Version = "0.0.1"

Update the Version variable to release a new version.

Requirements

  • Go 1.21+ (for maps package)
  • FFmpeg (must be in PATH)
  • yt-dlp (optional, for type=ytdlp support)

About

Universal media proxy with transcoding, HLS/DASH support, and yt-dlp integration

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors