Fix uploading .gif images
This commit is contained in:
parent
c141f24054
commit
730d79523f
@ -1066,8 +1066,8 @@ export default class AppMediaViewerBase<
|
|||||||
protected updateMediaSource(target: HTMLElement, url: string, tagName: 'video' | 'img') {
|
protected updateMediaSource(target: HTMLElement, url: string, tagName: 'video' | 'img') {
|
||||||
//if(target instanceof SVGSVGElement) {
|
//if(target instanceof SVGSVGElement) {
|
||||||
const el = target.tagName.toLowerCase() === tagName ? target : target.querySelector(tagName) as HTMLElement;
|
const el = target.tagName.toLowerCase() === tagName ? target : target.querySelector(tagName) as HTMLElement;
|
||||||
if(el) {
|
if(el && !findUpClassName(target, 'document')) {
|
||||||
if(!target.classList.contains('document-ico') && findUpClassName(target, 'attachment')) {
|
if(findUpClassName(target, 'attachment')) {
|
||||||
// two parentElements because element can be contained in aspecter
|
// two parentElements because element can be contained in aspecter
|
||||||
const preloader = target.parentElement.parentElement.querySelector('.preloader-container') as HTMLElement;
|
const preloader = target.parentElement.parentElement.querySelector('.preloader-container') as HTMLElement;
|
||||||
if(preloader) {
|
if(preloader) {
|
||||||
|
@ -12,7 +12,7 @@ import { toast } from "../toast";
|
|||||||
import { prepareAlbum, wrapDocument } from "../wrappers";
|
import { prepareAlbum, wrapDocument } from "../wrappers";
|
||||||
import CheckboxField from "../checkboxField";
|
import CheckboxField from "../checkboxField";
|
||||||
import SendContextMenu from "../chat/sendContextMenu";
|
import SendContextMenu from "../chat/sendContextMenu";
|
||||||
import { createPosterFromVideo, onMediaLoad } from "../../helpers/files";
|
import { createPosterFromMedia, createPosterFromVideo, onMediaLoad } from "../../helpers/files";
|
||||||
import { MyDocument } from "../../lib/appManagers/appDocsManager";
|
import { MyDocument } from "../../lib/appManagers/appDocsManager";
|
||||||
import I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
import I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
||||||
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
||||||
@ -23,6 +23,7 @@ import RichTextProcessor from "../../lib/richtextprocessor";
|
|||||||
import { MediaSize } from "../../helpers/mediaSizes";
|
import { MediaSize } from "../../helpers/mediaSizes";
|
||||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||||
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
|
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
|
||||||
|
import getGifDuration from "../../helpers/getGifDuration";
|
||||||
|
|
||||||
type SendFileParams = Partial<{
|
type SendFileParams = Partial<{
|
||||||
file: File,
|
file: File,
|
||||||
@ -291,7 +292,27 @@ export default class PopupNewMedia extends PopupElement {
|
|||||||
params.height = img.naturalHeight;
|
params.height = img.naturalHeight;
|
||||||
|
|
||||||
itemDiv.append(img);
|
itemDiv.append(img);
|
||||||
|
|
||||||
|
if(file.type === 'image/gif') {
|
||||||
|
params.noSound = true;
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
getGifDuration(img).then(duration => {
|
||||||
|
params.duration = Math.ceil(duration);
|
||||||
|
}),
|
||||||
|
|
||||||
|
createPosterFromMedia(img).then(thumb => {
|
||||||
|
params.thumb = {
|
||||||
|
url: URL.createObjectURL(thumb.blob),
|
||||||
|
...thumb
|
||||||
|
};
|
||||||
|
})
|
||||||
|
]).then(() => {
|
||||||
resolve(itemDiv);
|
resolve(itemDiv);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve(itemDiv);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,7 +577,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
|||||||
icoDiv.classList.add('document-ico');
|
icoDiv.classList.add('document-ico');
|
||||||
|
|
||||||
const cacheContext = appDownloadManager.getCacheContext(doc);
|
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
if((doc.thumbs?.length || (message.pFlags.is_outgoing && cacheContext.url && doc.type === 'photo')) && doc.mime_type !== 'image/gif') {
|
if((doc.thumbs?.length || (message.pFlags.is_outgoing && cacheContext.url && doc.type === 'photo'))/* && doc.mime_type !== 'image/gif' */) {
|
||||||
docDiv.classList.add('document-with-thumb');
|
docDiv.classList.add('document-with-thumb');
|
||||||
|
|
||||||
let imgs: HTMLImageElement[] = [];
|
let imgs: HTMLImageElement[] = [];
|
||||||
@ -593,7 +593,8 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
|||||||
boxHeight: 54,
|
boxHeight: 54,
|
||||||
loadPromises,
|
loadPromises,
|
||||||
withoutPreloader: true,
|
withoutPreloader: true,
|
||||||
lazyLoadQueue
|
lazyLoadQueue,
|
||||||
|
size: appPhotosManager.choosePhotoSize(doc, 54, 54, true)
|
||||||
});
|
});
|
||||||
icoDiv.style.width = icoDiv.style.height = '';
|
icoDiv.style.width = icoDiv.style.height = '';
|
||||||
if(wrapped.images.thumb) imgs.push(wrapped.images.thumb);
|
if(wrapped.images.thumb) imgs.push(wrapped.images.thumb);
|
||||||
@ -832,13 +833,20 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
let thumbImage: HTMLImageElement;
|
let thumbImage: HTMLImageElement;
|
||||||
let image: HTMLImageElement;
|
let image: HTMLImageElement;
|
||||||
let cacheContext: ThumbCache;
|
let cacheContext: ThumbCache;
|
||||||
|
const isGif = photo._ === 'document' && photo.mime_type === 'image/gif' && !size;
|
||||||
// if(withTail) {
|
// if(withTail) {
|
||||||
// image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
// image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
||||||
// } else {
|
// } else {
|
||||||
image = new Image();
|
image = new Image();
|
||||||
|
|
||||||
if(boxWidth && boxHeight && !size) { // !album
|
if(boxWidth && boxHeight && !size) { // !album
|
||||||
const set = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message);
|
const set = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, undefined, message, undefined, isGif ? {
|
||||||
|
_: 'photoSize',
|
||||||
|
w: photo.w,
|
||||||
|
h: photo.h,
|
||||||
|
size: photo.size,
|
||||||
|
type: 'full'
|
||||||
|
} : undefined);
|
||||||
size = set.photoSize;
|
size = set.photoSize;
|
||||||
isFit = set.isFit;
|
isFit = set.isFit;
|
||||||
cacheContext = appDownloadManager.getCacheContext(photo, size.type);
|
cacheContext = appDownloadManager.getCacheContext(photo, size.type);
|
||||||
@ -920,7 +928,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getDownloadPromise = () => {
|
const getDownloadPromise = () => {
|
||||||
const promise = photo._ === 'document' && photo.mime_type === 'image/gif' ?
|
const promise = isGif && !size ?
|
||||||
appDocsManager.downloadDoc(photo, /* undefined, */lazyLoadQueue?.queueId) :
|
appDocsManager.downloadDoc(photo, /* undefined, */lazyLoadQueue?.queueId) :
|
||||||
appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId, noAutoDownload);
|
appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId, noAutoDownload);
|
||||||
|
|
||||||
|
13
src/environment/imageMimeTypesSupport.ts
Normal file
13
src/environment/imageMimeTypesSupport.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import IS_WEBP_SUPPORTED from "./webpSupport";
|
||||||
|
|
||||||
|
const IMAGE_MIME_TYPES_SUPPORTED = new Set([
|
||||||
|
'image/jpeg',
|
||||||
|
'image/png',
|
||||||
|
'image/bmp'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if(IS_WEBP_SUPPORTED) {
|
||||||
|
IMAGE_MIME_TYPES_SUPPORTED.add('image/webp');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IMAGE_MIME_TYPES_SUPPORTED;
|
@ -1,21 +1,8 @@
|
|||||||
import IS_MOV_SUPPORTED from "./movSupport";
|
import IMAGE_MIME_TYPES_SUPPORTED from "./imageMimeTypesSupport";
|
||||||
import IS_WEBP_SUPPORTED from "./webpSupport";
|
import VIDEO_MIME_TYPES_SUPPORTED from "./videoMimeTypesSupport";
|
||||||
|
|
||||||
const MEDIA_MIME_TYPES_SUPPORTED = new Set([
|
const arr = [...IMAGE_MIME_TYPES_SUPPORTED].concat([...VIDEO_MIME_TYPES_SUPPORTED]);
|
||||||
'image/jpeg',
|
|
||||||
'image/png',
|
|
||||||
'image/gif',
|
|
||||||
'image/bmp',
|
|
||||||
'video/mp4',
|
|
||||||
'video/webm'
|
|
||||||
]);
|
|
||||||
|
|
||||||
if(IS_MOV_SUPPORTED) {
|
const MEDIA_MIME_TYPES_SUPPORTED = new Set(arr);
|
||||||
MEDIA_MIME_TYPES_SUPPORTED.add('video/quicktime');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(IS_WEBP_SUPPORTED) {
|
|
||||||
MEDIA_MIME_TYPES_SUPPORTED.add('image/webp');
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MEDIA_MIME_TYPES_SUPPORTED;
|
export default MEDIA_MIME_TYPES_SUPPORTED;
|
||||||
|
13
src/environment/videoMimeTypesSupport.ts
Normal file
13
src/environment/videoMimeTypesSupport.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import IS_MOV_SUPPORTED from "./movSupport";
|
||||||
|
|
||||||
|
const VIDEO_MIME_TYPES_SUPPORTED = new Set([
|
||||||
|
'image/gif', // have to display it as video
|
||||||
|
'video/mp4',
|
||||||
|
'video/webm'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if(IS_MOV_SUPPORTED) {
|
||||||
|
VIDEO_MIME_TYPES_SUPPORTED.add('video/quicktime');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VIDEO_MIME_TYPES_SUPPORTED;
|
@ -38,16 +38,29 @@ export function preloadVideo(url: string): Promise<HTMLVideoElement> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createPosterFromMedia(media: HTMLVideoElement | HTMLImageElement) {
|
||||||
|
let width: number, height: number;
|
||||||
|
if(media instanceof HTMLVideoElement) {
|
||||||
|
width = media.videoWidth;
|
||||||
|
height = media.videoHeight;
|
||||||
|
} else {
|
||||||
|
width = media.naturalWidth;
|
||||||
|
height = media.naturalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scaleMediaElement({
|
||||||
|
media,
|
||||||
|
mediaSize: makeMediaSize(width, height),
|
||||||
|
boxSize: makeMediaSize(320, 240),
|
||||||
|
quality: .9
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function createPosterFromVideo(video: HTMLVideoElement): ReturnType<typeof scaleMediaElement> {
|
export function createPosterFromVideo(video: HTMLVideoElement): ReturnType<typeof scaleMediaElement> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
video.onseeked = () => {
|
video.onseeked = () => {
|
||||||
video.onseeked = () => {
|
video.onseeked = () => {
|
||||||
scaleMediaElement({
|
createPosterFromMedia(video).then(resolve);
|
||||||
media: video,
|
|
||||||
mediaSize: makeMediaSize(video.videoWidth, video.videoHeight),
|
|
||||||
boxSize: makeMediaSize(320, 240),
|
|
||||||
quality: .9
|
|
||||||
}).then(resolve);
|
|
||||||
|
|
||||||
video.onseeked = undefined;
|
video.onseeked = undefined;
|
||||||
};
|
};
|
||||||
|
31
src/helpers/getGifDuration.ts
Normal file
31
src/helpers/getGifDuration.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* @returns duration in ms
|
||||||
|
*/
|
||||||
|
export default function getGifDuration(image: HTMLImageElement) {
|
||||||
|
const src = image.src;
|
||||||
|
|
||||||
|
return fetch(src)
|
||||||
|
.then(response => response.arrayBuffer())
|
||||||
|
.then(arrayBuffer => {
|
||||||
|
const d = new Uint8Array(arrayBuffer);
|
||||||
|
// Thanks to http://justinsomnia.org/2006/10/gif-animation-duration-calculation/
|
||||||
|
// And http://www.w3.org/Graphics/GIF/spec-gif89a.txt
|
||||||
|
let duration = 0;
|
||||||
|
for(let i = 0, length = d.length; i < length; ++i) {
|
||||||
|
// Find a Graphic Control Extension hex(21F904__ ____ __00)
|
||||||
|
if(d[i] == 0x21
|
||||||
|
&& d[i + 1] == 0xF9
|
||||||
|
&& d[i + 2] == 0x04
|
||||||
|
&& d[i + 7] == 0x00) {
|
||||||
|
// Swap 5th and 6th bytes to get the delay per frame
|
||||||
|
const delay = (d[i + 5] << 8) | (d[i + 4] & 0xFF);
|
||||||
|
|
||||||
|
// Should be aware browsers have a minimum frame delay
|
||||||
|
// e.g. 6ms for IE, 2ms modern browsers (50fps)
|
||||||
|
duration += delay < 2 ? 10 : delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return duration / 1000;
|
||||||
|
});
|
||||||
|
}
|
@ -59,6 +59,8 @@ import { getMiddleware } from "../../helpers/middleware";
|
|||||||
import assumeType from "../../helpers/assumeType";
|
import assumeType from "../../helpers/assumeType";
|
||||||
import appMessagesIdsManager from "./appMessagesIdsManager";
|
import appMessagesIdsManager from "./appMessagesIdsManager";
|
||||||
import type { MediaSize } from "../../helpers/mediaSizes";
|
import type { MediaSize } from "../../helpers/mediaSizes";
|
||||||
|
import IMAGE_MIME_TYPES_SUPPORTED from "../../environment/imageMimeTypesSupport";
|
||||||
|
import VIDEO_MIME_TYPES_SUPPORTED from "../../environment/videoMimeTypesSupport";
|
||||||
|
|
||||||
//console.trace('include');
|
//console.trace('include');
|
||||||
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
|
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
|
||||||
@ -643,7 +645,7 @@ export class AppMessagesManager {
|
|||||||
const message = this.generateOutgoingMessage(peerId, options);
|
const message = this.generateOutgoingMessage(peerId, options);
|
||||||
const replyToMsgId = options.replyToMsgId ? appMessagesIdsManager.getServerMessageId(options.replyToMsgId) : undefined;
|
const replyToMsgId = options.replyToMsgId ? appMessagesIdsManager.getServerMessageId(options.replyToMsgId) : undefined;
|
||||||
|
|
||||||
let attachType: string, apiFileName: string;
|
let attachType: 'document' | 'audio' | 'video' | 'voice' | 'photo', apiFileName: string;
|
||||||
|
|
||||||
const fileType = 'mime_type' in file ? file.mime_type : file.type;
|
const fileType = 'mime_type' in file ? file.mime_type : file.type;
|
||||||
const fileName = file instanceof File ? file.name : '';
|
const fileName = file instanceof File ? file.name : '';
|
||||||
@ -659,7 +661,7 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
const attributes: DocumentAttribute[] = [];
|
const attributes: DocumentAttribute[] = [];
|
||||||
|
|
||||||
const isPhoto = ['image/jpeg', 'image/png', 'image/bmp'].indexOf(fileType) >= 0;
|
const isPhoto = IMAGE_MIME_TYPES_SUPPORTED.has(fileType);
|
||||||
|
|
||||||
let photo: MyPhoto, document: MyDocument;
|
let photo: MyPhoto, document: MyDocument;
|
||||||
|
|
||||||
@ -718,7 +720,7 @@ export class AppMessagesManager {
|
|||||||
cacheContext.url = options.objectURL || '';
|
cacheContext.url = options.objectURL || '';
|
||||||
|
|
||||||
photo = appPhotosManager.savePhoto(photo);
|
photo = appPhotosManager.savePhoto(photo);
|
||||||
} else if(fileType.indexOf('video/') === 0) {
|
} else if(VIDEO_MIME_TYPES_SUPPORTED.has(fileType)) {
|
||||||
attachType = 'video';
|
attachType = 'video';
|
||||||
apiFileName = 'video.mp4';
|
apiFileName = 'video.mp4';
|
||||||
actionName = 'sendMessageUploadVideoAction';
|
actionName = 'sendMessageUploadVideoAction';
|
||||||
@ -752,7 +754,7 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
attributes.push({_: 'documentAttributeFilename', file_name: fileName || apiFileName});
|
attributes.push({_: 'documentAttributeFilename', file_name: fileName || apiFileName});
|
||||||
|
|
||||||
if(['document', 'video', 'audio', 'voice'].indexOf(attachType) !== -1 && !isDocument) {
|
if((['document', 'video', 'audio', 'voice'] as (typeof attachType)[]).indexOf(attachType) !== -1 && !isDocument) {
|
||||||
const thumbs: PhotoSize[] = [];
|
const thumbs: PhotoSize[] = [];
|
||||||
document = {
|
document = {
|
||||||
_: 'document',
|
_: 'document',
|
||||||
@ -4972,7 +4974,7 @@ export class AppMessagesManager {
|
|||||||
} else if(newDoc) {
|
} else if(newDoc) {
|
||||||
const doc = appDocsManager.getDoc('' + tempId);
|
const doc = appDocsManager.getDoc('' + tempId);
|
||||||
if(doc) {
|
if(doc) {
|
||||||
if(/* doc._ !== 'documentEmpty' && */doc.type && doc.type !== 'sticker') {
|
if(/* doc._ !== 'documentEmpty' && */doc.type && doc.type !== 'sticker' && doc.mime_type !== 'image/gif') {
|
||||||
const cacheContext = appDownloadManager.getCacheContext(newDoc);
|
const cacheContext = appDownloadManager.getCacheContext(newDoc);
|
||||||
const oldCacheContext = appDownloadManager.getCacheContext(doc);
|
const oldCacheContext = appDownloadManager.getCacheContext(doc);
|
||||||
Object.assign(cacheContext, oldCacheContext);
|
Object.assign(cacheContext, oldCacheContext);
|
||||||
|
@ -219,14 +219,19 @@ export class AppPhotosManager {
|
|||||||
return {image, loadPromise};
|
return {image, loadPromise};
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAttachmentSize(photo: MyPhoto | MyDocument,
|
public setAttachmentSize(
|
||||||
|
photo: MyPhoto | MyDocument,
|
||||||
element: HTMLElement | SVGForeignObjectElement,
|
element: HTMLElement | SVGForeignObjectElement,
|
||||||
boxWidth: number,
|
boxWidth: number,
|
||||||
boxHeight: number,
|
boxHeight: number,
|
||||||
noZoom = true,
|
noZoom = true,
|
||||||
message?: any,
|
message?: any,
|
||||||
pushDocumentSize?: boolean) {
|
pushDocumentSize?: boolean,
|
||||||
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight, undefined, pushDocumentSize);
|
photoSize?: ReturnType<AppPhotosManager['choosePhotoSize']>
|
||||||
|
) {
|
||||||
|
if(!photoSize) {
|
||||||
|
photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight, undefined, pushDocumentSize);
|
||||||
|
}
|
||||||
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
||||||
|
|
||||||
let size: MediaSize;
|
let size: MediaSize;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user