Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.5 KiB
162 lines
4.5 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import type {ServiceDownloadTaskPayload} from './serviceMessagePort'; |
|
import type ServiceMessagePort from './serviceMessagePort'; |
|
import deferredPromise, {CancellablePromise} from '../../helpers/cancellablePromise'; |
|
import makeError from '../../helpers/makeError'; |
|
import pause from '../../helpers/schedulers/pause'; |
|
|
|
type DownloadType = Uint8Array; |
|
type DownloadItem = ServiceDownloadTaskPayload & { |
|
// transformStream: TransformStream<DownloadType, DownloadType>, |
|
readableStream: ReadableStream<DownloadType>, |
|
// writableStream: WritableStream<DownloadType>, |
|
// writer: WritableStreamDefaultWriter<DownloadType>, |
|
// controller: TransformStreamDefaultController<DownloadType>, |
|
controller: ReadableStreamController<Uint8Array>, |
|
promise: CancellablePromise<void>, |
|
// downloadPromise: Promise<void>, |
|
used?: boolean |
|
}; |
|
const downloadMap: Map<string, DownloadItem> = new Map(); |
|
const DOWNLOAD_ERROR = makeError('UNKNOWN'); |
|
const DOWNLOAD_TEST = false; |
|
|
|
type A = Parameters<ServiceMessagePort<false>['addMultipleEventsListeners']>[0]; |
|
|
|
const events: A = { |
|
download: (payload) => { |
|
const {id} = payload; |
|
if(downloadMap.has(id)) { |
|
return Promise.reject(DOWNLOAD_ERROR); |
|
} |
|
|
|
// const y = (20 * 1024 * 1024) / payload.limitPart; |
|
// const strategy = new ByteLengthQueuingStrategy({highWaterMark: y}); |
|
// let controller: TransformStreamDefaultController<DownloadType>; |
|
const strategy = new CountQueuingStrategy({highWaterMark: 1}); |
|
// const transformStream = new TransformStream<DownloadType, DownloadType>(/* { |
|
// start: (_controller) => controller = _controller, |
|
// }, */undefined, strategy, strategy); |
|
|
|
// const {readable, writable} = transformStream; |
|
// const writer = writable.getWriter(); |
|
|
|
const promise = deferredPromise<void>(); |
|
promise.then(() => { |
|
setTimeout(() => { |
|
downloadMap.delete(id); |
|
}, 5e3); |
|
}, () => { |
|
downloadMap.delete(id); |
|
}); |
|
|
|
// writer.closed.then(promise.resolve, promise.reject); |
|
|
|
let controller: ReadableStreamController<any>; |
|
const readable = new ReadableStream({ |
|
start: (_controller) => { |
|
controller = _controller; |
|
}, |
|
|
|
cancel: (reason) => { |
|
promise.reject(DOWNLOAD_ERROR); |
|
} |
|
}, strategy); |
|
|
|
// writer.closed.catch(noop).finally(() => { |
|
// log.error('closed writer'); |
|
// onEnd(); |
|
// }); |
|
|
|
// const downloadPromise = writer.closed.catch(() => {throw DOWNLOAD_ERROR;}); |
|
const item: DownloadItem = { |
|
...payload, |
|
// transformStream, |
|
readableStream: readable, |
|
// writableStream: writable, |
|
// writer, |
|
// downloadPromise, |
|
promise, |
|
controller |
|
}; |
|
|
|
downloadMap.set(id, item); |
|
|
|
// return downloadPromise; |
|
return promise.catch(() => {throw DOWNLOAD_ERROR}); |
|
}, |
|
|
|
downloadChunk: ({id, chunk}) => { |
|
const item = downloadMap.get(id); |
|
if(!item) { |
|
return Promise.reject(); |
|
} |
|
|
|
// return item.controller.enqueue(chunk); |
|
// return item.writer.write(chunk); |
|
return item.controller.enqueue(chunk); |
|
}, |
|
|
|
downloadFinalize: (id) => { |
|
const item = downloadMap.get(id); |
|
if(!item) { |
|
return Promise.reject(); |
|
} |
|
|
|
item.promise.resolve(); |
|
// return item.controller.terminate(); |
|
// return item.writer.close(); |
|
return item.controller.close(); |
|
}, |
|
|
|
downloadCancel: (id) => { |
|
const item = downloadMap.get(id); |
|
if(!item) { |
|
return; |
|
} |
|
|
|
item.promise.reject(); |
|
// return item.controller.error(); |
|
// return item.writer.abort(); |
|
return item.controller.error(); |
|
} |
|
}; |
|
|
|
export default function handleDownload(serviceMessagePort: ServiceMessagePort<false>) { |
|
serviceMessagePort.addMultipleEventsListeners(events); |
|
|
|
return { |
|
onDownloadFetch, |
|
onClosedWindows: cancelAllDownloads |
|
}; |
|
} |
|
|
|
function onDownloadFetch(event: FetchEvent, params: string) { |
|
event.respondWith(pause(100).then(() => { |
|
const item = downloadMap.get(params); |
|
if(!item || (item.used && !DOWNLOAD_TEST)) { |
|
return; |
|
} |
|
|
|
item.used = true; |
|
const stream = item.readableStream; |
|
const response = new Response(stream, {headers: item.headers}); |
|
return response; |
|
})); |
|
|
|
// event.respondWith(response); |
|
} |
|
|
|
function cancelAllDownloads() { |
|
if(downloadMap.size) { |
|
for(const [id, item] of downloadMap) { |
|
// item.writer.abort().catch(noop); |
|
item.controller.error(); |
|
} |
|
} |
|
}
|
|
|