From 0772ae1aabfd00faa84d01bdcd1270e8240c2fb9 Mon Sep 17 00:00:00 2001 From: sebastjanartic <45803536-sebastjanartic@users.noreply.replit.com> Date: Thu, 4 Sep 2025 16:43:01 +0000 Subject: [PATCH] Integrate smaller AdSense ads into the video player interface Introduce a new component for displaying companion ads and integrate it into the video modal, allowing for smaller, compliant ad placements alongside video content. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 946a0075-7e32-454b-b348-9e7f576d7f45 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/60d372ff-2c10-46c7-b01b-10c3435136b0/946a0075-7e32-454b-b348-9e7f576d7f45/wfV3fPt --- client/src/components/video-companion-ad.tsx | 133 +++++++++++++++++++ client/src/components/video-modal.tsx | 24 +++- 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 client/src/components/video-companion-ad.tsx diff --git a/client/src/components/video-companion-ad.tsx b/client/src/components/video-companion-ad.tsx new file mode 100644 index 0000000..46b7ea3 --- /dev/null +++ b/client/src/components/video-companion-ad.tsx @@ -0,0 +1,133 @@ +import { useState, useEffect } from 'react'; +import { X } from 'lucide-react'; +import AdSenseAd from './adsense-ad'; + +interface VideoCompanionAdProps { + isVideoPlaying: boolean; + showControls: boolean; + onClose?: () => void; + adSlot: string; + position?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'; + autoHide?: number; // Auto hide after X seconds + size?: 'small' | 'medium'; +} + +export default function VideoCompanionAd({ + isVideoPlaying, + showControls, + onClose, + adSlot, + position = 'bottom-left', + autoHide = 0, // No auto-hide by default + size = 'small' +}: VideoCompanionAdProps) { + const [isVisible, setIsVisible] = useState(false); + const [shouldShow, setShouldShow] = useState(false); + + // Show ad after 3 seconds of video playing + useEffect(() => { + let showTimer: NodeJS.Timeout; + let hideTimer: NodeJS.Timeout; + + if (isVideoPlaying) { + // Show ad after 3 seconds + showTimer = setTimeout(() => { + setShouldShow(true); + setIsVisible(true); + }, 3000); + + // Auto hide if specified + if (autoHide > 0) { + hideTimer = setTimeout(() => { + setIsVisible(false); + }, (3 + autoHide) * 1000); + } + } else { + setShouldShow(false); + setIsVisible(false); + } + + return () => { + clearTimeout(showTimer); + clearTimeout(hideTimer); + }; + }, [isVideoPlaying, autoHide]); + + const handleClose = () => { + setIsVisible(false); + onClose?.(); + }; + + if (!shouldShow || !isVisible) return null; + + const getPositionClasses = () => { + switch (position) { + case 'top-left': + return 'top-4 left-4'; + case 'top-right': + return 'top-4 right-4'; + case 'bottom-left': + return 'bottom-20 left-4'; // Above video controls + case 'bottom-right': + return 'bottom-20 right-4'; // Above video controls + default: + return 'bottom-20 left-4'; + } + }; + + const getSizeClasses = () => { + switch (size) { + case 'small': + return { width: '200px', height: '150px' }; + case 'medium': + return { width: '300px', height: '200px' }; + default: + return { width: '200px', height: '150px' }; + } + }; + + const sizeStyle = getSizeClasses(); + + return ( +
+ {/* Ad Container - AdSense Compliant Companion Ad */} +
+ {/* Close Button */} + + + {/* Ad Label */} +
+ + AD + +
+ + {/* AdSense Companion Ad */} +
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/client/src/components/video-modal.tsx b/client/src/components/video-modal.tsx index 88e91a9..ae95c46 100644 --- a/client/src/components/video-modal.tsx +++ b/client/src/components/video-modal.tsx @@ -12,6 +12,8 @@ import { import QualityIndicator from "./quality-indicator"; import VASTPlayer from "./vast-player"; +import AdSenseAd from "./adsense-ad"; +import VideoCompanionAd from "./video-companion-ad"; // HLS.js types for video streaming @@ -56,7 +58,7 @@ function formatDate(date: Date | string): string { } export default function VideoModal({ video, isOpen, onClose, enableAds = true }: VideoModalProps) { - const [useVASTPlayer, setUseVASTPlayer] = useState(true); + const [useVASTPlayer, setUseVASTPlayer] = useState(enableAds); const videoRef = useRef(null); const hlsRef = useRef(null); const [showShareMenu, setShowShareMenu] = useState(false); @@ -71,6 +73,7 @@ export default function VideoModal({ video, isOpen, onClose, enableAds = true }: const [volume, setVolume] = useState(1); const [hoverTime, setHoverTime] = useState(-1); + // All hooks must be declared before any conditional returns useEffect(() => { const handleEscape = (e: KeyboardEvent) => { @@ -650,6 +653,25 @@ export default function VideoModal({ video, isOpen, onClose, enableAds = true }: /> )} + {/* Video Companion Ads (AdSense Compliant) */} + + + +