Last active
March 10, 2023 00:08
-
-
Save crazy4groovy/6dceb9e0ae76ae2f988fa0fe7b707d6e to your computer and use it in GitHub Desktop.
a throttled file downloader (Deno)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Timeout, TimeoutError } from "https://deno.land/x/timeout/mod.ts"; | |
const delay = (ms) => new Promise((res) => setTimeout(res, ms)); | |
function newThrottler({ isBusy, lock, unlock, waitMs, size }) { | |
async function throttler(cb, ...args) { | |
size(1); | |
await Promise.resolve(); | |
while (!!isBusy()) { | |
await delay(waitMs()); // waits in event loop queue, until it interrupts for another attempt! | |
} | |
lock(); | |
// ... DO ALL WORK for result | |
const result = await cb.call(this, ...args); | |
unlock(); | |
size(-1); | |
return result; | |
} | |
throttler.size = () => size(); // read-only | |
throttler.isBusy = isBusy; | |
return throttler; | |
} | |
const throttler = (threads: number) => newThrottler((function(){ | |
let size = 0; | |
let semaphore = 0; | |
return { | |
isBusy: () => (semaphore >= threads), | |
lock: () => (semaphore += 1), | |
unlock: () => (semaphore -= 1), | |
waitMs: () => 1000 + (1000 * Math.random()), | |
size: (n) => n ? (size += n) : size, | |
}; | |
})()); | |
export const createDownloadThrottled = | |
(threads: number) => { | |
const thr = throttler(threads); | |
function dl(...args) { thr(download, ...args); }; | |
Object.entries(thr).forEach(([k, v]) => { dl[k] = v; }); | |
return dl; | |
} | |
// Eg. const dl = createDownloadThrottled(4); | |
// dl('https://a.com/1.jpg', '~/imgs/1.jpg', myHeaders); | |
// console.log(dl.size()); | |
// console.log(dl.isBusy()); | |
export async function download( | |
source: string, | |
destination: string, | |
headers: any = {}, | |
): Promise<boolean> { | |
try { | |
const req = fetch(source, { headers }); | |
const response = await Timeout.race([req], headers.timeout || 9000); | |
const blob = await response.blob(); | |
const data = new Uint8Array(await blob.arrayBuffer()); | |
const file = await Deno.create(destination); | |
await Deno.writeAll(file, data); | |
Deno.close(file.rid); | |
return true; | |
} catch(err) { | |
if (err instanceof TimeoutError) { | |
console.error(`ERROR Timed out; skipping: ${source}`); | |
return false; | |
} | |
console.error("ERROR while dl'ing:", err.message); | |
console.error("---RETRYING..."); | |
await delay(1000); | |
return download(source, destination, headers); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment