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.

379 lines
12 KiB

5 years ago
import appUsersManager from "./appUsersManager";
import { calcImageInBox, isObject } from "../utils";
5 years ago
import fileManager from '../filemanager';
import { bytesFromHex } from "../bin_utils";
import apiFileManager from "../mtproto/apiFileManager";
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import { MTPhotoSize } from "../../types";
export type MTPhoto = {
_: 'photo' | 'photoEmpty' | string,
5 years ago
pFlags: any,
flags: number,
id: string,
access_hash: string,
file_reference: Uint8Array,
date: number,
sizes: Array<MTPhotoSize>,
dc_id: number,
5 years ago
user_id: number,
downloaded?: boolean | number,
url?: string
5 years ago
};
export class AppPhotosManager {
private photos: {
[id: string]: MTPhoto
} = {};
private documentThumbsCache: {
[docID: string]: {
downloaded: number,
url: string
}
} = {};
public windowW = 0;
public windowH = 0;
5 years ago
public static jf = new Uint8Array(bytesFromHex('ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00'));
public static Df = bytesFromHex('ffd9');
constructor() {
window.addEventListener('resize', (e) => {
this.windowW = document.body.scrollWidth;
this.windowH = document.body.scrollHeight;
5 years ago
});
this.windowW = document.body.scrollWidth;
this.windowH = document.body.scrollHeight;
5 years ago
}
5 years ago
public savePhoto(photo: MTPhoto, context?: any) {
4 years ago
if(this.photos[photo.id]) return Object.assign(this.photos[photo.id], photo);
5 years ago
/* if(context) {
Object.assign(photo, context);
} */ // warning
5 years ago
5 years ago
if(!photo.id) {
console.warn('no apiPhoto.id', photo);
} else this.photos[photo.id] = photo as any;
5 years ago
/* if(!('sizes' in photo)) return;
5 years ago
5 years ago
photo.sizes.forEach((photoSize: any) => {
5 years ago
if(photoSize._ == 'photoCachedSize') {
apiFileManager.saveSmallFile(photoSize.location, photoSize.bytes);
5 years ago
5 years ago
console.log('clearing photo cached size', photo);
5 years ago
// Memory
photoSize.size = photoSize.bytes.length;
delete photoSize.bytes;
photoSize._ = 'photoSize';
}
}); */
5 years ago
/* if(!photo.downloaded) {
photo.downloaded = apiFileManager.isFileExists({
_: 'inputPhotoFileLocation',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference
});
// apiFileManager.isFileExists({
// _: 'inputPhotoFileLocation',
// id: photo.id,
// access_hash: photo.access_hash,
// file_reference: photo.file_reference
// }).then(downloaded => {
// photo.downloaded = downloaded;
// });
} */
return photo;
5 years ago
}
public choosePhotoSize(photo: any, width = 0, height = 0) {
//if(Config.Navigator.retina) {
if(window.devicePixelRatio > 1) {
5 years ago
width *= 2;
height *= 2;
}
5 years ago
/*
s box 100x100
m box 320x320
x box 800x800
y box 1280x1280
w box 2560x2560
a crop 160x160
b crop 320x320
c crop 640x640
d crop 1280x1280 */
5 years ago
let bestPhotoSize: MTPhotoSize = {_: 'photoSizeEmpty'};
let sizes = (photo.sizes || photo.thumbs) as typeof bestPhotoSize[];
if(sizes) {
for(let photoSize of sizes) {
if(!photoSize.w || !photoSize.h) continue;
bestPhotoSize = photoSize;
let {w, h} = calcImageInBox(photoSize.w, photoSize.h, width, height);
if(w == width || h == height) {
break;
}
}
}
5 years ago
return bestPhotoSize;
}
public getUserPhotos(userID: number, maxID: number, limit: number) {
var inputUser = appUsersManager.getUserInput(userID);
return apiManager.invokeApi('photos.getUserPhotos', {
5 years ago
user_id: inputUser,
offset: 0,
limit: limit || 20,
max_id: maxID || 0
}).then((photosResult: any) => {
appUsersManager.saveApiUsers(photosResult.users);
var photoIDs = [];
var context = {user_id: userID};
for(var i = 0; i < photosResult.photos.length; i++) {
//this.savePhoto(photosResult.photos[i], context);
photosResult.photos[i] = this.savePhoto(photosResult.photos[i], context);
5 years ago
photoIDs.push(photosResult.photos[i].id);
}
return {
count: photosResult.count || photosResult.photos.length,
photos: photoIDs
};
});
}
public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
5 years ago
let arr: Uint8Array;
if(!isSticker) {
arr = AppPhotosManager.jf.concat(bytes.slice(3), AppPhotosManager.Df);
arr[164] = bytes[1];
arr[166] = bytes[2];
} else {
arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
5 years ago
}
//console.log('getPreviewURLFromBytes', bytes, arr, div, isSticker);
/* let reader = new FileReader();
reader.onloadend = () => {
let src = reader.result;
};
reader.readAsDataURL(blob); */
5 years ago
let blob = new Blob([arr], {type: "image/jpeg"});
return URL.createObjectURL(blob);
}
public getPreviewURLFromThumb(thumb: any, isSticker = false) {
return thumb.url ?? (thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
}
public setAttachmentPreview(bytes: Uint8Array | number[], element: HTMLElement | SVGForeignObjectElement, isSticker = false, background = false) {
let url = this.getPreviewURLFromBytes(bytes, isSticker);
5 years ago
if(background) {
5 years ago
let img = new Image();
img.src = url;
img.addEventListener('load', () => {
5 years ago
element.style.backgroundImage = 'url(' + url + ')';
});
5 years ago
return element;
5 years ago
} else {
if(element instanceof HTMLImageElement) {
element.src = url;
return element;
} else {
let img = new Image();
img.src = url;
element.append(img);
return img;
}
5 years ago
}
}
public setAttachmentSize(photoID: any, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, isSticker = false) {
5 years ago
let photo: /* MTDocument | MTPhoto */any = null;
5 years ago
if(typeof(photoID) === 'string') {
photo = this.photos[photoID];
if(!photo) return {_: 'photoEmpty'};
} else {
photo = photoID;
}
let photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
5 years ago
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
let sizes = photo.sizes || photo.thumbs;
if(!photo.downloaded && !isSticker && sizes && sizes[0].bytes) {
this.setAttachmentPreview(sizes[0].bytes, element, isSticker);
5 years ago
}
let width: number;
let height: number;
if(photo._ == 'document') {
width = photo.w || 512;
height = photo.h || 512;
5 years ago
} else {
width = photoSize.w || 100;
height = photoSize.h || 100;
}
let {w, h} = calcImageInBox(width, height, boxWidth, boxHeight);
if(element instanceof SVGForeignObjectElement) {
element.setAttributeNS(null, 'width', '' + w);
element.setAttributeNS(null, 'height', '' + h);
//console.log('set dimensions to svg element:', element, w, h);
} else {
element.style.width = w + 'px';
element.style.height = h + 'px';
5 years ago
}
return photoSize;
}
public preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise<Blob | void> {
let photo = this.getPhoto(photoID);
5 years ago
if(!photoSize) {
let fullWidth = this.windowW;
let fullHeight = this.windowH;
5 years ago
photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
}
5 years ago
let isDocument = photo._ == 'document';
let cacheContext = isDocument ? (this.documentThumbsCache[photo.id] ?? (this.documentThumbsCache[photo.id] = {downloaded: -1, url: ''})) : photo;
if(cacheContext.downloaded >= photoSize.size && cacheContext.url) {
return Promise.resolve();
}
5 years ago
if(!photoSize || photoSize._ == 'photoSizeEmpty') {
console.error('no photoSize by photo:', photo);
return Promise.reject('no photoSize');
}
// maybe it's a thumb
let isPhoto = photoSize.size && photo.access_hash && photo.file_reference;
let location = isPhoto ? {
_: isDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference,
thumb_size: photoSize.type
} : photoSize.location;
let promise: Promise<Blob>;
if(isPhoto/* && photoSize.size >= 1e6 */) {
//console.log('Photos downloadFile exec', photo);
promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size);
} else {
//console.log('Photos downloadSmallFile exec', photo, location);
promise = apiFileManager.downloadSmallFile(location);
}
5 years ago
promise.then(blob => {
if(!cacheContext.downloaded || cacheContext.downloaded < blob.size) {
cacheContext.downloaded = blob.size;
cacheContext.url = URL.createObjectURL(blob);
//console.log('wrote photo:', photo, photoSize, cacheContext, blob);
}
});
5 years ago
return promise;
5 years ago
}
public getDocumentCachedThumb(docID: string) {
return this.documentThumbsCache[docID];
}
5 years ago
public getPhoto(photoID: any): MTPhoto {
return isObject(photoID) ? photoID : this.photos[photoID];
5 years ago
}
5 years ago
public getInputByID(photoID: any) {
let photo = this.getPhoto(photoID);
return {
_: 'inputMediaPhoto',
flags: 0,
id: {
_: 'inputPhoto',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference
},
ttl_seconds: 0
};
}
5 years ago
public downloadPhoto(photoID: string) {
var photo = this.photos[photoID];
var ext = 'jpg';
var mimeType = 'image/jpeg';
var fileName = 'photo' + photoID + '.' + ext;
var fullWidth = this.windowW;
var fullHeight = this.windowH;
5 years ago
var fullPhotoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);
var inputFileLocation = {
// @ts-ignore
_: photo._ == 'document' ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference,
thumb_size: fullPhotoSize.type
5 years ago
};
try { // photo.dc_id, location, photoSize.size
let writer = fileManager.chooseSaveFile(fileName, ext, mimeType, fullPhotoSize.size);
writer.ready.then(() => {
console.log('ready');
apiFileManager.downloadFile(photo.dc_id, inputFileLocation, fullPhotoSize.size, {
5 years ago
mimeType: mimeType,
toFileEntry: writer
5 years ago
}).then(() => {
writer.close();
//writer.abort();
console.log('file save done', fileName, ext, mimeType, writer);
5 years ago
}, (e: any) => {
console.log('photo download failed', e);
});
});
} catch(err) {
console.error('err', err);
var cachedBlob = apiFileManager.getCachedFile(inputFileLocation)
5 years ago
if (cachedBlob) {
return fileManager.download(cachedBlob, mimeType, fileName);
}
apiFileManager.downloadFile(photo.dc_id, inputFileLocation, fullPhotoSize.size, {mimeType: mimeType})
5 years ago
.then((blob: Blob) => {
fileManager.download(blob, mimeType, fileName);
}, (e: any) => {
console.log('photo download failed', e);
});
}
5 years ago
}
}
export default new AppPhotosManager();