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.
163 lines
4.5 KiB
163 lines
4.5 KiB
2 years ago
|
/*
|
||
|
* 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();
|
||
|
}
|
||
|
}
|
||
|
}
|