diff --git a/components/Project/Download.js b/components/Project/Download.js index 63186833..303512bf 100644 --- a/components/Project/Download.js +++ b/components/Project/Download.js @@ -15,7 +15,7 @@ import ButtonLoading from 'components/ButtonLoading' import CheckBox from 'components/CheckBox' import ListBox from 'components/ListBox' -import { newTestamentList, usfmFileNames } from 'utils/config' +import { newTestamentList, obsStoryVerses, usfmFileNames } from 'utils/config' import { compileChapter, convertToUsfm, @@ -60,7 +60,10 @@ function Download({ switch (project?.type) { case 'obs': if (isBook) { - extraOptions = [{ label: 'ZIP', value: 'zip' }] + extraOptions = [ + { label: 'ZIP', value: 'zip' }, + { label: t('projects:Project'), value: 'project' }, + ] } else { extraOptions = [{ label: 'Markdown', value: 'markdown' }] } @@ -203,29 +206,45 @@ function Download({ : t('books:' + bookCode), }, ] - const createChapters = async (bookLink) => { + const createChapters = async (bookLink, typeProject) => { if (!bookLink) return null - - const { data: jsonChapterVerse, error: errorJsonChapterVerse } = - await getCountChaptersAndVerses({ - link: bookLink, - }) - if (errorJsonChapterVerse) { - return null + let chapterVerse = {} + if (typeProject === 'obs') { + chapterVerse = obsStoryVerses + } else { + const { data: jsonChapterVerse, error: errorJsonChapterVerse } = + await getCountChaptersAndVerses({ + link: bookLink, + }) + chapterVerse = jsonChapterVerse + if (errorJsonChapterVerse) { + return null + } } const chapters = {} - for (const chapterNum in jsonChapterVerse) { - if (Object.hasOwnProperty.call(jsonChapterVerse, chapterNum)) { - const verses = jsonChapterVerse[chapterNum] + for (const chapterNum in chapterVerse) { + if (Object.hasOwnProperty.call(chapterVerse, chapterNum)) { + const verses = chapterVerse[chapterNum] const newVerses = {} + + if (typeProject === 'obs') { + newVerses[0] = { text: '', enabled: false, history: [] } + } + for (let index = 1; index < verses + 1; index++) { newVerses[index] = { text: '', enabled: false, history: [] } } + + if (typeProject === 'obs') { + newVerses[200] = { text: '', enabled: false, history: [] } + } + chapters[chapterNum] = newVerses } } return chapters } + const getResourcesUrls = async (resources) => { if (!resources) return null const urls = {} @@ -235,6 +254,14 @@ function Download({ if (resource === 'tAcademy') { continue } + if (resource === 'obs') { + const { owner, repo } = resources[resource] + const url = ` ${ + process.env.NEXT_PUBLIC_NODE_HOST ?? 'https://git.door43.org' + }/${owner}/${repo}/archive/master.zip` + urls[resource] = url + continue + } const { owner, repo, commit, manifest } = resources[resource] const bookPath = manifest.projects.find((el) => el.identifier === bookCode)?.path const url = ` ${ @@ -265,7 +292,7 @@ function Download({ try { const parts = url.split('/') const baseUrl = parts.slice(0, 3).join('/') - const repo = parts[4].slice(0, -1) + const repo = parts[4].split('_')[0] + '_tw' const owner = parts[3] const newUrl = `${baseUrl}/${owner}/${repo}/archive/master.zip` const response = await axios.get(newUrl, { responseType: 'arraybuffer' }) @@ -319,7 +346,13 @@ function Download({ if (!chapters || !project) { return null } - const initChapters = Object.keys(chapters).reduce((acc, chapter) => { + + const sortedChapters = Object.fromEntries( + Object.entries(chapters) + .map(([key, value]) => [parseInt(key, 10), value]) + .sort(([a], [b]) => a - b) + ) + const initChapters = Object.keys(sortedChapters).reduce((acc, chapter) => { acc[chapter] = 0 return acc }, {}) @@ -338,15 +371,59 @@ function Download({ book: { code: bookCode, name: bookName }, resources: addResourceName(project.resources), mainResource: project.base_manifest.resource, + typeProject: project.type, + language: { is_rtl: project.is_rtl }, } return JSON.stringify(config) } - const downloadResources = async (resourcesUrls, zip) => { + const downloadResources = async (resourcesUrls, zip, typeProject) => { for (const resource in resourcesUrls) { - if (Object.hasOwnProperty.call(resourcesUrls, resource)) { - const url = resourcesUrls[resource] - try { + if (!Object.hasOwnProperty.call(resourcesUrls, resource)) continue + + const url = resourcesUrls[resource] + try { + if (resource === 'obs') { + const response = await axios.get(url, { responseType: 'arraybuffer' }) + if (response.status !== 200) + throw new Error(`Failed to fetch OBS archive: ${url}`) + + const obsZip = await JSZip.loadAsync(response.data) + + const rootFolder = Object.keys(obsZip.files).find( + (path) => obsZip.files[path].dir + ) + if (!rootFolder) throw new Error('No root folder found in OBS archive') + + const newObsZip = new JSZip() + for (const filePath of Object.keys(obsZip.files)) { + const file = obsZip.files[filePath] + if (file.dir || !filePath.startsWith(rootFolder)) continue + + const newPath = filePath.slice(rootFolder.length) + const content = await file.async('nodebuffer') + newObsZip.file(newPath, content) + } + + const newObsZipContent = await newObsZip.generateAsync({ type: 'nodebuffer' }) + zip.file('obs.zip', newObsZipContent) + + const { data: obsImagesUrl } = await getOBSImages() + if (!obsImagesUrl) throw new Error('OBS images URL is not defined') + const responseObsImages = await axios.get(obsImagesUrl, { + responseType: 'arraybuffer', + }) + + if (responseObsImages.status !== 200) + throw new Error(`Failed to fetch OBS images from storage: ${obsImagesUrl}`) + + const obsImagesZip = await JSZip.loadAsync(responseObsImages.data) + + const obsImagesZipContent = await obsImagesZip.generateAsync({ + type: 'nodebuffer', + }) + zip.file('obs-images-360px.zip', obsImagesZipContent) + } else { const response = await axios.get(url) if (response.status === 200) { const content = response.data @@ -354,9 +431,9 @@ function Download({ } else { throw new Error(`Failed to fetch resource: ${url}`) } - } catch (error) { - console.error(`Error loading: ${url}`, error) } + } catch (error) { + console.error(`Error loading ${url}:`, error) } } } @@ -382,6 +459,21 @@ function Download({ zip.folder(foldername) }) } + const getOBSImages = async () => { + try { + const response = await axios.get(`/api/obs-images`) + + if (response.status === 200) { + const content = response.data + return content + } else { + throw new Error(`Failed to fetch resource: ${url}`) + } + } catch (error) { + console.error(`Error loading ${url}:`, error) + return null + } + } const createOfflineProject = async (project, bookCode) => { try { const bookLink = project.base_manifest.books.find( @@ -390,7 +482,7 @@ function Download({ if (!bookLink) { throw new Error('Book link not found') } - const chapters = await createChapters(bookLink) + const chapters = await createChapters(bookLink, project.type) if (!chapters) { throw new Error('Chapters not created') } @@ -401,7 +493,7 @@ function Download({ if (!resourcesUrls) { throw new Error('Resource URLs not found') } - await downloadResources(resourcesUrls, zip) + await downloadResources(resourcesUrls, zip, project.type) const tWordsBuffer = await getTwords(resourcesUrls['twords']) if (!tWordsBuffer) { throw new Error('tWords not fetched') diff --git a/components/StartPage/Download.js b/components/StartPage/Download.js index d693eb8e..1aadcb07 100644 --- a/components/StartPage/Download.js +++ b/components/StartPage/Download.js @@ -53,32 +53,33 @@ function Download({ t, onClose }) { fetchVersion() }, []) - const getDownloadLink = () => { + const getDownloadLink = (type) => { if (os.os === 'Windows') { return os.architecture === '64-bit' - ? `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-x64-${version}.exe` - : `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-ia32-${version}.exe` + ? `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-x64-${version}-${type}.exe` + : `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-ia32-${version}-${type}.exe` } else if (os.os === 'Linux') { - return `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL_${version}.deb` + return `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL_${version}-${type}.deb` } return '#' } + const allLinks = [ { label: 'Windows 64-bit', - link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-x64-${version}.exe`, + link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-x64-${version}-obs.exe`, }, { label: 'Windows 32-bit', - link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-ia32-${version}.exe`, + link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL-win-ia32-${version}-obs.exe`, }, { label: 'Linux .deb', - link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL_${version}.deb`, + link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL_${version}-bible.deb`, }, { label: 'Linux AppImage', - link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL_${version}.AppImage`, + link: `https://github.com/hiscoder-com/level-desktop/releases/download/v${version}/LEVEL_${version}-bible.AppImage`, }, ] const isAvailableCurrentOs = availableOs.includes(os.os) @@ -99,9 +100,17 @@ function Download({ t, onClose }) {

{t('Download.p2')}

{isAvailableCurrentOs ? ( - - {t('Download.link')} - + <> + + {t('Download.linkObs')} + + + {t('Download.linkBible')} + + ) : ( allLinks.map((download) => ( { + console.error(errorMessage, error) + res.status(500).json({ error: 'Internal Server Error' }) + } + + switch (method) { + case 'GET': + try { + const { data: fileData, error: fileError } = supabaseService.storage + .from('obs-images') + .getPublicUrl('obs-images-360px.zip') + + if (fileError) { + console.error(`Error fetching URL for obs-images:`, fileError) + return null + } + + return res.status(200).json({ data: fileData?.publicUrl }) + } catch (error) { + return handleError(error, 'Server error:') + } + + default: + res.setHeader('Allow', ['GET']) + res.status(405).end(`Method ${method} Not Allowed`) + } +} diff --git a/public/locales/en/start-page.json b/public/locales/en/start-page.json index 5a4ec794..6bd02db4 100644 --- a/public/locales/en/start-page.json +++ b/public/locales/en/start-page.json @@ -11,7 +11,8 @@ "li2": "Run the file and follow the installation prompts.", "li3": "Once installed, open the program to explore the test project and familiarize yourself with the platform.", "p2": "We hope you enjoy using LEVEL! Stay tuned for updates and future releases for other operating systems.", - "link": "Download LEVEL Desktop" + "linkObs": "Download LEVEL Desktop with OBS project", + "linkBible": "Download LEVEL Desktop with Bible project" }, "Iagree": "I agree", "Idecline": "I decline", @@ -92,4 +93,4 @@ "text": "And this gospel of the kingdom will be preached in the whole world for a testimony to all the nations, and then the end will come." }, "YourMessageHasBeenSent": "Your message has been sent" -} +} \ No newline at end of file