198 lines
No EOL
5.7 KiB
TypeScript
198 lines
No EOL
5.7 KiB
TypeScript
import { useState, useCallback } from "react";
|
|
import { toast } from "sonner";
|
|
import { ApiResponseHandler } from "@/app/(presentation-generator)/services/api/api-error-handler";
|
|
import { ProcessedSlide, SlideData, FontData } from "../types";
|
|
|
|
export const useSlideProcessing = (
|
|
selectedFile: File | null,
|
|
slides: ProcessedSlide[],
|
|
setSlides: React.Dispatch<React.SetStateAction<ProcessedSlide[]>>,
|
|
fontsData: FontData | null,
|
|
setFontsData: React.Dispatch<React.SetStateAction<FontData | null>>
|
|
) => {
|
|
const [isProcessingPptx, setIsProcessingPptx] = useState(false);
|
|
|
|
// Process individual slide to HTML
|
|
const processSlideToHtml = useCallback(
|
|
async (slide: SlideData, index: number) => {
|
|
console.log(
|
|
`Starting to process slide ${slide.slide_number} at index ${index}`
|
|
);
|
|
|
|
// Update slide to processing state
|
|
setSlides((prev) =>
|
|
prev.map((s, i) =>
|
|
i === index ? { ...s, processing: true, error: undefined } : s
|
|
)
|
|
);
|
|
|
|
try {
|
|
const htmlResponse = await fetch("/api/v1/ppt/slide-to-html/", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
image: slide.screenshot_url,
|
|
xml: slide.xml_content,
|
|
}),
|
|
});
|
|
|
|
const htmlData = await ApiResponseHandler.handleResponse(
|
|
htmlResponse,
|
|
`Failed to convert slide ${slide.slide_number} to HTML`
|
|
);
|
|
|
|
console.log(`Successfully processed slide ${slide.slide_number}`);
|
|
|
|
// Update slide with success
|
|
setSlides((prev) => {
|
|
const newSlides = prev.map((s, i) =>
|
|
i === index
|
|
? {
|
|
...s,
|
|
processing: false,
|
|
processed: true,
|
|
html: htmlData.html,
|
|
}
|
|
: s
|
|
);
|
|
|
|
// Process next slide if available
|
|
const nextIndex = index + 1;
|
|
if (
|
|
nextIndex < newSlides.length &&
|
|
!newSlides[nextIndex].processed &&
|
|
!newSlides[nextIndex].processing
|
|
) {
|
|
console.log(
|
|
`Scheduling next slide ${nextIndex + 1} for processing`
|
|
);
|
|
setTimeout(() => {
|
|
const nextSlide = newSlides[nextIndex];
|
|
processSlideToHtml(nextSlide, nextIndex);
|
|
}, 1000); // 1 second delay between slides
|
|
}
|
|
|
|
return newSlides;
|
|
});
|
|
} catch (error) {
|
|
console.error(`Error processing slide ${slide.slide_number}:`, error);
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : "Failed to convert to HTML";
|
|
|
|
// Update slide with error
|
|
setSlides((prev) => {
|
|
const newSlides = prev.map((s, i) =>
|
|
i === index
|
|
? {
|
|
...s,
|
|
processing: false,
|
|
processed: false,
|
|
error: errorMessage,
|
|
}
|
|
: s
|
|
);
|
|
|
|
// Continue with next slide even if this one failed
|
|
const nextIndex = index + 1;
|
|
if (
|
|
nextIndex < newSlides.length &&
|
|
!newSlides[nextIndex].processed &&
|
|
!newSlides[nextIndex].processing
|
|
) {
|
|
console.log(`Scheduling next slide ${nextIndex + 1} after error`);
|
|
setTimeout(() => {
|
|
const nextSlide = newSlides[nextIndex];
|
|
processSlideToHtml(nextSlide, nextIndex);
|
|
}, 1000);
|
|
}
|
|
|
|
return newSlides;
|
|
});
|
|
}
|
|
},
|
|
[]
|
|
);
|
|
|
|
// Process PPTX file to extract slides
|
|
const processFile = useCallback(async () => {
|
|
if (!selectedFile) {
|
|
toast.error("Please select a PPTX file first");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsProcessingPptx(true);
|
|
|
|
const formData = new FormData();
|
|
formData.append("pptx_file", selectedFile);
|
|
|
|
const pptxResponse = await fetch("/api/v1/ppt/pptx-slides/process", {
|
|
method: "POST",
|
|
body: formData,
|
|
});
|
|
const pptxData = await ApiResponseHandler.handleResponse(
|
|
pptxResponse,
|
|
"Failed to process PPTX file"
|
|
);
|
|
|
|
if (!pptxData.success || !pptxData.slides?.length) {
|
|
throw new Error("No slides found in the PPTX file");
|
|
}
|
|
|
|
// Extract fonts data from the response
|
|
if (pptxData.fonts) {
|
|
setFontsData(pptxData.fonts);
|
|
}
|
|
|
|
// Initialize slides with skeleton state
|
|
const initialSlides: ProcessedSlide[] = pptxData.slides.map(
|
|
(slide: any) => ({
|
|
...slide,
|
|
processing: false,
|
|
processed: false,
|
|
})
|
|
);
|
|
|
|
setSlides(initialSlides);
|
|
|
|
toast.success(
|
|
`Successfully extracted ${pptxData.slides.length} slides! Converting to HTML...`
|
|
);
|
|
|
|
// Start processing first slide
|
|
setTimeout(() => {
|
|
console.log("Starting to process first slide");
|
|
processSlideToHtml(initialSlides[0], 0);
|
|
}, 500);
|
|
} catch (error) {
|
|
console.error("Error processing file:", error);
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : "An unexpected error occurred";
|
|
toast.error("Processing failed", {
|
|
description: errorMessage,
|
|
});
|
|
} finally {
|
|
setIsProcessingPptx(false);
|
|
}
|
|
}, [selectedFile, processSlideToHtml, setSlides, setFontsData]);
|
|
|
|
// Retry failed slide
|
|
const retrySlide = useCallback(
|
|
(index: number) => {
|
|
const slide = slides[index];
|
|
if (slide) {
|
|
processSlideToHtml(slide, index);
|
|
}
|
|
},
|
|
[slides, processSlideToHtml]
|
|
);
|
|
|
|
return {
|
|
isProcessingPptx,
|
|
processFile,
|
|
processSlideToHtml,
|
|
retrySlide,
|
|
};
|
|
};
|