Skip to content

Commit 785c96e

Browse files
fix(extension): import the best-quality Twitter video variant (#1351)
1 parent 7e7e820 commit 785c96e

2 files changed

Lines changed: 111 additions & 4 deletions

File tree

apps/browser-extension/utils/twitter-utils.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,45 @@ interface MediaEntity {
5656
}
5757
}
5858
video_info?: {
59-
variants?: Array<{
60-
url: string
61-
}>
59+
variants?: VideoVariant[]
6260
duration_millis?: number
6361
}
6462
}
6563

64+
export interface VideoVariant {
65+
url: string
66+
bitrate?: number
67+
content_type?: string
68+
}
69+
70+
/**
71+
* Twitter returns several video variants for a single video: an HLS `.m3u8`
72+
* playlist (no bitrate) plus multiple `video/mp4` renditions at different
73+
* bitrates, in no guaranteed order. Taking `variants[0]` therefore often stored
74+
* the HLS playlist URL (not a directly usable file) or the lowest-quality clip.
75+
* Pick the highest-bitrate MP4 instead, falling back to the first variant when
76+
* no MP4 rendition is present.
77+
*/
78+
export function pickBestVideoVariantUrl(
79+
variants: VideoVariant[] | undefined,
80+
): string {
81+
if (!variants || variants.length === 0) return ""
82+
83+
const mp4s = variants.filter(
84+
(v) => v.content_type === "video/mp4" || /\.mp4(?:\?|$)/i.test(v.url),
85+
)
86+
const pool = mp4s.length > 0 ? mp4s : variants
87+
88+
let best = pool[0]
89+
for (const variant of pool) {
90+
if ((variant.bitrate ?? 0) > (best?.bitrate ?? 0)) {
91+
best = variant
92+
}
93+
}
94+
95+
return best?.url || ""
96+
}
97+
6698
export interface Tweet {
6799
__typename?: string
68100
lang?: string
@@ -257,7 +289,7 @@ export function transformTweetData(
257289
const videos = media
258290
.filter((m) => m.type === "video")
259291
.map((m) => ({
260-
url: m.video_info?.variants?.[0]?.url || "",
292+
url: pickBestVideoVariantUrl(m.video_info?.variants),
261293
thumbnail_url: m.media_url_https,
262294
duration: m.video_info?.duration_millis || 0,
263295
}))
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { describe, expect, it } from "bun:test"
2+
import { pickBestVideoVariantUrl } from "./twitter-utils"
3+
4+
describe("pickBestVideoVariantUrl", () => {
5+
it("returns an empty string when there are no variants", () => {
6+
expect(pickBestVideoVariantUrl(undefined)).toBe("")
7+
expect(pickBestVideoVariantUrl([])).toBe("")
8+
})
9+
10+
it("picks the highest-bitrate mp4, not the first variant", () => {
11+
const variants = [
12+
{
13+
url: "https://video.twimg.com/playlist.m3u8",
14+
content_type: "application/x-mpegURL",
15+
},
16+
{
17+
url: "https://video.twimg.com/low.mp4",
18+
content_type: "video/mp4",
19+
bitrate: 256000,
20+
},
21+
{
22+
url: "https://video.twimg.com/high.mp4",
23+
content_type: "video/mp4",
24+
bitrate: 2176000,
25+
},
26+
{
27+
url: "https://video.twimg.com/mid.mp4",
28+
content_type: "video/mp4",
29+
bitrate: 832000,
30+
},
31+
]
32+
expect(pickBestVideoVariantUrl(variants)).toBe(
33+
"https://video.twimg.com/high.mp4",
34+
)
35+
})
36+
37+
it("does not return the HLS playlist when mp4 renditions exist", () => {
38+
const variants = [
39+
{
40+
url: "https://video.twimg.com/playlist.m3u8",
41+
content_type: "application/x-mpegURL",
42+
},
43+
{
44+
url: "https://video.twimg.com/only.mp4",
45+
content_type: "video/mp4",
46+
bitrate: 632000,
47+
},
48+
]
49+
expect(pickBestVideoVariantUrl(variants)).toBe(
50+
"https://video.twimg.com/only.mp4",
51+
)
52+
})
53+
54+
it("falls back to the first variant when no mp4 is present", () => {
55+
const variants = [
56+
{
57+
url: "https://video.twimg.com/playlist.m3u8",
58+
content_type: "application/x-mpegURL",
59+
},
60+
]
61+
expect(pickBestVideoVariantUrl(variants)).toBe(
62+
"https://video.twimg.com/playlist.m3u8",
63+
)
64+
})
65+
66+
it("detects mp4 by extension when content_type is absent", () => {
67+
const variants = [
68+
{ url: "https://video.twimg.com/240/vid.mp4?tag=12" },
69+
{ url: "https://video.twimg.com/720/vid.mp4?tag=12", bitrate: 2176000 },
70+
]
71+
expect(pickBestVideoVariantUrl(variants)).toBe(
72+
"https://video.twimg.com/720/vid.mp4?tag=12",
73+
)
74+
})
75+
})

0 commit comments

Comments
 (0)