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') {
|
||||
//if(target instanceof SVGSVGElement) {
|
||||
const el = target.tagName.toLowerCase() === tagName ? target : target.querySelector(tagName) as HTMLElement;
|
||||
if(el) {
|
||||
if(!target.classList.contains('document-ico') && findUpClassName(target, 'attachment')) {
|
||||
if(el && !findUpClassName(target, 'document')) {
|
||||
if(findUpClassName(target, 'attachment')) {
|
||||
// two parentElements because element can be contained in aspecter
|
||||
const preloader = target.parentElement.parentElement.querySelector('.preloader-container') as HTMLElement;
|
||||
if(preloader) {
|
||||
|
@ -12,7 +12,7 @@ import { toast } from "../toast";
|
||||
import { prepareAlbum, wrapDocument } from "../wrappers";
|
||||
import CheckboxField from "../checkboxField";
|
||||
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 I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
||||
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
||||
@ -23,6 +23,7 @@ import RichTextProcessor from "../../lib/richtextprocessor";
|
||||
import { MediaSize } from "../../helpers/mediaSizes";
|
||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
|
||||
import getGifDuration from "../../helpers/getGifDuration";
|
||||
|
||||
type SendFileParams = Partial<{
|
||||
file: File,
|
||||
@ -291,7 +292,27 @@ export default class PopupNewMedia extends PopupElement {
|
||||
params.height = img.naturalHeight;
|
||||
|
||||
itemDiv.append(img);
|
||||
resolve(itemDiv);
|
||||
|
||||
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);
|
||||
});
|
||||
} else {
|
||||
resolve(itemDiv);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -577,7 +577,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
||||
icoDiv.classList.add('document-ico');
|
||||
|
||||
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');
|
||||
|
||||
let imgs: HTMLImageElement[] = [];
|
||||
@ -593,7 +593,8 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
||||
boxHeight: 54,
|
||||
loadPromises,
|
||||
withoutPreloader: true,
|
||||
lazyLoadQueue
|
||||
lazyLoadQueue,
|
||||
size: appPhotosManager.choosePhotoSize(doc, 54, 54, true)
|
||||
});
|
||||
icoDiv.style.width = icoDiv.style.height = '';
|
||||
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 image: HTMLImageElement;
|
||||
let cacheContext: ThumbCache;
|
||||
const isGif = photo._ === 'document' && photo.mime_type === 'image/gif' && !size;
|
||||
// if(withTail) {
|
||||
// image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
||||
// } else {
|
||||
image = new Image();
|
||||
|
||||
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;
|
||||
isFit = set.isFit;
|
||||
cacheContext = appDownloadManager.getCacheContext(photo, size.type);
|
||||
@ -920,7 +928,7 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
||||
}
|
||||
|
||||
const getDownloadPromise = () => {
|
||||
const promise = photo._ === 'document' && photo.mime_type === 'image/gif' ?
|
||||
const promise = isGif && !size ?
|
||||
appDocsManager.downloadDoc(photo, /* undefined, */lazyLoadQueue?.queueId) :
|
||||
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 IS_WEBP_SUPPORTED from "./webpSupport";
|
||||
import IMAGE_MIME_TYPES_SUPPORTED from "./imageMimeTypesSupport";
|
||||
import VIDEO_MIME_TYPES_SUPPORTED from "./videoMimeTypesSupport";
|
||||
|
||||
const MEDIA_MIME_TYPES_SUPPORTED = new Set([
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/bmp',
|
||||
'video/mp4',
|
||||
'video/webm'
|
||||
]);
|
||||
const arr = [...IMAGE_MIME_TYPES_SUPPORTED].concat([...VIDEO_MIME_TYPES_SUPPORTED]);
|
||||
|
||||
if(IS_MOV_SUPPORTED) {
|
||||
MEDIA_MIME_TYPES_SUPPORTED.add('video/quicktime');
|
||||
}
|
||||
|
||||
if(IS_WEBP_SUPPORTED) {
|
||||
MEDIA_MIME_TYPES_SUPPORTED.add('image/webp');
|
||||
}
|
||||
const MEDIA_MIME_TYPES_SUPPORTED = new Set(arr);
|
||||
|
||||
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> {
|
||||
return new Promise((resolve, reject) => {
|
||||
video.onseeked = () => {
|
||||
video.onseeked = () => {
|
||||
scaleMediaElement({
|
||||
media: video,
|
||||
mediaSize: makeMediaSize(video.videoWidth, video.videoHeight),
|
||||
boxSize: makeMediaSize(320, 240),
|
||||
quality: .9
|
||||
}).then(resolve);
|
||||
createPosterFromMedia(video).then(resolve);
|
||||
|
||||
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 appMessagesIdsManager from "./appMessagesIdsManager";
|
||||
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');
|
||||
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
|
||||
@ -643,7 +645,7 @@ export class AppMessagesManager {
|
||||
const message = this.generateOutgoingMessage(peerId, options);
|
||||
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 fileName = file instanceof File ? file.name : '';
|
||||
@ -659,7 +661,7 @@ export class AppMessagesManager {
|
||||
|
||||
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;
|
||||
|
||||
@ -718,7 +720,7 @@ export class AppMessagesManager {
|
||||
cacheContext.url = options.objectURL || '';
|
||||
|
||||
photo = appPhotosManager.savePhoto(photo);
|
||||
} else if(fileType.indexOf('video/') === 0) {
|
||||
} else if(VIDEO_MIME_TYPES_SUPPORTED.has(fileType)) {
|
||||
attachType = 'video';
|
||||
apiFileName = 'video.mp4';
|
||||
actionName = 'sendMessageUploadVideoAction';
|
||||
@ -752,7 +754,7 @@ export class AppMessagesManager {
|
||||
|
||||
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[] = [];
|
||||
document = {
|
||||
_: 'document',
|
||||
@ -4972,7 +4974,7 @@ export class AppMessagesManager {
|
||||
} else if(newDoc) {
|
||||
const doc = appDocsManager.getDoc('' + tempId);
|
||||
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 oldCacheContext = appDownloadManager.getCacheContext(doc);
|
||||
Object.assign(cacheContext, oldCacheContext);
|
||||
|
@ -219,14 +219,19 @@ export class AppPhotosManager {
|
||||
return {image, loadPromise};
|
||||
}
|
||||
|
||||
public setAttachmentSize(photo: MyPhoto | MyDocument,
|
||||
public setAttachmentSize(
|
||||
photo: MyPhoto | MyDocument,
|
||||
element: HTMLElement | SVGForeignObjectElement,
|
||||
boxWidth: number,
|
||||
boxHeight: number,
|
||||
noZoom = true,
|
||||
message?: any,
|
||||
pushDocumentSize?: boolean) {
|
||||
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight, undefined, pushDocumentSize);
|
||||
pushDocumentSize?: boolean,
|
||||
photoSize?: ReturnType<AppPhotosManager['choosePhotoSize']>
|
||||
) {
|
||||
if(!photoSize) {
|
||||
photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight, undefined, pushDocumentSize);
|
||||
}
|
||||
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
||||
|
||||
let size: MediaSize;
|
||||
|
Loading…
Reference in New Issue
Block a user