fix: improve media extraction
This commit is contained in:
@@ -189,18 +189,21 @@ export function extractMedia(result: GraphqlTweetResult | undefined): TweetMedia
|
|||||||
|
|
||||||
// Extract video URL for video/animated_gif
|
// Extract video URL for video/animated_gif
|
||||||
if ((item.type === 'video' || item.type === 'animated_gif') && item.video_info?.variants) {
|
if ((item.type === 'video' || item.type === 'animated_gif') && item.video_info?.variants) {
|
||||||
// Find highest bitrate mp4 variant
|
// Prefer highest bitrate MP4, fall back to first MP4 when bitrate is missing.
|
||||||
const mp4Variants = item.video_info.variants
|
const mp4Variants = item.video_info.variants.filter(
|
||||||
.filter((v): v is { bitrate: number; content_type: string; url: string } =>
|
(v): v is { bitrate?: number; content_type: string; url: string } =>
|
||||||
v.content_type === 'video/mp4' && typeof v.bitrate === 'number' && typeof v.url === 'string'
|
v.content_type === 'video/mp4' && typeof v.url === 'string',
|
||||||
)
|
);
|
||||||
|
const mp4WithBitrate = mp4Variants
|
||||||
|
.filter((v): v is { bitrate: number; content_type: string; url: string } => typeof v.bitrate === 'number')
|
||||||
.sort((a, b) => b.bitrate - a.bitrate);
|
.sort((a, b) => b.bitrate - a.bitrate);
|
||||||
|
const selectedVariant = mp4WithBitrate[0] ?? mp4Variants[0];
|
||||||
|
|
||||||
if (mp4Variants.length > 0) {
|
if (selectedVariant) {
|
||||||
mediaItem.videoUrl = mp4Variants[0].url;
|
mediaItem.videoUrl = selectedVariant.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.video_info.duration_millis) {
|
if (typeof item.video_info.duration_millis === 'number') {
|
||||||
mediaItem.durationMs = item.video_info.duration_millis;
|
mediaItem.durationMs = item.video_info.duration_millis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,31 @@ describe('extractMedia', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('falls back to first mp4 when bitrate is missing', () => {
|
||||||
|
const result: GraphqlTweetResult = {
|
||||||
|
legacy: {
|
||||||
|
extended_entities: {
|
||||||
|
media: [
|
||||||
|
{
|
||||||
|
type: 'video',
|
||||||
|
media_url_https: 'https://pbs.twimg.com/ext_tw_video_thumb/123/img/thumb.jpg',
|
||||||
|
video_info: {
|
||||||
|
variants: [
|
||||||
|
{ content_type: 'video/mp4', url: 'https://video.twimg.com/no-bitrate.mp4' },
|
||||||
|
{ content_type: 'application/x-mpegURL', url: 'https://video.twimg.com/pl.m3u8' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const media = extractMedia(result);
|
||||||
|
expect(media).toHaveLength(1);
|
||||||
|
expect(media![0].videoUrl).toBe('https://video.twimg.com/no-bitrate.mp4');
|
||||||
|
});
|
||||||
|
|
||||||
it('extracts animated_gif with video url', () => {
|
it('extracts animated_gif with video url', () => {
|
||||||
const result: GraphqlTweetResult = {
|
const result: GraphqlTweetResult = {
|
||||||
legacy: {
|
legacy: {
|
||||||
@@ -140,6 +165,29 @@ describe('extractMedia', () => {
|
|||||||
expect(media![0].durationMs).toBeUndefined();
|
expect(media![0].durationMs).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps zero duration for videos', () => {
|
||||||
|
const result: GraphqlTweetResult = {
|
||||||
|
legacy: {
|
||||||
|
extended_entities: {
|
||||||
|
media: [
|
||||||
|
{
|
||||||
|
type: 'video',
|
||||||
|
media_url_https: 'https://pbs.twimg.com/ext_tw_video_thumb/zero.jpg',
|
||||||
|
video_info: {
|
||||||
|
duration_millis: 0,
|
||||||
|
variants: [{ content_type: 'video/mp4', url: 'https://video.twimg.com/zero.mp4' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const media = extractMedia(result);
|
||||||
|
expect(media).toHaveLength(1);
|
||||||
|
expect(media![0].durationMs).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('handles multiple media items', () => {
|
it('handles multiple media items', () => {
|
||||||
const result: GraphqlTweetResult = {
|
const result: GraphqlTweetResult = {
|
||||||
legacy: {
|
legacy: {
|
||||||
|
|||||||
Reference in New Issue
Block a user