Browse Source

Fix uploading .gif images

master
morethanwords 3 years ago
parent
commit
730d79523f
  1. 4
      src/components/appMediaViewerBase.ts
  2. 25
      src/components/popups/newMedia.ts
  3. 16
      src/components/wrappers.ts
  4. 13
      src/environment/imageMimeTypesSupport.ts
  5. 21
      src/environment/mediaMimeTypesSupport.ts
  6. 13
      src/environment/videoMimeTypesSupport.ts
  7. 25
      src/helpers/files.ts
  8. 31
      src/helpers/getGifDuration.ts
  9. 12
      src/lib/appManagers/appMessagesManager.ts
  10. 11
      src/lib/appManagers/appPhotosManager.ts

4
src/components/appMediaViewerBase.ts

@ -1066,8 +1066,8 @@ export default class AppMediaViewerBase< @@ -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) {

25
src/components/popups/newMedia.ts

@ -12,7 +12,7 @@ import { toast } from "../toast"; @@ -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"; @@ -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 { @@ -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);
}
};
}

16
src/components/wrappers.ts

@ -577,7 +577,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS @@ -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 @@ -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 @@ -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 @@ -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

@ -0,0 +1,13 @@ @@ -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;

21
src/environment/mediaMimeTypesSupport.ts

@ -1,21 +1,8 @@ @@ -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

@ -0,0 +1,13 @@ @@ -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;

25
src/helpers/files.ts

@ -38,16 +38,29 @@ export function preloadVideo(url: string): Promise<HTMLVideoElement> { @@ -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

@ -0,0 +1,31 @@ @@ -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;
});
}

12
src/lib/appManagers/appMessagesManager.ts

@ -59,6 +59,8 @@ import { getMiddleware } from "../../helpers/middleware"; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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);

11
src/lib/appManagers/appPhotosManager.ts

@ -219,14 +219,19 @@ export class AppPhotosManager { @@ -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…
Cancel
Save