{"id":45208648,"url":"https://github.com/ido-pluto/ipull","last_synced_at":"2026-02-20T16:05:52.829Z","repository":{"id":190826364,"uuid":"680592451","full_name":"ido-pluto/ipull","owner":"ido-pluto","description":"Super Fast 🔥 File Downloader 🚀 (parallels connections)","archived":false,"fork":false,"pushed_at":"2025-07-08T20:16:22.000Z","size":5931,"stargazers_count":50,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-25T14:17:59.864Z","etag":null,"topics":["cli","cli-downloader","cli-progress","curl","download-manager","downloader","fast-download-speed","fetch","idm","ipull","nodejs","parallel-algorithm","pull","resume-download","transfer-speed","wget"],"latest_commit_sha":null,"homepage":"https://ido-pluto.github.io/ipull/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ido-pluto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"ido-pluto"}},"created_at":"2023-08-19T18:38:08.000Z","updated_at":"2025-07-28T10:39:41.000Z","dependencies_parsed_at":"2023-08-26T15:43:46.860Z","dependency_job_id":"a5161fda-f248-4f46-b0e7-cb889212c774","html_url":"https://github.com/ido-pluto/ipull","commit_stats":null,"previous_names":["ido-pluto/ipull"],"tags_count":62,"template":false,"template_full_name":null,"purl":"pkg:github/ido-pluto/ipull","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ido-pluto%2Fipull","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ido-pluto%2Fipull/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ido-pluto%2Fipull/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ido-pluto%2Fipull/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ido-pluto","download_url":"https://codeload.github.com/ido-pluto/ipull/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ido-pluto%2Fipull/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29656590,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T09:27:29.698Z","status":"ssl_error","status_checked_at":"2026-02-20T09:26:12.373Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","cli-downloader","cli-progress","curl","download-manager","downloader","fast-download-speed","fetch","idm","ipull","nodejs","parallel-algorithm","pull","resume-download","transfer-speed","wget"],"created_at":"2026-02-20T16:05:51.522Z","updated_at":"2026-02-20T16:05:52.824Z","avatar_url":"https://github.com/ido-pluto.png","language":"TypeScript","funding_links":["https://github.com/sponsors/ido-pluto"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003ch1\u003eiPull\u003c/h1\u003e\n    \u003cimg src=\"./assets/ipull-logo-rounded.png\" height=\"200px\" /\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Build](https://github.com/ido-pluto/ipull/actions/workflows/build.yml/badge.svg)](https://github.com/ido-pluto/ipull/actions/workflows/build.yml)\n[![License](https://badgen.net/badge/color/MIT/green?label=license)](https://www.npmjs.com/package/ipull)\n[![Types](https://badgen.net/badge/color/TypeScript/blue?label=types)](https://www.npmjs.com/package/ipull)\n[![npm downloads](https://img.shields.io/npm/dt/ipull.svg)](https://www.npmjs.com/package/ipull)\n[![Version](https://badgen.net/npm/v/ipull)](https://www.npmjs.com/package/ipull)\n\n\u003c/div\u003e\n\u003cbr /\u003e\n\n\u003e Super fast file downloader with multiple connections\n\n```bash\nnpx ipull http://example.com/file.large\n```\n\n![pull-example](./assets/pull-file.gif)\n\n## Features\n\n- Download using parallels connections\n- Maximize download speed (automatic parallelization, 3+)\n- Pausing and resuming downloads\n- Node.js and browser support\n- Smart retry on fail\n- CLI Progress bar\n- Download statistics (speed, time left, etc.)\n\n### NodeJS API\n\n```ts\nimport {downloadFile} from 'ipull';\n\nconst downloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    directory: './this/path', // or 'savePath' for full path\n    cliProgress: true, // Show progress bar in the CLI (default: false)\n    parallelStreams: 3 // Number of parallel connections (default: 3)\n});\n\nawait downloader.download();\n```\n\n## Browser support\n\nDownload a file in the browser using multiple connections\n\n```ts\nimport {downloadFileBrowser} from \"ipull/dist/browser.js\";\n\nconst downloader = await downloadFileBrowser({\n    url: 'https://example.com/file.large',\n    acceptRangeIsKnown: true, // overcome CORS, force multi-connection download (use only if you know the server supports range requests)\n    // defaultFetchDownloadInfo: { // set download info manually to overcome CORS issues \u0026\u0026 prevent multiple requests\n    //     acceptRange: true,\n    //     length: 40789822,\n    // }\n});\n\nawait downloader.download();\nimage.src = downloader.writeStream.resultAsBlobURL();\n\nconsole.log(downloader.writeStream.result); // Uint8Array\n```\n\n### Custom stream\n\nYou can use a custom stream\n\n```ts\nimport {downloadFileBrowser} from \"ipull/dist/browser.js\";\n\nconst downloader = await downloadFileBrowser({\n    url: 'https://example.com/file.large',\n    onWrite: (cursor: number, buffers: Uint8Array[], options) =\u003e {\n        const totalLength = buffers.reduce((acc, buffer) =\u003e acc + buffer.length, 0);\n        console.log(`Writing ${totalLength} bytes at cursor ${cursor}, with options: ${JSON.stringify(options)}`);\n    }\n});\n\nawait downloader.download();\nconsole.log(downloader.writeStream.result.length === 0); // true, because we write to a custom stream\n```\n\n## CLI\n\n```\nUsage: ipull [options] [files...]\n\nPull/copy files from a remote server/local directory\n\nArguments:\n  files                         Files to pull/copy\n\nOptions:\n  -s --save [path]              Save location (directory/file)\n  -c --connections [number]     Number of parallel connections (default: \"4\")\n  -p --program [type]           The download strategy (choices: \"stream\", \"chunks\")\n  -t --truncate-name            Truncate file names in the CLI status to make them appear shorter\n  -V, --version                 output the version number\n  -h, --help                    display help for command\n\nCommands:\n  set [options] [path] \u003cvalue\u003e  Set download locations\n\n```\n\n### Set custom save directory\n\nYou can set a custom save directory by using the `set` command.\n\n```bash\nipull set .zip ~/Downloads/zips\n```\n\n(use `default` to set the default save directory)\n\n## Advanced usage\n\n### Skip existing files\n\nSkip downloading files that already exist in the save location and have the same size.\n\n```ts\nimport {downloadFile} from 'ipull';\n\nconst downloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    directory: './this/path',\n    skipExisting: true\n});\n```\n\n### Download file from parts\n\nConsolidate multiple files parts into one file.\nBeneficial for downloading large files from servers that limit file size. (e.g. HuggingFace models)\n\n```ts\nimport {downloadFile} from 'ipull';\n\nconst downloadParts = [\n    \"https://example.com/file.large-part-1\",\n    \"https://example.com/file.large-part-2\",\n    \"https://example.com/file.large-part-3\",\n];\n\nconst downloader = await downloadFile({\n    partURLs: downloadParts,\n    directory: './this/path',\n    filename: 'file.large'\n});\n\nawait downloader.download();\n```\n\n** The split must be binary and not a zip-split\n\n### Custom headers\n\nYou can set custom headers for the download request\n\n```ts\nimport {downloadFile} from 'ipull';\n\nconst downloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    savePath: './this/path/file.large',\n    headers: {\n        'Authorization': 'Bearer token 1'\n    },\n    // You can also add alternative headers in case of an 400-499 error\n    tryHeaders: [\n        {\n            Authorization: 'Bearer token 2'\n        }\n    ]\n});\n\nawait downloader.download();\n```\n\n### Abort download\n\nYou can cancel the download by calling the `close` method (it will not delete the file).\n\nIf you want to also delete the file, you can call the `closeAndDeleteFile` method.\n\n```ts\nimport {downloadFile} from 'ipull';\n\nconst downloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    directory: './this/path'\n});\n\nsetTimeout(() =\u003e {\n    downloader.close();\n}, 5_000);\n\nawait downloader.download();\n```\n\n### Pause \u0026 Resume download\n\n```ts\nimport {downloadFile} from 'ipull';\n\nconst downloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    directory: './this/path'\n});\n\nsetInterval(() =\u003e {\n    downloader.pause();\n    setTimeout(() =\u003e {\n        downloader.resume();\n    }, 5_000);\n}, 10_000);\n\nawait downloader.download();\n```\n\n** The pause may take a few seconds to actually pause the download, because it waits for the current connections to\nfinish\n\n### Error handling\n\nIf a network/file-system error occurs, the download will automatically retry\nwith [async-retry](https://www.npmjs.com/package/async-retry)\n\nIf the maximum reties was reached the download will fail and an error will be thrown from the `download()` call:\n\n```ts\nimport {downloadFile} from 'ipull';\n\ntry {\n    const downloader = await downloadFile({\n        url: 'https://example.com/file.large',\n        directory: './this/path'\n    });\n\n    await downloader.download();\n} catch (error) {\n    console.error(`Download failed: ${error.message}`);\n}\n```\n\n### Download Stuck\n\nIn some edge cases, the re-try mechanism may give the illusion that the download is stuck.\n\n(You can see this in the progress object that \"retrying\" is true)\n\nTo debug this, disable the re-try mechanism:\n\n```js\nconst downloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    directory: './this/path',\n    retry: {\n        retries: 0\n    },\n    retryFetchDownloadInfo: {\n        retries: 0\n    }\n});\n```\n### Listening to events\n\nEvents are emitted using the `EventEmitter` pattern and can be listened to using the `on` method\n\n```ts\ninterface DownloadEngineEvents {\n    start: [];\n    paused: [];\n    resumed: [];\n    progress: [FormattedStatus];\n    save: [DownloadProgressInfo];\n    finished: [];\n    closed: [];\n}\n\nconst downloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    directory: './this/path'\n});\n\ndownloader.on(\"progress\", (progress) =\u003e {\n    console.log(`Downloaded ${progress.transferred} bytes`);\n});\n```\n\n### Remote Download Listing\n\nIf you want to show in the CLI the progress of a file downloading in remote.\n\n```ts\nconst originaldownloader = await downloadFile({\n    url: 'https://example.com/file.large',\n    directory: './this/path'\n});\n\nconst remoteDownloader = downloadFileRemote({\n    cliProgress: true\n});\n\noriginaldownloader.on(\"progress\", (progress) =\u003e {\n    remoteDownloader.emitRemoteProgress(progress);\n});\n\nawait originaldownloader.download();\n```\n\n### Download multiple files\n\nIf you want to download multiple files, you can use the `downloadSequence` function.\n\nBy default, it will download files one by one, but you can set the `parallel` option to download them in parallel.\nIt is better to download one file at a time if you are downloading from the same server (as it may limit the number of\nconnections).\n\n```ts\nimport {downloadFile, downloadSequence} from \"ipull\";\n\nconst downloader = await downloadSequence(\n    {\n        cliProgress: true,\n        // parallelDownloads: 2, download 2 files in parallel, default is 1\n    },\n    downloadFile({\n        url: \"https://example.com/file1.large\",\n        directory: \".\"\n    }),\n    downloadFile({\n        url: \"https://example.com/file2.large\",\n        directory: \".\"\n    }),\n);\n\nconsole.log(`Downloading ${downloader.downloads.length} files...`);\nawait downloader.download();\n```\n\n### Custom progress bar\n\n```ts\nimport {downloadFile, FormattedStatus} from \"ipull\";\n\nfunction createProgressBar({fileName, ...data}: FormattedStatus) {\n    return `${fileName} ${JSON.stringify(data)}`;\n}\n\nconst downloader = await downloadFile({\n    url: \"https://example.com/file.large\",\n    directory: \"./this/path\",\n    cliStyle: createProgressBar\n});\n\nawait downloader.download();\n```\n\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\" width=\"360\"\u003e\n    \u003cimg alt=\"Star please\" src=\"./assets/star-please.png\" width=\"360\" margin=\"auto\" /\u003e\n    \u003cbr/\u003e\n    \u003cp align=\"right\"\u003e\n        \u003ci\u003eIf you like this repo, star it ✨\u003c/i\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\n    \u003c/p\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fido-pluto%2Fipull","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fido-pluto%2Fipull","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fido-pluto%2Fipull/lists"}