mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-06 07:28:53 -05:00
Add audio poster support for audio files in video players
Introduces an audio-poster.jpg image and updates both chapters and video editor React video player components to display a poster image for audio files when no poster is provided. Also adds a posterUrl field to MEDIA_DATA and ensures fallback logic for poster images is consistent across iOS and standard video players.
This commit is contained in:
parent
bfcb774183
commit
54e2f1d7e4
BIN
frontend-tools/chapters-editor/client/public/audio-poster.jpg
Normal file
BIN
frontend-tools/chapters-editor/client/public/audio-poster.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 695 KiB |
@ -11,6 +11,7 @@ interface IOSVideoPlayerProps {
|
|||||||
const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps) => {
|
const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps) => {
|
||||||
const [videoUrl, setVideoUrl] = useState<string>('');
|
const [videoUrl, setVideoUrl] = useState<string>('');
|
||||||
const [iosVideoRef, setIosVideoRef] = useState<HTMLVideoElement | null>(null);
|
const [iosVideoRef, setIosVideoRef] = useState<HTMLVideoElement | null>(null);
|
||||||
|
const [posterImage, setPosterImage] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
// Refs for hold-to-continue functionality
|
// Refs for hold-to-continue functionality
|
||||||
const incrementIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
const incrementIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
@ -26,15 +27,24 @@ const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps
|
|||||||
|
|
||||||
// Get the video source URL from the main player
|
// Get the video source URL from the main player
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let url = '';
|
||||||
if (videoRef.current && videoRef.current.querySelector('source')) {
|
if (videoRef.current && videoRef.current.querySelector('source')) {
|
||||||
const source = videoRef.current.querySelector('source') as HTMLSourceElement;
|
const source = videoRef.current.querySelector('source') as HTMLSourceElement;
|
||||||
if (source && source.src) {
|
if (source && source.src) {
|
||||||
setVideoUrl(source.src);
|
url = source.src;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback to sample video if needed
|
// Fallback to sample video if needed
|
||||||
setVideoUrl('/videos/sample-video.mp4');
|
url = '/videos/sample-video.mp4';
|
||||||
}
|
}
|
||||||
|
setVideoUrl(url);
|
||||||
|
|
||||||
|
// Check if the media is an audio file and set poster image
|
||||||
|
const isAudioFile = url.match(/\.(mp3|wav|ogg|m4a|aac|flac)$/i) !== null;
|
||||||
|
|
||||||
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty
|
||||||
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
|
setPosterImage(mediaPosterUrl || (isAudioFile ? '/audio-poster.jpg' : undefined));
|
||||||
}, [videoRef]);
|
}, [videoRef]);
|
||||||
|
|
||||||
// Function to jump 15 seconds backward
|
// Function to jump 15 seconds backward
|
||||||
@ -127,6 +137,7 @@ const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps
|
|||||||
x-webkit-airplay="allow"
|
x-webkit-airplay="allow"
|
||||||
preload="auto"
|
preload="auto"
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
|
poster={posterImage}
|
||||||
>
|
>
|
||||||
<source src={videoUrl} type="video/mp4" />
|
<source src={videoUrl} type="video/mp4" />
|
||||||
<p>Your browser doesn't support HTML5 video.</p>
|
<p>Your browser doesn't support HTML5 video.</p>
|
||||||
|
|||||||
@ -38,6 +38,13 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
const sampleVideoUrl =
|
const sampleVideoUrl =
|
||||||
(typeof window !== 'undefined' && (window as any).MEDIA_DATA?.videoUrl) || '/videos/sample-video.mp4';
|
(typeof window !== 'undefined' && (window as any).MEDIA_DATA?.videoUrl) || '/videos/sample-video.mp4';
|
||||||
|
|
||||||
|
// Check if the media is an audio file
|
||||||
|
const isAudioFile = sampleVideoUrl.match(/\.(mp3|wav|ogg|m4a|aac|flac)$/i) !== null;
|
||||||
|
|
||||||
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty
|
||||||
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
|
const posterImage = mediaPosterUrl || (isAudioFile ? '/audio-poster.jpg' : undefined);
|
||||||
|
|
||||||
// Detect iOS device and Safari browser
|
// Detect iOS device and Safari browser
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkIOS = () => {
|
const checkIOS = () => {
|
||||||
@ -354,6 +361,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
x-webkit-airplay="allow"
|
x-webkit-airplay="allow"
|
||||||
controls={false}
|
controls={false}
|
||||||
muted={isMuted}
|
muted={isMuted}
|
||||||
|
poster={posterImage}
|
||||||
>
|
>
|
||||||
<source src={sampleVideoUrl} type="video/mp4" />
|
<source src={sampleVideoUrl} type="video/mp4" />
|
||||||
{/* Safari fallback for audio files */}
|
{/* Safari fallback for audio files */}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ if (typeof window !== 'undefined') {
|
|||||||
window.MEDIA_DATA = {
|
window.MEDIA_DATA = {
|
||||||
videoUrl: '',
|
videoUrl: '',
|
||||||
mediaId: '',
|
mediaId: '',
|
||||||
|
posterUrl: ''
|
||||||
};
|
};
|
||||||
window.lastSeekedPosition = 0;
|
window.lastSeekedPosition = 0;
|
||||||
}
|
}
|
||||||
@ -15,6 +16,7 @@ declare global {
|
|||||||
MEDIA_DATA: {
|
MEDIA_DATA: {
|
||||||
videoUrl: string;
|
videoUrl: string;
|
||||||
mediaId: string;
|
mediaId: string;
|
||||||
|
posterUrl?: string;
|
||||||
};
|
};
|
||||||
seekToFunction?: (time: number) => void;
|
seekToFunction?: (time: number) => void;
|
||||||
lastSeekedPosition: number;
|
lastSeekedPosition: number;
|
||||||
|
|||||||
BIN
frontend-tools/video-editor/client/public/audio-poster.jpg
Normal file
BIN
frontend-tools/video-editor/client/public/audio-poster.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 695 KiB |
@ -11,6 +11,7 @@ interface IOSVideoPlayerProps {
|
|||||||
const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps) => {
|
const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps) => {
|
||||||
const [videoUrl, setVideoUrl] = useState<string>('');
|
const [videoUrl, setVideoUrl] = useState<string>('');
|
||||||
const [iosVideoRef, setIosVideoRef] = useState<HTMLVideoElement | null>(null);
|
const [iosVideoRef, setIosVideoRef] = useState<HTMLVideoElement | null>(null);
|
||||||
|
const [posterImage, setPosterImage] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
// Refs for hold-to-continue functionality
|
// Refs for hold-to-continue functionality
|
||||||
const incrementIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
const incrementIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
@ -26,15 +27,24 @@ const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps
|
|||||||
|
|
||||||
// Get the video source URL from the main player
|
// Get the video source URL from the main player
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let url = '';
|
||||||
if (videoRef.current && videoRef.current.querySelector('source')) {
|
if (videoRef.current && videoRef.current.querySelector('source')) {
|
||||||
const source = videoRef.current.querySelector('source') as HTMLSourceElement;
|
const source = videoRef.current.querySelector('source') as HTMLSourceElement;
|
||||||
if (source && source.src) {
|
if (source && source.src) {
|
||||||
setVideoUrl(source.src);
|
url = source.src;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback to sample video if needed
|
// Fallback to sample video if needed
|
||||||
setVideoUrl('/videos/sample-video.mp4');
|
url = '/videos/sample-video.mp3';
|
||||||
}
|
}
|
||||||
|
setVideoUrl(url);
|
||||||
|
|
||||||
|
// Check if the media is an audio file and set poster image
|
||||||
|
const isAudioFile = url.match(/\.(mp3|wav|ogg|m4a|aac|flac)$/i) !== null;
|
||||||
|
|
||||||
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty
|
||||||
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
|
setPosterImage(mediaPosterUrl || (isAudioFile ? '/audio-poster.jpg' : undefined));
|
||||||
}, [videoRef]);
|
}, [videoRef]);
|
||||||
|
|
||||||
// Function to jump 15 seconds backward
|
// Function to jump 15 seconds backward
|
||||||
@ -127,6 +137,7 @@ const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps
|
|||||||
x-webkit-airplay="allow"
|
x-webkit-airplay="allow"
|
||||||
preload="auto"
|
preload="auto"
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
|
poster={posterImage}
|
||||||
>
|
>
|
||||||
<source src={videoUrl} type="video/mp4" />
|
<source src={videoUrl} type="video/mp4" />
|
||||||
<p>Your browser doesn't support HTML5 video.</p>
|
<p>Your browser doesn't support HTML5 video.</p>
|
||||||
|
|||||||
@ -36,7 +36,14 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
const [tooltipTime, setTooltipTime] = useState(0);
|
const [tooltipTime, setTooltipTime] = useState(0);
|
||||||
|
|
||||||
const sampleVideoUrl =
|
const sampleVideoUrl =
|
||||||
(typeof window !== 'undefined' && (window as any).MEDIA_DATA?.videoUrl) || '/videos/sample-video.mp4';
|
(typeof window !== 'undefined' && (window as any).MEDIA_DATA?.videoUrl) || '/videos/sample-video.mp3';
|
||||||
|
|
||||||
|
// Check if the media is an audio file
|
||||||
|
const isAudioFile = sampleVideoUrl.match(/\.(mp3|wav|ogg|m4a|aac|flac)$/i) !== null;
|
||||||
|
|
||||||
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty
|
||||||
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
|
const posterImage = mediaPosterUrl || (isAudioFile ? '/audio-poster.jpg' : undefined);
|
||||||
|
|
||||||
// Detect iOS device
|
// Detect iOS device
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -344,6 +351,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
x-webkit-airplay="allow"
|
x-webkit-airplay="allow"
|
||||||
controls={false}
|
controls={false}
|
||||||
muted={isMuted}
|
muted={isMuted}
|
||||||
|
poster={posterImage}
|
||||||
>
|
>
|
||||||
<source src={sampleVideoUrl} type="video/mp4" />
|
<source src={sampleVideoUrl} type="video/mp4" />
|
||||||
<p>Your browser doesn't support HTML5 video.</p>
|
<p>Your browser doesn't support HTML5 video.</p>
|
||||||
|
|||||||
BIN
static/chapters_editor/audio-poster.jpg
Normal file
BIN
static/chapters_editor/audio-poster.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 695 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
static/video_editor/audio-poster.jpg
Normal file
BIN
static/video_editor/audio-poster.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 695 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user