import React from 'react'; import { Box, Typography, Chip, Stack, Paper, Accordion, AccordionSummary, AccordionDetails, Divider, IconButton, Tooltip, } from '@mui/material'; import Grid from '@mui/material/GridLegacy'; import { ExpandMore as ExpandMoreIcon, PhotoCamera as CameraIcon, LocationOn as LocationIcon, DateRange as DateIcon, Settings as SettingsIcon, AspectRatio as AspectRatioIcon, ColorLens as ColorIcon, Copyright as CopyrightIcon, Person as PersonIcon, Business as BusinessIcon, FileCopy as DocumentIcon, ContentCopy as CopyIcon, } from '@mui/icons-material'; import { modernTokens } from '../theme'; // Define border radius values since they might not be in modernTokens const borderRadius = { sm: 4, md: 8, lg: 12, xl: 16, }; interface MetadataParserProps { metadata: Record; fileType: string; compact?: boolean; } interface ParsedMetadata { category: string; icon: React.ReactElement; items: Array<{ label: string; value: any; type: 'text' | 'date' | 'location' | 'technical' | 'copyable'; unit?: string; }>; } const MetadataParser: React.FC = ({ metadata, fileType, compact = false }) => { const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); }; const parseExifData = (exif: Record): ParsedMetadata[] => { const sections: ParsedMetadata[] = []; // Camera Information const cameraInfo = []; if (exif.make) cameraInfo.push({ label: 'Camera Make', value: exif.make, type: 'text' as const }); if (exif.model) cameraInfo.push({ label: 'Camera Model', value: exif.model, type: 'text' as const }); if (exif.lens_make) cameraInfo.push({ label: 'Lens Make', value: exif.lens_make, type: 'text' as const }); if (exif.lens_model) cameraInfo.push({ label: 'Lens Model', value: exif.lens_model, type: 'text' as const }); if (cameraInfo.length > 0) { sections.push({ category: 'Camera', icon: , items: cameraInfo, }); } // Technical Settings const technicalInfo = []; if (exif.focal_length) technicalInfo.push({ label: 'Focal Length', value: exif.focal_length, type: 'technical' as const, unit: 'mm' }); if (exif.aperture) technicalInfo.push({ label: 'Aperture', value: `f/${exif.aperture}`, type: 'technical' as const }); if (exif.exposure_time) technicalInfo.push({ label: 'Shutter Speed', value: exif.exposure_time, type: 'technical' as const, unit: 's' }); if (exif.iso) technicalInfo.push({ label: 'ISO', value: exif.iso, type: 'technical' as const }); if (exif.flash) technicalInfo.push({ label: 'Flash', value: exif.flash, type: 'text' as const }); if (technicalInfo.length > 0) { sections.push({ category: 'Camera Settings', icon: , items: technicalInfo, }); } // Image Properties const imageInfo = []; if (exif.width && exif.height) { imageInfo.push({ label: 'Dimensions', value: `${exif.width} × ${exif.height}`, type: 'technical' as const, unit: 'px' }); } if (exif.resolution_x && exif.resolution_y) { imageInfo.push({ label: 'Resolution', value: `${exif.resolution_x} × ${exif.resolution_y}`, type: 'technical' as const, unit: 'dpi' }); } if (exif.color_space) imageInfo.push({ label: 'Color Space', value: exif.color_space, type: 'text' as const }); if (exif.orientation) imageInfo.push({ label: 'Orientation', value: exif.orientation, type: 'text' as const }); if (imageInfo.length > 0) { sections.push({ category: 'Image Properties', icon: , items: imageInfo, }); } // Location Data if (exif.gps_latitude && exif.gps_longitude) { sections.push({ category: 'Location', icon: , items: [ { label: 'Coordinates', value: `${exif.gps_latitude}, ${exif.gps_longitude}`, type: 'location' as const }, ...(exif.gps_altitude ? [{ label: 'Altitude', value: exif.gps_altitude, type: 'technical' as const, unit: 'm' }] : []), ], }); } // Timestamps const dateInfo = []; if (exif.date_time_original) dateInfo.push({ label: 'Date Taken', value: exif.date_time_original, type: 'date' as const }); if (exif.date_time_digitized) dateInfo.push({ label: 'Date Digitized', value: exif.date_time_digitized, type: 'date' as const }); if (dateInfo.length > 0) { sections.push({ category: 'Timestamps', icon: , items: dateInfo, }); } return sections; }; const parsePdfMetadata = (pdf: Record): ParsedMetadata[] => { const sections: ParsedMetadata[] = []; // Document Information const docInfo = []; if (pdf.title) docInfo.push({ label: 'Title', value: pdf.title, type: 'text' as const }); if (pdf.author) docInfo.push({ label: 'Author', value: pdf.author, type: 'text' as const }); if (pdf.subject) docInfo.push({ label: 'Subject', value: pdf.subject, type: 'text' as const }); if (pdf.keywords) docInfo.push({ label: 'Keywords', value: pdf.keywords, type: 'text' as const }); if (docInfo.length > 0) { sections.push({ category: 'Document Info', icon: , items: docInfo, }); } // Technical Details const techInfo = []; if (pdf.creator) techInfo.push({ label: 'Created With', value: pdf.creator, type: 'text' as const }); if (pdf.producer) techInfo.push({ label: 'PDF Producer', value: pdf.producer, type: 'text' as const }); if (pdf.pdf_version) techInfo.push({ label: 'PDF Version', value: pdf.pdf_version, type: 'technical' as const }); if (pdf.page_count) techInfo.push({ label: 'Pages', value: pdf.page_count, type: 'technical' as const }); if (pdf.encrypted !== undefined) techInfo.push({ label: 'Encrypted', value: pdf.encrypted ? 'Yes' : 'No', type: 'text' as const }); if (techInfo.length > 0) { sections.push({ category: 'Technical', icon: , items: techInfo, }); } // Timestamps const dateInfo = []; if (pdf.creation_date) dateInfo.push({ label: 'Created', value: pdf.creation_date, type: 'date' as const }); if (pdf.modification_date) dateInfo.push({ label: 'Modified', value: pdf.modification_date, type: 'date' as const }); if (dateInfo.length > 0) { sections.push({ category: 'Timestamps', icon: , items: dateInfo, }); } return sections; }; const parseOfficeMetadata = (office: Record): ParsedMetadata[] => { const sections: ParsedMetadata[] = []; // Document Properties const docInfo = []; if (office.title) docInfo.push({ label: 'Title', value: office.title, type: 'text' as const }); if (office.author) docInfo.push({ label: 'Author', value: office.author, type: 'text' as const }); if (office.company) docInfo.push({ label: 'Company', value: office.company, type: 'text' as const }); if (office.manager) docInfo.push({ label: 'Manager', value: office.manager, type: 'text' as const }); if (office.category) docInfo.push({ label: 'Category', value: office.category, type: 'text' as const }); if (docInfo.length > 0) { sections.push({ category: 'Document Properties', icon: , items: docInfo, }); } // Application Info const appInfo = []; if (office.application) appInfo.push({ label: 'Application', value: office.application, type: 'text' as const }); if (office.app_version) appInfo.push({ label: 'Version', value: office.app_version, type: 'technical' as const }); if (office.template) appInfo.push({ label: 'Template', value: office.template, type: 'text' as const }); if (appInfo.length > 0) { sections.push({ category: 'Application', icon: , items: appInfo, }); } return sections; }; const parseGenericMetadata = (data: Record): ParsedMetadata[] => { const sections: ParsedMetadata[] = []; // Group remaining metadata const otherItems = Object.entries(data) .filter(([key, value]) => value !== null && value !== undefined && value !== '') .map(([key, value]) => ({ label: key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()), value: typeof value === 'object' ? JSON.stringify(value) : String(value), type: 'text' as const, })); if (otherItems.length > 0) { sections.push({ category: 'Additional Properties', icon: , items: otherItems, }); } return sections; }; const formatValue = (item: any) => { switch (item.type) { case 'date': try { return new Date(item.value).toLocaleString(); } catch { return item.value; } case 'location': return item.value; case 'technical': return `${item.value}${item.unit ? ` ${item.unit}` : ''}`; case 'copyable': return ( {item.value} copyToClipboard(item.value)}> ); default: return item.value; } }; // Parse metadata based on file type let parsedSections: ParsedMetadata[] = []; if (fileType.includes('image') && metadata.exif) { parsedSections = [...parsedSections, ...parseExifData(metadata.exif)]; } if (fileType.includes('pdf') && metadata.pdf) { parsedSections = [...parsedSections, ...parsePdfMetadata(metadata.pdf)]; } if ((fileType.includes('officedocument') || fileType.includes('msword')) && metadata.office) { parsedSections = [...parsedSections, ...parseOfficeMetadata(metadata.office)]; } // Add any remaining metadata const remainingMetadata = { ...metadata }; delete remainingMetadata.exif; delete remainingMetadata.pdf; delete remainingMetadata.office; if (Object.keys(remainingMetadata).length > 0) { parsedSections = [...parsedSections, ...parseGenericMetadata(remainingMetadata)]; } if (parsedSections.length === 0) { return ( No detailed metadata available for this file type ); } if (compact) { return ( {parsedSections.slice(0, 2).map((section, index) => ( {section.icon} {section.category} {section.items.slice(0, 3).map((item, itemIndex) => ( {item.label} {formatValue(item)} ))} ))} {parsedSections.length > 2 && ( +{parsedSections.length - 2} more sections... )} ); } return ( {parsedSections.map((section, index) => ( } sx={{ borderRadius: borderRadius.lg, '& .MuiAccordionSummary-content': { alignItems: 'center', }, }} > {section.icon} {section.category} {section.items.map((item, itemIndex) => ( {item.label} {formatValue(item)} ))} ))} ); }; export default MetadataParser;