Skip to content

N0M4D-D3V/batch-bunny

Repository files navigation

████████████████████████████████████████████████████████████████████████████████████████████████
                        ⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
                        ⠀⠀⠀⠀⠀⠀⠀⣰⠋⣷⠀⠀⠀⠀⠀ ⠀⠀⢀⡼⢲⠀⠀⠀⠀⠀⠀⠀⠀ ⢀⡖⢳⠄⠀⠀⠀⠀⠀⠀⠀⠀
                        ⠀⠀⠀⠀⠀⠀⣸⠃⢀⣿⠖⢳⡆⠀⠀⠀⢀⡞⠀⣸⣠⠤⡄⠀⠀⠀⠀⢠⡟⠁⣸⣤⠶⡄⠀⠀⠀⠀⠀⠀
                        ⠀⠀⠀⠀⠀⢰⡇⣴⡟⠁⠀⡼⠁⠀⠀⠀⡾⢀⣴⠏⠀⢠⠇⠀⠀⠀⠀⡾⢀⣴⠋⠀⢰⠇⠀⠀⠀⠀⠀⠀
                        ⠀⠀⣀⠴⠋⠉⠘⢹⡇⢀⡼⠳⠤⣤⡤⠒⠣⢟⡇⠀⢠⣏⣀⡀⣠⠴⠚⠣⢯⡇⠀⣠⢯⣄⣀⠀⠀⠀⠀⠀
                        ⠀⡼⠃⢀⣄⠀⠀⠘⠓⠋⠀⢠⡞⠁⠀⠀⠀⠘⢧⠴⠋⠀⢠⠟⠁⠀⠀⠀⠘⠧⠞⠁⠀⠀⠈⠙⢦⡀⠀⠀
                        ⢸⡇⠀⠈⠉⠀⠀⠀⠀⠀⢠⣏⡀⠐⠟⠃⠀⠀⠀⠀⠀⢠⣏⠀⠘⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠹⡄⠀
                        ⠀⠙⠦⣤⣀⣤⠤⠀⠀⠀⠈⠳⣄⡀⠀⠀⢀⠀⠀⠀⠀⠀⠻⢆⣀⢀⡀⣀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⣿⠀
                        ⠀⠀⠀⢻⡀⠀⡀⠀⠀⠀⠀⠀⠀⢻⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⢹⡉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   ⠀⡿⣄
                        ⠀⠀⠀⢨⠇⢀⡿⠀⠀⢀⣀⡤⠄⠈⣧⠀⢲⠀⠀⠀⠀⢀⡀⠀⠈⣷⠀⢸⡀⠀⠀⠀⣀⡀⠀⠀⠀ ⠀⣡⠟
                        ⠀⠀⠀⠙⠒⢯⣀⣠⠴⠻⢧⣄⠤⠼⢥⣴⠋⠀⢀⡴⡟⠉⢀⣀⣸⡧⣴⠋⢀⣀⡴⣟⣁⣀⣀⡠⠴⠊⠁⠀
                        ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠉⠁⠀⠉⠉⠉⠀⠀⠀⠈⠉⠉⠀⠀⠈⠉⠁⠀⠀⠀⠀⠀⠀
                                        <[version 0.0.1]>

██████  ██    ██ ███    ██ ███    ██ ██    ██       ██   ██ ██████  ██       ██████  ██ ████████
██   ██ ██    ██ ████   ██ ████   ██  ██  ██         ██ ██  ██   ██ ██      ██  ████ ██    ██
██████  ██    ██ ██ ██  ██ ██ ██  ██   ████   █████   ███   ██████  ██      ██ ██ ██ ██    ██
██   ██ ██    ██ ██  ██ ██ ██  ██ ██    ██           ██ ██  ██      ██      ████  ██       ██
██████   ██████  ██   ████ ██   ████    ██          ██   ██ ██      ███████  ██████  ██    ██

████████████████████████████████████████████████████████████████████████████████████████████████

Technical, no-BS guide to download Bunny.net embeds in batch, then extract normalized podcast audio — with a one-shot orchestrator on top.


Contents


Overview

  1. bunny_downloader.py parses a Bunny iframe URL, discovers the MP4/HLS source, and downloads it (HLS is remuxed to MP4 via ffmpeg). It sends the correct Referer/Origin headers to avoid hotlink 403s.
  2. batch_bunny.py reads a custom list file and calls bunny_downloader.py one by one. All videos are written to ./downloads/video/. If no output name is provided, it autoincrements (1.mp4, 2.mp4, …).
  3. bunny_podcaster.py converts each video in ./downloads/video/ to a podcast-ready MP3 in ./downloads/audio/ with real-time progress and loudness normalization.
  4. main.py orchestrates both steps based on config.bunny.

Requirements

  • Python ≥ 3.9
  • ffmpeg and ffprobe available in PATH
    • Linux: apt install ffmpeg / dnf install ffmpeg / brew install ffmpeg (macOS)
    • Windows: use a static build and add to PATH
  • Network access to Bunny CDN endpoints from your machine

Optional but recommended:

  • A Python virtual environment

Folder Layout

.
├─ bunny_downloader.py
├─ batch_bunny.py
├─ bunny_podcaster.py
├─ main.py
├─ config.bunny
├─ list.bunny
└─ downloads/
   ├─ video/   # mp4s saved by batch_bunny.py
   └─ audio/   # mp3s produced by bunny_podcaster.py

Scripts

bunny_downloader.py

Purpose: Download a single Bunny video given an iframe URL.

Key features

  • Parses the iframe HTML (meta/OG + HLS sources).
  • Sends Referer/Origin headers (in both requests and ffmpeg) to bypass hotlink filters.
  • HLS: picks the best variant and remuxes to MP4 (no re-encode).
  • Fallback to direct MP4 if exposed via og:video:url.

Usage

python bunny_downloader.py   --iframe "https://iframe.mediadelivery.net/embed/<ACCOUNT>/<VIDEO_ID>"   --out "./downloads/video/sample.mp4"

# Force direct MP4 (if present in OG tags)
python bunny_downloader.py --iframe "<...>" --out "./downloads/video/out.mp4" --prefer-mp4

# Manually set Referer/Origin (optional)
python bunny_downloader.py --iframe "<...>" --out "./downloads/video/out.mp4"   --referer "https://iframe.mediadelivery.net"

Important: A 403 may still happen if the CDN uses URL signing or session cookies; see Troubleshooting.


batch_bunny.py

Purpose: Batch-download from a list file using bunny_downloader.py.

Behavior

  • Always saves to ./downloads/video/.
  • Lines without an explicit output name get an auto-increment filename: 1.mp4, 2.mp4, …
  • Lines with explicit output names are sanitized and de-duplicated (suffix _1, _2, … if needed).
  • Runs in series (one by one), prints a summary.

Usage

python batch_bunny.py --file ./list.bunny
python batch_bunny.py --file ./list.bunny --prefer-mp4
python batch_bunny.py --file ./list.bunny --referer "https://iframe.mediadelivery.net"
python batch_bunny.py --file ./list.bunny --bunny-script ./bunny_downloader.py

bunny_podcaster.py

Purpose: Extract podcast-style audio from all videos in ./downloads/video/ into ./downloads/audio/.

Audio output

  • MP3 @ 44.1 kHz
  • Mono by default (use --stereo to keep stereo)
  • 96 kbps CBR by default (configurable)
  • EBU R128 loudness normalization (≈ −16 LUFS)
  • Real-time progress per file: %, processed time, speed, ETA (uses -progress pipe:1 + ffprobe)

Usage

# Default: mono 96k
python bunny_podcaster.py

# Stereo 128k
python bunny_podcaster.py --stereo --bitrate 128k

# Overwrite existing MP3s
python bunny_podcaster.py --overwrite

# Different extension (e.g., .m4a via external change – script defaults to MP3 encoder)
python bunny_podcaster.py --ext .mp3

Outputs are written to ./downloads/audio/.


main.py

Purpose: High-level orchestrator. Reads config.bunny, runs batch download, then (optionally) runs podcast export.

Usage

python main.py
# or
python main.py --config ./config.bunny

Flow

  1. Validates paths and config.
  2. Runs batch_bunny.py with configured flags.
  3. If [podcast].enabled = true, runs bunny_podcaster.py with configured audio options.
  4. Exits with the batch’s exit code (useful for CI).

Configuration

config.bunny is an INI file:

[batch]
batch_script = ./batch_bunny.py
bunny_dl     = ./bunny_downloader.py
list_file    = ./list.bunny
prefer_mp4   = true
referer      = https://iframe.mediadelivery.net

[podcast]
enabled  = true
script   = ./bunny_podcaster.py   ; point to your actual audio script
bitrate  = 96k
stereo   = false
overwrite= false
ext      = .mp3

If you renamed the audio script, set [podcast].script to the correct path.


List File Format

list.bunny supports one entry per line:

  • Just the iframe URL (auto-number output):
https://iframe.mediadelivery.net/embed/399263/c2c8521b-2329-494a-8294-f5f9e71bf902
  • URL + explicit output name (any of these separators: |, ,, ->):
https://iframe.../embed/.../aaaaaaaa-bbbb | class_01.mp4
https://iframe.../embed/.../bbbbbbbb-cccc -> my_video.mp4
https://iframe.../embed/.../cccccccc-dddd , lesson3

The name is sanitized; .mp4 is appended if missing.

  • Comments / blanks are ignored:
# this is a comment

Examples

1) Batch only

python batch_bunny.py --file ./list.bunny --prefer-mp4   --referer "https://iframe.mediadelivery.net"
# Videos → ./downloads/video/

2) Full pipeline via orchestrator

# config.bunny prepared as above
python main.py
# Videos → ./downloads/video/
# Audios → ./downloads/audio/

3) Single video

python bunny_downloader.py   --iframe "https://iframe.mediadelivery.net/embed/399263/<VIDEO_ID>"   --out "./downloads/video/demo.mp4"

4) Convert to podcast with progress

python bunny_podcaster.py --stereo --bitrate 128k

Troubleshooting

  • 403 Forbidden (m3u8 or segments)
    The script already sends Referer/Origin. If 403 persists, the CDN likely enforces URL signing and/or session cookies.
    → Conclusion for audits: not downloadable off-site without valid signed URL/session.

  • ffmpeg/ffprobe not found
    Install them and ensure they’re in PATH. The scripts will exit early with a clear message.

  • Playlist without variants
    bunny_downloader.py detects media playlists (no #EXT-X-STREAM-INF) and downloads directly.

  • Output collisions
    Batch will suffix duplicates (_1, _2, …). Auto-numbering scans existing N.mp4 files and continues from the highest.

  • Weird resolutions
    Variant parser infers height from RESOLUTION=WxH, NAME="1080p", or the URL itself (/.../1080p/..., .../1920x1080/...).


Exit Codes

  • 0 — success
  • 1 — missing ffmpeg/ffprobe or processing error
  • 2 — invalid/missing inputs (config/list/script not found)
  • 3 — podcast stage script missing (orchestrator)

Orchestrator (main.py) returns the batch exit code (even if podcast ran).


Notes for Security Audits

  • This tooling is intended for authorized security testing.
  • If assets are protected by hotlink checks, signed URLs, session tokens, or DRM, downloads should fail when requested off-site — that’s expected and should be recorded as properly protected.
  • If assets are retrievable off-site without such controls, document the precise request headers/flows demonstrating the exposure.

That’s it. Plug your list, run the batch, and get clean, normalized podcast audio with progress.

About

BBD - Python script for download files from Bunny Servers

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors

Languages