fix(post-form): auto-convert YouTube links to thumbnail URLs

Add a countdown notice and shared hook to replace YouTube watch links
with img.youtube.com thumbnails in post and reply forms, with i18n copy.
This commit is contained in:
Tommaso Casaburi
2026-06-02 14:59:14 +07:00
parent ea7883b107
commit 0a601c9392
42 changed files with 462 additions and 81 deletions
@@ -46,6 +46,7 @@ import {
getMediaDimensions,
getPostMediaTypeLabel,
getYouTubeEmbedPostMediaFileLink,
getYouTubeThumbnailUrlFromLink,
} from '../media-utils';
const clearMemoizedCache = (fn: unknown) => {
@@ -125,6 +126,8 @@ describe('media-utils', () => {
expect(getYouTubeEmbedPostMediaFileLink(mediaInfo)).toBe('https://img.youtube.com/vi/abc123/0.jpg');
expect(getPostMediaTypeLabel(mediaInfo, 'iframe', (key) => key)).toBe('youtube_video');
expect(getYouTubeEmbedPostMediaFileLink({ type: 'iframe', url: 'https://streamable.com/clip123' })).toBeUndefined();
expect(getYouTubeThumbnailUrlFromLink('https://youtu.be/short123')).toBe('https://img.youtube.com/vi/short123/0.jpg');
expect(getYouTubeThumbnailUrlFromLink('https://example.com/watch?v=not-youtube')).toBeUndefined();
});
it('recognizes which media types expose thumbnails', () => {
+20 -5
View File
@@ -1,5 +1,5 @@
import localForageLru from '@bitsocial/bitsocial-react-hooks/dist/lib/localforage-lru/index.js';
import { canEmbed, getYouTubeVideoId } from '../../components/embed/embed-utils';
import { canEmbed, getYouTubeVideoId, youtubeHosts } from '../../components/embed/embed-utils';
import memoize from 'memoizee';
import { isPrivateNetworkHostname, isValidURL, parseHttpUrl } from './url-utils';
import { Capacitor, CapacitorHttp } from '@capacitor/core';
@@ -65,6 +65,22 @@ export const getPostMediaTypeLabel = (commentMediaInfo: CommentMediaInfo | undef
return getDisplayMediaInfoType(resolvedType, t);
};
const isYouTubeLikeUrl = (url: URL): boolean => youtubeHosts.has(url.host) || url.host.startsWith('yt.');
export const getYouTubeThumbnailUrl = (url: URL): string | undefined => {
if (!isYouTubeLikeUrl(url)) {
return undefined;
}
const videoId = getYouTubeVideoId(url);
return videoId ? `https://img.youtube.com/vi/${videoId}/0.jpg` : undefined;
};
export const getYouTubeThumbnailUrlFromLink = (link: string): string | undefined => {
const parsedUrl = parseHttpUrl(link.trim());
return parsedUrl ? getYouTubeThumbnailUrl(parsedUrl) : undefined;
};
export const getHasThumbnail = memoize(
(commentMediaInfo: CommentMediaInfo | undefined, link: string | undefined): boolean => {
if (!link || !commentMediaInfo) return false;
@@ -81,10 +97,9 @@ export const getHasThumbnail = memoize(
);
const getPatternThumbnailUrl = (url: URL): string | undefined => {
const videoId = getYouTubeVideoId(url);
if (videoId) {
return `https://img.youtube.com/vi/${videoId}/0.jpg`;
}
const youtubeThumbnailUrl = getYouTubeThumbnailUrl(url);
if (youtubeThumbnailUrl) return youtubeThumbnailUrl;
if (url.host.includes('streamable.com')) {
const videoId = url.pathname.split('/')[1];
return `https://cdn-cf-east.streamable.com/image/${videoId}.jpg`;