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
This commit is contained in:
sebastjanartic 2025-09-04 16:43:01 +00:00
parent 00f9419cb8
commit 0772ae1aab
2 changed files with 156 additions and 1 deletions

View File

@ -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 (
<div
className={`absolute z-40 ${getPositionClasses()} transition-all duration-300 ${
isVisible ? 'opacity-100 scale-100' : 'opacity-0 scale-95'
}`}
style={sizeStyle}
>
{/* Ad Container - AdSense Compliant Companion Ad */}
<div className="relative bg-white/95 backdrop-blur-sm rounded-lg overflow-hidden border border-gray-200 shadow-xl">
{/* Close Button */}
<button
onClick={handleClose}
className="absolute top-1 right-1 z-10 w-5 h-5 bg-gray-800/80 hover:bg-gray-800 rounded-full flex items-center justify-center transition-colors"
>
<X className="w-3 h-3 text-white" />
</button>
{/* Ad Label */}
<div className="absolute top-1 left-1 z-10">
<span className="bg-[#da234d] text-white text-xs px-1.5 py-0.5 rounded font-medium">
AD
</span>
</div>
{/* AdSense Companion Ad */}
<div className="p-1 pt-6 w-full h-full">
<AdSenseAd
adSlot={adSlot}
adFormat="rectangle"
width={size === 'small' ? 200 : 300}
height={size === 'small' ? 150 : 200}
className="w-full h-full"
style={{
display: 'block',
width: '100%',
height: '100%'
}}
/>
</div>
</div>
</div>
);
}

View File

@ -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<HTMLVideoElement>(null);
const hlsRef = useRef<Hls | null>(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) */}
<VideoCompanionAd
isVideoPlaying={isPlaying}
showControls={showControls}
adSlot="5629279662"
position="bottom-right"
size="small"
autoHide={20}
/>
<VideoCompanionAd
isVideoPlaying={isPlaying}
showControls={showControls}
adSlot="1372934919"
position="top-right"
size="small"
autoHide={25}
/>
</div>