Message: Support web page videos; Always play videos inline (#1233)

This commit is contained in:
Alexander Zinchuk 2021-07-06 19:12:42 +03:00
parent f15d616e20
commit 5facc1e705
18 changed files with 166 additions and 92 deletions

View File

@ -27,7 +27,7 @@ import { DELETED_COMMENTS_CHANNEL_ID, LOCAL_MESSAGE_ID_BASE, SERVICE_NOTIFICATIO
import { pick } from '../../../util/iteratees'; import { pick } from '../../../util/iteratees';
import { getApiChatIdFromMtpPeer } from './chats'; import { getApiChatIdFromMtpPeer } from './chats';
import { buildStickerFromDocument } from './symbols'; import { buildStickerFromDocument } from './symbols';
import { buildApiPhoto, buildApiThumbnailFromStripped, buildApiPhotoSize } from './common'; import { buildApiPhoto, buildApiThumbnailFromStripped } from './common';
import { interpolateArray } from '../../../util/waveform'; import { interpolateArray } from '../../../util/waveform';
import { getCurrencySign } from '../../../components/middle/helpers/getCurrencySign'; import { getCurrencySign } from '../../../components/middle/helpers/getCurrencySign';
import { buildPeer } from '../gramjsBuilders'; import { buildPeer } from '../gramjsBuilders';
@ -511,26 +511,25 @@ export function buildWebPage(media: GramJs.TypeMessageMedia): ApiWebPage | undef
const { id, photo, document } = media.webpage; const { id, photo, document } = media.webpage;
let video;
if (document instanceof GramJs.Document && document.mimeType.startsWith('video')) {
video = buildVideoFromDocument(document);
}
return { return {
id: Number(id), id: Number(id),
...pick(media.webpage, [ ...pick(media.webpage, [
'url', 'url',
'displayUrl', 'displayUrl',
'type',
'siteName', 'siteName',
'title', 'title',
'description', 'description',
'duration',
]), ]),
photo: photo && photo instanceof GramJs.Photo photo: photo instanceof GramJs.Photo ? buildApiPhoto(photo) : undefined,
? { document: !video && document ? buildApiDocument(document) : undefined,
id: String(photo.id), video,
thumbnail: buildApiThumbnailFromStripped(photo.sizes),
sizes: photo.sizes
.filter((s: any): s is GramJs.PhotoSize => s instanceof GramJs.PhotoSize)
.map(buildApiPhotoSize),
}
: undefined,
// TODO support video and embed
...(document && { hasDocument: true }),
}; };
} }

View File

@ -292,7 +292,12 @@ export function isMessageWithMedia(message: GramJs.Message | GramJs.UpdateServic
|| ( || (
media instanceof GramJs.MessageMediaWebPage media instanceof GramJs.MessageMediaWebPage
&& media.webpage instanceof GramJs.WebPage && media.webpage instanceof GramJs.WebPage
&& media.webpage.photo instanceof GramJs.Photo && (
media.webpage.photo instanceof GramJs.Photo || (
media.webpage.document instanceof GramJs.Document
&& media.webpage.document.mimeType.startsWith('video')
)
)
) )
); );
} }

View File

@ -22,6 +22,15 @@ export function addMessageToLocalDb(message: GramJs.Message | GramJs.MessageServ
localDb.documents[String(message.media.document.id)] = message.media.document; localDb.documents[String(message.media.document.id)] = message.media.document;
} }
if (
message instanceof GramJs.Message
&& message.media instanceof GramJs.MessageMediaWebPage
&& message.media.webpage instanceof GramJs.WebPage
&& message.media.webpage.document instanceof GramJs.Document
) {
localDb.documents[String(message.media.webpage.document.id)] = message.media.webpage.document;
}
if (message instanceof GramJs.MessageService && 'photo' in message.action) { if (message instanceof GramJs.MessageService && 'photo' in message.action) {
addPhotoToLocalDb(message.action.photo); addPhotoToLocalDb(message.action.photo);
} }

View File

@ -145,6 +145,11 @@ async function download(
if (entity.media instanceof GramJs.MessageMediaDocument && entity.media.document instanceof GramJs.Document) { if (entity.media instanceof GramJs.MessageMediaDocument && entity.media.document instanceof GramJs.Document) {
fullSize = entity.media.document.size; fullSize = entity.media.document.size;
} }
if (entity.media instanceof GramJs.MessageMediaWebPage
&& entity.media.webpage instanceof GramJs.WebPage
&& entity.media.webpage.document instanceof GramJs.Document) {
fullSize = entity.media.webpage.document.size;
}
} else if (entity instanceof GramJs.Photo) { } else if (entity instanceof GramJs.Photo) {
mimeType = 'image/jpeg'; mimeType = 'image/jpeg';
} else if (entityType === 'sticker' && sizeType) { } else if (entityType === 'sticker' && sizeType) {
@ -187,6 +192,12 @@ function getMessageMediaMimeType(message: GramJs.Message, sizeType?: string) {
return message.media.document!.mimeType; return message.media.document!.mimeType;
} }
if (message.media instanceof GramJs.MessageMediaWebPage
&& message.media.webpage instanceof GramJs.WebPage
&& message.media.webpage.document instanceof GramJs.Document) {
return message.media.webpage.document.mimeType;
}
return undefined; return undefined;
} }

View File

@ -152,11 +152,14 @@ export interface ApiWebPage {
id: number; id: number;
url: string; url: string;
displayUrl: string; displayUrl: string;
type?: string;
siteName?: string; siteName?: string;
title?: string; title?: string;
description?: string; description?: string;
photo?: ApiPhoto; photo?: ApiPhoto;
hasDocument?: true; duration?: number;
document?: ApiDocument;
video?: ApiVideo;
} }
export interface ApiMessageForwardInfo { export interface ApiMessageForwardInfo {

View File

@ -9,7 +9,7 @@
margin-top: 1.5rem; margin-top: 1.5rem;
} }
&.without-photo::before { &.without-media::before {
content: attr(data-initial); content: attr(data-initial);
width: 3rem; width: 3rem;
height: 3rem; height: 3rem;
@ -86,7 +86,7 @@
padding: .25rem 3.75rem 0 0; padding: .25rem 3.75rem 0 0;
.Media, .Media,
&.without-photo::before { &.without-media::before {
left: auto; left: auto;
right: 0; right: 0;
} }

View File

@ -57,13 +57,14 @@ const WebLink: FC<OwnProps> = ({ message, senderTitle, onMessageClick }) => {
title, title,
description, description,
photo, photo,
video,
} = linkData; } = linkData;
const truncatedDescription = !senderTitle && trimText(description, MAX_TEXT_LENGTH); const truncatedDescription = !senderTitle && trimText(description, MAX_TEXT_LENGTH);
const className = buildClassName( const className = buildClassName(
'WebLink scroll-item', 'WebLink scroll-item',
!photo && 'without-photo', (!photo && !video) && 'without-media',
); );
return ( return (

View File

@ -37,6 +37,7 @@ import {
getMessagePhoto, getMessagePhoto,
getMessageVideo, getMessageVideo,
getMessageWebPagePhoto, getMessageWebPagePhoto,
getMessageWebPageVideo,
getPhotoFullDimensions, getPhotoFullDimensions,
getVideoDimensions, getVideoDimensions,
IDimensions, IDimensions,
@ -107,12 +108,15 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
const animationKey = useRef<number>(null); const animationKey = useRef<number>(null);
const isOpen = Boolean(avatarOwner || messageId); const isOpen = Boolean(avatarOwner || messageId);
const webPagePhoto = message ? getMessageWebPagePhoto(message) : undefined; const webPagePhoto = message ? getMessageWebPagePhoto(message) : undefined;
const webPageVideo = message ? getMessageWebPageVideo(message) : undefined;
const photo = message ? getMessagePhoto(message) : undefined; const photo = message ? getMessagePhoto(message) : undefined;
const video = message ? getMessageVideo(message) : undefined; const video = message ? getMessageVideo(message) : undefined;
const isWebPagePhoto = Boolean(webPagePhoto); const isWebPagePhoto = Boolean(webPagePhoto);
const isPhoto = Boolean(photo || webPagePhoto); const isWebPageVideo = Boolean(webPageVideo);
const isVideo = Boolean(video); const messageVideo = video || webPageVideo;
const isGif = video ? video.isGif : undefined; const isVideo = Boolean(messageVideo);
const isPhoto = Boolean(!isVideo && (photo || webPagePhoto));
const isGif = (messageVideo) ? messageVideo.isGif : undefined;
const isFromSharedMedia = origin === MediaViewerOrigin.SharedMedia; const isFromSharedMedia = origin === MediaViewerOrigin.SharedMedia;
const isFromSearch = origin === MediaViewerOrigin.SearchResult; const isFromSearch = origin === MediaViewerOrigin.SearchResult;
const slideAnimation = animationLevel >= 1 ? 'mv-slide' : 'none'; const slideAnimation = animationLevel >= 1 ? 'mv-slide' : 'none';
@ -129,10 +133,10 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
const [isFooterHidden, setIsFooterHidden] = useState<boolean>(false); const [isFooterHidden, setIsFooterHidden] = useState<boolean>(false);
const messageIds = useMemo(() => { const messageIds = useMemo(() => {
return isWebPagePhoto && messageId return (isWebPagePhoto || isWebPageVideo) && messageId
? [messageId] ? [messageId]
: getChatMediaMessageIds(chatMessages || {}, collectionIds || [], isFromSharedMedia); : getChatMediaMessageIds(chatMessages || {}, collectionIds || [], isFromSharedMedia);
}, [isWebPagePhoto, messageId, chatMessages, collectionIds, isFromSharedMedia]); }, [isWebPagePhoto, isWebPageVideo, messageId, chatMessages, collectionIds, isFromSharedMedia]);
const selectedMediaMessageIndex = messageId ? messageIds.indexOf(messageId) : -1; const selectedMediaMessageIndex = messageId ? messageIds.indexOf(messageId) : -1;
const isFirst = selectedMediaMessageIndex === 0 || selectedMediaMessageIndex === -1; const isFirst = selectedMediaMessageIndex === 0 || selectedMediaMessageIndex === -1;
@ -185,9 +189,11 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
} }
const photoDimensions = isPhoto ? getPhotoFullDimensions(( const photoDimensions = isPhoto ? getPhotoFullDimensions((
isWebPagePhoto ? getMessageWebPagePhoto(message!) : getMessagePhoto(message!) isWebPagePhoto ? webPagePhoto : photo
)!) : undefined;
const videoDimensions = isVideo ? getVideoDimensions((
isWebPageVideo ? webPageVideo : video
)!) : undefined; )!) : undefined;
const videoDimensions = isVideo ? getVideoDimensions(getMessageVideo(message!)!) : undefined;
useEffect(() => { useEffect(() => {
if (!IS_SINGLE_COLUMN_LAYOUT) { if (!IS_SINGLE_COLUMN_LAYOUT) {
@ -436,7 +442,7 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
posterData={bestImageData} posterData={bestImageData}
posterSize={message && calculateMediaViewerDimensions(videoDimensions!, hasFooter, true)} posterSize={message && calculateMediaViewerDimensions(videoDimensions!, hasFooter, true)}
downloadProgress={downloadProgress} downloadProgress={downloadProgress}
fileSize={video!.size} fileSize={messageVideo!.size}
isMediaViewerOpen={isOpen} isMediaViewerOpen={isOpen}
noPlay={!isActive} noPlay={!isActive}
onClose={close} onClose={close}

View File

@ -2,7 +2,13 @@ import { ApiMessage } from '../../../api/types';
import { MediaViewerOrigin } from '../../../types'; import { MediaViewerOrigin } from '../../../types';
import { ANIMATION_END_DELAY } from '../../../config'; import { ANIMATION_END_DELAY } from '../../../config';
import { getMessageContent, getPhotoFullDimensions, getVideoDimensions } from '../../../modules/helpers'; import {
getMessageContent,
getMessageWebPagePhoto,
getMessageWebPageVideo,
getPhotoFullDimensions,
getVideoDimensions,
} from '../../../modules/helpers';
import { import {
AVATAR_FULL_DIMENSIONS, AVATAR_FULL_DIMENSIONS,
calculateDimensions, calculateDimensions,
@ -28,9 +34,13 @@ export function animateOpening(
let isVideo = false; let isVideo = false;
let mediaSize; let mediaSize;
if (message) { if (message) {
const { photo, video, webPage } = getMessageContent(message); const { photo, video } = getMessageContent(message);
isVideo = Boolean(video); const webPagePhoto = getMessageWebPagePhoto(message);
mediaSize = video ? getVideoDimensions(video)! : getPhotoFullDimensions((photo || webPage!.photo)!)!; const webPageVideo = getMessageWebPageVideo(message);
isVideo = Boolean(video || webPageVideo);
mediaSize = isVideo
? getVideoDimensions((video || webPageVideo)!)!
: getPhotoFullDimensions((photo || webPagePhoto)!)!;
} else { } else {
mediaSize = AVATAR_FULL_DIMENSIONS; mediaSize = AVATAR_FULL_DIMENSIONS;
} }

View File

@ -39,6 +39,10 @@
bottom: .05rem; bottom: .05rem;
} }
&.with-video .media-inner { // TODO add support for video in previews in composer
display: none;
}
.site-title, .site-title,
.site-description { .site-description {
flex: 1; flex: 1;

View File

@ -667,6 +667,8 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
observeIntersection={observeIntersectionForMedia} observeIntersection={observeIntersectionForMedia}
noAvatars={noAvatars} noAvatars={noAvatars}
shouldAutoLoad={shouldAutoLoadMedia} shouldAutoLoad={shouldAutoLoadMedia}
shouldAutoPlay={shouldAutoPlayMedia}
lastSyncTime={lastSyncTime}
onMediaClick={handleMediaClick} onMediaClick={handleMediaClick}
onCancelMediaTransfer={handleCancelUpload} onCancelMediaTransfer={handleCancelUpload}
/> />

View File

@ -9,10 +9,11 @@ import { formatMediaDuration } from '../../../util/dateFormat';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { calculateVideoDimensions } from '../../common/helpers/mediaDimensions'; import { calculateVideoDimensions } from '../../common/helpers/mediaDimensions';
import { import {
canMessagePlayVideoInline,
getMediaTransferState, getMediaTransferState,
getMessageMediaFormat, getMessageMediaFormat,
getMessageMediaHash, getMessageMediaHash,
getMessageVideo,
getMessageWebPageVideo,
isForwardedMessage, isForwardedMessage,
isOwnMessage, isOwnMessage,
} from '../../../modules/helpers'; } from '../../../modules/helpers';
@ -62,9 +63,8 @@ const Video: FC<OwnProps> = ({
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const videoRef = useRef<HTMLVideoElement>(null); const videoRef = useRef<HTMLVideoElement>(null);
const video = message.content.video!; const video = (getMessageVideo(message) || getMessageWebPageVideo(message))!;
const localBlobUrl = video.blobUrl; const localBlobUrl = video.blobUrl;
const canPlayInline = Boolean(localBlobUrl) || canMessagePlayVideoInline(video);
const isIntersecting = useIsIntersecting(ref, observeIntersection); const isIntersecting = useIsIntersecting(ref, observeIntersection);
@ -87,13 +87,13 @@ const Video: FC<OwnProps> = ({
); );
const fullMediaData = localBlobUrl || mediaData; const fullMediaData = localBlobUrl || mediaData;
const isInline = Boolean(canPlayInline && isIntersecting && fullMediaData); const isInline = Boolean(isIntersecting && fullMediaData);
const { isBuffered, bufferingHandlers } = useBuffering(!shouldAutoLoad); const { isBuffered, bufferingHandlers } = useBuffering(!shouldAutoLoad);
const { isUploading, isTransferring, transferProgress } = getMediaTransferState( const { isUploading, isTransferring, transferProgress } = getMediaTransferState(
message, message,
uploadProgress || downloadProgress, uploadProgress || downloadProgress,
shouldDownload && (canPlayInline && !isBuffered), shouldDownload && !isBuffered,
); );
const wasDownloadDisabled = usePrevious(isDownloadAllowed) === false; const wasDownloadDisabled = usePrevious(isDownloadAllowed) === false;
const { const {
@ -107,6 +107,8 @@ const Video: FC<OwnProps> = ({
setPlayProgress(Math.max(0, e.currentTarget.currentTime - 1)); setPlayProgress(Math.max(0, e.currentTarget.currentTime - 1));
}, []); }, []);
const duration = video.duration || (videoRef.current && videoRef.current.duration) || 0;
const isOwn = isOwnMessage(message); const isOwn = isOwnMessage(message);
const isForwarded = isForwardedMessage(message); const isForwarded = isForwardedMessage(message);
const { width, height } = dimensions || calculateVideoDimensions(video, isOwn, isForwarded, noAvatars); const { width, height } = dimensions || calculateVideoDimensions(video, isOwn, isForwarded, noAvatars);
@ -122,15 +124,15 @@ const Video: FC<OwnProps> = ({
if (onCancelUpload) { if (onCancelUpload) {
onCancelUpload(message); onCancelUpload(message);
} }
} else if (canPlayInline && !fullMediaData) { } else if (!fullMediaData) {
setIsDownloadAllowed((isAllowed) => !isAllowed); setIsDownloadAllowed((isAllowed) => !isAllowed);
} else if (canPlayInline && fullMediaData && !isPlayAllowed) { } else if (fullMediaData && !isPlayAllowed) {
setIsPlayAllowed(true); setIsPlayAllowed(true);
videoRef.current!.play(); videoRef.current!.play();
} else if (onClick) { } else if (onClick) {
onClick(message.id); onClick(message.id);
} }
}, [isUploading, canPlayInline, fullMediaData, isPlayAllowed, onClick, onCancelUpload, message]); }, [isUploading, fullMediaData, isPlayAllowed, onClick, onCancelUpload, message]);
const className = buildClassName('media-inner dark', !isUploading && 'interactive'); const className = buildClassName('media-inner dark', !isUploading && 'interactive');
const videoClassName = buildClassName('full-media', transitionClassNames); const videoClassName = buildClassName('full-media', transitionClassNames);
@ -140,9 +142,8 @@ const Video: FC<OwnProps> = ({
: ''; : '';
const shouldRenderInlineVideo = isInline; const shouldRenderInlineVideo = isInline;
const shouldRenderHqPreview = !canPlayInline && mediaData; const shouldRenderPlayButton = (isDownloadAllowed && !isPlayAllowed && !shouldRenderSpinner);
const shouldRenderPlayButton = !canPlayInline || (isDownloadAllowed && !isPlayAllowed && !shouldRenderSpinner); const shouldRenderDownloadButton = !isDownloadAllowed;
const shouldRenderDownloadButton = canPlayInline && !isDownloadAllowed;
return ( return (
<div <div
@ -189,15 +190,6 @@ const Video: FC<OwnProps> = ({
<source src={fullMediaData} /> <source src={fullMediaData} />
</video> </video>
)} )}
{shouldRenderHqPreview && (
<img
src={mediaData}
className={`full-media ${transitionClassNames}`}
width={width}
height={height}
alt=""
/>
)}
{shouldRenderPlayButton && ( {shouldRenderPlayButton && (
<i className="icon-large-play" /> <i className="icon-large-play" />
)} )}
@ -209,13 +201,11 @@ const Video: FC<OwnProps> = ({
{shouldRenderDownloadButton && ( {shouldRenderDownloadButton && (
<i className="icon-download" /> <i className="icon-download" />
)} )}
{isTransferring && !canPlayInline ? ( {isTransferring ? (
<span className="message-upload-progress">{Math.round(transferProgress * 100)}%</span>
) : isTransferring && canPlayInline ? (
<span className="message-upload-progress">...</span> <span className="message-upload-progress">...</span>
) : ( ) : (
<div className="message-media-duration"> <div className="message-media-duration">
{video.isGif ? 'GIF' : formatMediaDuration(video.duration - playProgress)} {video.isGif ? 'GIF' : formatMediaDuration(Math.max(duration - playProgress, 0))}
</div> </div>
)} )}
</div> </div>

View File

@ -46,6 +46,11 @@
} }
} }
&.with-video .media-inner{
margin-top: 0.5rem !important;
margin-bottom: 1rem !important;
}
&.with-square-photo { &.with-square-photo {
display: flex; display: flex;
margin-bottom: 1rem; margin-bottom: 1rem;

View File

@ -11,6 +11,7 @@ import buildClassName from '../../../util/buildClassName';
import SafeLink from '../../common/SafeLink'; import SafeLink from '../../common/SafeLink';
import Photo from './Photo'; import Photo from './Photo';
import Video from './Video';
import './WebPage.scss'; import './WebPage.scss';
@ -21,7 +22,9 @@ type OwnProps = {
observeIntersection?: ObserveFn; observeIntersection?: ObserveFn;
noAvatars?: boolean; noAvatars?: boolean;
shouldAutoLoad?: boolean; shouldAutoLoad?: boolean;
shouldAutoPlay?: boolean;
inPreview?: boolean; inPreview?: boolean;
lastSyncTime?: number;
onMediaClick?: () => void; onMediaClick?: () => void;
onCancelMediaTransfer?: () => void; onCancelMediaTransfer?: () => void;
}; };
@ -31,7 +34,9 @@ const WebPage: FC<OwnProps> = ({
observeIntersection, observeIntersection,
noAvatars, noAvatars,
shouldAutoLoad, shouldAutoLoad,
shouldAutoPlay,
inPreview, inPreview,
lastSyncTime,
onMediaClick, onMediaClick,
onCancelMediaTransfer, onCancelMediaTransfer,
}) => { }) => {
@ -58,16 +63,16 @@ const WebPage: FC<OwnProps> = ({
title, title,
description, description,
photo, photo,
video,
} = webPage; } = webPage;
const isMediaInteractive = (photo || video) && onMediaClick && !isSquarePhoto;
const isMediaInteractive = photo && onMediaClick && !isSquarePhoto && !webPage.hasDocument;
const truncatedDescription = trimText(description, MAX_TEXT_LENGTH); const truncatedDescription = trimText(description, MAX_TEXT_LENGTH);
const className = buildClassName( const className = buildClassName(
'WebPage', 'WebPage',
photo isSquarePhoto && 'with-square-photo',
? (isSquarePhoto && 'with-square-photo') !photo && !video && !inPreview && 'without-media',
: (!inPreview && 'without-photo'), video && 'with-video',
); );
return ( return (
@ -76,7 +81,7 @@ const WebPage: FC<OwnProps> = ({
data-initial={(siteName || displayUrl)[0]} data-initial={(siteName || displayUrl)[0]}
dir="auto" dir="auto"
> >
{photo && ( {photo && !video && (
<Photo <Photo
message={message} message={message}
observeIntersection={observeIntersection} observeIntersection={observeIntersection}
@ -97,6 +102,18 @@ const WebPage: FC<OwnProps> = ({
<p className="site-description">{renderText(truncatedDescription, ['emoji', 'br'])}</p> <p className="site-description">{renderText(truncatedDescription, ['emoji', 'br'])}</p>
)} )}
</div> </div>
{!inPreview && video && (
<Video
message={message}
observeIntersection={observeIntersection!}
noAvatars={noAvatars}
shouldAutoLoad={shouldAutoLoad}
shouldAutoPlay={shouldAutoPlay}
lastSyncTime={lastSyncTime}
onClick={isMediaInteractive ? handleMediaClick : undefined}
onCancelUpload={onCancelMediaTransfer}
/>
)}
</div> </div>
); );
}; };

View File

@ -313,7 +313,7 @@
} }
} }
.message-content.media { .message-content.media, .WebPage {
.media-inner { .media-inner {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -65,7 +65,7 @@ export function buildContentClassName(
} else if (webPage) { } else if (webPage) {
classNames.push('web-page'); classNames.push('web-page');
if (webPage.photo) { if (webPage.photo || webPage.video) {
classNames.push('media'); classNames.push('media');
} }
} }

View File

@ -13,7 +13,6 @@ export type IDimensions = {
type Target = 'micro' | 'pictogram' | 'inline' | 'viewerPreview' | 'viewerFull' | 'download'; type Target = 'micro' | 'pictogram' | 'inline' | 'viewerPreview' | 'viewerFull' | 'download';
const MAX_INLINE_VIDEO_SIZE = 10 * 1024 ** 2; // 10 MB
export function getMessageContent(message: ApiMessage) { export function getMessageContent(message: ApiMessage) {
return message.content; return message.content;
@ -88,12 +87,24 @@ export function getMessageWebPagePhoto(message: ApiMessage) {
return webPage ? webPage.photo : undefined; return webPage ? webPage.photo : undefined;
} }
export function getMessageWebPageDocument(message: ApiMessage) {
const webPage = getMessageWebPage(message);
return webPage ? webPage.document : undefined;
}
export function getMessageWebPageVideo(message: ApiMessage): ApiVideo | undefined {
const webPage = getMessageWebPage(message);
if (!webPage) return undefined;
return webPage.video;
}
export function getMessageMediaThumbnail(message: ApiMessage) { export function getMessageMediaThumbnail(message: ApiMessage) {
const media = getMessagePhoto(message) const media = getMessagePhoto(message)
|| getMessageVideo(message) || getMessageVideo(message)
|| getMessageDocument(message) || getMessageDocument(message)
|| getMessageSticker(message) || getMessageSticker(message)
|| getMessageWebPagePhoto(message); || getMessageWebPagePhoto(message)
|| getMessageWebPageVideo(message);
if (!media) { if (!media) {
return undefined; return undefined;
@ -116,55 +127,51 @@ export function getMessageMediaHash(
photo, video, sticker, audio, voice, document, photo, video, sticker, audio, voice, document,
} = message.content; } = message.content;
const webPagePhoto = getMessageWebPagePhoto(message); const webPagePhoto = getMessageWebPagePhoto(message);
const webPageVideo = getMessageWebPageVideo(message);
if (!(photo || video || sticker || webPagePhoto || audio || voice || document)) { const messageVideo = video || webPageVideo;
const messagePhoto = photo || webPagePhoto;
if (!(messagePhoto || messageVideo || sticker || audio || voice || document)) {
return undefined; return undefined;
} }
const base = getMessageKey(message); const base = getMessageKey(message);
if (photo || webPagePhoto) { if (messageVideo) {
switch (target) { switch (target) {
case 'micro': case 'micro':
case 'pictogram': case 'pictogram':
return `${base}?size=m`; return `${base}?size=m`;
case 'inline': case 'inline':
if (hasMessageLocalBlobUrl(message)) { return !hasMessageLocalBlobUrl(message) ? getVideoOrAudioBaseHash(messageVideo, base) : undefined;
return undefined;
}
return `${base}?size=x`;
case 'viewerPreview':
return `${base}?size=x`;
case 'viewerFull':
return `${base}?size=z`;
}
}
if (video) {
switch (target) {
case 'micro':
case 'pictogram':
return `${base}?size=m`;
case 'inline':
if (hasMessageLocalBlobUrl(message)) {
return undefined;
}
if (canMessagePlayVideoInline(video)) {
return getVideoOrAudioBaseHash(video, base);
}
return `${base}?size=z`;
case 'viewerPreview': case 'viewerPreview':
return `${base}?size=m`; return `${base}?size=m`;
case 'viewerFull': case 'viewerFull':
return getVideoOrAudioBaseHash(video, base); return getVideoOrAudioBaseHash(messageVideo, base);
case 'download': case 'download':
return `${base}?download`; return `${base}?download`;
} }
} }
if (messagePhoto) {
switch (target) {
case 'micro':
case 'pictogram':
return `${base}?size=m`;
case 'inline':
if (hasMessageLocalBlobUrl(message)) {
return undefined;
}
return `${base}?size=x`;
case 'viewerPreview':
return `${base}?size=x`;
case 'viewerFull':
return `${base}?size=z`;
}
}
if (document) { if (document) {
switch (target) { switch (target) {
case 'micro': case 'micro':
@ -235,10 +242,12 @@ export function getMessageMediaFormat(
sticker, video, audio, voice, sticker, video, audio, voice,
} = message.content; } = message.content;
const fullVideo = video || getMessageWebPageVideo(message);
if (sticker && target === 'inline' && sticker.isAnimated) { if (sticker && target === 'inline' && sticker.isAnimated) {
return ApiMediaFormat.Lottie; return ApiMediaFormat.Lottie;
} else if (video && IS_PROGRESSIVE_SUPPORTED && ( } else if (fullVideo && IS_PROGRESSIVE_SUPPORTED && (
(target === 'viewerFull') || (target === 'inline' && canMessagePlayVideoInline(video)) target === 'viewerFull' || target === 'inline'
)) { )) {
return ApiMediaFormat.Progressive; return ApiMediaFormat.Progressive;
} else if (audio || voice) { } else if (audio || voice) {
@ -254,12 +263,18 @@ export function getMessageMediaFormat(
} }
export function getMessageMediaFilename(message: ApiMessage) { export function getMessageMediaFilename(message: ApiMessage) {
const { photo, video, webPage } = message.content; const { photo, video } = message.content;
const webPagePhoto = getMessageWebPagePhoto(message);
const webPageVideo = getMessageWebPageVideo(message);
if (photo || (webPage && webPage.photo)) { if (photo || webPagePhoto) {
return `photo${message.date}.jpeg`; return `photo${message.date}.jpeg`;
} }
if (webPageVideo) {
return webPageVideo.fileName;
}
if (video) { if (video) {
return video.fileName; return video.fileName;
} }
@ -273,10 +288,6 @@ export function hasMessageLocalBlobUrl(message: ApiMessage) {
return (photo && photo.blobUrl) || (video && video.blobUrl) || (document && document.previewBlobUrl); return (photo && photo.blobUrl) || (video && video.blobUrl) || (document && document.previewBlobUrl);
} }
export function canMessagePlayVideoInline(video: ApiVideo): boolean {
return video.isGif || video.isRound || video.size <= MAX_INLINE_VIDEO_SIZE;
}
export function getChatMediaMessageIds( export function getChatMediaMessageIds(
messages: Record<number, ApiMessage>, listedIds: number[], reverseOrder = false, messages: Record<number, ApiMessage>, listedIds: number[], reverseOrder = false,
) { ) {
@ -360,7 +371,7 @@ export function getMessageContentIds(
export function getMediaDuration(message: ApiMessage) { export function getMediaDuration(message: ApiMessage) {
const { audio, voice, video } = getMessageContent(message); const { audio, voice, video } = getMessageContent(message);
const media = audio || voice || video; const media = audio || voice || video || getMessageWebPageVideo(message);
if (!media) { if (!media) {
return undefined; return undefined;
} }

View File

@ -29,6 +29,7 @@ import {
isChatGroup, isChatGroup,
isChatSuperGroup, isChatSuperGroup,
getMessageVideo, getMessageVideo,
getMessageWebPageVideo,
} from '../helpers'; } from '../helpers';
import { findLast } from '../../util/iteratees'; import { findLast } from '../../util/iteratees';
import { selectIsStickerFavorite } from './symbols'; import { selectIsStickerFavorite } from './symbols';
@ -679,7 +680,7 @@ export function selectShouldAutoLoadMedia(
} }
export function selectShouldAutoPlayMedia(global: GlobalState, message: ApiMessage) { export function selectShouldAutoPlayMedia(global: GlobalState, message: ApiMessage) {
const video = getMessageVideo(message); const video = getMessageVideo(message) || getMessageWebPageVideo(message);
if (!video) { if (!video) {
return undefined; return undefined;
} }