Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 115 additions & 23 deletions components/Project/Download.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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' }]
}
Expand Down Expand Up @@ -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 = {}
Expand All @@ -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 = ` ${
Expand Down Expand Up @@ -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' })
Expand Down Expand Up @@ -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
}, {})
Expand All @@ -338,25 +371,69 @@ 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
zip.file(`${resource}.${url.split('.').pop()}`, content)
} 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)
}
}
}
Expand All @@ -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(
Expand All @@ -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')
}
Expand All @@ -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')
Expand Down
32 changes: 32 additions & 0 deletions pages/api/obs-images.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { supabaseService } from 'utils/supabaseService'

export default async function handler(req, res) {
const { method } = req

const handleError = (error, errorMessage) => {
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`)
}
}