mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-05 23:18:53 -05:00
Refactor audio poster handling and Vite config
Audio poster image is now imported as a module and referenced via AUDIO_POSTER_URL in both chapters-editor and video-editor players. Removed custom file copy plugin from Vite config, updated asset handling to use Vite's asset import and output options, and added type declarations for image modules.
This commit is contained in:
parent
95cb6904ce
commit
67ba1cd467
@ -0,0 +1,6 @@
|
|||||||
|
// Import the audio poster image as a module
|
||||||
|
// Vite will handle this and provide the correct URL
|
||||||
|
import audioPosterJpg from '../../public/audio-poster.jpg';
|
||||||
|
|
||||||
|
export const AUDIO_POSTER_URL = audioPosterJpg;
|
||||||
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import { formatTime } from '@/lib/timeUtils';
|
import { formatTime } from '@/lib/timeUtils';
|
||||||
|
import { AUDIO_POSTER_URL } from '@/assets/audioPosterUrl';
|
||||||
import '../styles/IOSVideoPlayer.css';
|
import '../styles/IOSVideoPlayer.css';
|
||||||
|
|
||||||
interface IOSVideoPlayerProps {
|
interface IOSVideoPlayerProps {
|
||||||
@ -45,7 +46,7 @@ const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps
|
|||||||
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
||||||
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
||||||
setPosterImage(isValidPoster ? mediaPosterUrl : (isAudioFile ? '/audio-poster.jpg' : undefined));
|
setPosterImage(isValidPoster ? mediaPosterUrl : (isAudioFile ? AUDIO_POSTER_URL : undefined));
|
||||||
}, [videoRef]);
|
}, [videoRef]);
|
||||||
|
|
||||||
// Function to jump 15 seconds backward
|
// Function to jump 15 seconds backward
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useRef, useEffect, useState } from 'react';
|
import React, { useRef, useEffect, useState } from 'react';
|
||||||
import { formatTime, formatDetailedTime } from '@/lib/timeUtils';
|
import { formatTime, formatDetailedTime } from '@/lib/timeUtils';
|
||||||
|
import { AUDIO_POSTER_URL } from '@/assets/audioPosterUrl';
|
||||||
import logger from '../lib/logger';
|
import logger from '../lib/logger';
|
||||||
import '../styles/VideoPlayer.css';
|
import '../styles/VideoPlayer.css';
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
||||||
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
||||||
const posterImage = isValidPoster ? mediaPosterUrl : (isAudioFile ? '/audio-poster.jpg' : undefined);
|
const posterImage = isValidPoster ? mediaPosterUrl : (isAudioFile ? AUDIO_POSTER_URL : undefined);
|
||||||
|
|
||||||
// Detect iOS device and Safari browser
|
// Detect iOS device and Safari browser
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
32
frontend-tools/chapters-editor/client/src/vite-env.d.ts
vendored
Normal file
32
frontend-tools/chapters-editor/client/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.jpeg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.png' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.svg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.gif' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.webp' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
@ -2,33 +2,12 @@ import { defineConfig } from 'vite';
|
|||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import path, { dirname } from 'path';
|
import path, { dirname } from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { copyFileSync, mkdirSync } from 'fs';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
// Plugin to copy audio-poster.jpg to build output
|
|
||||||
const copyAudioPoster = () => {
|
|
||||||
return {
|
|
||||||
name: 'copy-audio-poster',
|
|
||||||
closeBundle() {
|
|
||||||
const outDir = path.resolve(__dirname, '../../../static/video_editor');
|
|
||||||
const sourceFile = path.resolve(__dirname, 'client/public/audio-poster.jpg');
|
|
||||||
const destFile = path.resolve(outDir, 'audio-poster.jpg');
|
|
||||||
|
|
||||||
try {
|
|
||||||
mkdirSync(outDir, { recursive: true });
|
|
||||||
copyFileSync(sourceFile, destFile);
|
|
||||||
console.log('✓ Copied audio-poster.jpg to build output');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error copying audio-poster.jpg:', error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), copyAudioPoster()],
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': path.resolve(__dirname, 'client', 'src'),
|
'@': path.resolve(__dirname, 'client', 'src'),
|
||||||
@ -53,8 +32,14 @@ export default defineConfig({
|
|||||||
output: {
|
output: {
|
||||||
assetFileNames: (assetInfo) => {
|
assetFileNames: (assetInfo) => {
|
||||||
if (assetInfo.name === 'style.css') return 'chapters-editor.css';
|
if (assetInfo.name === 'style.css') return 'chapters-editor.css';
|
||||||
return assetInfo.name;
|
// Keep original names for image assets
|
||||||
|
if (assetInfo.name && /\.(png|jpe?g|svg|gif|webp)$/i.test(assetInfo.name)) {
|
||||||
|
return assetInfo.name;
|
||||||
|
}
|
||||||
|
return assetInfo.name || 'asset-[hash][extname]';
|
||||||
},
|
},
|
||||||
|
// Inline small assets, emit larger ones
|
||||||
|
inlineDynamicImports: true,
|
||||||
globals: {
|
globals: {
|
||||||
react: 'React',
|
react: 'React',
|
||||||
'react-dom': 'ReactDOM',
|
'react-dom': 'ReactDOM',
|
||||||
@ -65,5 +50,7 @@ export default defineConfig({
|
|||||||
outDir: '../../../static/video_editor',
|
outDir: '../../../static/video_editor',
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
external: ['react', 'react-dom'],
|
external: ['react', 'react-dom'],
|
||||||
|
// Inline assets smaller than 100KB, emit larger ones
|
||||||
|
assetsInlineLimit: 102400,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
// Import the audio poster image as a module
|
||||||
|
// Vite will handle this and provide the correct URL
|
||||||
|
import audioPosterJpg from '../../public/audio-poster.jpg';
|
||||||
|
|
||||||
|
export const AUDIO_POSTER_URL = audioPosterJpg;
|
||||||
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import { formatTime } from '@/lib/timeUtils';
|
import { formatTime } from '@/lib/timeUtils';
|
||||||
|
import { AUDIO_POSTER_URL } from '@/assets/audioPosterUrl';
|
||||||
import '../styles/IOSVideoPlayer.css';
|
import '../styles/IOSVideoPlayer.css';
|
||||||
|
|
||||||
interface IOSVideoPlayerProps {
|
interface IOSVideoPlayerProps {
|
||||||
@ -45,7 +46,7 @@ const IOSVideoPlayer = ({ videoRef, currentTime, duration }: IOSVideoPlayerProps
|
|||||||
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
||||||
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
||||||
setPosterImage(isValidPoster ? mediaPosterUrl : (isAudioFile ? '/audio-poster.jpg' : undefined));
|
setPosterImage(isValidPoster ? mediaPosterUrl : (isAudioFile ? AUDIO_POSTER_URL : undefined));
|
||||||
}, [videoRef]);
|
}, [videoRef]);
|
||||||
|
|
||||||
// Function to jump 15 seconds backward
|
// Function to jump 15 seconds backward
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useRef, useEffect, useState } from 'react';
|
import React, { useRef, useEffect, useState } from 'react';
|
||||||
import { formatTime, formatDetailedTime } from '@/lib/timeUtils';
|
import { formatTime, formatDetailedTime } from '@/lib/timeUtils';
|
||||||
|
import { AUDIO_POSTER_URL } from '@/assets/audioPosterUrl';
|
||||||
import logger from '../lib/logger';
|
import logger from '../lib/logger';
|
||||||
import '../styles/VideoPlayer.css';
|
import '../styles/VideoPlayer.css';
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
// Get posterUrl from MEDIA_DATA, or use audio-poster.jpg as fallback for audio files when posterUrl is empty, null, or "None"
|
||||||
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
const mediaPosterUrl = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.posterUrl) || '';
|
||||||
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
const isValidPoster = mediaPosterUrl && mediaPosterUrl !== 'None' && mediaPosterUrl.trim() !== '';
|
||||||
const posterImage = isValidPoster ? mediaPosterUrl : (isAudioFile ? '/audio-poster.jpg' : undefined);
|
const posterImage = isValidPoster ? mediaPosterUrl : (isAudioFile ? AUDIO_POSTER_URL : undefined);
|
||||||
|
|
||||||
// Detect iOS device
|
// Detect iOS device
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
32
frontend-tools/video-editor/client/src/vite-env.d.ts
vendored
Normal file
32
frontend-tools/video-editor/client/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.jpeg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.png' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.svg' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.gif' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.webp' {
|
||||||
|
const src: string;
|
||||||
|
export default src;
|
||||||
|
}
|
||||||
|
|
||||||
@ -32,11 +32,17 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
// Ensure CSS file has a predictable name
|
// Ensure CSS file has a predictable name and keep image assets
|
||||||
assetFileNames: (assetInfo) => {
|
assetFileNames: (assetInfo) => {
|
||||||
if (assetInfo.name === 'style.css') return 'video-editor.css';
|
if (assetInfo.name === 'style.css') return 'video-editor.css';
|
||||||
return assetInfo.name;
|
// Keep original names for image assets
|
||||||
|
if (assetInfo.name && /\.(png|jpe?g|svg|gif|webp)$/i.test(assetInfo.name)) {
|
||||||
|
return assetInfo.name;
|
||||||
|
}
|
||||||
|
return assetInfo.name || 'asset-[hash][extname]';
|
||||||
},
|
},
|
||||||
|
// Inline small assets, emit larger ones
|
||||||
|
inlineDynamicImports: true,
|
||||||
// Add this to ensure the final bundle exposes React correctly
|
// Add this to ensure the final bundle exposes React correctly
|
||||||
globals: {
|
globals: {
|
||||||
'react': 'React',
|
'react': 'React',
|
||||||
@ -47,6 +53,8 @@ export default defineConfig({
|
|||||||
// Output to Django's static directory
|
// Output to Django's static directory
|
||||||
outDir: '../../../static/video_editor',
|
outDir: '../../../static/video_editor',
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
external: ['react', 'react-dom']
|
external: ['react', 'react-dom'],
|
||||||
|
// Inline assets smaller than 100KB, emit larger ones
|
||||||
|
assetsInlineLimit: 102400,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user