fix(post form): publish twimg query-format links with path extension

Normalize pbs.twimg.com media URLs that use ?format= to their file-extension form at publish time without rewriting the input field.
This commit is contained in:
Tommaso Casaburi
2026-06-04 18:00:25 +07:00
parent 8badc2b7a5
commit 3ece699a6f
4 changed files with 146 additions and 30 deletions
@@ -45,6 +45,7 @@ import {
getLinkMediaInfo,
getMediaDimensions,
getPostMediaTypeLabel,
getTwimgMediaFilePublishUrl,
getYouTubeEmbedPostMediaFileLink,
getYouTubeThumbnailUrlFromLink,
} from '../media-utils';
@@ -192,6 +193,14 @@ describe('media-utils', () => {
});
});
it('normalizes known twimg query-format media links for publishing', () => {
expect(getTwimgMediaFilePublishUrl('https://pbs.twimg.com/media/HJxnhNKWMAAhqFU?format=jpg&name=medium')).toBe('https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.jpg');
expect(getTwimgMediaFilePublishUrl('http://pbs.twimg.com/media/HJxnhNKWMAAhqFU?format=PNG&name=small')).toBe('https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.png');
expect(getTwimgMediaFilePublishUrl('https://pbs.twimg.com/media/HJxnhNKWMAAhqFU.jpg?format=png&name=medium')).toBeUndefined();
expect(getTwimgMediaFilePublishUrl('https://example.com/media/HJxnhNKWMAAhqFU?format=jpg&name=medium')).toBeUndefined();
expect(getTwimgMediaFilePublishUrl('https://pbs.twimg.com/media/HJxnhNKWMAAhqFU?format=txt&name=medium')).toBeUndefined();
});
it('builds comment media info and strips thumbnails for blacklisted domains', () => {
testState.canEmbedHosts = new Set(['www.youtube.com']);
+22
View File
@@ -112,6 +112,7 @@ const KNOWN_VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov', 'avi', 'mkv', 'flv', 'wmv'
const KNOWN_AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma'];
const KNOWN_SWF_EXTENSIONS = ['swf'];
const KNOWN_MEDIA_EXTENSIONS = new Set([...KNOWN_IMAGE_EXTENSIONS, ...KNOWN_VIDEO_EXTENSIONS, ...KNOWN_AUDIO_EXTENSIONS, ...KNOWN_SWF_EXTENSIONS]);
const TWIMG_MEDIA_FORMAT_EXTENSIONS = new Set(['jpg', 'jpeg', 'png', 'webp', 'gif']);
// some sites don't show thumbnails, so the backend-side thumbnail fetching needs to be disabled, or it might fetch non-thumbnails such as emojis
const THUMBNAIL_BLACKLISTED_DOMAINS = ['twitter.com', 'x.com'];
@@ -157,6 +158,27 @@ const getDirectMediaExtension = (url: URL): string => {
return pathParts.length > 1 ? pathParts[pathParts.length - 1] : '';
};
export const getTwimgMediaFilePublishUrl = (link: string): string | undefined => {
const url = parseHttpUrl(link.trim());
if (!url || url.hostname.toLowerCase() !== 'pbs.twimg.com') {
return undefined;
}
const pathParts = url.pathname.split('/').filter(Boolean);
const [mediaPath, mediaId] = pathParts;
if (pathParts.length !== 2 || mediaPath !== 'media' || !mediaId || mediaId.includes('.')) {
return undefined;
}
const format = url.searchParams.get('format')?.toLowerCase();
if (!format || !TWIMG_MEDIA_FORMAT_EXTENSIONS.has(format)) {
return undefined;
}
url.protocol = 'https:';
return `${url.origin}/media/${mediaId}.${format}`;
};
export const getLinkMediaInfo = memoize(
(link: string): CommentMediaInfo | undefined => {
if (!isValidURL(link)) {