Package Download Manager for Termux.
pdm is a single stdlib-only Python file that drives aria2c and yt-dlp. It
decides which of the two a link needs, expands repository pages into their file
lists.
Most phone downloaders take a file link and fetch it. pdm takes the link you
actually have:
| You paste | pdm downloads |
|---|---|
https://huggingface.co/owner/model |
every file in the repo, in parallel |
https://huggingface.co/owner/model/blob/main/x.bin |
the file, not the HTML viewer |
https://github.com/owner/repo |
the latest release assets, or the source zip |
https://github.com/owner/repo/blob/main/x.py |
the raw file |
https://youtu.be/... |
video as mp4, audio as m4a, or a whole playlist |
| any direct file, magnet or torrent | straight through aria2c |
curl -L https://huggingface.co/owner/model gives you 133 KB of HTML. That gap,
not raw speed, is the point of this tool.
pkg install -y git
git clone https://github.com/DavutKaraoglan/pdm
cd pdm
sh install.shinstall.sh installs aria2, ffmpeg, yt-dlp, a JS runtime and
termux-api, then symlinks pdm into $PREFIX/bin, so the command works from
any folder. It skips whatever is already present, so it is safe to re-run.
Give Termux access to your storage once, otherwise downloads stay inside the Termux home:
termux-setup-storageCheck the result any time:
pdm doctorpdm 1.0
python 3.13.13
aria2c aria2 version 1.37.0
yt-dlp 2026.08.19
ffmpeg ffmpeg version 8.1.2
js node (/data/data/com.termux/files/usr/bin/node)
wakelock termux-wake-lock (downloads survive the screen going off)
notify termux-api (needs the Termux:API app too)
gallery termux-media-scan (downloads show up in Gallery and CapCut)
folder /storage/emulated/0/Download (writable)
queue 0 entries
pdm get -N also needs the Termux:API app from
F-Droid; the termux-api
package on its own is just the CLI half and the notification will not appear.
cd pdm && git pull
pip install -U yt-dlpThe symlink keeps pointing at the same file, so nothing else has to be redone.
rm "$PREFIX/bin/pdm"
rm -rf ~/.config/pdm ~/.local/share/pdmpdm get <link> # download now (the `get` can be omitted)
pdm get -M <link> # push the link as hard as it goes
pdm get -a <link> # audio only, m4a
pdm get -q 1080 <link> # cap the height, still mp4
pdm get -p <playlist> # whole playlist
pdm get -o ~/models <link> # pick the folder
pdm get -i links.txt # one link per line
pdm formats <link> # list what the site offerspdm status # what is downloading, and what stopped halfway
pdm stop 001 # stop one, or --all
pdm resume # continue everything that was interrupted
pdm status --clear # forget the stopped entries insteadEvery download is recorded while it runs, so a kill, a crash or a closed terminal leaves something to go back to:
#001 stopped background pid 28966 since 19:59:27
https://huggingface.co/hf-internal-testing/tiny-random-gpt2
(4/10) model.safetensors
stopped entries can be picked up with: pdm resume
resume re-runs the same link with the same options; aria2c and yt-dlp both
continue from the part file rather than starting over. Files left behind by a
download pdm has no record of are listed too, since re-sending that link
finishes them the same way.
If you would rather have the space back than finish them:
pdm clean # list the half finished files and their size
pdm clean -y # delete themAnything written to in the last 30 seconds is skipped, so a running download cannot be cleaned out from under itself.
pdm add <link> # queue it
pdm add --at 22:30 <link> # or at a time: 22:30, +45m, '2026-10-01 09:00'
pdm queue # list
pdm run # work through what is due
pdm run -j 2 --daemon # two at a time, wait for scheduled entries
pdm rm 003 # drop one
pdm clear --all # drop everythingpdm get -b <link> # detach; survives closing Termux
pdm get -N <link> # progress in the notification area-b re-runs the command in its own session, writes to
~/.local/share/pdm/logs/ and turns notifications on, since there is no
terminal left to print to. Either way pdm holds a wake lock while downloading,
so the transfer keeps going when the screen goes off.
install.sh links share/termux-url-opener into ~/bin, which is what Termux
runs when a link is shared to it. Hit share -> Termux in Chrome, YouTube or
anywhere else and a session opens with the link ready:
https://youtu.be/jNQXAC9IVRw
1 video 2 audio 3 1080p 4 queue 5 cancel
choice [1]:
Enter picks video. The download runs in that session so you can watch the bar,
and -N mirrors it to the notification shade; Termux keeps the session alive
while you go back to the browser. 4 only drops the link in the queue for a
later pdm run.
pdm config # show everything
pdm config out /sdcard/Download
pdm config conns 16 # aria2c allows 1-16
pdm config limit 500K # speed cap, empty for none
pdm config jobs 2 # queue parallelismStored in ~/.config/pdm/config.json; PDM_CONFIG and PDM_QUEUE override the
paths. Without an out setting, downloads land in /storage/emulated/0/Download.
| Flag | Meaning |
|---|---|
-M, --max-speed |
16 connections, no rate limit, 4 files at once |
-a, --audio |
audio only (m4a) |
-q, --quality N |
cap video height |
-p, --playlist |
download the whole playlist |
-o, --out DIR |
download folder |
-n, --name |
file name or yt-dlp output template |
-x, --conns N |
connections per server (max 16) |
--limit RATE |
speed cap, e.g. 500K |
--cookies FILE |
cookies file for logged-in sites |
-b, --background |
detach from the terminal |
-N, --notify |
Android notification progress |
--quiet |
no progress line |
--dry-run |
print the command, download nothing |
- Speed comes from aria2c, not from
pdm. On a single file it is within a second or two ofcurl. On a 10-file repo,-Mwas about 25% faster than acurlloop in local testing because the link never sits idle between files. - YouTube needs a JS runtime (
nodeordeno) for the nsig challenge, or formats go missing.install.shhandles it. - Video comes down as mp4, audio as m4a, so the files open in Gallery, CapCut and the stock player without a convert step. VP9 tracks are skipped even when the site wraps them in mp4, because Android's mp4 reader ignores them and the file then never reaches the gallery. On Instagram that means the progressive H.264 file instead of the slightly larger VP9 stream.
- A file written from Termux is invisible to other apps until the media scanner
is told about it. After every download
pdmrunstermux-media-scanon the folder, which is why the result shows up in Gallery right away. - When aria2c cannot follow a stream (YouTube's SABR path),
pdmretries with yt-dlp's own downloader automatically. - Names taken from repository listings are checked before they reach aria2c, so
a
../or a newline in a listed file name cannot write outside the folder. pdm.pyis the whole program: standard library only, Python 3.9 or newer. Nothing is imported that Termux does not already ship.
MIT, see LICENSE. aria2c, yt-dlp and ffmpeg are separate
projects under their own licenses.