pappy-media-api
v0.9.1
Published
Universal media engine & downloader — search, resolve, grab, and download music, categorized movies, videos, galleries, and 200+ batch items from 520+ platforms with parallel download acceleration, audio/video stream muxing, ID3 album art tagging, target-
Maintainers
Readme
pappy-media-api
The standalone media engine — search, resolve and download, entirely on YOUR machine.
No hosted API. No API keys. No rate limits. No server bill.
YouTube · TikTok · Pinterest · SoundCloud · Dailymotion · Vimeo · Streamable · Twitch · Bluesky · Instagram · Reddit · X · Facebook · Threads · Snapchat · Rumble · Tumblr · Movies (legal) · Subtitles · iTunes · 1800+ sites (via the optional yt-dlp tier)
💸 Why this exists
Hosted media APIs cost money every month — per request, per user, per server.
pappy-media-api flips the model: the engine runs inside your app. Your users' machines do the work,
so your costs are… zero. Install it, call it, ship.
npm install pappy-media-apiimport { UMedia } from "pappy-media-api";
const media = new UMedia();
// 1) Search — music leads with REAL 30-second previews
const { data } = await media.search({ q: "afrobeats", type: "music", limit: 3 });
for (const item of data.items) console.log(item.title, "—", item.previewKind, "→", item.previewUrl);
// 2) Resolve a post → ALL media, in order
const { data: post } = await media.resolve("https://www.tiktok.com/@user/photo/7690630628697459990");
console.log(post.itemCount, post.counts); // 30 { images: 30, … } — never truncated silently
// 3) Download everything to disk
const { data: job } = await media.download({ url: post.sourceUrl, quality: "best", dir: "./out" });
console.log(job.requestedQuality, "→", job.selectedQuality); // "best" → "720p" — real, never inventedThat's the whole model: your code, your machine, your files.
🎯 The contract that keeps your app honest
🏷️ Quality is never manufactured
requestedQuality vs selectedQuality on every download. A 240p source never arrives wearing a
1080p sticker. fallback: true tells you exactly when reality differed from the request.
🖼️ Galleries arrive complete
100 photos → 100 files, in order (001 - …, 002 - …). Anything that failed is named in
failedItems[] with the reason — never silent, never partial-by-accident.
🎧 Previews drop first
Music search leads with real 30s iTunes clips (previewKind: "clip30s"), movies with YouTube
trailers, social posts with official embeds. previewKind: "none" beats a fake preview.
🧯 Typed failures, always
UMediaError with a stable code (RATE_LIMITED, MEDIA_NOT_FOUND, PROVIDER_UNAVAILABLE,
SSRF_BLOCKED, …), plus engine.attempts[] — the real trail of which providers were tried and why.
🗺️ What runs where (honest edition)
| Platform | Search | Resolve | Download | How |
|---|---|---|---|---|
| YouTube | ✅ | ✅ | ✅ * | InnerTube — search & metadata always; stream URLs depend on your network (see below) |
| TikTok | — | ✅ | ✅ | video posts = direct CDN mp4; photo galleries = every slide the platform publicly exposes, truncated: true when it degrades galleries |
| Pinterest | — | ✅ | ✅ | image pins, video pins (direct mp4 + HLS variants), idea/story pins, pin.it short links |
| SoundCloud | — | ✅ | ✅ | full tracks as direct progressive MP3 (+ HLS) with real titles/artists |
| Dailymotion | — | ✅ | ✅ | public player metadata — HLS/progressive renditions (HLS saved as .ts) |
| Vimeo | — | ✅ | ✅ | player config — progressive mp4 when served, HLS otherwise |
| Streamable | — | ✅ | ✅ | direct mp4 renditions |
| Twitch | — | ✅ | ✅ | clips = direct CloudFront mp4s; VODs/livestreams via the yt-dlp tier |
| Bluesky | — | ✅ | ✅ | public AppView API — full-size images + video playlists |
| Instagram | — | ✅ | ✅ | oEmbed→mobile info API: photos, videos, full carousels |
| Reddit | — | ✅ | ✅ | posts + galleries + video with its audio sidecar (DC IPs may 403 — typed honest error) |
| X | — | ✅ | ✅ | syndication token + guest GraphQL: photos/videos/gifs at real bitrates |
| Facebook | — | ✅ | ✅ | reels/watch/videos/shares/fb.watch via the web player JSON |
| Threads | — | ✅ | ✅ | post SSR state + og media (Meta gates some networks — typed honest error) |
| Snapchat | — | ✅ | ✅ | spotlight videos (direct sc-cdn mp4) + public stories/highlights — ALL snaps in order |
| Rumble | — | ✅ best-effort | ✅ best-effort | embed API direct mp4s (site blocks datacenter IPs) |
| Tumblr | — | ✅ | ✅ | mobile API: video/audio/photo posts + reblog trails, all in order |
| iTunes | ✅ | — | ✅ previews | real 30s clips, rock solid |
| 1800+ sites | — | ✅ | ✅ | optional tier: pip install yt-dlp — also used automatically as a fallback when a platform is bot-gating your network; labeled source: "yt-dlp (auto-fallback)" |
| Movies (legal) | ✅ | ✅ metadata | ✅ trailers/free films | Wikipedia metadata (keyless) + Archive.org full movies + YouTube trailers; categorized cinema |
| Music Engine | ✅ | ✅ | ✅ | Multi-source (iTunes 30s clips, SoundCloud full progressive MP3s, Archive.org) |
| Mass Grabber | — | ✅ | ✅ | Universal page & gallery extractor: scrapes 200+ photos, videos, audios, and files into disk or ZIP |
| Subtitles | — | ✅ | ✅ | YouTube caption tracks (keyless) + OpenSubtitles (your own free key); .srt/.vtt output |
🎵 Universal Music Engine
Search, resolve, and download music across iTunes, SoundCloud, Internet Archive, and YouTube with rich metadata, album art, 30-second previews, and direct progressive audio streams.
import { UMedia } from "pappy-media-api";
const media = new UMedia();
// Multi-source music search with audio stream and preview URLs
const res = await media.searchMusic({ q: "Daft Punk", limit: 5 });
for (const track of res.data.items) {
console.log(`${track.artist} - ${track.title} (${track.source})`);
console.log(` Preview: ${track.previewUrl}`);
console.log(` Direct Stream: ${track.streamUrl || track.downloadUrl}`);
}CLI:
# Search across all sources with audio preview links
npx pappy-media-api music "Starboy" --limit 5
# Search specifically on SoundCloud for full tracks
npx pappy-media-api music "lofi beats" --source soundcloud --limit 10🎬 Categorized Movies & Cinema
Browse and discover cinema categorized across 16 global industries and genres:
- Regional Industries:
hollywood,bollywood,nollywood,korean(K-Drama),chinese(Wuxia),donghua(Chinese Animation),japanese,anime - Genres:
action,comedy,horror,scifi,documentary,classics(Public Domain) - Mature & Adult (Strict Opt-in):
18+_movies,18+_anime(adult: truerequired)
Direct MP4 downloads are provided for full feature films via Internet Archive, alongside legal official trailers on YouTube.
// 1) List available movie categories
const categories = media.movieCategories();
// 2) Search public domain classic movies (with direct MP4 downloadUrl)
const classics = await media.searchMovies({
q: "Charade",
category: "classics",
source: "archive",
});
console.log(classics.data.items[0].downloadUrl);
// → https://archive.org/download/Charade/Charade.mp4
// 3) Search action cinema
const actionFilms = await media.searchMovies({
q: "martial arts",
category: "action",
limit: 5,
});
// 4) Access adult-themed cinema (strictly gated opt-in)
const mature = await media.searchMovies({
q: "uncut",
category: "18+_movies",
adult: true,
});CLI:
# List all movie categories
npx pappy-media-api movie-categories
# Search classic movies
npx pappy-media-api movies "night of the living dead" --category classics
# Include adult categories (opt-in)
npx pappy-media-api movie-categories --adult🌐 Universal Mass Media Grabber (200+ Media Items)
Extract and download every photo, video, audio file, and document embedded inside any webpage or gallery URL. Perfect for scraping 50, 100, or 200+ assets at once with automatic batch downloading and optional .zip bundle generation.
// 1) Grab all media metadata from any webpage
const grabResult = await media.grab("https://example.com/gallery", {
limit: 200, // Extract up to 200 items
type: "all", // "all" | "image" | "video" | "audio" | "document"
});
console.log(`Found ${grabResult.data.itemCount} items!`);
// 2) Download all extracted items into a folder or zip
const downloadResult = await media.downloadGrab(grabResult, {
dir: "./downloads",
zip: true, // Package all downloaded files into a .zip archive
concurrency: 5, // Concurrent download workers
onProgress: (done, total) => console.log(`Downloaded ${done}/${total}...`),
});CLI:
# Grab and inspect all media on a webpage
npx pappy-media-api grab "https://example.com/page" --limit 100
# Grab, download all media, and zip them into an archive
npx pappy-media-api grab "https://example.com/gallery" --limit 200 -o ./downloads --zip🏛️ 520 Platform Registry & Spec
pappy-media-api includes an exhaustive registry of 520 digital media platforms across 12 distinct industry categories:
- Major Social Networks (
youtube,tiktok,instagram,facebook,x,threads,snapchat,reddit, …) - Music & Audio Platforms (
soundcloud,audiomack,bandcamp,mixcloud,spotify, …) - Chinese & Regional Platforms (
douyin,kuaishou,bilibili,weibo,iqiyi,youku, …) - Video & Streaming Hubs (
twitch,vimeo,dailymotion,kick,rumble, …) - Anime & Animation (
crunchyroll,hidive,donghua,iqiyi, …) - Image & Art Boards (
pinterest,deviantart,pixiv,artstation,imgur, …) - News, Podcasts, E-Learning, Sports, Cloud Drives, and Adult Streaming.
import { getPlatform, queryPlatforms, detectPlatform } from "pappy-media-api";
// Identify platform from URL
const detected = detectPlatform("https://www.tiktok.com/@user/video/123");
// Query platforms in the registry
const musicPlatforms = queryPlatforms({ category: "music_audio", tier: "A" });🎬 Movies & subtitles (legal)
Discovery + metadata — real, keyless, no piracy:
const media = new UMedia();
// metadata for any film: title, year, plot, poster, rating
const { data } = await media.movie({ q: "Inception" });
// → { title: "Inception", year: 2010, poster: "https://…", description: "2010 film by…" }
// optional: richer metadata + ratings via your own free TMDB key
await media.movie({ q: "Inception", tmdbKey: process.env.PAPPY_TMDB_KEY });
// legal playback sources: official trailers + officially free full movies on YouTube
const films = await media.searchMovies({ q: "inception", limit: 10 });Subtitles — two legal engines, real .srt/.vtt files:
// 1) a YouTube video's own captions (keyless; same network caveat as streams)
await media.youtubeSubtitles({ url: "https://youtu.be/…", lang: "en", format: "srt" });
const langs = await media.captionLanguages("https://youtu.be/…"); // what's available
// 2) any movie or TV episode via OpenSubtitles (bring your own free key)
// get one at opensubtitles.com/consumers, then:
const subs = media.subtitles({ apiKey: process.env.PAPPY_OPENSUBTITLES_KEY });
const found = await subs.search({ imdbId: "tt1375666", languages: ["en", "fr"] });
await subs.download({ fileId: found.data.results[0].files[0].fileId, dir: "./subs" });Converters are exported too: vttToSrt, youtubeXmlToSrt, srtToVtt.
CLI: pappy-media-api movie "Inception" · pappy-media-api movies "inception" · pappy-media-api subtitles <youtube-url> --lang en --format srt
18+ content is strictly gated. No adult provider ships in this engine (
adult_enabled: false,adultProviders: []), and no DRM or paywall is ever circumvented.
* About YouTube stream URLs: YouTube bot-gates datacenter IPs and serves formats without URLs.
On typical home connections download() just works; on hard networks you'll get a typed
PROVIDER_UNAVAILABLE with the real reason — or run download({ … }) via
downloadWithYtdlp() / pappy-media-api download <url> --ytdlp which handles PO tokens, cookies and muxing.
We'd rather tell you the truth than fake a download.
Direct media URLs always work: give resolve()/download() any direct .mp4/.m4a/.jpg/… link and it
goes straight to disk — no platform needed.
// the power tier — one function, 1800+ sites, hard networks
await media.downloadWithYtdlp({ url: "https://example.com/anything", quality: "720p", dir: "./out" });📚 API
| Call | What it does |
|---|---|
| media.searchMusic({q, limit, source, adult}) | Music engine across iTunes, SoundCloud, Archive.org with audio streams & previews |
| media.searchMovies({q, category, limit, source, adult}) | Categorized cinema (Hollywood, Bollywood, Nollywood, Korean, Anime, Action, 18+ opt-in) |
| media.movieCategories({adult}) | Registry of 16 movie categories & genres |
| media.grab(url, {limit, type, dir}) | Mass media grabber extracting 200+ images/videos/audio from any page |
| media.downloadGrab(grabRes, {dir, zip}) | Batch downloader for grabbed items with progress and optional ZIP packaging |
| media.acceleratedDownload({url, dest, concurrency}) | Parallel multi-segment chunk downloader with HTTP Range support |
| media.mux({video, audio, output}) | Lossless video and audio stream merger (MP4/MKV) via ffmpeg |
| media.extractAudio({input, output, format, bitrate}) | High quality audio extraction from video or stream URLs |
| media.tagAudio({filePath, metadata, cover}) | ID3v2 metadata & album artwork thumbnail tagger |
| media.downloadMusic(track, {dir, tag}) | Download track from music search, embed ID3 tags & artwork |
| media.compress({input, output, targetSizeMb}) | Target-size video compressor (optimizes for Discord, WhatsApp, social limits) |
| media.embedSubtitles({video, subtitles, output, mode}) | Subtitle track embedder (soft timed text tracks or burned hardsubs) |
| media.createPreviewGif({input, output, duration}) | Animated GIF/WebP preview clip generator with 2-pass palette optimization |
| media.createStoryboard({input, output, rows, cols}) | Keyframe storyboard contact sheet image generator |
| media.record({url, output, durationSeconds}) | Live stream and HLS/DASH broadcast recorder |
| media.inspect(url) | Deep media inspector — streams, codecs, resolutions, subtitles, formats |
| media.serve({port, host}) | Embedded zero-dependency HTTP streaming proxy & REST microservice |
| media.search({q, type, limit, adult}) | Multi-source search — type: video | music | movie | short_video |
| media.resolve(url) | Every media item of a post, in order + honest engine.attempts |
| media.download({url, quality, dir, accelerate, zip, onProgress}) | Saves files to disk — quality: original | best | 2160p … 144p | audio |
| media.downloadWithYtdlp({url, quality, dir}) | yt-dlp tier (requires yt-dlp on PATH) |
| media.capabilities() | What THIS machine can actually do — never overstated |
| media.status() | Real adapter status |
Error codes — INVALID_REQUEST · INVALID_URL · SSRF_BLOCKED · AUTH_REQUIRED ·
AUTH_INVALID · RATE_LIMITED · MEDIA_NOT_FOUND · CONTENT_PRIVATE · ACCESS_RESTRICTED ·
QUALITY_UNAVAILABLE · PROVIDER_ERROR · PROVIDER_UNAVAILABLE · TIMEOUT · NOT_FOUND · INTERNAL
🖥️ CLI
# 1. Embedded Streaming Proxy & Microservice Server
npx pappy-media-api serve --port 3333 # boots HTTP server + HTML5 dashboard
# 2. Deep Media Stream Inspector
npx pappy-media-api inspect "https://example.com/video" # codecs, formats, bitrates, subtitles
# 3. High-Speed Segmented Parallel Download
npx pappy-media-api download "https://example.com/movie.mp4" --accelerate --concurrency 4
npx pappy-media-api download "https://example.com/video" --cookies cookies.txt # session bypass
# 4. Target-Size Video Compressor & Subtitle Embedder
npx pappy-media-api compress video.mp4 -o discord.mp4 --target 25mb # fits under 25MB
npx pappy-media-api embed-subs --video movie.mp4 --subs movie.srt -o subbed.mp4 # soft or --burn
# 5. Visual Storyboards & Animated GIF Previews
npx pappy-media-api preview video.mp4 --gif -o preview.gif # animated preview
npx pappy-media-api preview video.mp4 --storyboard -o contact_sheet.jpg # 3x3 keyframes
# 6. Live Broadcast & HLS Stream Recorder
npx pappy-media-api record "https://live.example.com/stream.m3u8" --duration 60s -o live.mp4
# 7. In-Process Stream Muxer & Audio Extractor
npx pappy-media-api mux --video video.mp4 --audio audio.m4a -o synced.mp4
npx pappy-media-api extract-audio video.mp4 -o song.mp3 --bitrate 320k
# 8. Music Engine with ID3 & Album Art Injection
npx pappy-media-api music "Starboy" --limit 5
npx pappy-media-api music "lofi study" --source soundcloud --download --tag
# 9. Categorized Movies & Public Domain Features
npx pappy-media-api movie-categories
npx pappy-media-api movies "night of the living dead" --category classics
npx pappy-media-api movies "kung fu" --category chinese
# 10. Mass Media Grabber (200+ items)
npx pappy-media-api grab "https://example.com/gallery" --limit 200 -o ./downloads --zip
# 11. Social & Video Resolvers
npx pappy-media-api search "lofi beats" --type music --limit 5
npx pappy-media-api resolve "https://www.tiktok.com/@user/photo/7690630628697459990"
npx pappy-media-api download "https://youtu.be/jNQXAC9IVRw" --quality best -o ./out
npx pappy-media-api download "https://example.com/video" --ytdlp # power tier
npx pappy-media-api status
npx pappy-media-api doctor # start here if a download failsDownloads on a bot-gated network
YouTube (and some platforms) withhold stream URLs from datacenter networks — the native tier honestly reports that, then falls back automatically:
# 1. check what you have and what's missing
npx pappy-media-api doctor
# 2. if yt-dlp is missing, install it — that's the whole fix
pip install yt-dlp # or: pipx install yt-dlp / brew install yt-dlp
# 3. the same command now just works
npx pappy-media-api download "https://youtu.be/jNQXAC9IVRw" -o ./outconst media = new UMedia();
const r = await media.download({ url, dir: "./out" });
r.data.source; // "youtube" (native) | "yt-dlp (auto-fallback)" | "tiktok"
r.data.files; // [{ name, path, size }]With yt-dlp installed, download() uses it only when the native tier couldn't
get a URL — everything else stays on the fast native path. Force it with
noYtdlpFallback: true to require the native tier only.
🧯 Safety, built in
- SSRF hygiene — private, loopback and internal hosts are refused (
SSRF_BLOCKED). - No secrets to manage — there are none. No keys, no tokens, no dashboards.
- Zero telemetry. Your users' requests never touch anyone's server — because there isn't one.
🤖 Production Showcase: Pappy / Omega (Telegram Media OS)
pappy-media-api powers Pappy / Omega — a production-grade Telegram-native Media Operating System:
- 🎵 Universal Music Search & DJ Streaming: iTunes 30s previews, SoundCloud full tracks, ID3 album art tagging, and group voice-call DJ streaming.
- 🎬 16 Categorized Movie Industries: Hollywood, Bollywood, Nollywood, Korean, Chinese, Japanese, Anime, Donghua, Action, and Classics with legal public-domain MP4s and trailers.
- 🌐 Mass Web Media Grabber: Scrapes and bundles up to 200+ images/videos from any page into direct ZIP downloads.
- 🗜️ Target-Size Video Compression: Dynamically compresses high-res video down to 48MB to seamlessly fit under Telegram's 50MB Bot API upload ceiling.
- ⚡ Multi-Segment Range Accelerator: 3–5x faster downloads with parallel chunk reconstruction.
- 🖼️ Complete Social Galleries: Downloads every item in TikTok, Instagram, Reddit, X, and Pinterest posts with zero silent truncation.
Built to save you the hosting bill. Ship the engine, not the infrastructure.
MIT © 2026 · part of the Unified Media API project
