diff --git a/packages/core/src/content/link-preview/content/video.ts b/packages/core/src/content/link-preview/content/video.ts index 50c535fd..e5699ff5 100644 --- a/packages/core/src/content/link-preview/content/video.ts +++ b/packages/core/src/content/link-preview/content/video.ts @@ -5,7 +5,7 @@ export type DetectedVideo = { url: string; }; -const VIDEO_EXTENSIONS = new Set([".mp4", ".webm", ".mov", ".m4v"]); +const VIDEO_EXTENSIONS = new Set([".mp4", ".webm", ".mov", ".m4v", ".m3u8"]); function resolveAbsoluteUrl(candidate: string, baseUrl: string): string | null { const trimmed = candidate.trim(); diff --git a/packages/core/src/content/url.ts b/packages/core/src/content/url.ts index 58b23891..66b39709 100644 --- a/packages/core/src/content/url.ts +++ b/packages/core/src/content/url.ts @@ -34,6 +34,7 @@ export const DIRECT_MEDIA_EXTENSIONS = [ "opus", "aiff", "wma", + "m3u8", ] as const; const DIRECT_MEDIA_EXTENSION_SET = new Set(DIRECT_MEDIA_EXTENSIONS); const DIRECT_MEDIA_URL_PATTERN = new RegExp( diff --git a/src/media-cache.ts b/src/media-cache.ts index 63f0cf3b..d305b809 100644 --- a/src/media-cache.ts +++ b/src/media-cache.ts @@ -87,6 +87,8 @@ const resolveExtension = (filename: string | null, mediaType: string | null): st if (normalized.includes("audio/flac")) return ".flac"; if (normalized.includes("video/mp4")) return ".mp4"; if (normalized.includes("video/webm")) return ".webm"; + if (normalized.includes("application/vnd.apple.mpegurl") || normalized.includes("application/x-mpegurl")) + return ".m3u8"; return ".bin"; }; diff --git a/tests/content.url.test.ts b/tests/content.url.test.ts index bdbddfc3..cae3da16 100644 --- a/tests/content.url.test.ts +++ b/tests/content.url.test.ts @@ -53,6 +53,7 @@ describe("content/url", () => { expect(isDirectMediaUrl("https://example.com/voice.opus")).toBe(true); expect(isDirectMediaUrl("https://example.com/clip.avi")).toBe(true); expect(isDirectMediaUrl("https://example.com/track.wma#t=10")).toBe(true); + expect(isDirectMediaUrl("https://example.com/playlist.m3u8")).toBe(true); expect(isDirectMediaUrl("https://example.com/article")).toBe(false); });