From 1fe1e84f001277291f4fbae4492422e0b8a9587b Mon Sep 17 00:00:00 2001 From: Ketan Kumar Baboo Date: Mon, 2 Dec 2024 11:46:08 +0530 Subject: [PATCH] implemented download project functionality --- UI/app/components/BooksList.js | 10 +- UI/app/components/DownloadProject.js | 143 +++++++++++++++++++++++++++ UI/app/components/DragAndDrop.js | 14 +-- UI/app/page.js | 9 +- UI/app/utils/usfmProcessor.js | 26 ++--- 5 files changed, 174 insertions(+), 28 deletions(-) create mode 100644 UI/app/components/DownloadProject.js diff --git a/UI/app/components/BooksList.js b/UI/app/components/BooksList.js index d082f88..ca0e591 100644 --- a/UI/app/components/BooksList.js +++ b/UI/app/components/BooksList.js @@ -30,13 +30,15 @@ import useAudioTranscription from "./useAudioTranscription"; import TextToAudioConversion from "./TextToAudioConversion"; import source_languages from "../store/source_languages.json"; import major_languages from "../store/major_languages.json"; +import DownloadProject from "./DownloadProject"; const BooksList = ({ projectInstance, files, setFiles, + jsonFiles, projectName, - bibleMetaData, + licenseData }) => { const [books, setBooks] = useState([]); const [bookData, setBookData] = useState(null); @@ -627,7 +629,7 @@ const BooksList = ({ }, [playingAudio]); const downloadUSFM = (book) => { - processUSFM(projectInstance, book.name, bibleMetaData); + processUSFM(projectInstance, book.name, true); }; const resetProject = () => { @@ -986,9 +988,7 @@ const BooksList = ({ > Reset Project - + ); diff --git a/UI/app/components/DownloadProject.js b/UI/app/components/DownloadProject.js new file mode 100644 index 0000000..b0e3bd1 --- /dev/null +++ b/UI/app/components/DownloadProject.js @@ -0,0 +1,143 @@ +import { Button } from "@mui/material"; +import { styles } from "../StyledComponents"; +import JSZip from "jszip"; +import { processUSFM } from "../utils/usfmProcessor"; +import Swal from "sweetalert2"; + +const DownloadProject = ({ + projectName, + projectInstance, + jsonFiles, + licenseData, +}) => { + const downloadProject = async () => { + const zip = new JSZip(); + const projectFolder = zip.folder(projectName); + const textFolder = projectFolder?.folder("text-1"); + const audioFolder = projectFolder?.folder("audio"); + const audioIngredientsFolder = audioFolder?.folder("ingredients"); + const textIngredientsFolder = textFolder?.folder("ingredients"); + let metaData; + let versification; + let projectSetting; + + console.log("json files", jsonFiles); + console.log("licenseData", licenseData); + + if (licenseData) { + textIngredientsFolder?.file("license.md", licenseData); + audioIngredientsFolder?.file("license.md", licenseData); + } + + for (const json of jsonFiles) { + if (json?.name.endsWith("metadata.json")) { + metaData = json?.content || null; + projectFolder?.file("metadata.json", JSON.stringify(metaData, null, 2)); + textFolder?.file("metadata.json", JSON.stringify(metaData, null, 2)); + } else if (json?.name.endsWith("versification.json")) { + versification = json?.content || null; + textIngredientsFolder?.file( + "versification.json", + JSON.stringify(versification, null, 2) + ); + audioIngredientsFolder?.file( + "versification.json", + JSON.stringify(versification, null, 2) + ); + } else if ( + json?.name.endsWith("ag-settings.json") || + json?.name.endsWith("scribe-settings.json") + ) { + projectSetting = json?.content || null; + const fileName = json?.name.endsWith("scribe-settings.json") + ? "scribe-settings.json" + : "ag-settings.json"; + + textIngredientsFolder?.file( + fileName, + JSON.stringify(projectSetting, null, 2) + ); + audioIngredientsFolder?.file( + fileName, + JSON.stringify(projectSetting, null, 2) + ); + } + } + + try { + const keys = await projectInstance.keys(); + const fetchedData = ( + await Promise.all(keys.map((key) => projectInstance.getItem(key))) + ).filter((data) => data !== null); + + console.log("fetched data from indexed db", fetchedData); + + for (const data of fetchedData) { + if (!data?.book) continue; + + console.log("processing data for book:", data.book); + + // const bookTextFolder = textFolder?.folder(data.book); + const usfmBookData = await processUSFM( + projectInstance, + data.book, + false + ); + if (usfmBookData) { + const encoder = new TextEncoder(); + const usfmBytes = encoder.encode(usfmBookData); + + textIngredientsFolder?.file(`${data.book}.usfm`, usfmBytes, { + binary: true, + compression: "DEFLATE", + compressionOptions: { + level: 6, + }, + comment: "UTF-8 encoded USFM file", + }); + } + + if (data?.generatedAudio) { + const bookAudioFolder = audioIngredientsFolder?.folder(data.book); + const chapterFolder = bookAudioFolder?.folder(data.chapter); + const audioBlob = await data.generatedAudio.arrayBuffer(); + const audioExtension = data.generatedAudio.name.split('.').pop(); + const audioFileName = `${data.chapter}_${data.verse}.${audioExtension}` + chapterFolder?.file(audioFileName, audioBlob); + } + } + + const zipBlob = await zip.generateAsync({ + type: "blob", + compression: "DEFLATE", + compressionOptions: { + level: 6, + }, + }); + + const blobUrl = URL.createObjectURL(zipBlob); + const a = document.createElement("a"); + a.href = blobUrl; + a.download = "project.zip"; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(blobUrl); + } catch (error) { + console.error("Error during project download:", error); + Swal.fire("Error", `Error during downloading project`, "error"); + } + }; + + return ( + + ); +}; + +export default DownloadProject; diff --git a/UI/app/components/DragAndDrop.js b/UI/app/components/DragAndDrop.js index 5055697..2823635 100644 --- a/UI/app/components/DragAndDrop.js +++ b/UI/app/components/DragAndDrop.js @@ -17,8 +17,8 @@ const DragAndDrop = ({ onFilesExtracted }) => { const extractedBooks = []; const jsonFiles = []; let maxVersesData = {}; - let bibleMetaData = {}; let projectName = ""; + let licenseContent = ""; const processAudioFile = ( bookName, @@ -52,6 +52,11 @@ const DragAndDrop = ({ onFilesExtracted }) => { if (!projectName) projectName = pathParts[0]; + if (relativePath.endsWith("license.md")) { + licenseContent = await file.async("string"); + continue; + } + if (relativePath.endsWith(".json")) { const fileData = await file.async("string"); const parsedContent = JSON.parse(fileData); @@ -78,11 +83,6 @@ const DragAndDrop = ({ onFilesExtracted }) => { file.name.endsWith("metadata.json") ) { try { - const localizedBibles = parsedContent["localizedNames"]; - bibleMetaData = - typeof localizedBibles === "string" - ? JSON.parse(localizedBibles) - : localizedBibles; const metadataProjectname = parsedContent["identification"]?.name?.["en"]; if ( @@ -125,7 +125,7 @@ const DragAndDrop = ({ onFilesExtracted }) => { jsonFiles, projectName, maxVersesData, - bibleMetaData + licenseContent ); } catch (error) { console.error("Error extracting zip file:", error); diff --git a/UI/app/page.js b/UI/app/page.js index 523f45d..bf73a5d 100644 --- a/UI/app/page.js +++ b/UI/app/page.js @@ -11,9 +11,9 @@ export default function Home() { const [files, setFiles] = useState([]); const [jsonFiles, setJsonFiles] = useState([]); const [maxVerses, setMaxVerses] = useState(null); - const [bibleMetaData, setBibleMetadata] = useState(null); const [projectName, setProjectName] = useState(""); const [projectDB, setProjectDB] = useState(null); + const [licenseData, setLicenseData] = useState(null); useEffect(() => { if (projectName) { @@ -30,12 +30,12 @@ export default function Home() { jsonFiles, projectName, maxVersesData, - bibleMetaData + licenseContent ) => { setJsonFiles(jsonFiles); setProjectName(projectName); setMaxVerses(maxVersesData); - setBibleMetadata(bibleMetaData); + setLicenseData(licenseContent) validateBooks(extractedFiles, maxVersesData); const sortedBooks = sortBooks(extractedFiles); setFiles(sortedBooks); @@ -164,8 +164,9 @@ export default function Home() { projectInstance={projectDB} files={files} setFiles={setFiles} + jsonFiles ={jsonFiles} projectName={projectName} - bibleMetaData={bibleMetaData} + licenseData = {licenseData} /> )} diff --git a/UI/app/utils/usfmProcessor.js b/UI/app/utils/usfmProcessor.js index 117afc4..b231bd4 100644 --- a/UI/app/utils/usfmProcessor.js +++ b/UI/app/utils/usfmProcessor.js @@ -1,7 +1,7 @@ import Swal from "sweetalert2"; import LocalizedNames from "../store/localizedNames.json"; -export const processUSFM = async (projectInstance, selectedBook, bibleMetaData) => { +export const processUSFM = async (projectInstance, selectedBook, download = true) => { const keys = await projectInstance.keys(); const filteredKeys = keys.filter((key) => key.startsWith(selectedBook)); @@ -20,20 +20,22 @@ export const processUSFM = async (projectInstance, selectedBook, bibleMetaData) } return a.chapter - b.chapter; // Sort by chapter }); - // const metaData = bibleMetaData[selectedBook] const metaData = LocalizedNames[selectedBook] const usfmContent = generateUSFMContent(sortedData, selectedBook, metaData); - // Create a Blob and trigger download - const blob = new Blob([usfmContent], { type: "text/plain;charset=utf-8" }); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = `${selectedBook}.usfm`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(url); + if (download) { + const blob = new Blob([usfmContent], { type: "text/plain;charset=utf-8" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${selectedBook}.usfm`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + } else { + return usfmContent; + } }; const generateUSFMContent = (sortedData, selectedBook, metaData) => {