diff --git a/package.json b/package.json index 4abcf7930..e6152d5dd 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ }, "dependencies": { "@auth/prisma-adapter": "^1.0.6", - "nextjs-toploader": "^1.6.11", "@discordjs/core": "^1.1.1", "@discordjs/next": "^0.1.1-dev.1673526225-a580768.0", "@icons-pack/react-simple-icons": "^9.4.0", @@ -44,24 +43,30 @@ "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.7", "@types/bcrypt": "^5.0.2", + "@types/hls.js": "^1.0.0", "@types/jsonwebtoken": "^9.0.5", + "@types/video-react": "^0.15.7", + "@types/video.js": "^7.3.58", "@uiw/react-md-editor": "^4.0.4", "axios": "^1.6.2", "bcrypt": "^5.1.1", "canvas": "^2.11.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "dashjs": "^4.7.4", "dayjs": "^1.11.10", "discord-oauth2": "^2.11.0", "discord.js": "^14.14.1", "embla-carousel-react": "^8.0.0", "fuse.js": "^7.0.0", + "hls.js": "^1.5.8", "jose": "^5.2.2", "jsonwebtoken": "^9.0.2", "lucide-react": "^0.321.0", "next": "14.0.2", "next-auth": "^4.24.5", "next-themes": "^0.2.1", + "nextjs-toploader": "^1.6.11", "node-fetch": "^3.3.2", "notion-client": "^6.16.0", "pdf-lib": "^1.17.1", @@ -77,7 +82,8 @@ "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", "vaul": "^0.8.9", - "video.js": "^8.6.1", + "video-react": "^0.16.0", + "video.js": "^8.12.0", "videojs-contrib-eme": "^3.11.1", "videojs-mobile-ui": "^1.1.1", "videojs-seek-buttons": "^4.0.3", @@ -101,7 +107,6 @@ "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "autoprefixer": "^10.0.1", - "eslint": "^8.56.0", "eslint-plugin-storybook": "^0.8.0", "husky": "^9.0.7", @@ -109,6 +114,6 @@ "prettier": "^3.2.4", "prisma": "^5.6.0", "tailwindcss": "^3.3.0", - "ts-node": "^10.9.2", + "ts-node": "^10.9.2" } } diff --git a/src/actions/videopreview/videoPreview.tsx b/src/actions/videopreview/videoPreview.tsx index 3e0611227..86db32902 100644 --- a/src/actions/videopreview/videoPreview.tsx +++ b/src/actions/videopreview/videoPreview.tsx @@ -4,15 +4,24 @@ import db from '@/db'; export default async function VideoPreview({ contentId, }: { - contentId: number; + contentId: number | undefined; }) { + if (!contentId) return null; const videoMetadata = await db.videoMetadata.findFirst({ where: { contentId }, - select: { video_360p_1: true }, + select: { + video_360p_1: true, + video_360p_2: true, + video_360p_3: true, + video_360p_4: true, + }, }); + if (!videoMetadata) return null; + const videoUrl = + videoMetadata.video_360p_1 || + videoMetadata.video_360p_2 || + videoMetadata.video_360p_3 || + videoMetadata.video_360p_4; - if (videoMetadata) { - return videoMetadata.video_360p_1; - } - return null; + return videoUrl; } diff --git a/src/components/ContentCard.tsx b/src/components/ContentCard.tsx index 8e2ec2cd3..c35c5c843 100644 --- a/src/components/ContentCard.tsx +++ b/src/components/ContentCard.tsx @@ -50,7 +50,7 @@ export const ContentCard = ({ )} {type === 'video' && ( -
+
{contentDuration && formatTime(contentDuration)}
)} @@ -70,7 +70,7 @@ export const ContentCard = ({ {type === 'video' && (
{ + +const CustomReactPlayer = ({ url }:{url:string}) => { + const playerRef = useRef(null); + + useEffect(() => { + //@ts-ignore + const video = playerRef.current?.video?.video; + if (video) { + video.autoplay = true; + } + if (url.endsWith('.m3u8')) { + if (Hls.isSupported()) { + const hls = new Hls(); + hls.loadSource(url); + hls.attachMedia(video); + return () => hls.destroy(); + } else if (video.canPlayType('application/vnd.apple.mpegurl')) { + video.src = url; + } + } else if (url.endsWith('.mpd')) { + const player = dashjs.MediaPlayer().create(); + player.initialize(video, url, true); + return () => player.reset(); + } else { + video.src = url; + } + }, [url]); + + return ( + //@ts-ignore + + + + + ); +}; + +const VideoThumbnail = ({ imageUrl, contentId }:any) => { const [videoUrl, setVideoUrl] = useState(null); const [hover, setHover] = useState(false); useEffect(() => { - async function fetchVideoUrl() { - const url = await VideoPreview({ contentId }); - setVideoUrl(url); - } + let isMounted = true; + + const fetchVideoUrl = async () => { + if (contentId !== undefined) { + try { + const url = await VideoPreview({ contentId }); + if (isMounted && url) { + setVideoUrl(url); + } + } catch (error) { + console.error('Failed to fetch video URL', error); + } + } + }; + fetchVideoUrl(); + + return () => { + isMounted = false; + setVideoUrl(null); + }; }, [contentId]); + const handleMouseEnter = () => { setHover(true); }; @@ -26,24 +76,21 @@ const VideoThumbnail = ({ const handleMouseLeave = () => { setHover(false); }; + return ( -
-
+
+
{hover && videoUrl ? ( -
- -
+ ) : ( Video Thumbnail )}