diff --git a/app/admin/page.tsx b/app/admin/page.tsx index 2fcdd61..edcc176 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -66,11 +66,18 @@ function CardTypeSelect({ ); } -const isVideoUrl = (url: string) => /\.(mp4|webm|mov|m4v|ogv)(\?|$)/i.test(url); +const VIDEO_EXTENSIONS = 'mp4|m4v|webm|mov|qt|mkv|avi|divx|wmv|asf|flv|f4v|3gp|3gpp|3g2|mts|m2ts|ts|mpg|mpeg|vob|mxf|ogv|ogg'; +// Sottoinsieme di formati video davvero riproducibili dai browser moderni +const PLAYBACK_SUPPORTED_VIDEO = 'mp4|m4v|webm|mov|qt|ogv|ogg'; +const PLAYBACK_SUPPORTED_LABEL = 'MP4, M4V, WebM, MOV, OGV'; + +const isVideoUrl = (url: string) => new RegExp(`\\.(${VIDEO_EXTENSIONS})(\\?|$)`, 'i').test(url); const isPdfFile = (file: File) => file.type === 'application/pdf' || /\.pdf$/i.test(file.name); const isVideoFile = (file: File) => - file.type.startsWith('video/') || /\.(mp4|webm|mov|m4v|ogv)$/i.test(file.name); + file.type.startsWith('video/') || new RegExp(`\\.(${VIDEO_EXTENSIONS})$`, 'i').test(file.name); +const isPlayableVideoFile = (file: File) => + new RegExp(`\\.(${PLAYBACK_SUPPORTED_VIDEO})$`, 'i').test(file.name); const extractFileName = (url: string): string => { const match = url.match(/[?&]name=([^&]+)/); @@ -171,7 +178,7 @@ export default function AdminDashboard() { const [uploading, setUploading] = useState<{ [key: string]: boolean }>({}); // NEW UI STATES: Toast and Confirm Dialog - const [toast, setToast] = useState(null); + const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null); const [confirmDialog, setConfirmDialog] = useState<{ message: string, onConfirm: () => void } | null>(null); const [pdfProgress, setPdfProgress] = useState<{ name: string; page: number; total: number } | null>(null); @@ -179,9 +186,9 @@ export default function AdminDashboard() { const externalLinksOn = portal.externalLinkEnabled ?? EXTERNAL_LINK_DEFAULT; // Helper to show auto-dismissing toast - const showToast = (message: string) => { - setToast(message); - setTimeout(() => setToast(null), 3000); + const showToast = (message: string, type: 'success' | 'error' = 'success') => { + setToast({ message, type }); + setTimeout(() => setToast(null), type === 'error' ? 6000 : 3000); }; useEffect(() => { @@ -217,8 +224,33 @@ export default function AdminDashboard() { let pendingCover: string | null = null; const canPromote = () => startedWithoutCover && !pendingCover; - const uploaded: MediaItem[] = []; + // Pre-filtro: scarta video con formati non riproducibili nei browser + const rejected: string[] = []; + const acceptedFiles: File[] = []; for (const file of Array.from(files)) { + if (isVideoFile(file) && !isPlayableVideoFile(file)) { + rejected.push(file.name); + } else { + acceptedFiles.push(file); + } + } + if (rejected.length > 0) { + const list = rejected.length <= 3 + ? rejected.join(', ') + : `${rejected.slice(0, 3).join(', ')} e altri ${rejected.length - 3}`; + showToast( + `Formato non supportato! I formati supportati sono: ${PLAYBACK_SUPPORTED_LABEL}. File ignorati: ${list}`, + 'error' + ); + } + if (acceptedFiles.length === 0) { + setUploading(prev => ({ ...prev, extraMedia: false })); + e.target.value = ''; + return; + } + + const uploaded: MediaItem[] = []; + for (const file of acceptedFiles) { try { if (isPdfFile(file)) { const items = await pdfToImageItems(file, (page, total) => @@ -701,7 +733,7 @@ export default function AdminDashboard() {
-
- ✓ +
+
+ {toast.type === 'error' ? '!' : '✓'}
- {toast} + {toast.message}
)} diff --git a/components/PublicGrid.tsx b/components/PublicGrid.tsx index f0b4ccd..8006010 100644 --- a/components/PublicGrid.tsx +++ b/components/PublicGrid.tsx @@ -2,7 +2,8 @@ import { useState, useEffect, useCallback, useRef } from 'react'; import { Card, MediaItem } from '@/types'; -const isVideoUrl = (url: string) => /\.(mp4|webm|mov|m4v|ogv)(\?|$)/i.test(url); +const VIDEO_EXTENSIONS = 'mp4|m4v|webm|mov|qt|mkv|avi|divx|wmv|asf|flv|f4v|3gp|3gpp|3g2|mts|m2ts|ts|mpg|mpeg|vob|mxf|ogv|ogg'; +const isVideoUrl = (url: string) => new RegExp(`\\.(${VIDEO_EXTENSIONS})(\\?|$)`, 'i').test(url); function MediaCarousel({ items,