diff --git a/package-lock.json b/package-lock.json
index 7f8544c..98a2697 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "frontend-helper",
"version": "0.1.0",
"dependencies": {
+ "hls.js": "1.7.3",
"next": "15.5.28",
"react": "19.0.0",
"react-dom": "19.0.0"
@@ -4321,6 +4322,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/hls.js": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.7.3.tgz",
+ "integrity": "sha512-MsPlx6yVW4Qv4C7mEVou4/gk/5cN2dVxOTnrNjPSmyH2f0Ln+/UkG10Ax1PE6OvrmTVPKgQYozTtBaPOuCzq7A==",
+ "license": "Apache-2.0"
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
diff --git a/package.json b/package.json
index e37a838..e50b341 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"test": "vitest run"
},
"dependencies": {
+ "hls.js": "1.7.3",
"next": "15.5.28",
"react": "19.0.0",
"react-dom": "19.0.0"
diff --git a/src/app/globals.css b/src/app/globals.css
index e143da7..f586a20 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -24,6 +24,8 @@ button:disabled { opacity: .5; cursor: not-allowed; }
.post-who time { color: var(--grey); font-size: 13px; }
.post-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.5; }
.post-media img { display: block; width: 100%; height: auto; border-radius: 8px; }
+.post-video { background: #000; border-radius: 8px; overflow: hidden; }
+.post-video video { display: block; width: 100%; max-height: 520px; background: #000; }
.post-stats { display: flex; gap: 16px; color: var(--grey); font-size: 14px; }
.heart { color: var(--red); }
.feed-message { text-align: center; color: var(--grey); padding: 32px 16px; }
diff --git a/src/components/PostCard.tsx b/src/components/PostCard.tsx
index 8b9e0a7..f36d04a 100644
--- a/src/components/PostCard.tsx
+++ b/src/components/PostCard.tsx
@@ -1,3 +1,4 @@
+import { VideoPlayer } from "@/components/VideoPlayer";
import { initials, relativeTime, type CardPost } from "@/lib/feed-model";
export function PostCard({ post, now }: { post: CardPost; now?: number }) {
@@ -19,7 +20,11 @@ export function PostCard({ post, now }: { post: CardPost; now?: number }) {
{post.text ?
{post.text}
: null}
{post.media.map((m, i) =>
- m.src ? (
+ m.kind === "video" && m.hlsUrl ? (
+
+
+
+ ) : m.src ? (
{/* eslint-disable-next-line @next/next/no-img-element */}

diff --git a/src/components/VideoPlayer.tsx b/src/components/VideoPlayer.tsx
new file mode 100644
index 0000000..be96a78
--- /dev/null
+++ b/src/components/VideoPlayer.tsx
@@ -0,0 +1,69 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+import { choosePlayback, supportsNativeHls } from "@/lib/video-playback";
+
+export interface VideoPlayerProps {
+ hlsUrl: string;
+ poster: string | null;
+ label: string;
+}
+
+/**
+ * Real