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.
 
 
 
 
 

366 lines
10 KiB

import {RichTextProcessor} from '../richtextprocessor';
import { CancellablePromise, deferredPromise } from '../polyfill';
import { isObject, getFileURL, FileURLType } from '../utils';
import opusDecodeController from '../opusDecodeController';
import { MTDocument, inputDocumentFileLocation, MTPhotoSize } from '../../types';
import { getFileNameByLocation } from '../bin_utils';
import appDownloadManager, { Download, ResponseMethod, DownloadBlob } from './appDownloadManager';
import appPhotosManager from './appPhotosManager';
class AppDocsManager {
private docs: {[docID: string]: MTDocument} = {};
private downloadPromises: {[docID: string]: CancellablePromise<Blob>} = {};
public saveDoc(apiDoc: MTDocument, context?: any) {
//console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);
if(this.docs[apiDoc.id]) {
const d = this.docs[apiDoc.id];
if(apiDoc.thumbs) {
if(!d.thumbs) d.thumbs = apiDoc.thumbs;
/* else if(apiDoc.thumbs[0].bytes && !d.thumbs[0].bytes) {
d.thumbs.unshift(apiDoc.thumbs[0]);
} else if(d.thumbs[0].url) { // fix for converted thumb in safari
apiDoc.thumbs[0] = d.thumbs[0];
} */
}
d.file_reference = apiDoc.file_reference;
return d;
//return Object.assign(d, apiDoc, context);
//return context ? Object.assign(d, context) : d;
}
if(context) {
Object.assign(apiDoc, context);
}
this.docs[apiDoc.id] = apiDoc;
apiDoc.attributes.forEach((attribute: any) => {
switch(attribute._) {
case 'documentAttributeFilename':
apiDoc.file_name = RichTextProcessor.wrapPlainText(attribute.file_name);
break;
case 'documentAttributeAudio':
apiDoc.duration = attribute.duration;
apiDoc.audioTitle = attribute.title;
apiDoc.audioPerformer = attribute.performer;
apiDoc.type = attribute.pFlags.voice && apiDoc.mime_type == "audio/ogg" ? 'voice' : 'audio';
/* if(apiDoc.type == 'audio') {
apiDoc.supportsStreaming = true;
} */
break;
case 'documentAttributeVideo':
apiDoc.duration = attribute.duration;
apiDoc.w = attribute.w;
apiDoc.h = attribute.h;
//apiDoc.supportsStreaming = attribute.pFlags?.supports_streaming/* && apiDoc.size > 524288 */;
if(/* apiDoc.thumbs && */attribute.pFlags.round_message) {
apiDoc.type = 'round';
} else /* if(apiDoc.thumbs) */ {
apiDoc.type = 'video';
}
break;
case 'documentAttributeSticker':
if(attribute.alt !== undefined) {
apiDoc.stickerEmojiRaw = attribute.alt;
apiDoc.stickerEmoji = RichTextProcessor.wrapRichText(apiDoc.stickerEmojiRaw, {noLinks: true, noLinebreaks: true});
}
if(attribute.stickerset) {
if(attribute.stickerset._ == 'inputStickerSetEmpty') {
delete attribute.stickerset;
} else if(attribute.stickerset._ == 'inputStickerSetID') {
apiDoc.stickerSetInput = attribute.stickerset;
}
}
if(/* apiDoc.thumbs && */apiDoc.mime_type == 'image/webp') {
apiDoc.type = 'sticker';
apiDoc.sticker = 1;
}
break;
case 'documentAttributeImageSize':
apiDoc.w = attribute.w;
apiDoc.h = attribute.h;
break;
case 'documentAttributeAnimated':
if((apiDoc.mime_type == 'image/gif' || apiDoc.mime_type == 'video/mp4')/* && apiDoc.thumbs */) {
apiDoc.type = 'gif';
}
apiDoc.animated = true;
break;
}
});
if(!apiDoc.mime_type) {
switch(apiDoc.type) {
case 'gif':
apiDoc.mime_type = 'video/mp4';
break;
case 'video':
case 'round':
apiDoc.mime_type = 'video/mp4';
break;
case 'sticker':
apiDoc.mime_type = 'image/webp';
break;
case 'audio':
apiDoc.mime_type = 'audio/mpeg';
break;
case 'voice':
apiDoc.mime_type = 'audio/ogg';
break;
default:
apiDoc.mime_type = 'application/octet-stream';
break;
}
}
if((apiDoc.type == 'gif' && apiDoc.size > 8e6) || apiDoc.type == 'audio' || apiDoc.type == 'video') {
apiDoc.supportsStreaming = true;
}
if(!apiDoc.file_name) {
apiDoc.file_name = '';
}
if(apiDoc.mime_type == 'application/x-tgsticker' && apiDoc.file_name == "AnimatedSticker.tgs") {
apiDoc.type = 'sticker';
apiDoc.animated = true;
apiDoc.sticker = 2;
}
if(apiDoc._ == 'documentEmpty') {
apiDoc.size = 0;
}
if(!apiDoc.url) {
apiDoc.url = this.getFileURL(apiDoc);
}
return apiDoc;
}
public getDoc(docID: string | MTDocument): MTDocument {
return isObject(docID) && typeof(docID) !== 'string' ? docID : this.docs[docID as string];
}
public getMediaInput(doc: MTDocument) {
return {
_: 'inputMediaDocument',
flags: 0,
id: {
_: 'inputDocument',
id: doc.id,
access_hash: doc.access_hash,
file_reference: doc.file_reference
},
ttl_seconds: 0
};
}
public getInput(doc: MTDocument, thumbSize?: string): inputDocumentFileLocation {
return {
_: 'inputDocumentFileLocation',
id: doc.id,
access_hash: doc.access_hash,
file_reference: doc.file_reference,
thumb_size: thumbSize
};
}
public getFileURL(doc: MTDocument, download = false, thumb?: MTPhotoSize) {
const inputFileLocation = this.getInput(doc, thumb?.type);
let type: FileURLType;
if(download) {
type = 'download';
} else if(thumb) {
type = 'thumb';
} else if(doc.supportsStreaming) {
type = 'stream';
} else {
type = 'document';
}
let mimeType: string;
if(thumb) {
mimeType = doc.sticker ? 'image/webp' : 'image/jpeg'/* doc.mime_type */;
} else {
mimeType = doc.mime_type || 'application/octet-stream';
}
return getFileURL(type, {
dcID: doc.dc_id,
location: inputFileLocation,
size: thumb ? thumb.size : doc.size,
mimeType: mimeType,
fileName: doc.file_name
});
}
public getThumbURL(doc: MTDocument, useBytes = true) {
if(doc.thumbs?.length) {
let thumb: MTPhotoSize;
if(!useBytes) {
thumb = doc.thumbs.find(t => !t.bytes);
}
if(!thumb) {
thumb = doc.thumbs[0];
}
if(thumb.bytes) {
return appPhotosManager.getPreviewURLFromBytes(doc.thumbs[0].bytes, !!doc.sticker);
} else {
return this.getFileURL(doc, false, thumb);
}
}
return '';
}
public getInputFileName(doc: MTDocument, thumbSize?: string) {
return getFileNameByLocation(this.getInput(doc, thumbSize), {fileName: doc.file_name});
}
public downloadDoc(docID: string | MTDocument, toFileEntry?: any): CancellablePromise<Blob> {
const doc = this.getDoc(docID);
if(doc._ == 'documentEmpty') {
return Promise.reject();
}
if(doc.downloaded && !toFileEntry) {
if(doc.url) return Promise.resolve(null);
/* const cachedBlob = apiFileManager.getCachedFile(inputFileLocation);
if(cachedBlob) {
return Promise.resolve(cachedBlob);
} */
}
if(this.downloadPromises[doc.id]) {
return this.downloadPromises[doc.id];
}
const deferred = deferredPromise<Blob>();
const url = this.getFileURL(doc);
fetch(url).then(res => res.blob())
/* downloadPromise */.then((blob) => {
if(blob) {
doc.downloaded = true;
if(doc.type == 'voice' && !opusDecodeController.isPlaySupported()/* && false */) {
let reader = new FileReader();
reader.onloadend = (e) => {
let uint8 = new Uint8Array(e.target.result as ArrayBuffer);
//console.log('sending uint8 to decoder:', uint8);
opusDecodeController.decode(uint8).then(result => {
doc.url = result.url;
deferred.resolve(blob);
}, (err) => {
delete doc.downloaded;
deferred.reject(err);
});
};
reader.readAsArrayBuffer(blob);
return;
} else if(doc.type && doc.sticker != 2) {
doc.url = url;
}
}
deferred.resolve(blob);
}, (e) => {
console.log('document download failed', e);
deferred.reject(e);
}).finally(() => {
//deferred.notify = downloadPromise.notify = deferred.cancel = downloadPromise.cancel = null;
});
return this.downloadPromises[doc.id] = deferred;
}
public downloadDocNew(docID: string | MTDocument/* , method: ResponseMethod = 'blob' */): DownloadBlob {
const doc = this.getDoc(docID);
if(doc._ == 'documentEmpty') {
throw new Error('Document empty!');
}
const fileName = this.getInputFileName(doc);
let download: DownloadBlob = appDownloadManager.getDownload(fileName);
if(download) {
return download;
}
download = appDownloadManager.download(doc.url, fileName/* , method */);
const originalPromise = download;
originalPromise.then((blob) => {
doc.downloaded = true;
if(!doc.supportsStreaming) {
doc.url = URL.createObjectURL(blob);
}
});
if(doc.type == 'voice' && !opusDecodeController.isPlaySupported()) {
download = originalPromise.then(async(blob) => {
let reader = new FileReader();
await new Promise((resolve, reject) => {
reader.onloadend = (e) => {
let uint8 = new Uint8Array(e.target.result as ArrayBuffer);
//console.log('sending uint8 to decoder:', uint8);
opusDecodeController.decode(uint8).then(result => {
doc.url = result.url;
resolve();
}, (err) => {
delete doc.downloaded;
reject(err);
});
};
reader.readAsArrayBuffer(blob);
});
return blob;
//return originalPromise;
//return new Response(blob);
});
}
return download;
}
public saveDocFile(doc: MTDocument) {
const url = this.getFileURL(doc, true);
const fileName = this.getInputFileName(doc);
return appDownloadManager.downloadToDisc(fileName, url, doc.file_name);
}
}
const appDocsManager = new AppDocsManager();
// @ts-ignore
if(process.env.NODE_ENV != 'production') {
(window as any).appDocsManager = appDocsManager;
}
export default appDocsManager;