Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions apps/browser-extension/utils/twitter-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,45 @@ interface MediaEntity {
}
}
video_info?: {
variants?: Array<{
url: string
}>
variants?: VideoVariant[]
duration_millis?: number
}
}

export interface VideoVariant {
url: string
bitrate?: number
content_type?: string
}

/**
* Twitter returns several video variants for a single video: an HLS `.m3u8`
* playlist (no bitrate) plus multiple `video/mp4` renditions at different
* bitrates, in no guaranteed order. Taking `variants[0]` therefore often stored
* the HLS playlist URL (not a directly usable file) or the lowest-quality clip.
* Pick the highest-bitrate MP4 instead, falling back to the first variant when
* no MP4 rendition is present.
*/
export function pickBestVideoVariantUrl(
variants: VideoVariant[] | undefined,
): string {
if (!variants || variants.length === 0) return ""

const mp4s = variants.filter(
(v) => v.content_type === "video/mp4" || /\.mp4(?:\?|$)/i.test(v.url),
)
const pool = mp4s.length > 0 ? mp4s : variants

let best = pool[0]
for (const variant of pool) {
if ((variant.bitrate ?? 0) > (best?.bitrate ?? 0)) {
best = variant
}
}

return best?.url || ""
}

export interface Tweet {
__typename?: string
lang?: string
Expand Down Expand Up @@ -257,7 +289,7 @@ export function transformTweetData(
const videos = media
.filter((m) => m.type === "video")
.map((m) => ({
url: m.video_info?.variants?.[0]?.url || "",
url: pickBestVideoVariantUrl(m.video_info?.variants),
thumbnail_url: m.media_url_https,
duration: m.video_info?.duration_millis || 0,
}))
Expand Down
75 changes: 75 additions & 0 deletions apps/browser-extension/utils/twitter-video-variant.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { describe, expect, it } from "bun:test"
import { pickBestVideoVariantUrl } from "./twitter-utils"

describe("pickBestVideoVariantUrl", () => {
it("returns an empty string when there are no variants", () => {
expect(pickBestVideoVariantUrl(undefined)).toBe("")
expect(pickBestVideoVariantUrl([])).toBe("")
})

it("picks the highest-bitrate mp4, not the first variant", () => {
const variants = [
{
url: "https://video.twimg.com/playlist.m3u8",
content_type: "application/x-mpegURL",
},
{
url: "https://video.twimg.com/low.mp4",
content_type: "video/mp4",
bitrate: 256000,
},
{
url: "https://video.twimg.com/high.mp4",
content_type: "video/mp4",
bitrate: 2176000,
},
{
url: "https://video.twimg.com/mid.mp4",
content_type: "video/mp4",
bitrate: 832000,
},
]
expect(pickBestVideoVariantUrl(variants)).toBe(
"https://video.twimg.com/high.mp4",
)
})

it("does not return the HLS playlist when mp4 renditions exist", () => {
const variants = [
{
url: "https://video.twimg.com/playlist.m3u8",
content_type: "application/x-mpegURL",
},
{
url: "https://video.twimg.com/only.mp4",
content_type: "video/mp4",
bitrate: 632000,
},
]
expect(pickBestVideoVariantUrl(variants)).toBe(
"https://video.twimg.com/only.mp4",
)
})

it("falls back to the first variant when no mp4 is present", () => {
const variants = [
{
url: "https://video.twimg.com/playlist.m3u8",
content_type: "application/x-mpegURL",
},
]
expect(pickBestVideoVariantUrl(variants)).toBe(
"https://video.twimg.com/playlist.m3u8",
)
})

it("detects mp4 by extension when content_type is absent", () => {
const variants = [
{ url: "https://video.twimg.com/240/vid.mp4?tag=12" },
{ url: "https://video.twimg.com/720/vid.mp4?tag=12", bitrate: 2176000 },
]
expect(pickBestVideoVariantUrl(variants)).toBe(
"https://video.twimg.com/720/vid.mp4?tag=12",
)
})
})
Loading