Stirling-PDF/frontend/src/hooks/tools/convert/useConvertParameters.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

299 lines
9.5 KiB
TypeScript
Raw Normal View History

2025-07-30 14:24:23 +01:00
import { useState, useEffect } from 'react';
2025-07-23 11:25:55 +01:00
import {
COLOR_TYPES,
OUTPUT_OPTIONS,
2025-07-30 14:24:23 +01:00
FIT_OPTIONS,
2025-07-23 11:25:55 +01:00
TO_FORMAT_OPTIONS,
CONVERSION_MATRIX,
type ColorType,
2025-07-30 14:24:23 +01:00
type OutputOption,
type FitOption
2025-07-23 11:25:55 +01:00
} from '../../../constants/convertConstants';
2025-07-31 13:47:48 +01:00
import { getEndpointName as getEndpointNameUtil, getEndpointUrl, isImageFormat, isWebFormat } from '../../../utils/convertUtils';
import { detectFileExtension as detectFileExtensionUtil } from '../../../utils/fileUtils';
2025-07-23 11:25:55 +01:00
export interface ConvertParameters {
fromExtension: string;
toExtension: string;
imageOptions: {
colorType: ColorType;
dpi: number;
singleOrMultiple: OutputOption;
2025-07-30 14:24:23 +01:00
fitOption: FitOption;
autoRotate: boolean;
combineImages: boolean;
2025-07-23 11:25:55 +01:00
};
2025-07-31 13:47:48 +01:00
htmlOptions: {
zoomLevel: number;
};
2025-07-31 14:46:14 +01:00
emailOptions: {
includeAttachments: boolean;
maxAttachmentSizeMB: number;
downloadHtml: boolean;
includeAllRecipients: boolean;
};
2025-07-31 15:23:38 +01:00
pdfaOptions: {
outputFormat: string;
};
2025-07-30 14:24:23 +01:00
isSmartDetection: boolean;
2025-07-31 13:47:48 +01:00
smartDetectionType: 'mixed' | 'images' | 'web' | 'none';
2025-07-23 11:25:55 +01:00
}
export interface ConvertParametersHook {
parameters: ConvertParameters;
updateParameter: (parameter: keyof ConvertParameters, value: any) => void;
resetParameters: () => void;
validateParameters: () => boolean;
getEndpointName: () => string;
getEndpoint: () => string;
getAvailableToExtensions: (fromExtension: string) => Array<{value: string, label: string, group: string}>;
2025-07-30 14:24:23 +01:00
analyzeFileTypes: (files: Array<{name: string}>) => void;
2025-07-23 11:25:55 +01:00
}
const initialParameters: ConvertParameters = {
fromExtension: '',
toExtension: '',
imageOptions: {
colorType: COLOR_TYPES.COLOR,
dpi: 300,
singleOrMultiple: OUTPUT_OPTIONS.MULTIPLE,
2025-07-30 14:24:23 +01:00
fitOption: FIT_OPTIONS.MAINTAIN_ASPECT,
autoRotate: true,
combineImages: true,
2025-07-23 11:25:55 +01:00
},
2025-07-31 13:47:48 +01:00
htmlOptions: {
zoomLevel: 1.0,
},
2025-07-31 14:46:14 +01:00
emailOptions: {
includeAttachments: true,
maxAttachmentSizeMB: 10,
downloadHtml: false,
includeAllRecipients: false,
},
2025-07-31 15:23:38 +01:00
pdfaOptions: {
outputFormat: 'pdfa-1',
},
2025-07-30 14:24:23 +01:00
isSmartDetection: false,
smartDetectionType: 'none',
2025-07-23 11:25:55 +01:00
};
export const useConvertParameters = (): ConvertParametersHook => {
const [parameters, setParameters] = useState<ConvertParameters>(initialParameters);
const updateParameter = (parameter: keyof ConvertParameters, value: any) => {
setParameters(prev => ({ ...prev, [parameter]: value }));
};
const resetParameters = () => {
setParameters(initialParameters);
};
const validateParameters = () => {
const { fromExtension, toExtension } = parameters;
if (!fromExtension || !toExtension) return false;
// Check if conversion is supported
const supportedToExtensions = CONVERSION_MATRIX[fromExtension];
if (!supportedToExtensions || !supportedToExtensions.includes(toExtension)) {
return false;
}
// Additional validation for image conversions
if (['png', 'jpg'].includes(toExtension)) {
return parameters.imageOptions.dpi >= 72 && parameters.imageOptions.dpi <= 600;
}
return true;
};
const getEndpointName = () => {
2025-07-30 14:24:23 +01:00
const { fromExtension, toExtension, isSmartDetection, smartDetectionType } = parameters;
if (isSmartDetection) {
if (smartDetectionType === 'mixed') {
// Mixed file types -> PDF using file-to-pdf endpoint
return 'file-to-pdf';
} else if (smartDetectionType === 'images') {
// All images -> PDF using img-to-pdf endpoint
return 'img-to-pdf';
2025-07-31 13:47:48 +01:00
} else if (smartDetectionType === 'web') {
// All web files -> PDF using html-to-pdf endpoint
return 'html-to-pdf';
2025-07-30 14:24:23 +01:00
}
}
2025-07-23 17:23:25 +01:00
return getEndpointNameUtil(fromExtension, toExtension);
2025-07-23 11:25:55 +01:00
};
const getEndpoint = () => {
2025-07-30 14:24:23 +01:00
const { fromExtension, toExtension, isSmartDetection, smartDetectionType } = parameters;
if (isSmartDetection) {
if (smartDetectionType === 'mixed') {
// Mixed file types -> PDF using file-to-pdf endpoint
return '/api/v1/convert/file/pdf';
} else if (smartDetectionType === 'images') {
// All images -> PDF using img-to-pdf endpoint
return '/api/v1/convert/img/pdf';
2025-07-31 13:47:48 +01:00
} else if (smartDetectionType === 'web') {
// All web files -> PDF using html-to-pdf endpoint
return '/api/v1/convert/html/pdf';
2025-07-30 14:24:23 +01:00
}
}
2025-07-23 17:23:25 +01:00
return getEndpointUrl(fromExtension, toExtension);
2025-07-23 11:25:55 +01:00
};
const getAvailableToExtensions = (fromExtension: string) => {
if (!fromExtension) return [];
2025-07-30 20:03:11 +01:00
let supportedExtensions = CONVERSION_MATRIX[fromExtension] || [];
// If no explicit conversion exists, but file-to-pdf might be available,
// fall back to 'any' conversion (which converts unknown files to PDF via file-to-pdf)
if (supportedExtensions.length === 0 && fromExtension !== 'any') {
supportedExtensions = CONVERSION_MATRIX['any'] || [];
}
2025-07-23 11:25:55 +01:00
return TO_FORMAT_OPTIONS.filter(option =>
supportedExtensions.includes(option.value)
);
};
2025-07-30 14:24:23 +01:00
const analyzeFileTypes = (files: Array<{name: string}>) => {
2025-07-31 13:47:48 +01:00
if (files.length === 0) {
2025-07-31 15:28:28 +01:00
// No files - only reset smart detection, keep user's format choices
2025-07-31 13:47:48 +01:00
setParameters(prev => ({
...prev,
isSmartDetection: false,
2025-07-31 15:28:28 +01:00
smartDetectionType: 'none'
// Don't reset fromExtension and toExtension - let user keep their choices
2025-07-31 13:47:48 +01:00
}));
return;
}
if (files.length === 1) {
2025-07-31 15:28:28 +01:00
// Single file - use regular detection with smart target selection
2025-07-31 13:47:48 +01:00
const detectedExt = detectFileExtensionUtil(files[0].name);
2025-07-30 20:03:11 +01:00
let fromExt = detectedExt;
let availableTargets = detectedExt ? CONVERSION_MATRIX[detectedExt] || [] : [];
// If no explicit conversion exists for this file type, fall back to 'any'
// which will attempt file-to-pdf conversion if available
if (availableTargets.length === 0) {
fromExt = 'any';
availableTargets = CONVERSION_MATRIX['any'] || [];
}
2025-07-31 15:28:28 +01:00
setParameters(prev => {
// Check if current toExtension is still valid for the new fromExtension
const currentToExt = prev.toExtension;
const isCurrentToExtValid = availableTargets.includes(currentToExt);
// Auto-select target only if:
// 1. No current target is set, OR
// 2. Current target is invalid for new source type, OR
// 3. There's only one possible target (forced conversion)
let newToExtension = currentToExt;
if (!currentToExt || !isCurrentToExtValid) {
newToExtension = availableTargets.length === 1 ? availableTargets[0] : '';
}
return {
...prev,
isSmartDetection: false,
smartDetectionType: 'none',
fromExtension: fromExt,
toExtension: newToExtension
};
});
2025-07-30 14:24:23 +01:00
return;
}
// Multiple files - analyze file types
2025-07-31 13:47:48 +01:00
const extensions = files.map(file => detectFileExtensionUtil(file.name));
2025-07-30 14:24:23 +01:00
const uniqueExtensions = [...new Set(extensions)];
if (uniqueExtensions.length === 1) {
2025-07-31 15:28:28 +01:00
// All files are the same type - use regular detection with smart target selection
2025-07-30 20:03:11 +01:00
const detectedExt = uniqueExtensions[0];
let fromExt = detectedExt;
let availableTargets = CONVERSION_MATRIX[detectedExt] || [];
// If no explicit conversion exists for this file type, fall back to 'any'
if (availableTargets.length === 0) {
fromExt = 'any';
availableTargets = CONVERSION_MATRIX['any'] || [];
}
2025-07-31 15:28:28 +01:00
setParameters(prev => {
// Check if current toExtension is still valid for the new fromExtension
const currentToExt = prev.toExtension;
const isCurrentToExtValid = availableTargets.includes(currentToExt);
// Auto-select target only if:
// 1. No current target is set, OR
// 2. Current target is invalid for new source type, OR
// 3. There's only one possible target (forced conversion)
let newToExtension = currentToExt;
if (!currentToExt || !isCurrentToExtValid) {
newToExtension = availableTargets.length === 1 ? availableTargets[0] : '';
}
return {
...prev,
isSmartDetection: false,
smartDetectionType: 'none',
fromExtension: fromExt,
toExtension: newToExtension
};
});
2025-07-30 14:24:23 +01:00
} else {
// Mixed file types
const allImages = uniqueExtensions.every(ext => isImageFormat(ext));
2025-07-31 13:47:48 +01:00
const allWeb = uniqueExtensions.every(ext => isWebFormat(ext));
2025-07-30 14:24:23 +01:00
if (allImages) {
// All files are images - use image-to-pdf conversion
setParameters(prev => ({
...prev,
isSmartDetection: true,
smartDetectionType: 'images',
fromExtension: 'image',
toExtension: 'pdf'
}));
2025-07-31 13:47:48 +01:00
} else if (allWeb) {
// All files are web files - use html-to-pdf conversion
setParameters(prev => ({
...prev,
isSmartDetection: true,
smartDetectionType: 'web',
fromExtension: 'html',
toExtension: 'pdf'
}));
2025-07-30 14:24:23 +01:00
} else {
// Mixed non-image types - use file-to-pdf conversion
setParameters(prev => ({
...prev,
isSmartDetection: true,
smartDetectionType: 'mixed',
fromExtension: 'any',
toExtension: 'pdf'
}));
}
}
};
2025-07-23 11:25:55 +01:00
return {
parameters,
updateParameter,
resetParameters,
validateParameters,
getEndpointName,
getEndpoint,
getAvailableToExtensions,
2025-07-30 14:24:23 +01:00
analyzeFileTypes,
2025-07-23 11:25:55 +01:00
};
};